]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge tag 'v2.0.25' into master.
authorPeter Powell <petpow@saberuk.com>
Sun, 12 Nov 2017 17:33:21 +0000 (17:33 +0000)
committerPeter Powell <petpow@saberuk.com>
Sun, 12 Nov 2017 17:33:21 +0000 (17:33 +0000)
579 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
docs/conf/filter.conf.example
docs/conf/helpop-full.conf.example
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
docs/conf/modules/unrealircd.conf.example
docs/conf/opers.conf.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]
extras/m_sqloper.mssql.sql [deleted file]
extras/m_sqloper.mysql.sql
extras/m_sqloper.postgresql.sql
extras/m_sqloper.sqlite3.sql
include/aligned_storage.h [new file with mode: 0644]
include/bancache.h
include/base.h
include/builtinmodes.h [new file with mode: 0644]
include/caller.h
include/channels.h
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/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/cap.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/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/ldap.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/spanningtree.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/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
include/threadengine.h
include/threadengines/threadengine_pthread.h
include/threadengines/threadengine_win32.h
include/timer.h
include/typedefs.h
include/uid.h
include/usermanager.h
include/users.h
include/xline.h
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/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/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/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_ison.cpp [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_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_privmsg.cpp [new file with mode: 0644]
src/coremods/core_reloadmodule.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_mode.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/core_user.cpp [new file with mode: 0644]
src/coremods/core_user/core_user.h [new file with mode: 0644]
src/coremods/core_userhost.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
src/modes/cmode_l.cpp
src/modes/cmode_o.cpp [deleted file]
src/modes/cmode_v.cpp [deleted file]
src/modes/umode_o.cpp
src/modes/umode_s.cpp
src/modmanager_dynamic.cpp
src/modmanager_static.cpp
src/modules.cpp
src/modules/account.h [deleted file]
src/modules/extra/m_geoip.cpp
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/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_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
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_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_globalload.cpp
src/modules/m_globops.cpp
src/modules/m_halfop.cpp [deleted file]
src/modules/m_helpop.cpp
src/modules/m_hidechans.cpp
src/modules/m_hidelist.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_capnotify.cpp [new file with mode: 0644]
src/modules/m_ircv3_chghost.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_joinflood.cpp
src/modules/m_jumpserver.cpp
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
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
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
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
win/.gitignore
win/CMakeLists.txt
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..6fdf33b
--- /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..e9d91b29bee6a7d3224c55b714c7b74f85b3752f 100644 (file)
@@ -2,31 +2,29 @@
 *.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_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
 
index bb82add9b681014f029101c3e75a69201a3d26d2..f5c1fff540ae41ce3def149ad2ddb03092fc8d0a 100644 (file)
@@ -3,8 +3,8 @@ compiler:
   - gcc
 dist: trusty
 env:
-  - PURE_STATIC=1
-  -
+  - TEST_BUILD_DYNAMIC=1
+  - TEST_BUILD_STATIC=1
 language: cpp
 notifications:
   email: false
index f3b3c3c32af37b99a08b4ff962bf6590ea5252f1..6e61106963590ab779cc11dbb4ee83226e46b63a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,3 +1,10 @@
+### Important Notice
+
+The `master` branch contains the latest development version. If you are running
+a server then you probably want the `insp20` branch. You can obtain this from
+the [releases](https://github.com/inspircd/inspircd/releases) page or by running
+`git checkout insp20` if you are installing via Git.
+
 ### About
 
 InspIRCd is a modular Internet Relay Chat (IRC) server written in C++ for Linux,
index 39968db6e6932954db2535c28606a2480602d9d8..04981f6fdcc186f3fef0397fd36526da3d0d252d 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,244 @@ 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_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') ? 'true' : 'false';
-print $config{HAS_EVENTFD} eq 'true' ? "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";
+       @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;
 }
+$config{GROUP} = $group[0];
+$config{GID}   = $group[2];
 
-################################################################################
-#                        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};
-               }
-       }
-       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";
-                               }
-                       }
+# 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;
+}
+$config{USER} = $user[0];
+$config{UID}  = $user[2];
+
+# Clear the screen.
+system 'tput', 'clear' if $interactive;
+
+# Warn the user about clock drifting when running on OpenVZ.
+if (-e '/proc/user_beancounters' || -e '/proc/vz/vzaquota') {
+       print_warning <<'EOW';
+You are building InspIRCd inside of an an OpenVZ container. If you
+plan to use InspIRCd in this container then you should make sure that NTP is
+configured on the Hardware Node. Failure to do so may result in clock drifting!
+EOW
+}
+
+# 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 http://www.inspircd.org/ or by
+running `git checkout insp20` if you are installing from Git.
+EOW
+       if (!prompt_bool $interactive, 'I understand this warning and want to continue anyway.', $opt_development // 0) {
+               say STDERR 'If you understand this warning and still want to continue pass the --development flag.' unless $interactive;
+               exit 1;
+       }
+}
+
+# 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
-       {
-               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";
-
-dumphash();
-
-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);
+} else {
+       # TODO: finish modulemanager rewrite and replace this code with:
+       # system './modulemanager', 'enable', '--auto';
+       enable_extras 'm_ssl_gnutls.cpp' unless system 'pkg-config --exists gnutls >/dev/null 2>&1';
+       enable_extras 'm_ssl_mbedtls.cpp' if -e '/usr/include/mbedtls/ssl.h';
+       enable_extras 'm_ssl_openssl.cpp' unless system 'pkg-config --exists openssl >/dev/null 2>&1';
 }
-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"));
-
-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";
-       }
+# Generate SSL certificates.
+if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, 'Would you like to generate SSL certificates now?', $interactive) {
+       system './tools/genssl', 'auto';
 }
 
-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";
-}
+# Cache the distribution label so that its not lost when --update is run.
+$config{DISTRIBUTION} = $opt_distribution_label if $opt_distribution_label;
 
-depcheck();
-writefiles(1);
-makecache();
+write_configure_cache %config;
+parse_templates \%config, \%compiler, \%version;
 
-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;32mhttps://wiki.inspircd.org/Installation_From_Tarball\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);
+for my $file (<src/modules/m_*>) {
+       my $module = basename $file, '.cpp';
+       say "  * $module" if -l $file;
 }
 
-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";
-       }
-}
+print_format <<"EOM";
 
-our $SHARED = "";
+<|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}
 
-my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
+<|GREEN Execution Group:|> $config{GROUP} ($config{GID})
+<|GREEN Execution User:|>  $config{USER} ($config{UID})
+<|GREEN Socket Engine:|>   $config{SOCKETENGINE}
 
-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
+To build with these settings run '<|GREEN make -j${\get_cpu_count}|>' now.
 
-/* 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} eq "true") {
-                       print FILEHANDLE "#define HAS_STRLCPY\n";
-               }
-               if ($config{HAS_STDINT} eq "true") {
-                       print FILEHANDLE "#define HAS_STDINT\n";
-               }
-               if ($config{HAS_EVENTFD} eq 'true') {
-                       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";
-                       }
-               }
-       }
-
-       # Write all .in files.
-       my $tmp = "";
-       my $file = "";
-       my $exe = "inspircd";
-
-       # 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 +413,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 +460,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 +496,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..aa5a34ba6ae8c473e9d15139bc9581801703d52d 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,7 +93,7 @@ EXCLUDE                =
 EXCLUDE_SYMLINKS       = YES
 EXCLUDE_PATTERNS       = */.git/* \
                          */doxygen/* \
-                         */commands/* \
+                         */coremods/* \
                          */modes/* \
                          */modules/*
 EXCLUDE_SYMBOLS        =
@@ -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">
-
index ea9e081478bb209505e0c26cdc296f6690ecb44e..23924d14b8f015e7cdf297bfdba798f157518fa9 100644 (file)
@@ -1,4 +1,4 @@
-# Configuration file for m_censor.so
+# Configuration file for the censor module
 
 # The tags for this module are formatted as follows:
 #
index 45e5d28537a54c9ca48916c785677971343172bf..ea62efd1f51a31c08b1a298bccb1c45769c05c88 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,7 +6,7 @@
 #               reason="reason for filtering"
 #               action="action to take"
 #               flags="filter flags"
-#              duration="optional length of gline">
+#               duration="optional length of gline">
 #
 # Valid actions for 'action' are:
 #
 # c: Strip color codes from text before trying to match
 # *: Represents all of the above flags
 # -: Does nothing, a no-op for when you do not want to specify any flags
-#
-# IMPORTANT NOTE: Because the InspIRCd config reader places special meaning on the
-# '\' character, you must use '\\' if you wish to specify a '\' character in a regular
-# expression. For example, to indicate numbers, use \\d and not \d. This does not
-# apply when adding a regular expression over irc with the /FILTER command.
 
-# Example filters for m_filter:
+# Example filters:
 #
 # <keyword pattern="*qwerty*" reason="You qwertied!" action="block" flags="pn">
 # <keyword pattern="*killmenow*" reason="As you request." action="kill" flags="*">
 # <keyword pattern="*blah*" reason="Dont blah!" action="gline" duration="1d6h" flags="-">
 
-# An example regexp filter for m_filter_pcre:
+# An example regexp filter:
 #
 # <keyword pattern="^blah.*?$" reason="Dont blah!" action="gline" duration="1d6h" flags="pnPq">
 
-# An example of excluding a channel from filtering:
-# <exemptfromfilter channel="#help">
+# 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">
+
+# Note that messages *from* services are never subject to filtering;
+# <exemptfromfilter> tags are only for exempting messages sent *to* the
+# configured targets.
index ed82c6e02d07e7750bd722576f86e1bef3ba09d8..883ec9b165f0a272effd4bc80125567923a23931 100644 (file)
@@ -28,20 +28,18 @@ you searched for. Please try again.">
 -------------
 
 PRIVMSG   NOTICE   NICK      JOIN      PART
-CYCLE     KNOCK    MODE      DEVOICE   TOPIC
+CYCLE     KNOCK    MODE      OPER      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
+LIST      NAMES    WHO       MOTD
 ADMIN     MAP      LINKS     LUSERS    TIME
 STATS     VERSION  INFO      MODULES   COMMANDS
 SSLINFO
 
-USER      PASS     PING     PONG       QUIT
-
-OPER">
+USER      PASS     PING     PONG       QUIT">
 
 <helpop key="sslinfo" value="/SSLINFO <nick>
 
@@ -102,22 +100,21 @@ This command accepts multiple nicks like so:
 
 Authenticate for a vhost using the specified username and password.">
 
-<helpop key="remove" value="/REMOVE <nick> <channel> [<reason>]
+<helpop key="remove" value="/REMOVE <channel> <nick> [<reason>]
 
 Removes a user from a channel you specify. You must be at least a
 channel halfoperator to remove a user. A removed user will part with
 a message stating they were removed from the channel and by whom.">
 
-<helpop key="fpart" value="/FPART <channel> <nick> [<reason>]
+<helpop key="rmode" value="/RMODE [channel] [modeletter] {[pattern]}
 
-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.">
+Removes listmodes from a channel.
+E.g. /RMODE #Chan b m:* will remove all mute extbans.">
 
-<helpop key="devoice" value="/DEVOICE <channel>
+<helpop key="fpart" value="/FPART <channel> <nick> [<reason>]
 
-Devoices yourself on the specified channel.">
+This behaves identically to /REMOVE. /REMOVE is a built-in mIRC command
+which caused trouble for some users.">
 
 <helpop key="silence" value="/SILENCE - Shows a list of silenced masks
 /SILENCE +<mask> [<flags>] - Add a mask
@@ -277,11 +274,6 @@ 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.
@@ -392,7 +384,7 @@ SAJOIN    SAPART    SAMODE      SATOPIC  SAKICK
 
 KILL      SAQUIT    GLINE       ZLINE    QLINE
 KLINE     RLINE     ELINE       CBAN     SHUN
-FILTER    OJOIN
+FILTER    OJOIN     CLEARCHAN
 
 CONNECT   SQUIT     RCONNECT    RSQUIT
 
@@ -412,7 +404,7 @@ This command returns the number of local and global clients matched,
 and the percentage of clients matched, plus how they were matched
 (by IP address or by hostname).">
 
-<helpop key="lockserv" value="/LOCKSERV
+<helpop key="lockserv" value="/LOCKSERV :[<message>]
 
 Locks out all new connections notifying connecting users that the
 service is temporarily closed and to try again later.">
@@ -538,13 +530,14 @@ 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>
+<helpop key="sajoin" value="/SAJOIN [<nick>] <channel>[,<channel>]
 
-Forces the user to join the 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>
+<helpop key="sapart" value="/SAPART <nick> <channel>[,<channel>]
 
-Forces the user to part the channel.">
+Forces the user to part the channel(s).">
 
 <helpop key="samode" value="/SAMODE <target> (+|-)<modes> [<parameters for modes>]
 
@@ -622,17 +615,15 @@ The duration may be specified in seconds, or in the format
 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
 5 minutes and 6 seconds. All fields in this format are optional.">
 
-<helpop key="die" value="/DIE <password>
+<helpop key="die" value="/DIE <server>
 
 This command shuts down the local server. A single parameter is
-required, which must match the password in the configuration for the
-command to function.">
+required, which must match the name of the local server.">
 
-<helpop key="restart" value="/RESTART <password>
+<helpop key="restart" value="/RESTART <server>
 
 This command restarts the local server. A single parameter is
-required, which must match the password in the configuration for the
-command to function.">
+required, which must match the name of the local server.">
 
 <helpop key="commands" value="/COMMANDS
 
@@ -663,30 +654,30 @@ Disconnects the server matching the given server mask from this server.">
 Lists currently loaded modules, their memory offsets, version numbers,
 and flags. If you are not an operator, you will see reduced detail.">
 
-<helpop key="loadmodule" value="/LOADMODULE <filename.so>
+<helpop key="loadmodule" value="/LOADMODULE <modname>
 
 Loads the specified module into the local server.">
 
-<helpop key="unloadmodule" value="/UNLOADMODULE <filename.so>
+<helpop key="unloadmodule" value="/UNLOADMODULE <modname>
 
 Unloads a module from the local server. The module cannot have the
 static flag set (see the output of /MODULES).">
 
-<helpop key="reloadmodule" value="/RELOADMODULE <filename.so>
+<helpop key="reloadmodule" value="/RELOADMODULE <modname>
 
 Unloads and reloads a module on the local server. This module cannot
 have the static flag set (see the output of /MODULES).">
 
-<helpop key="gloadmodule" value="/GLOADMODULE <filename.so>
+<helpop key="gloadmodule" value="/GLOADMODULE <modname>
 
 Loads the specified module on all linked servers.">
 
-<helpop key="gunloadmodule" value="/GUNLOADMODULE <filename.so>
+<helpop key="gunloadmodule" value="/GUNLOADMODULE <modname>
 
 Unloads a module from all linked servers. The module cannot have the
 static flag set (see the output of /MODULES).">
 
-<helpop key="greloadmodule" value="/GRELOADMODULE <filename.so>
+<helpop key="greloadmodule" value="/GRELOADMODULE <modname>
 
 Unloads and reloads a module on all linked servers. This module cannot
 have the static flag set (see the output of /MODULES).">
@@ -768,6 +759,16 @@ This command clears the DNS cache of the local server.">
 
 Closes all unregistered connections to the local server.">
 
+<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.
@@ -829,15 +830,15 @@ using their cloak when they quit.">
 
  v <nickname>       Gives voice to <nickname>, allowing them to speak
                     while the channel is +m.
- h <nickname>       Gives halfop status to <nickname> (this mode can
-                    be disabled).
+ h <nickname>       Gives halfop status to <nickname> (requires
+                    customprefix module).
  o <nickname>       Gives op status to <nickname>.
  a <nickname>       Gives protected status to <nickname>, preventing
                     them from them from being kicked (+q only,
-                    requires chanprotect module).
+                    requires customprefix module).
  q <nickname>       Gives owner status to <nickname>, preventing them
                     from being kicked (Services or only, requires
-                    chanprotect module).
+                    customprefix module).
 
  b <hostmask>       Bans <hostmask> from the channel.
  e <hostmask>       Excepts <hostmask> from bans (requires
@@ -894,6 +895,9 @@ using their cloak when they quit.">
                     module).
  D                  Delays join messages from users until they
                     message the channel (requires delayjoin module).
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similar messages.
+                    Kicks as default, blocks with ~ and bans with *
+                    The last two parameters are optional.
  F <changes>:<sec>  Blocks nick changes when they equal or exceed the
                     specified rate (requires nickflood module).
  G                  Censors messages to the channel based on the
@@ -1041,16 +1045,20 @@ Matching extbans:
 
  j:<channel>   Matches anyone in the given channel. Does not support
                wildcards (requires channelban module).
+ n:<class>     Matches users in a matching connect class (requires
+               classban module).
  r:<realname>  Matches users with a matching realname (requires gecosban
                module).
  s:<server>    Matches users on a matching server (requires serverban
                module).
- z:<certfp>    Matches users having the given SSL certificate
-               fingerprint (requires sslmodes module).
+ z:<certfp>    Matches users with a matching SSL certificate fingerprint
+               (requires sslmodes module)
  O:<opertype>  Matches IRCops of a matching type, mostly useful as an
                an invite exception (requires operchans module).
  R:<account>   Matches users logged into a matching account (requires
                services account module).
+ U:<banmask>   Matches unregistered users matching the given banmask.
+               (requires services account module).
 
 Acting extbans:
 
@@ -1074,8 +1082,6 @@ Acting extbans:
                users (requires stripcolor module).
  T:<banmask>   Blocks notices from matching users (requires nonotice
                module).
- U:<banmask>   Blocks unregistered users matching the given banmask.
-               (requires services account).
 
 A ban given to an Acting extban may either be a nick!user@host mask
 (unless stated otherwise), matched against users as for a normal ban,
index b4d3214f6e74e85af23096ad31f9ea7200b5e128..68328272ed1c5718cd22c52bea88d03aa3b94a44 100644 (file)
@@ -1,4 +1,4 @@
-# Sample configuration file for m_helpop.so
+# Sample configuration file for the helpop module.
 # You can either copy this into your conf folder and set up the module to use it,
 # or you can customize the responses for your network and/or add more.
 #
@@ -31,20 +31,18 @@ you searched for. Please try again.">
 -------------
 
 PRIVMSG   NOTICE   NICK      JOIN      PART
-CYCLE     KNOCK    MODE      DEVOICE   TOPIC
+CYCLE     KNOCK    MODE      OPER      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
+LIST      NAMES    WHO       MOTD
 ADMIN     MAP      LINKS     LUSERS    TIME
 STATS     VERSION  INFO      MODULES   COMMANDS
 SSLINFO
 
-USER      PASS     PING     PONG       QUIT
-
-OPER">
+USER      PASS     PING     PONG       QUIT">
 
 <helpop key="coper" value="Oper Commands
 -------------
@@ -62,7 +60,7 @@ SAJOIN    SAPART    SAMODE      SATOPIC  SAKICK
 
 KILL      SAQUIT    GLINE       ZLINE    QLINE
 KLINE     RLINE     ELINE       CBAN     SHUN
-FILTER
+FILTER    CLEARCHAN
 
 CONNECT   SQUIT     RCONNECT    RSQUIT
 
@@ -117,15 +115,15 @@ LOCKSERV       UNLOCKSERV">
 
  v <nickname>       Gives voice to <nickname>, allowing them to speak
                     while the channel is +m.
- h <nickname>       Gives halfop status to <nickname> (this mode can
-                    be disabled).
+ h <nickname>       Gives halfop status to <nickname> (requires
+                    customprefix module).
  o <nickname>       Gives op status to <nickname>.
  a <nickname>       Gives protected status to <nickname>, preventing
                     them from them from being kicked (+q only,
-                    requires chanprotect module).
+                    requires customprefix module).
  q <nickname>       Gives owner status to <nickname>, preventing them
                     from being kicked (Services or only, requires
-                    chanprotect module).
+                    customprefix module).
 
  b <hostmask>       Bans <hostmask> from the channel.
  e <hostmask>       Excepts <hostmask> from bans (requires
@@ -182,6 +180,9 @@ LOCKSERV       UNLOCKSERV">
                     module).
  D                  Delays join messages from users until they
                     message the channel (requires delayjoin module).
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similar messages.
+                    Kicks as default, blocks with ~ and bans with *
+                    The last two parameters are optional.
  F <changes>:<sec>  Blocks nick changes when they equal or exceed the
                     specified rate (requires nickflood module).
  G                  Censors messages to the channel based on the
@@ -278,6 +279,8 @@ Matching extbans:
 
  j:<channel>   Matches anyone in the given channel. Does not support
                wildcards (requires channelban module).
+ n:<class>     Matches users in a matching connect class (requires
+               classban module).
  r:<realname>  Matches users with a matching realname (requires gecosban
                module).
  s:<server>    Matches users on a matching server (requires serverban
@@ -288,6 +291,8 @@ Matching extbans:
                an invite exception (requires operchans module).
  R:<account>   Matches users logged into a matching account (requires
                services account module).
+ U:<banmask>   Matches unregistered users matching the given banmask.
+               (requires services account module).
 
 Acting extbans:
 
@@ -311,8 +316,6 @@ Acting extbans:
                users (requires stripcolor module).
  T:<banmask>   Blocks notices from matching users (requires nonotice
                module).
- U:<banmask>   Blocks unregistered users matching the given banmask.
-               (requires services account).
 
 A ban given to an Acting extban may either be a nick!user@host mask
 (unless stated otherwise), matched against users as for a normal ban,
index 8ec6616bd5dd82d68afb8819286f5a5c4d2cd2a9..61d1a8c122194e5d5380eafddf550ace06cb06f2 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
+      # wiki page for the SSL module you are using for more details.
+      #
+      # You will need to load the ssl_openssl module for OpenSSL, ssl_gnutls
+      # for GnuTLS and ssl_mbedtls for mbedTLS.
       ssl="gnutls"
+
+      # defer: When this is non-zero, connections will not be handed over to
+      # 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">
+
+# You can define a custom <sslprofile> tag which defines the SSL configuration
+# for this listener. See the wiki page for the SSL module you are using for
+# more details.
+#
+# Alternatively, you can use one of the default SSL profiles which are created
+# when you have not defined any:
+#   "openssl" (requires the ssl_openssl module)
+#   "gnutls" (requires the ssl_gnutls module)
+#   "mbedtls" (requires the ssl_mbedtls module)
+#
+# When linking servers, the OpenSSL, GnuTLS, and mbedTLS implementations are
+# completely link-compatible and can be used alongside each other on each end
+# of the link without any significant issues.
 
 <bind address="" port="7000,7001" type="servers">
 <bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
 
 
-#-#-#-#-#-#-#-#-#-#-  DIE/RESTART CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-
-#                                                                     #
-#   You can configure the passwords here which you wish to use for    #
-#   the /DIE and /RESTART commands. Only trusted 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="">
-
-
 #-#-#-#-#-#-#-#-#-#-  CONNECTIONS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 #   This is where you can configure which connections are allowed     #
          # allow: What IP addresses/hosts to allow for this block.
          allow="203.0.113.*"
 
-         # hash: what hash this password is hashed with. requires the module
-         # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
-         # loaded and the password hashing module (m_password_hash.so)
-         # loaded. Options here are: "md5", "sha256" and "ripemd160".
-         # Optional, but recommended. Create hashed passwords with:
-         # /mkpasswd <hash> <password>
-         #hash="sha256"
+         # hash: the hash function this password is hashed with. Requires the
+         # module for the selected function (bcrypt, md5, sha1, sha256, or
+         # ripemd160) and the password hashing module (password_hash) to be
+         # loaded.
+         # You may also use any of the above other than bcrypt prefixed with
+         # either "hmac-" or "pbkdf2-hmac-" (requires the pbkdf2 module).
+         # Create hashed passwords with: /mkpasswd <hash> <password>
+         #hash="bcrypt"
 
          # password: Password to use for this block/user(s)
          password="secret"
 
          # 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)
          timeout="10"
          # 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.
+         # Enabling this option requires that the conn_umodes module be loaded.
          # This entry is highly recommended to use for/with IP Cloaking/masking.
-         # For the example to work, this also requires that the m_cloaking
+         # For the example to work, this also requires that the "cloaking"
          # module be loaded as well.
          modes="+x"
 
          # requireident, requiressl, requireaccount: require that users of this
          # block have a valid ident response, use SSL, or have authenticated.
-         # Requires m_ident, m_sslinfo, or m_services_account respectively.
+         # Requires ident, sslinfo, or the services_account module, respectively.
          requiressl="on"
          # NOTE: For requireaccount, you must complete the signon prior to full
          # connection. Currently, this is only possible by using SASL
          #   \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)
          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
          # 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="5000"
 
          # modes: Usermodes that are set on users in this block on connect.
-         # Enabling this option requires that the m_conn_umodes module be loaded.
+         # Enabling this option requires that the conn_umodes module be loaded.
          # This entry is highly recommended to use for/with IP Cloaking/masking.
-         # For the example to work, this also requires that the m_cloaking
+         # For the example to work, this also requires that the cloaking
          # module be loaded as well.
          modes="+x">
 
 
 # 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 - http://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 - http://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
 # matches the channels name applies the banlimit 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.                   #
+# matched, the banlist size defaults to 100 entries.                  #
 #                                                                     #
 
-<banlist chan="#largechan" limit="128">
-<banlist chan="*" limit="69">
+<banlist chan="#largechan" limit="200">
+<banlist chan="*" limit="100">
 
 #-#-#-#-#-#-#-#-#-#-#-  DISABLED FEATURES  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
          # 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"
 
          # 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."
 
          # exemptchanops: exemptions for channel access restrictions based on prefix.
          exemptchanops="nonick:v flood:o"
 
          # 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"
+
              # 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.
           hideulines="no"
           # (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 channels a user is in. Values:
 
 <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"
         # 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">
+#  <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.
 #
 #  - 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   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
          nick="ChanServ"
 
          # reason: Reason to display on /nick.
-         reason="Reserved For Services">
-
-<badnick nick="NickServ" reason="Reserved For Services">
-<badnick nick="OperServ" reason="Reserved For Services">
-<badnick nick="MemoServ" reason="Reserved For Services">
+         reason="Reserved for a network service">
 
 <badhost
          # host: ident@hostname to ban.
 # 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
 # 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">
+#<include file="examples/modules/unrealircd.conf.example">
 
 # Settings similar to Charybdis IRCd defaults.
-#<include file="conf/examples/modules/charybdis.conf.example">
+#<include file="examples/modules/charybdis.conf.example">
 
+#-#-#-#-#-#-#-#-#-#-#-# SERVICES CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
+#                                                                     #
+# If you use services you will probably want to include one of the    #
+# following files which set up aliases, nick reservations and filter  #
+# exemptions for services pseudoclients:                              #
+#
+# Anope users should uncomment this:
+#<include file="examples/services/anope.conf.example">
+#
+# Atheme users should uncomment this:
+#<include file="examples/services/atheme.conf.example">
+#
+# Users of other services should uncomment this:
+#<include file="examples/services/generic.conf.example">
 
 #########################################################################
 #                                                                       #
index ba18c5325205ff46150a2f80709f74e1c46f060d..e11c4fe32a9ed2875d888d786235be4162aa0d71 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 wiki page for the SSL
+      # module you are using for more details.
       #
-      # You will need to load the m_ssl_openssl.so module for OpenSSL,
-      # m_ssl_gnutls.so for GnuTLS. The server port that you connect to
-      # must be capable of accepting this type of connection.
+      # You will need to load the ssl_openssl module for OpenSSL, ssl_gnutls
+      # for GnuTLS and ssl_mbedtls for mbedTLS. The server port that you
+      # connect to must be capable of accepting this type of connection.
       ssl="gnutls"
 
       # fingerprint: If defined, this option will force servers to be
-      # authenticated using SSL Fingerprints. See https://wiki.inspircd.org/SSL
-      # for more information. This will require an SSL link for both inbound
-      # and outbound connections.
+      # authenticated using SSL certificate fingerprints. See
+      # https://wiki.inspircd.org/SSL for more information. This will
+      # require an SSL link for both inbound and outbound connections.
       #fingerprint=""
 
       # bind: Local IP address to bind to.
@@ -75,7 +77,7 @@
       ipaddr="penguin.example.org"
       port="7000"
       allowmask="203.0.113.0/24"
-      timeout="300"
+      timeout="5m"
       ssl="gnutls"
       bind="1.2.3.4"
       statshidden="no"
 # Simple autoconnect block. This enables automatic connection of a server
 # Recommended setup is to have leaves connect to the hub, and have no
 # automatic connections started by the hub.
-<autoconnect period="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">
 
 
index b672367f967e93bc85906f093e55361ad548a251..cf79dafce3b538f5fd8d9a667e5bfaa7b6cb7739 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://wiki.inspircd.org/2.0/Modules for a list of modules and    #
+#  https://wiki.inspircd.org/3.0/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">
+#<module name="sha256">
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # RIPEMD160 module: Allows other modules to generate RIPEMD160 hashes,
 # usually for cryptographic uses and security.
 #
 # IMPORTANT:
 # Other modules may rely on this module being loaded to function.
-#<module name="m_ripemd160.so">
+#<module name="ripemd160">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Abbreviation module: Provides the ability to abbreviate commands a-la
 # BBC BASIC keywords.
-#<module name="m_abbreviation.so">
+#<module name="abbreviation">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Alias module: Allows you to define server-side command aliases.
-#<module name="m_alias.so">
+#<module name="alias">
 #
 # Set the 'prefix' for in-channel aliases (fantasy commands) to the
 # specified character. If not set, the default is "!".
@@ -72,9 +72,9 @@
 #
 #-#-#-#-#-#-#-#-#-#-#-  ALIAS DEFINITIONS  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# If you have the m_alias.so module loaded, you may also define       #
-# aliases as shown below. They are commonly used to provide shortcut  #
-# commands to services, however they are not limited to just this use.#
+# If you have the alias module loaded, you may also define aliases as #
+# shown below. They are commonly used to provide shortcut commands to #
+# services, however they are not limited to just this use.            #
 # An alias tag requires the following values to be defined in it:     #
 #                                                                     #
 # text        -      The text to detect as the actual command line.   #
 #                    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.
 #
 # This alias fixes a glitch in xchat 2.6.x and above and the way it
 # assumes IDENTIFY must be prefixed by a colon (:) character. It should
-# be placed ABOVE the default NICKSERV alias (the first example) listed
-# above.
+# be placed ABOVE the default NICKSERV alias.
 #
 #<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
 #  requires="NickServ" uline="yes">
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Allowinvite module: Gives channel mode +A to allow all users to use
 # /INVITE, and extban A to deny invite from specific masks.
-#<module name="m_allowinvite.so">
+#<module name="allowinvite">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Alltime module: Shows time on all connected servers at once.
 # This module is oper-only and provides /ALLTIME.
 # To use, ALLTIME must be in one of your oper class blocks.
-#<module name="m_alltime.so">
+#<module name="alltime">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Auditorium module: Adds channel mode +u which makes everyone else
 # except you in the channel invisible, used for large meetings etc.
-#<module name="m_auditorium.so">
+#<module name="auditorium">
 #
 # Auditorium settings:
 #
 # 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">
+#<module name="blockcaps">
 #
 #-#-#-#-#-#-#-#-#-#-#-  BLOCKCAPS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# percent        - How many percent of text must be caps before text  #
-#                  will be blocked.                                   #
+# percent        - What percentage of the text must be caps before    #
+#                  text will be blocked.                              #
 #                                                                     #
 # minlen         - The minimum length a line must be for the block    #
 #                  percent to have any effect.                        #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Block color module: Blocking color-coded messages with chan mode +c.
-#<module name="m_blockcolor.so">
+#<module name="blockcolor">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Botmode module: Adds the user mode +B. If set on a user, it will
 # show that the user is a bot in /WHOIS.
-#<module name="m_botmode.so">
+#<module name="botmode">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # CallerID module: Adds usermode +g which activates hybrid-style
 # callerid: block all private messages unless you /ACCEPT first.
-#<module name="m_callerid.so">
+#<module name="callerid">
 #
 #-#-#-#-#-#-#-#-#-#-#- CALLERID  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
 # maxaccepts     - Maximum number of entries a user can add to his    #
 # 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).         #
+# cooldown       - Amount of time that must pass since the last       #
+#                  notification sent to a user before he can be sent  #
+#                  another. Default is 1 minute.                      #
 #<callerid maxaccepts="16"
 #          operoverride="no"
 #          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">
+#<module name="censor">
 #
 #-#-#-#-#-#-#-#-#-#-#-  CENSOR  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# Optional - If you specify to use the m_censor module, then you must #
+# Optional - If you specify to use the censor module, then you must   #
 # specify some censor tags. See also:                                 #
-# https://wiki.inspircd.org/Modules/2.0/censor                        #
+# https://wiki.inspircd.org/Modules/3.0/censor                        #
 #
-#<include file="conf/examples/censor.conf.example">
+#<include file="examples/censor.conf.example">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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://wiki.inspircd.org/Modules/2.0/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 http://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.
+#
+# <cgihost type="ident" mask="198.51.100.0/24">
+# <cgihost type="ident" mask="*.ident.gateway.example.com">
+#
+# 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.
 # 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.
-#<chanhistory maxlines="20" notice="yes">
+# If bots is set to yes, it will also send to users marked with +B
+#<chanhistory maxlines="20" 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
 # 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">
-
+#<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.
+#<module name="classban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
+# 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
+# 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.                                   #
 #                                                                     #
-# 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.                     #
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #
 #<cloak mode="half"
 #       key="secret"
+#       domainparts="3"
 #       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">
+#<module name="close">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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
 # (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.
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Deaf module: Adds support for the usermode +d - deaf to channel
 # messages and channel notices.
-#<module name="m_deaf.so">
+#<module name="deaf">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Delay join module: Adds the channel mode +D which delays all JOIN
 # 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.   #
 #<goodchan name="#funtimes">                                          #
 # Glob masks are accepted here also.                                  #
 
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Devoice module: Let users devoice themselves using /DEVOICE #chan.
-#<module name="m_devoice.so">
-
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # DNS blacklist module: Provides support for looking up IPs on one or #
 # more blacklists.                                                    #
-#<module name="m_dnsbl.so">                                           #
+#<module name="dnsbl">                                                #
 #                                                                     #
-# For configuration options please see the wiki page for m_dnsbl at   #
-# https://wiki.inspircd.org/Modules/2.0/dnsbl                         #
+# For configuration options please see the wiki page for dnsbl at     #
+# https://wiki.inspircd.org/Modules/3.0/dnsbl                         #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Exempt channel operators module: Provides support for allowing      #
 # channel operators to be exempt from some channel modes.  Supported  #
 # modes are blockcaps, noctcp, blockcolor, nickflood, flood, censor,  #
 # filter, regmoderated, nonick, nonotice, and stripcolor.             #
-#<module name="m_exemptchanops.so">                                   #
+#<module name="exemptchanops">                                        #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Filter module: Provides message filtering, similar to SPAMFILTER.   #
-#<module name="m_filter.so">
+#<module name="filter">
 #                                                                     #
-# This module depends upon a regex provider such as m_regex_pcre or   #
-# m_regex_glob to function. You must specify which of these you want  #
-# m_filter to use via the tag below.                                  #
+# This module depends upon a regex provider such as regex_pcre or     #
+# regex_glob to function. You must specify which of these you want    #
+# the filter module to use via the tag below.                         #
 #                                                                     #
 # Valid engines are:                                                  #
 #                                                                     #
-# glob   - Glob patterns, provided via m_regex_glob.                  #
-# 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">                                           #
 #                                                                     #
 # 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">
+# 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.                            #
 #
 #-#-#-#-#-#-#-#-#-#-#-  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.                                                      #
 #                                                                     #
-#<include file="conf/examples/filter.conf.example">
+#<include file="examples/filter.conf.example">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">                                         #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Gecos ban: Implements extended ban 'r', which stops anyone matching
 # a mask like +b r:*realname?here* from joining a channel.
-#<module name="m_gecosban.so">
+#<module name="gecosban">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # GeoIP module: Allows the server admin to match users by country code.
 # This module requires GeoIP to be installed on your system,
 # use your package manager to find the appropriate packages
 # or check the InspIRCd wiki page for this module.
-#<module name="m_geoip.so">
+#<module name="geoip">
 #
 # The actual allow/ban actions are done by connect classes, not by the
 # GeoIP module. An example connect class to ban people from russia or
 # 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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
+#<module name="globalload">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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/inspircd.helpop-full.example">
+# If you specify to use the helpop module, then specify below the     #
+# path to the helpop.conf file.                                       #
+#                                                                     #
+#<include file="examples/inspircd.helpop-full.example">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Hide chans module: Allows users to hide their channels list from non-
 # opers by setting user mode +I on themselves.
-#<module name="m_hidechans.so">
+#<module name="hidechans">
 #
 # This mode can optionally prevent opers from seeing channels on a +I
 # user, for more privacy if set to true.
 # 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 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://wiki.inspircd.org/Modules/2.0/hostchange for help.      #
+# See https://wiki.inspircd.org/Modules/3.0/hostchange for help.      #
 #                                                                     #
 #<host suffix="invalid.org" separator="." prefix="">
 #<hostchange mask="*@42.theanswer.example.org" action="addnick">
 #<hostchange mask="a@example.com" action="set" value="foo.bar.baz">
 #<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
 
+# hostcycle: If loaded, when a user gets a host or ident set, it will
+# cycle them in all their channels. If not loaded it will simply change
+# their host/ident without cycling them.
+# This module is compatible with the ircv3_chghost module. Clients
+# supporting the chghost extension will get the chghost message instead
+# of seeing a host cycle.
+#<module name="hostcycle">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # httpd module: Provides HTTP server support for InspIRCd.
-#<module name="m_httpd.so">
+#<module name="httpd">
 #
 #-#-#-#-#-#-#-#-#-#-#-#-  HTTPD   CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
 #
-# If you choose to use the m_httpd.so module, then you will need to add
+# If you choose to use the httpd module, then you will need to add
 # a <bind> tag with type "httpd", and load at least one of the other
-# m_httpd_* modules to provide pages to display.
+# httpd_* modules to provide pages to display.
 #
 # You can adjust the timeout for HTTP connections below. All HTTP
-# connections will be closed after (roughly) this many seconds.
+# 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 configuration of the server to be
-# viewed over HTTP. Requires m_httpd.so to be loaded for it to function.
-#<module name="m_httpd_config.so">
+# viewed over HTTP. Requires httpd to be loaded for it to function.
+#<module name="httpd_config">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # HTTP stats module: Provides basic stats pages over HTTP.
-# Requires m_httpd.so to be loaded for it to function.
-#<module name="m_httpd_stats.so">
+# Requires httpd to be loaded for it to function.
+#<module name="httpd_stats">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Ident: Provides RFC 1413 ident lookup support.
 # When this module is loaded <connect:allow> tags may have an optional
 # useident="yes|no" boolean value, determining whether or not to lookup
 # ident on users matching that connect tag.
-#<module name="m_ident.so">
+#<module name="ident">
 #
 #-#-#-#-#-#-#-#-#-#-#-#-   IDENT CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# Optional - If you are using the m_ident.so module, then you can     #
-# specify the timeout for ident lookups here. If not defined, it will #
-# default to 5 seconds. This is a non-blocking timeout which holds    #
-# the user in a 'connecting' state until the lookup is complete.      #
+# Optional - If you are using the ident module, then you can specify  #
+# the timeout for ident lookups here. If not defined, it will default #
+# to 5 seconds. This is a non-blocking timeout which holds the user   #
+# in a 'connecting' state until the lookup is complete.               #
 # The bind value indicates which IP to bind outbound requests to.     #
+# nolookupprefix: If on, the idents of users being in a connect class #
+# with ident lookups disabled (i.e. <connect useident="off">) will be #
+# prefixed with a "~". If off, the ident of those users will not be   #
+# prefixed. Default is off.                                           #
 #
-#<ident timeout="5">
+#<ident timeout="5" nolookupprefix="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/
 #
-#<module name="m_ircv3.so">
+#<module name="ircv3">
 # The following block can be used to control which extensions are
-# enabled. Note that extended-join can be incompatible with m_delayjoin
+# enabled. Note that extended-join can be incompatible with delayjoin
 # and host cycling.
 #<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 cap-notify module: Provides the cap-notify IRCv3.2 extension.
+# Required for IRCv3.2 conformance.
+#<module name="ircv3_capnotify">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 chghost module: Provides the chghost IRCv3.2 extension which
+# allows capable clients to learn when the host/ident of another user
+# changes without cycling the user. This module is compatible with the
+# hostcycle module. If both are loaded, clients supporting the chghost
+# extension will get the chghost message and won't see host cycling.
+#<module name="ircv3_chghost">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 echo-message module: Provides the echo-message IRCv3.2
+# extension which allows capable clients to get an acknowledgement when
+# their messages are delivered and learn what modifications, if any,
+# were applied to them.
+#<module name="ircv3_echomessage">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 invite-notify module: Provides the invite-notify IRCv3.2
+# extension which notifies supporting clients when a user invites
+# another user into a channel. This respects <options:announceinvites>.
+#<module name="ircv3_invitenotify">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Join flood module: Adds support for join flood protection +j X:Y.
-# Closes the channel for 60 seconds if X users join in Y seconds.
-#<module name="m_joinflood.so">
+# Closes the channel for N seconds if X users join in Y seconds.
+#<module name="joinflood">
+#
+# The number of seconds to close the channel for:
+#<joinflood duration="1m">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Jump server module: Adds support for the RPL_REDIR numeric.
 # To use, JUMPSERVER must be in one of your oper class blocks.
 # If your server is redirecting new clients and you get disconnected,
 # do a REHASH from shell to open up again.
-#<module name="m_jumpserver.so">
+#<module name="jumpserver">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Anti auto rejoin: Adds support for prevention of auto-rejoin (+J).
-#<module name="m_kicknorejoin.so">
-# 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   #
 # 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 wiki for more   #
-# info https://wiki.inspircd.org/Modules/2.0/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 wiki for    #
-# more: https://wiki.inspircd.org/Modules/2.0/mysql                   #
+# mysql is more complex than described here, see the wiki for more    #
+# info: https://wiki.inspircd.org/Modules/3.0/mysql                   #
 #
 #<database module="mysql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database2">
 
 # modes via long-form mode names via +Z and the /PROP command.
 # For example, to set a ban, do /mode #channel +Z ban=foo!bar@baz or
 # /PROP #channel ban=foo!bar@baz
-#<module name="m_namedmodes.so">
+#<module name="namedmodes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # NAMESX module: Provides support for the NAMESX extension which allows
 # clients to see all the prefixes set on a user without getting confused.
 # This is supported by mIRC, x-chat, klient, and maybe more.
-#<module name="m_namesx.so">
+#<module name="namesx">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # National characters module:
 # 1) Allows using national characters in nicknames.
 # 2) Allows using custom (national) casemapping over the network.
-#<module name="m_nationalchars.so">
+#<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">
+#<module name="noctcp">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # No kicks module: Adds the +Q channel mode and the Q: extban to deny
 # certain users from kicking.
-#<module name="m_nokicks.so">
+#<module name="nokicks">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # No nicks module: Adds the +N channel mode, as well as the 'N' extban.
 # +N stops all users from changing their nick, the N extban stops
 # anyone from matching a +b N:nick!user@host mask from changing their
 # nick.
-#<module name="m_nonicks.so">
+#<module name="nonicks">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # No part message module: Adds extban 'p' to block part messages from #
 # matching users.                                                     #
-#<module name="m_nopartmsg.so">
+#<module name="nopartmsg">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # No notice module: Adds the channel mode +T and the extban 'T' to
 # block specific users from noticing the channel.
-#<module name="m_nonotice.so">
+#<module name="nonotice">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Network business join module:
 # Allows an oper to join a channel using /OJOIN, giving them +Y on the
 # channel which makes them immune to kick/deop/etc.
-#<module name="m_ojoin.so">
+#<module name="ojoin">
 #
 # Specify the prefix that +Y will grant here.
 # Leave 'prefix' empty if you do not wish +Y to grant a prefix.
 # /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.
 #
 # Load this module if you want all your IRC operators to have channel
 # operator powers.
-#<module name="m_operprefix.so">
+#<module name="operprefix">
 #
 # You may additionally customise the prefix character.
 #<operprefix prefix="!">
 # Oper MOTD module: Provides support for separate message of the day
 # on oper-up.
 # This module is oper-only.
-#<module name="m_opermotd.so">
+#<module name="opermotd">
 #
 #-#-#-#-#-#-#-#-#-#-#   OPERMOTD CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# If you are using the m_opermotd.so module, specify the motd here.   #
+# If you are using the opermotd module, specify the motd here.   #
 #                                                                     #
 # onoper        - If on, the message is sent on /OPER, otherwise it's #
 #                 only sent when /OPERMOTD is used.                   #
 #                 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" processcolors="false">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Override module: Adds support for oper override.
 # This module is oper-only.
-#<module name="m_override.so">
+#<module name="override">
 #
 #-#-#-#-#-#-#-#-#-#-#   OVERRIDE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_override.so is too complex it describe here, see the wiki:        #
-# https://wiki.inspircd.org/Modules/2.0/override                      #
+# override is too complex it describe here, see the wiki:             #
+# http://wiki.inspircd.org/Modules/override                           #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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.
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 wiki for    #
-# more: https://wiki.inspircd.org/Modules/2.0/pgsql                   #
+# pgsql is more complex than described here, see the wiki for         #
+# more: https://wiki.inspircd.org/Modules/3.0/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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
 
 # This also breaks linking to servers that do not have the option.    #
 # This defaults to false for the 2.0 version, it will be enabled in   #
 # all the future versions.                                            #
-#<module name="m_redirect.so">
+#<module name="redirect">
 #<redirect antiredirect="true">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for glob or wildcard (?/*) matching.
-# You must have at least 1 provider loaded to use m_filter or m_rline
+# You must have at least 1 provider loaded to use the filter or rline
 # modules. This module has no additional requirements, as it uses the
 # matching already present in InspIRCd core.
-#<module name="m_regex_glob.so">
+#<module name="regex_glob">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for PCRE (Perl-Compatible Regular
 # Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter or
+# rline modules.
+#<module name="regex_pcre">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Regular Expression Provider for RE2 Regular Expressions.
+# You need libre2 installed and in your include/library paths in order
+# to compile and load this module.
+#<module name="regex_re2">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for POSIX regular expressions.
 # You shouldn't need any additional libraries on a POSIX-compatible
 # system (i.e.: any Linux, BSD, but not Windows). You must have at
-# least 1 provider loaded to use m_filter or m_rline.
+# least 1 provider loaded to use filter or rline.
 # On POSIX-compliant systems, regex syntax can be found by using the
 # command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for C++11 std::regex regular expressions.
 # 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
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_regonlycreate.so">
+#<module name="regonlycreate">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Remove module: Adds the /REMOVE command which is a peaceful
 # alternative to /KICK.
-#<module name="m_remove.so">
+#<module name="remove">
+#
+# supportnokicks: If true, /REMOVE is not allowed on channels where the
+# nokicks (+Q) mode is set. Defaults to false.
+# protectedrank: Members having this rank or above may not be /REMOVE'd
+# by anyone. Set to 0 to disable this feature. Defaults to 50000.
+#<remove supportnokicks="true" protectedrank="50000">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# A module to block, kick or ban upon similar messages being uttered several times.
+# Syntax [~*][lines]:[sec]{[:difference]}{[:matchlines]}
+# ~ is to block, * is to ban, default is kick.
+# lines - In mode 1 the amount of lines that has to match consecutively - In mode 2 the size of the backlog to keep for matching
+# seconds - How old the message has to be before it's invalidated.
+# distance - Edit distance, in percent, between two strings to trigger on.
+# matchlines - 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" maxlines="20" maxdistance="50" maxtime="0" size="512">
+#<module name="repeat">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Restricted channels module: Allows only opers to create channels.
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_restrictchans.so">
+#<module name="restrictchans">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Restrict message module: Allows users to only message opers.
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_restrictmsg.so">
-#
-# 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">
+#<module name="rline">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
 #
 # Also, this is where you set what Regular Expression engine is to be
 # used. If you ever change it while running, all of your R-Lines will
 # be wiped. This is the regex engine used by all R-Lines set, and
-# m_regex_<engine>.so must be loaded, or rline will be non-functional
+# regex_<engine> must be loaded, or rline will be non-functional
 # until you load it or change the engine to one that is loaded.
 #
 #<rline matchonnickchange="yes" engine="pcre">
 # 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 mods to remove list modes en masse.
+# Syntax: /rmode <channel> <mode> [pattern]
+# E.g. '/rmode #Channel b m:*' will remove all mute-extbans on the channel.
+#<module name="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.
 # +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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Serverban: Implements extended ban 's', which stops anyone connected
 # to a server matching a mask like +b s:server.mask.here from joining.
-#<module name="m_serverban.so">
+#<module name="serverban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SHA1 module: Allows other modules to generate SHA1 hashes.
+# Required by the WebSocket module.
+#<module name="sha1">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Showfile: Provides support for showing a text file to users when    #
+# they enter a command.                                               #
+# This module adds one command for each <showfile> tag that shows the #
+# given file to the user as a series of messages or numerics.         #
+#<module name="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. #
+# colors  - If true, color codes (\c, \b, \u, etc.) will be processed #
+#           and sent as ANSI colors. If false (default) the file will #
+#           be displayed as-is.                                       #
+#                                                                     #
+# 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"
+#          colors="true"
+#          introtext="Server rules:"
+#          endtext="End of server rules.">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Show whois module: Adds the +W usermode which allows opers to see
 # when they are /WHOIS'd.
 # This module is oper-only by default.
-#<module name="m_showwhois.so">
+#<module name="showwhois">
 #
 # If you wish, you may also let users set this mode. Only opers with the
-# users/auspex priv will see real hosts of people, though. 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
 # channel mode +z 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
 # if enabled. You must answer 'yes' in ./configure when asked or
 # manually symlink the source for this module from the directory
 # src/modules/extra, if you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+#<module name="ssl_gnutls">
 #
 #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
-# https://wiki.inspircd.org/Modules/2.0/ssl_gnutls                    #
+# ssl_gnutls is too complex to describe here, see the wiki:           #
+# https://wiki.inspircd.org/Modules/3.0/ssl_gnutls                    #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SSL info module: Allows users to retrieve information about other
 # users' peer SSL certificates and keys. This can be used by client
-# scripts to validate users. For this to work, one of m_ssl_gnutls.so
-# or m_ssl_openssl.so must be loaded. This module also adds the
+# scripts to validate users. For this to work, one of ssl_gnutls
+# or ssl_openssl must be loaded. This module also adds the
 # "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the
+# opers to use SSL cert fingerprints to verify their identity and the
 # ability to force opers to use SSL connections in order to oper up.
 # It is highly recommended to load this module if you use SSL on your
 # network.
 # For how to use the oper features, please see the first example <oper> tag
 # in opers.conf.example.
 #
-#<module name="m_sslinfo.so">
+#<module name="sslinfo">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# mbedTLS SSL module: Adds support for SSL/TLS connections using mbedTLS.
+#<module name="ssl_mbedtls">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
 #
 #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
-# https://wiki.inspircd.org/Modules/2.0/ssl_openssl                   #
+# ssl_openssl is too complex to describe here, see the wiki:          #
+# https://wiki.inspircd.org/Modules/3.0/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 wiki for   #
-# more: https://wiki.inspircd.org/Modules/2.0/sqlite3                 #
+# sqlite is more complex than described here, see the wiki for more   #
+# info: https://wiki.inspircd.org/Modules/3.0/sqlite3                 #
 #
 #<database module="sqlite" hostname="/full/path/to/database.db" id="anytext">
 
 # ./configure --enable-extras=m_sqlauth.cpp
 # and run make install, then uncomment this module to enable it.
 #
-#<module name="m_sqlauth.so">
+#<module name="sqlauth">
 #
 #-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_sqlauth.so is too complex to describe here, see the wiki:         #
-# https://wiki.inspircd.org/Modules/2.0/sqlauth                       #
+# sqlauth is too complex to describe here, see the wiki:              #
+# https://wiki.inspircd.org/Modules/3.0/sqlauth                       #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SQL oper module: Allows you to store oper credentials in an SQL table
 # ./configure --enable-extras=m_sqloper.cpp
 # and run make install, then uncomment this module to enable it.
 #
-#<module name="m_sqloper.so">
+#<module name="sqloper">
 #
 #-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # dbid       - Database ID to use (see SQL modules).                  #
 # hash       - Hashing provider to use for password hashing.          #
 #                                                                     #
-# See also: https://wiki.inspircd.org/Modules/2.0/sqloper             #
+# See also: https://wiki.inspircd.org/Modules/3.0/sqloper             #
 #                                                                     #
-#<sqloper dbid="1" hash="md5">
+#<sqloper dbid="1" hash="bcrypt">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# StartTLS module: Implements STARTTLS, which allows clients          #
+# connected to non SSL enabled ports to enable SSL, if a proper SSL   #
+# module is loaded (either ssl_gnutls or ssl_openssl).                #
+#<module name="starttls">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be   #
 # added/removed by Services.                                          #
-#<module name="m_svshold.so">
-# 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">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# WebSocket module: Adds HTML5 WebSocket support.
+# Specify hook="websocket" in a <bind> tag to make that port accept
+# WebSocket connections. Compatible with SSL/TLS.
+# Requires SHA-1 hash support available in the sha1 module.
+#<module name="websocket">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # XLine database: Stores all *Lines (G/Z/K/R/any added by other modules)
 # in a file which is re-loaded on restart. This is useful
 # for two reasons: it keeps bans so users may not evade them, and on
 # bigger networks, server connections will take less time as there will
 # be a lot less bans to apply - as most of them will already be there.
-#<module name="m_xline_db.so">
+#<module name="xline_db">
 
 # Specify the filename for the xline database here.
-#<xlinedb filename="data/xline.db">
+#<xlinedb filename="xline.db">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #    ____                _   _____ _     _       ____  _ _   _        #
 #   |  _ <  __/ (_| | (_| |   | | | | | | \__ \ | |_) | | |_|_|       #
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
-# 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">
index 91260c7d3133979923736996ce05c3d7d02d789a..97436c54891a0e2bebe1267bdbc55ca3c0275519 100644 (file)
@@ -1,6 +1,6 @@
-<module name="m_md5.so">
-<module name="m_sha256.so">
-<module name="m_alias.so">
+<module name="md5">
+<module name="sha256">
+<module name="alias">
 <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
 <alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
 <alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
 <alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
 <alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
 
-<module name="m_banexception.so">
-<module name="m_banredirect.so">
-<module name="m_blockcolor.so">
-<module name="m_callerid.so">
+<module name="banexception">
+<module name="banredirect">
+<module name="blockcolor">
+<module name="callerid">
 <callerid maxaccepts="16"
           operoverride="no"
           tracknick="no"
           cooldown="60">
 
-<module name="m_cap.so">
-<module name="m_cban.so">
+<module name="cap">
+<module name="cban">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # CGI:IRC module: Adds support for automatic host changing in CGI:IRC
 # (http://cgiirc.sourceforge.net).
-#<module name="m_cgiirc.so">
+#<module name="cgiirc">
 #
 #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
 #
-# Optional - If you specify to use m_cgiirc, then you must specify one
+# Optional - If you specify to use cgiirc, then you must specify one
 # or more cgihost tags which indicate authorised CGI:IRC servers which
 # will be connecting to your network, and an optional cgiirc tag.
-# For more information see: https://wiki.inspircd.org/Modules/2.0/cgiirc
+# For more information see: https://wiki.inspircd.org/Modules/3.0/cgiirc
 #
 # Set to yes if you want to notice opers when CGI clients connect
 # <cgiirc opernotice="no">
 # sessions maximum.
 #
 
-<module name="m_chancreate.so">
+<module name="chancreate">
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Channel names module: Allows disabling channels which have certain
 # characters in the channel name such as bold, colorcodes, etc. which
 # can be quite annoying and allow users to on occasion have a channel
 # that looks like the name of another channel on the network.
-<module name="m_channames.so">
+<module name="channames">
 
 <channames
        # denyrange: characters or range of characters to deny in channel
        # in channel names.
        allowrange="">
 
-<module name="m_channelban.so">
-<module name="m_chghost.so">
+<module name="channelban">
+<module name="chghost">
 <hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
-<module name="m_chgident.so">
-<module name="m_chgname.so">
-<module name="m_cloaking.so">
+<module name="chgident">
+<module name="chgname">
+<module name="cloaking">
 #
 #-#-#-#-#-#-#-#-#-#-#- CLOAKING  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# If you specify the m_cloaking.so module as above, you must define   #
+# If you specify the cloaking module as above, you must define        #
 # cloak keys, and optionally a cloak prefix as shown below. The cloak #
 # keys must be shared across the network for correct cloaking.        #
 #                                                                     #
        key="secret"
        prefix="net-">
 
-<module name="m_close.so">
-<module name="m_conn_umodes.so">
+<module name="close">
+<module name="conn_umodes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Connectban: Provides IP connection throttling. Any IP range that connects
 #<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
 # This allows for 10 connections in an hour with a 10 minute ban if that is exceeded.
 #
-#<module name="m_connectban.so">
+#<module name="connectban">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Connection throttle module.
-#<module name="m_connflood.so">
+#<module name="connflood">
 #
 #-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
 #  seconds, maxconns -  Amount of connections per <seconds>.
 #<connflood seconds="30" maxconns="3" timeout="30"
 #   quitmsg="Throttled" bootwait="10">
 
-<module name="m_deaf.so">
-<module name="m_dnsbl.so">
-<module name="m_gecosban.so">
-<module name="m_globalload.so">
-<module name="m_ident.so">
+<module name="deaf">
+<module name="dnsbl">
+<module name="gecosban">
+<module name="globalload">
+<module name="ident">
 <ident timeout="1">
-<module name="m_inviteexception.so">
-<module name="m_joinflood.so">
-<module name="m_knock.so">
-<module name="m_namesx.so">
-<module name="m_operchans.so">
-<module name="m_operlog.so">
-<module name="m_opermodes.so">
-<module name="m_password_hash.so">
-<module name="m_permchannels.so">
-<module name="m_muteban.so">
-<module name="m_redirect.so">
+<module name="inviteexception">
+<module name="joinflood">
+<module name="knock">
+<module name="namesx">
+<module name="operchans">
+<module name="operlog">
+<module name="opermodes">
+<module name="password_hash">
+<module name="permchannels">
+<module name="muteban">
+<module name="redirect">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for glob or wildcard (?/*) matching.
-# You must have at least 1 provider loaded to use m_filter or m_rline
-# modules. This module has no additional requirements, as it uses the
-# matching already present in InspIRCd core.
-#<module name="m_regex_glob.so">
+# You must have at least 1 provider loaded to use the filter or the
+# rline modules. This module has no additional requirements, as it uses
+# the matching already present in InspIRCd core.
+#<module name="regex_glob">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for PCRE (Perl-Compatible Regular
 # Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter or
+# the rline module.
+#<module name="regex_pcre">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for POSIX regular expressions.
 # You shouldn't need any additional libraries on a POSIX-compatible
 # system (ie: any Linux, BSD, but not Windows). You must have at least
-# 1 provider loaded to use m_filter or m_rline.
+# 1 provider loaded to use the filter or the rline module.
 # On POSIX-compliant systems, regex syntax can be found by using the
 # command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Registered users only channel creation
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_regonlycreate.so">
+#<module name="regonlycreate">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Ban users through regular expression patterns
-#<module name="m_rline.so">
+#<module name="rline">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
 #
 # Also, this is where you set what Regular Expression engine is to be
 # used. If you ever change it while running, all of your R-Lines will be
 # wiped. This is the regex engine used by all R-Lines set, and
-# m_regex_<engine>.so must be loaded, or rline will be nonfunctional
+# regex_<engine> must be loaded, or rline will be nonfunctional
 # until you load it or change the engine to one that is loaded.
 #
 #<rline matchonnickchange="yes" engine="pcre">
 # use glob. For this reason, is recommended to use a real regex engine
 # so that at least \s or [[:space:]] is available.
 
-<module name="m_sasl.so">
-<module name="m_servprotect.so">
-<module name="m_services_account.so">
-<module name="m_sethost.so">
-<module name="m_serverban.so">
-<module name="m_showwhois.so">
+<module name="sasl">
+<module name="servprotect">
+<module name="services_account">
+<module name="sethost">
+<module name="serverban">
+<module name="showwhois">
 <showwhois opersonly="yes" showfromopers="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # channel mode +z and the 'z' extban which matches SSL client
 # certificate fingerprints.
 # Does not do anything useful without a working SSL module (see below).
-#<module name="m_sslmodes.so">
+#<module name="sslmodes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
 # if enabled. You must answer 'yes' in ./configure when asked or
 # manually symlink the source for this module from the directory
 # src/modules/extra, if you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+#<module name="ssl_gnutls">
 #
 #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
-# https://wiki.inspircd.org/Modules/2.0/ssl_gnutls                    #
+# ssl_gnutls is too complex to describe here, see the wiki:           #
+# https://wiki.inspircd.org/Modules/3.0/ssl_gnutls                    #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SSL Info module: Allows users to retrieve information about other
 # user's peer SSL certificates and keys. This can be used by client
-# scripts to validate users. For this to work, one of m_ssl_gnutls.so
-# or m_ssl_openssl.so must be loaded. This module also adds the
+# scripts to validate users. For this to work, one of ssl_gnutls
+# or ssl_openssl must be loaded. This module also adds the
 # "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the ability
-# to force opers to use SSL connections in order to oper up.
+# opers to use SSL cert fingerprints to verify their identity and the
+# ability to force opers to use SSL connections in order to oper up.
 # It is highly recommended to load this module especially if
 # you use SSL on your network.
 # For how to use the oper features, please see the first example <oper> tag
 # in opers.conf.example.
 #
-#<module name="m_sslinfo.so">
+#<module name="sslinfo">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
 #
 #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
-# https://wiki.inspircd.org/Modules/2.0/ssl_openssl                   #
+# ssl_openssl is too complex to describe here, see the wiki:          #
+# https://wiki.inspircd.org/Modules/3.0/ssl_openssl                   #
 
-<module name="m_stripcolor.so">
-<module name="m_svshold.so">
-<module name="m_tline.so">
-<module name="m_uhnames.so">
-<module name="m_watch.so">
+<module name="stripcolor">
+<module name="svshold">
+<module name="tline">
+<module name="uhnames">
+<module name="watch">
 <watch maxentries="32">
-<module name="m_xline_db.so">
+<module name="xline_db">
 
-<module name="m_spanningtree.so">
+<module name="spanningtree">
index 65d71339455709cf2ca2b430798282b11ea3312c..8ebdd2c1f6db5f137efef6db23bf7bd5580562df 100644 (file)
@@ -1,8 +1,8 @@
-<module name="m_md5.so">
-<module name="m_sha256.so">
+<module name="md5">
+<module name="sha256">
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Alias module: Allows you to define server-side command aliases.
-<module name="m_alias.so">
+<module name="alias">
 <fantasy prefix="!" allowbots="no">
 # Aliases
 <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
 #<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
 #  requires="NickServ" uline="yes">
 
-<module name="m_allowinvite.so">
-<module name="m_alltime.so">
-<module name="m_auditorium.so">
+<module name="allowinvite">
+<module name="alltime">
+<module name="auditorium">
 <auditorium showops="yes" operoverride="yes">
-<module name="m_banexception.so">
-<module name="m_blockcaps.so">
+<module name="banexception">
+<module name="blockcaps">
 <blockcaps percent="50"
            minlen="5"
            capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
-<module name="m_blockcolor.so">
-<module name="m_botmode.so">
-<module name="m_censor.so">
+<module name="blockcolor">
+<module name="botmode">
+<module name="censor">
 <include file="inspircd.censor.example">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # CGI:IRC module: Adds support for automatic host changing in CGI:IRC
 # (http://cgiirc.sourceforge.net).
-#<module name="m_cgiirc.so">
+#<module name="cgiirc">
 #
 #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
 #
-# Optional - If you specify to use m_cgiirc, then you must specify one
+# Optional - If you specify to use cgiirc, then you must specify one
 # or more cgihost tags which indicate authorised CGI:IRC servers which
 # will be connecting to your network, and an optional cgiirc tag.
-# For more information see: https://wiki.inspircd.org/Modules/2.0/cgiirc
+# For more information see: https://wiki.inspircd.org/Modules/3.0/cgiirc
 #
 # Set to yes if you want to notice opers when CGI clients connect
 # <cgiirc opernotice="no">
 # sessions maximum.
 #
 
-<module name="m_chanfilter.so">
+<module name="chanfilter">
 <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">
+<module name="check">
+<module name="chghost">
 <hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
 
-<module name="m_chgident.so">
-<module name="m_chgname.so">
-<module name="m_cloaking.so">
+<module name="chgident">
+<module name="chgname">
+<module name="cloaking">
 <cloak mode="half"
        key="secret"
        prefix="net-">
 
-<module name="m_close.so">
-<module name="m_clones.so">
-<module name="m_commonchans.so">
+<module name="close">
+<module name="clones">
+<module name="commonchans">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Auto join on connect module: Allows you to force users to join one
 # or more channels automatically upon connecting to the server.
-#<module name="m_conn_join.so">
+#<module name="conn_join">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
 #
-# If you have m_conn_join.so loaded, you can configure it using the
-# follow values:
+# If you have the conn_join module loaded, you can configure it below:
 #
 #<autojoin channel="#one,#two,#three">
 
-<module name="m_conn_umodes.so">
-<module name="m_cycle.so">
+<module name="conn_umodes">
+<module name="cycle">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Connection throttle module.
-#<module name="m_connflood.so">
+#<module name="connflood">
 #
 #-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
 #  seconds, maxconns -  Amount of connections per <seconds>.
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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
 #
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 
-<module name="m_deaf.so">
-<module name="m_denychans.so"> 
+<module name="deaf">
+<module name="denychans"> 
 #<badchan name="#gods*" allowopers="yes" reason="Tortoises!">         #
 #<badchan name="#heaven" redirect="#hell" reason="Nice try!">         #
 
-<module name="m_devoice.so">
-
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
 #                                                                     #
 # 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">
+# 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.                            #
 #
 #-#-#-#-#-#-#-#-#-#-#-  FILTER  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# Optional - If you specify to use the m_filter module, then          #
+# Optional - If you specify to use the filter module, then            #
 # specfiy below the path to the filter.conf file, or define some      #
 # <filter> tags.                                                      #
 #                                                                     #
 #<include file="filter.conf">
 
-<module name="m_gecosban.so">
-<module name="m_globops.so">
-<module name="m_globalload.so">
-<module name="m_halfop.so">
-<module name="m_helpop.so">
+<module name="gecosban">
+<module name="globops">
+<module name="globalload">
+<module name="halfop">
+<module name="helpop">
 <include file="inspircd.helpop-full.example">
 
-<module name="m_hidechans.so">
+<module name="hidechans">
 <hidechans affectsopers="false">
 
-<module name="m_hideoper.so">
-<module name="m_ident.so">
+<module name="hideoper">
+<module name="ident">
 <ident timeout="1">
-<module name="m_inviteexception.so">
-<module name="m_joinflood.so">
-<module name="m_jumpserver.so">
-<module name="m_knock.so">
-<module name="m_messageflood.so">
-<module name="m_namesx.so">
-<module name="m_nickflood.so">
-<module name="m_noctcp.so">
-<module name="m_nokicks.so">
-<module name="m_nonicks.so">
-<module name="m_nopartmsg.so">
-<module name="m_nonotice.so">
-<module name="m_operchans.so">
+<module name="inviteexception">
+<module name="joinflood">
+<module name="jumpserver">
+<module name="knock">
+<module name="messageflood">
+<module name="namesx">
+<module name="nickflood">
+<module name="noctcp">
+<module name="nokicks">
+<module name="nonicks">
+<module name="nopartmsg">
+<module name="nonotice">
+<module name="operchans">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Oper join module: Auto-joins opers to a channel upon oper-up.
-# This module is oper-only. For the user equivalent, see m_conn_join.
-<module name="m_operjoin.so">
+# This module is oper-only. For the user equivalent, see the conn_join
+# module.
+<module name="operjoin">
 #
 #-#-#-#-#-#-#-#-#-#-#   OPERJOIN CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# If you are using the m_operjoin.so module, specify options here:    #
+# If you are using the operjoin module, specify options here:         #
 #                                                                     #
 # channel     -      The channel name to join, can also be a comma    #
 #                    separated list eg. "#channel1,#channel2".        #
 #
 #<type name="Helper" autojoin="#help" classes="...">
 
-<module name="m_operlog.so">
+<module name="operlog">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Oper MOTD module: Provides support for separate message of the day
 # on oper-up.
 # This module is oper-only.
-#<module name="m_opermotd.so">
+#<module name="opermotd">
 #
 #-#-#-#-#-#-#-#-#-#-#   OPERMOTD CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# If you are using the m_opermotd.so module, specify the motd here    #
+# If you are using the opermotd module, specify the motd here         #
 #                                                                     #
 # onoper        - If on, the message is sent on /OPER, otherwise it's #
 #                 only sent when /OPERMOTD is used.                   #
 #                                                                     #
 #<opermotd file="oper.motd" onoper="yes">
 
-<module name="m_override.so">
+<module name="override">
 #-#-#-#-#-#-#-#-#-#-#   OVERRIDE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_override.so is too complex to describe here, see the wiki:        #
-# https://wiki.inspircd.org/Modules/2.0/override                      #
+# The override module is too complex to describe here, see the wiki:  #
+# https://wiki.inspircd.org/Modules/3.0/override                      #
 
-<module name="m_operlevels.so">
-<module name="m_opermodes.so">
-<module name="m_password_hash.so">
-<module name="m_muteban.so">
+<module name="operlevels">
+<module name="opermodes">
+<module name="password_hash">
+<module name="muteban">
 
-<module name="m_redirect.so">
-<module name="m_regex_glob.so">
+<module name="redirect">
+<module name="regex_glob">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for PCRE (Perl-Compatible Regular
 # Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter
+# or the rline module.
+#<module name="regex_pcre">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for POSIX Regular Expressions.
 # You shouldn't need any additional libraries on a POSIX-compatible
 # system (ie: any Linux, BSD, but not Windows). You must have at least
-# 1 provider loaded to use m_filter or m_rline.
+# 1 provider loaded to use the filter or rline module.
 # On POSIX-compliant systems, regex syntax can be found by using the
 # command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for TRE Regular Expressions.
 # 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
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_regonlycreate.so">
+#<module name="regonlycreate">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Restricted channels module: Allows only opers to create channels.
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_restrictchans.so">
+#<module name="restrictchans">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Restrict message module: Allows users to only message opers.
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_restrictmsg.so">
-
-<module name="m_sajoin.so">
-<module name="m_sakick.so">
-<module name="m_samode.so">
-<module name="m_sanick.so">
-<module name="m_sapart.so">
-<module name="m_saquit.so">
-<module name="m_satopic.so">
-<module name="m_servprotect.so">
-<module name="m_seenicks.so">
-<module name="m_setidle.so">
-<module name="m_services_account.so">
-<module name="m_sethost.so">
-<module name="m_setident.so">
-<module name="m_setname.so">
-<module name="m_showwhois.so">
+#<module name="restrictmsg">
+
+<module name="sajoin">
+<module name="sakick">
+<module name="samode">
+<module name="sanick">
+<module name="sapart">
+<module name="saquit">
+<module name="satopic">
+<module name="servprotect">
+<module name="seenicks">
+<module name="setidle">
+<module name="services_account">
+<module name="sethost">
+<module name="setident">
+<module name="setname">
+<module name="showwhois">
 <showwhois opersonly="yes" showfromopers="yes">
 
-<module name="m_shun.so">
+<module name="shun">
 <shun enabledcommands="PING PONG QUIT PART JOIN" notifyuser="no" affectopers="no">
 
-<module name="m_sslmodes.so">
+<module name="sslmodes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+#<module name="ssl_gnutls">
 #
 #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
-# https://wiki.inspircd.org/Modules/2.0/ssl_gnutls                    #
+# ssl_gnutls is too complex to describe here, see the wiki:           #
+# https://wiki.inspircd.org/Modules/3.0/ssl_gnutls                    #
 
-<module name="m_sslinfo.so">
+<module name="sslinfo">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
 #
 #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
-# https://wiki.inspircd.org/Modules/2.0/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">
+# ssl_openssl is too complex to describe here, see the wiki:          #
+# https://wiki.inspircd.org/Modules/3.0/ssl_openssl                   #
+
+<module name="stripcolor">
+<module name="svshold">
+<module name="swhois">
+<module name="tline">
+<module name="uhnames">
+<module name="userip">
+<module name="watch">
 <watch maxentries="32">
 
-<module name="m_spanningtree.so">
+<module name="spanningtree">
index 75b54faf049066889c11c10004f63c5dcfe9a3b9..5e1ec28f5a42a3f7ee555772e0cfafdacc565b4c 100644 (file)
      #   - 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.
+     #   - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE
      # 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)
      #
      # *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="*"
@@ -55,8 +55,6 @@
 
 <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="NetAdmin"
 
     # classes: Classes (blocks above) that this type belongs to.
     # vhost: Host 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 be loaded.
     modes="+s +cCqQ">
 
 <type name="GlobalOp" classes="SACommands OperChat BanControl HostCloak ServerLink" vhost="ircop.omega.example.org">
       host="attila@inspircd.org *@2001:db8::/32"
 
       # ** ADVANCED ** This option is disabled by default.
-      # fingerprint: When using the m_sslinfo module, you may specify
+      # fingerprint: When using the sslinfo module, you may specify
       # a key fingerprint here. This can be obtained by using the /sslinfo
       # command while the module is loaded, and is also noticed on connect.
       # This enhances security by verifying that the person opering up has
       # a matching SSL client certificate, which is very difficult to
       # forge (impossible unless preimage attacks on the hash exist).
-      # If m_sslinfo isn't loaded, this option will be ignored.
+      # If the sslinfo module isn't loaded, this option will be ignored.
       #fingerprint="67cb9dc013248a829bb2171ed11becd4"
 
-      # autologin: If an SSL fingerprint for this oper is specified, you can
-      # have the oper block automatically log in. This moves all security of the
-      # oper block to the protection of the client certificate, so be sure that
-      # the private key is well-protected! Requires m_sslinfo.
+      # autologin: If an SSL certificate fingerprint for this oper is specified,
+      # you can have the oper block automatically log in. This moves all security
+      # of the oper block to the protection of the client certificate, so be sure
+      # that the private key is well-protected! Requires the sslinfo module.
       #autologin="on"
 
       # sslonly: If on, this oper can only oper up if they're using a SSL connection.
       # Setting this option adds a decent bit of security. Highly recommended
       # if the oper is on wifi, or specifically, unsecured wifi. Note that it
       # is redundant to specify this option if you specify a fingerprint.
-      # This setting only takes effect if m_sslinfo is loaded.
+      # This setting only takes effect if the sslinfo module is loaded.
       #sslonly="yes"
 
       # vhost: Overrides the vhost in the type block. Class and modes may also
       # Remember: This is case sensitive.
       name="Adam"
 
-      # hash: What hash this password is hashed with.
-      # Requires the module for selected hash (m_md5.so, m_sha256.so
-      # or m_ripemd160.so) be loaded and the password hashing module
-      # (m_password_hash.so) loaded.
-      # Options here are: "md5", "sha256" and "ripemd160", or one of
-      # these prefixed with "hmac-", e.g.: "hmac-sha256".
+      # hash: the hash function this password is hashed with. Requires the
+      # module for the selected function (bcrypt, md5, sha1, sha256, or
+      # ripemd160) and the password hashing module (password_hash) to be
+      # loaded.
+      # You may also use any of the above other than bcrypt prefixed with
+      # either "hmac-" or "pbkdf2-hmac-" (requires the pbkdf2 module).
       # Create hashed passwords with: /mkpasswd <hash> <password>
-      hash="hmac-sha256"
+      hash="bcrypt"
 
       # password: A hash of the password (see above option) hashed
-      # with /mkpasswd <hash> <password>. See m_password_hash in modules.conf
-      # for more information about password hashing.
+      # with /mkpasswd <hash> <password>. See the password_hash module
+      # in modules.conf for more information about password hashing.
       password="qQmv3LcF$Qh63wzmtUqWp9OXnLwe7yv1GcBwHpq59k2a0UrY8xe0"
 
       # host: What hostnames and IPs are allowed to use this operator account.
diff --git a/docs/conf/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..603bb53
--- /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="PRIVMSG $requirement :GLOBAL $2-" requires="Global" uline="yes" operonly="yes">
diff --git a/docs/conf/services/atheme.conf.example b/docs/conf/services/atheme.conf.example
new file mode 100644 (file)
index 0000000..0370879
--- /dev/null
@@ -0,0 +1,52 @@
+# This file contains aliases and nickname reservations which are used
+# by Atheme. See http://atheme.net for more information on Atheme.
+
+# This file inherits from the generic config to avoid repetition.
+<include file="examples/services/generic.conf.example">
+
+# Long hand aliases for services pseudoclients.
+<alias text="ALIS"      replace="PRIVMSG $requirement :$2-" requires="ALIS"      uline="yes">
+<alias text="CHANFIX"   replace="PRIVMSG $requirement :$2-" requires="ChanFix"   uline="yes">
+<alias text="GAMESERV"  replace="PRIVMSG $requirement :$2-" requires="GameServ"  uline="yes">
+<alias text="GLOBAL"    replace="PRIVMSG $requirement :$2-" requires="Global"    uline="yes" operonly="yes">
+<alias text="GROUPSERV" replace="PRIVMSG $requirement :$2-" requires="GroupServ" uline="yes">
+<alias text="HELPSERV"  replace="PRIVMSG $requirement :$2-" requires="HelpServ"  uline="yes">
+<alias text="INFOSERV"  replace="PRIVMSG $requirement :$2-" requires="InfoServ"  uline="yes">
+<alias text="PROXYSCAN" replace="PRIVMSG $requirement :$2-" requires="Proxyscan" uline="yes" operonly="yes">
+<alias text="RPGSERV"   replace="PRIVMSG $requirement :$2-" requires="RPGServ"   uline="yes">
+
+# Short hand aliases for services pseudoclients.
+<alias text="CF" replace="PRIVMSG $requirement :$2-" requires="ChanFix"   uline="yes">
+<alias text="GL" replace="PRIVMSG $requirement :$2-" requires="Global"    uline="yes" operonly="yes">
+<alias text="GS" replace="PRIVMSG $requirement :$2-" requires="GroupServ" uline="yes">
+<alias text="IS" replace="PRIVMSG $requirement :$2-" requires="InfoServ"  uline="yes">
+<alias text="LS" replace="PRIVMSG $requirement :$2-" requires="ALIS"      uline="yes">
+<alias text="PS" replace="PRIVMSG $requirement :$2-" requires="Proxyscan" uline="yes" operonly="yes">
+<alias text="RS" replace="PRIVMSG $requirement :$2-" requires="RPGServ"   uline="yes">
+
+# These short hand aliases conflict with other pseudoclients. You can enable
+# them but you will need to comment out the uncommented ones above first,
+#<alias text="GS" replace="PRIVMSG $requirement :$2-" requires="GameServ" uline="yes">
+#<alias text="HS" replace="PRIVMSG $requirement :$2-" requires="HelpServ" uline="yes">
+
+# Prevent clients from using the nicknames of services pseudoclients.
+<badnick nick="ALIS"      reason="Reserved for a network service">
+<badnick nick="ChanFix"   reason="Reserved for a network service">
+<badnick nick="GameServ"  reason="Reserved for a network service">
+<badnick nick="GroupServ" reason="Reserved for a network service">
+<badnick nick="HelpServ"  reason="Reserved for a network service">
+<badnick nick="InfoServ"  reason="Reserved for a network service">
+<badnick nick="Proxyscan" reason="Reserved for a network service">
+<badnick nick="RPGServ"   reason="Reserved for a network service">
+<badnick nick="SaslServ"  reason="Reserved for a network service">
+
+# Exempt services pseudoclients from filters.
+<exemptfromfilter target="ALIS">
+<exemptfromfilter target="ChanFix">
+<exemptfromfilter target="GameServ">
+<exemptfromfilter target="GroupServ">
+<exemptfromfilter target="HelpServ">
+<exemptfromfilter target="InfoServ">
+<exemptfromfilter target="Proxyscan">
+<exemptfromfilter target="RPGServ">
+<exemptfromfilter target="SaslServ">
diff --git a/docs/conf/services/generic.conf.example b/docs/conf/services/generic.conf.example
new file mode 100644 (file)
index 0000000..93b89ea
--- /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="PRIVMSG $requirement :$2-" requires="BotServ"  uline="yes">
+<alias text="CHANSERV" replace="PRIVMSG $requirement :$2-" requires="ChanServ" uline="yes">
+<alias text="HOSTSERV" replace="PRIVMSG $requirement :$2-" requires="HostServ" uline="yes">
+<alias text="MEMOSERV" replace="PRIVMSG $requirement :$2-" requires="MemoServ" uline="yes">
+<alias text="NICKSERV" replace="PRIVMSG $requirement :$2-" requires="NickServ" uline="yes">
+<alias text="OPERSERV" replace="PRIVMSG $requirement :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="STATSERV" replace="PRIVMSG $requirement :$2-" requires="StatServ" uline="yes">
+
+# Short hand aliases for services pseudoclients.
+<alias text="BS" replace="PRIVMSG $requirement :$2-" requires="BotServ"  uline="yes">
+<alias text="CS" replace="PRIVMSG $requirement :$2-" requires="ChanServ" uline="yes">
+<alias text="HS" replace="PRIVMSG $requirement :$2-" requires="HostServ" uline="yes">
+<alias text="MS" replace="PRIVMSG $requirement :$2-" requires="MemoServ" uline="yes">
+<alias text="NS" replace="PRIVMSG $requirement :$2-" requires="NickServ" uline="yes">
+<alias text="OS" replace="PRIVMSG $requirement :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="SS" replace="PRIVMSG $requirement :$2-" requires="StatServ" uline="yes">
+
+# /ID [account] <password>
+# Identifies to a services account.
+<alias text="ID"       format="*" replace="PRIVMSG $requirement :IDENTIFY $2-" requires="NickServ" uline="yes">
+<alias text="IDENTIFY" format="*" replace="PRIVMSG $requirement :IDENTIFY $2-" requires="NickServ" uline="yes">
+
+# Prevent clients from using the nicknames of services pseudoclients.
+<badnick nick="BotServ"  reason="Reserved for a network service">
+<badnick nick="ChanServ" reason="Reserved for a network service">
+<badnick nick="Global"   reason="Reserved for a network service">
+<badnick nick="HostServ" reason="Reserved for a network service">
+<badnick nick="MemoServ" reason="Reserved for a network service">
+<badnick nick="NickServ" reason="Reserved for a network service">
+<badnick nick="OperServ" reason="Reserved for a network service">
+<badnick nick="StatServ" reason="Reserved for a network service">
+
+# Exempt services pseudoclients from filters.
+<exemptfromfilter target="BotServ">
+<exemptfromfilter target="ChanServ">
+<exemptfromfilter target="Global">
+<exemptfromfilter target="HostServ">
+<exemptfromfilter target="MemoServ">
+<exemptfromfilter target="NickServ">
+<exemptfromfilter target="OperServ">
+<exemptfromfilter target="StatServ">
diff --git a/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/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
index 293a2aa7041c8faf4ccf669ff3652ffd9dc1d29e..f4349580682b2903d2f21156cb3214fb53c39b86 100644 (file)
@@ -1,24 +1,9 @@
--- MySQL dump 9.11
---
--- Host: localhost    Database: brain
--- ------------------------------------------------------
--- Server version      4.0.20
-
---
--- Table structure for table `ircd_opers`
---
-
 CREATE TABLE ircd_opers (
   id bigint(20) NOT NULL auto_increment,
   username text,
   password text,
   hostname text,
   type text,
+  active tinyint(1) NOT NULL DEFAULT 1,
   PRIMARY KEY  (id)
-) TYPE=MyISAM;
-
---
--- Dumping data for table `ircd_opers`
---
-
-
+) ENGINE=MyISAM;
index fd640949fc182cfec7421c1ffd603f5fbecc04a5..4244abc22ca668dc4cc54f82fcc6ff06a6e6ff0a 100644 (file)
@@ -1,14 +1,10 @@
---
--- PostgreSQL database dump
---
-
 CREATE TABLE ircd_opers (
     id serial NOT NULL,
     username text,
     "password" text,
     hostname text,
-    "type" text
+    "type" text,
+    active boolean NOT NULL DEFAULT 1
 );
 ALTER TABLE ONLY ircd_opers
     ADD CONSTRAINT ircd_opers_pkey PRIMARY KEY (id);
-
index 1bb2937b8d146ea208016f652f9ef45d87b6bf9a..1c607e664e8afa84ca8d80a60fc6d65af8068e6c 100644 (file)
@@ -3,5 +3,5 @@ id integer primary key,
 username text,
 password text,
 hostname text,
-type text);
-
+type text,
+active integer NOT NULL DEFAULT 1);
diff --git a/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..6e19e1ebe753d4afb0a3e309bb20e323782edfb7 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 glines/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/builtinmodes.h b/include/builtinmodes.h
new file mode 100644 (file)
index 0000000..49198b6
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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/>.
+ */
+
+#pragma once
+
+#include "mode.h"
+#include "channels.h"
+#include "listmode.h"
+
+/** Channel mode +b
+ */
+class ModeChannelBan : public ListModeBase
+{
+ public:
+       ModeChannelBan()
+               : ListModeBase(NULL, "ban", 'b', "End of channel ban list", 367, 368, true, "maxbans")
+       {
+       }
+};
+
+/** Channel mode +k
+ */
+class ModeChannelKey : public ParamMode<ModeChannelKey, LocalStringExt>
+{
+       static const std::string::size_type maxkeylen = 32;
+ public:
+       ModeChannelKey();
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+       void SerializeParam(Channel* chan, const std::string* key, std::string& out);
+       ModeAction OnSet(User* source, Channel* chan, std::string& param);
+};
+
+/** Channel mode +l
+ */
+class ModeChannelLimit : public ParamMode<ModeChannelLimit, LocalIntExt>
+{
+ public:
+       ModeChannelLimit();
+       bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
+       void SerializeParam(Channel* chan, intptr_t n, std::string& out);
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter);
+};
+
+/** Channel mode +o
+ */
+class ModeChannelOp : public PrefixMode
+{
+ public:
+       ModeChannelOp()
+               : PrefixMode(NULL, "op", 'o', OP_VALUE, '@')
+       {
+               ranktoset = ranktounset = OP_VALUE;
+       }
+};
+
+/** Channel mode +v
+ */
+class ModeChannelVoice : public PrefixMode
+{
+ public:
+       ModeChannelVoice()
+               : PrefixMode(NULL, "voice", 'v', VOICE_VALUE, '+')
+       {
+               selfremove = false;
+               ranktoset = ranktounset = HALFOP_VALUE;
+       }
+};
+
+/** 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();
+       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();
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+};
index 40574771efff63b5e5c678b0f50f85f160da8009..47f896ef612dafb5dbfe47574cf68ab032ea10ae 100644 (file)
@@ -3,6 +3,7 @@
  *
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2012 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
  */
 
 
-#ifndef CALLER_H
-#define CALLER_H
+#pragma once
+
+#if defined HAS_CXX11_VARIADIC_TEMPLATES
+
+template<typename ReturnType, typename... Args> class CoreExport Handler : public classbase
+{
+ public:
+       virtual ~Handler() { }
+       virtual ReturnType Call(Args...) = 0;
+};
+
+template<typename ReturnType, typename... Args> class CoreExport Caller
+{
+ public:
+       Handler<ReturnType, Args...>* target;
+
+       Caller(Handler<ReturnType, Args...>* initial) : target(initial) { }
+       virtual ~Caller() { }
+
+       virtual ReturnType operator()(const Args&... params)
+       {
+               return this->target->Call(params...);
+       }
+};
+
+/* Below here is compat with the old API */
+#define HandlerBase0 Handler
+#define HandlerBase1 Handler
+#define HandlerBase2 Handler
+#define HandlerBase3 Handler
+#define HandlerBase4 Handler
+#define HandlerBase5 Handler
+#define HandlerBase6 Handler
+#define HandlerBase7 Handler
+#define HandlerBase8 Handler
+
+#define caller1 Caller
+#define caller2 Caller
+#define caller3 Caller
+#define caller4 Caller
+#define caller5 Caller
+#define caller6 Caller
+#define caller7 Caller
+#define caller8 Caller
+
+#define DEFINE_HANDLER0(NAME, RETURN) \
+       class CoreExport NAME : public Handler<RETURN> { public: NAME() { } virtual RETURN Call(); }
+
+#define DEFINE_HANDLER1(NAME, RETURN, V1) \
+       class CoreExport NAME : public Handler<RETURN, V1> { public: NAME() { } virtual RETURN Call(V1); }
+
+#define DEFINE_HANDLER2(NAME, RETURN, V1, V2) \
+       class CoreExport NAME : public Handler<RETURN, V1, V2> { public: NAME() { } virtual RETURN Call(V1, V2); }
+
+#define DEFINE_HANDLER3(NAME, RETURN, V1, V2, V3) \
+       class CoreExport NAME : public Handler<RETURN, V1, V2, V3> { public: NAME() { } virtual RETURN Call(V1, V2, V3); }
+
+#define DEFINE_HANDLER4(NAME, RETURN, V1, V2, V3, V4) \
+       class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4); }
+
+#define DEFINE_HANDLER5(NAME, RETURN, V1, V2, V3, V4, V5) \
+       class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4, V5> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5); }
+
+#define DEFINE_HANDLER6(NAME, RETURN, V1, V2, V3, V4, V5, V6) \
+       class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4, V5, V6> { public: 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 Handler<RETURN, V1, V2, V3, V4, V5, V6, V7> { public: 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 Handler<RETURN, V1, V2, V3, V4, V5, V6, V7, V8> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7, V8); }
+
+#else
 
 /** 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
index dda53f69da0b0be756bbcd9d5de59d76aaca4c50..be872b7fe7bb55dca3cda73d5e466835a4e4294a 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();
+       long 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,28 +187,50 @@ 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.
+        * If the user could not be joined to a channel, the return value is NULL.
+        */
+       static Channel* JoinUser(LocalUser* user, std::string channame, bool override = false, const std::string& key = "");
+
+       /** 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
         */
-       static Channel* JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS = 0);
+       Membership* ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false);
 
        /** Write to a channel, from a user, using va_args for text
         * @param user User whos details to prefix the line with
@@ -301,48 +302,12 @@ class CoreExport Channel : public Extensible, public InviteBase
        /** 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
-        */
-       long GetMaxBans();
-
        /** 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;'
         * @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 showkey);
 
        /** Get the value of a users prefix on this channel.
         * @param user The user to look up
@@ -357,24 +322,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 +336,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);
+}
index f9e3a740c3012bc3e955f2b6da841600707cdde7..ec5ebba485ad1f02c82f15f559c96ff54d603024 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
         */
-       bool ProcessCommand(LocalUser *user, std::string &cmd);
+       void ProcessCommand(LocalUser* user, std::string& cmd);
 
- public:
        /** Command list, a hash_map of command names to Command*
         */
-       Commandtable cmdlist;
+       CommandMap cmdlist;
 
+ public:
        /** Default constructor.
         */
        CommandParser();
 
+       /** Get a command name -> Command* map containing all client to server commands
+        * @return A map of command handlers keyed by command names
+        */
+       const CommandMap& GetCommands() const { return cmdlist; }
+
        /** Calls the handler for a given command.
         * @param commandname The command to find. This should be in uppercase.
         * @param parameters Parameter list
         * @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 std::vector<std::string>& 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 +73,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 std::vector<std::string>& 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(std::string &buffer,LocalUser *user);
 
        /** Add a new command to the commands hash
         * @param f The new Command to add to the list
@@ -120,23 +128,23 @@ 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 std::vector<std::string>& source, bool prefix_final = false, CommandBase* custom_translator = NULL);
 };
 
 /** A lookup table of values for multiplier characters used by
@@ -165,5 +173,3 @@ 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
 };
-
-#endif
index d333541228903ae722d7d58d9f9928093bd10053..070858cc504140282a26d2c8457b041d4954ecc7 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;
+               /** Full name (GECOS)
+                */
+               const std::string gecos;
 
-/** 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();
 };
-
-/** 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..1e6fc3d
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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 <unordered_map>
+# include <type_traits>
+#else
+# define TR1NS std::tr1
+# include <tr1/array>
+# 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
+
+/**
+ * These macros enable the detection of the C++11 variadic templates in
+ * compilers which support them.
+ */
+#if __cplusplus >= 201103L
+# define HAS_CXX11_VARIADIC_TEMPLATES
+#elif defined __clang__
+# if __has_feature(cxx_variadic_templates)
+#  define HAS_CXX11_VARIADIC_TEMPLATES
+# endif
+#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
+# if defined __GXX_EXPERIMENTAL_CXX0X__
+#  define HAS_CXX11_VARIADIC_TEMPLATES
+# endif
+#elif _MSC_FULL_VER >= 170051025
+# define HAS_CXX11_VARIADIC_TEMPLATES
+#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..c9790c59f7d22ccb22527b620b7933463f3e6698 100644 (file)
@@ -17,6 +17,8 @@
  */
 
 
+#pragma once
+
 struct fpos
 {
        std::string filename;
@@ -31,7 +33,7 @@ struct fpos
 
 enum ParseFlags
 {
-       FLAG_USE_XML = 1,
+       FLAG_USE_COMPAT = 1,
        FLAG_NO_EXEC = 2,
        FLAG_NO_INC = 4
 };
@@ -39,7 +41,7 @@ enum ParseFlags
 struct ParseStack
 {
        std::vector<std::string> reading;
-       std::map<std::string, std::string> vars;
+       insp::flat_map<std::string, std::string, irc::insensitive_swo> vars;
        ConfigDataHash& output;
        ConfigFileCache& FilesOutput;
        std::stringstream& errstr;
@@ -51,8 +53,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);
 };
@@ -76,5 +77,3 @@ struct FileWrapper
                }
        }
 };
-
-
index 4a697d05c314a1edbe5de12ec9240978f2cb3b43..9fcb9c6a333f72a12fed7dfc08a1201c91092c53 100644 (file)
@@ -21,8 +21,7 @@
  */
 
 
-#ifndef INSPIRCD_CONFIGREADER
-#define INSPIRCD_CONFIGREADER
+#pragma once
 
 #include <sstream>
 #include <string>
 /** 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 = "", size_t minlen = 0, size_t maxlen = UINT32_MAX);
        /** Get the value of an option, using def if it does not exist */
-       long getInt(const std::string& key, long def = 0);
+       long getInt(const std::string& key, long def = 0, long min = LONG_MIN, long max = LONG_MAX);
        /** Get the value of an option, using def if it does not exist */
        double getFloat(const std::string& key, double def = 0);
        /** 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
+        */
+       long getDuration(const std::string& key, long def = 0, long min = LONG_MIN, long max = LONG_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)
@@ -59,12 +68,22 @@ class CoreExport ConfigTag : public refcountbase
         */
        bool readString(const std::string& key, std::string& value, bool allow_newline = false);
 
+       /** 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 def).
+        * @param key The key name, used in the warning message
+        * @param res The value to verify and modify if needed
+        * @param def The default value, res will be set to this if (min <= res <= max) doesn't hold true
+        * @param min Minimum accepted value for res
+        * @param max Maximum accepted value for res
+        */
+       void CheckRange(const std::string& key, long& res, long def, long min, long max);
+
        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);
 };
@@ -93,14 +112,18 @@ class ServerLimits
        size_t MaxGecos;
        /** 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,11 +153,6 @@ struct CommandLineConf
         */
        bool writelog;
 
-       /** True if we have been told to run the testsuite from the commandline,
-        * rather than entering the mainloop.
-        */
-       bool TestSuite;
-
        /** Saved argc from startup
         */
        int argc;
@@ -142,15 +160,14 @@ 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;
+       typedef insp::flat_set<std::string> PrivSet;
+       PrivSet AllowedOperCommands;
+       PrivSet AllowedPrivs;
 
        /** Allowed user modes from oper classes. */
        std::bitset<64> AllowedUserModes;
@@ -167,14 +184,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,6 +206,36 @@ class CoreExport ServerConfig
        void CrossCheckConnectBlocks(ServerConfig* current);
 
  public:
+       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);
+
+               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
         * @param tag The name of the tag to get
@@ -228,6 +275,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;
@@ -242,27 +292,14 @@ class CoreExport ServerConfig
         */
        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;
-
        /** 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,95 +312,17 @@ 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];
+       std::bitset<64> DisabledUModes;
 
        /** 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;
+       std::bitset<64> DisabledCModes;
 
        /** If set to true, then all opers on this server are
         * shown with a generic 'is an IRC operator' line rather
@@ -376,17 +335,6 @@ class CoreExport ServerConfig
         */
        bool RestrictBannedUsers;
 
-       /** If this is set to true, then mode lists (e.g
-        * MODE \#chan b) are hidden from unprivileged
-        * users.
-        */
-       bool HideModeLists[256];
-
-       /** The number of seconds the DNS subsystem
-        * will wait before timing out any request.
-        */
-       int dns_timeout;
-
        /** The size of the read() buffer in the user
         * handling code, used to read data into a user's
         * recvQ.
@@ -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.
@@ -451,16 +406,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.
         */
@@ -474,52 +419,37 @@ 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;
+       std::string CaseMapping;
 
        /** 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;
-
        /** 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 +468,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 +483,18 @@ 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);
+       /** Disables the commands specified in <disabled:commands>. */
+       bool ApplyDisabledCommands();
 
-       /** Check if a file exists.
-        * @param file The full path to a file
-        * @return True if the file exists and is readable.
+       /** Escapes a value for storage in a configuration key.
+        * @param str The string to escape.
+        * @param xml Are we using the XML config format?
         */
-       static bool FileExists(const char* file);
-
-       /** If this value is true, invites will bypass more than just +i
-        */
-       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
@@ -618,4 +522,13 @@ class CoreExport ConfigReaderThread : public Thread
        bool IsDone() { return done; }
 };
 
-#endif
+class CoreExport ConfigStatus
+{
+ public:
+       User* const srcuser;
+
+       ConfigStatus(User* user = NULL)
+               : srcuser(user)
+       {
+       }
+};
index f7ca1335ed244f16514d457d4fa035971fab2996..d92be58bc706f9dafa78f9771ad671a1f2f1eda7 100644 (file)
@@ -14,8 +14,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef CONSOLECOLORS_H
-#define CONSOLECOLORS_H
+
+#pragma once
 
 #include <ostream>
 
@@ -29,72 +29,70 @@ extern HANDLE g_hStdout;
 
 inline std::ostream& con_green(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
-    return s;
+       SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
+       return s;
 }
 
 inline std::ostream& con_red(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY|g_wBackgroundColor);
-    return s;
+       SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY|g_wBackgroundColor);
+       return s;
 }
 
 inline std::ostream& con_white(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|g_wBackgroundColor);
-    return s;
+       SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|g_wBackgroundColor);
+       return s;
 }
 
 inline std::ostream& con_white_bright(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
-    return s;
+       SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
+       return s;
 }
 
 inline std::ostream& con_bright(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor);
-    return s;
+       SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor);
+       return s;
 }
 
 inline std::ostream& con_reset(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, g_wOriginalColors);
-    return s;
+       SetConsoleTextAttribute(g_hStdout, g_wOriginalColors);
+       return s;
 }
 
 #else
 
 inline std::ostream& con_green(std::ostream &s)
 {
-    return s << "\033[1;32m";
+       return s << "\033[1;32m";
 }
 
 inline std::ostream& con_red(std::ostream &s)
 {
-    return s << "\033[1;31m";
+       return s << "\033[1;31m";
 }
 
 inline std::ostream& con_white(std::ostream &s)
 {
-    return s << "\033[0m";
+       return s << "\033[0m";
 }
 
 inline std::ostream& con_white_bright(std::ostream &s)
 {
-    return s << "\033[1m";
+       return s << "\033[1m";
 }
 
 inline std::ostream& con_bright(std::ostream &s)
 {
-    return s << "\033[1m";
+       return s << "\033[1m";
 }
 
 inline std::ostream& con_reset(std::ostream &s)
 {
-    return s << "\033[0m";
+       return s << "\033[0m";
 }
 
 #endif
-
-#endif
diff --git a/include/convto.h b/include/convto.h
new file mode 100644 (file)
index 0000000..eaf14f6
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** Template function to convert any input type to std::string
+ */
+template<typename T> inline std::string ConvNumeric(const T& in)
+{
+       if (in == 0)
+               return "0";
+       T quotient = in;
+       std::string out;
+       while (quotient)
+       {
+               out += "0123456789"[std::abs((long)quotient % 10)];
+               quotient /= 10;
+       }
+       if (in < 0)
+               out += '-';
+       std::reverse(out.begin(), out.end());
+       return out;
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const int in)
+{
+       return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const long in)
+{
+       return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const char* in)
+{
+       return in;
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const bool in)
+{
+       return (in ? "1" : "0");
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(char in)
+{
+       return std::string(1, in);
+}
+
+inline const std::string& ConvToStr(const std::string& in)
+{
+       return in;
+}
+
+/** Template function to convert any input type to std::string
+ */
+template <class T> inline std::string ConvToStr(const T& in)
+{
+       std::stringstream tmp;
+       if (!(tmp << in))
+               return std::string();
+       return tmp.str();
+}
+
+/** Template function to convert any input type to any other type
+ * (usually an integer or numeric type)
+ */
+template<typename T> inline long ConvToInt(const T& in)
+{
+       std::stringstream tmp;
+       if (!(tmp << in))
+               return 0;
+       return atol(tmp.str().c_str());
+}
+
+inline uint64_t ConvToUInt64(const std::string& in)
+{
+       uint64_t ret;
+       std::istringstream tmp(in);
+       if (!(tmp >> ret))
+               return 0;
+       return ret;
+}
index f9cd08cb3ae9dc8b209770bb05d1de4d6d7c468c..9376dbbcc4df88a2a4761079b27ad541dec90369 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,7 +107,7 @@ 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:
        /** User flags needed to execute the command or 0
@@ -120,10 +128,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;
@@ -162,43 +166,16 @@ 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)
-       {
-       }
+       CommandBase(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0);
 
-       /** Handle the command from a user.
-        * @param parameters The parameters for the command.
-        * @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;
-
-       virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               return ROUTE_LOCALONLY;
-       }
+       virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 
        /** Encode a parameter for server->server transmission.
         * Used for parameters for which the translation type is TR_CUSTOM.
         * @param parameter The parameter to encode. Can be modified in place.
         * @param index The parameter index (0 == first parameter).
         */
-       virtual void EncodeParameter(std::string& parameter, int index)
-       {
-       }
-
-       /** Decode a parameter from server->server transmission.
-        * Not currently used in this version of InspIRCd.
-        * Used for parameters for which the translation type is TR_CUSTOM.
-        * @param parameter The parameter to decode. Can be modified in place.
-        * @param index The parameter index (0 == first parameter).
-        */
-       virtual void DecodeParameter(std::string& parameter, int index)
-       {
-       }
+       virtual void EncodeParameter(std::string& parameter, int index);
 
        /** Disable or enable this command.
         * @param setting True to disable the command.
@@ -224,7 +201,34 @@ class CoreExport Command : public ServiceProvider
                return works_before_reg;
        }
 
-       virtual ~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.
+        */
+       bool force_manual_route;
+
+       Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0);
+
+       /** Handle the command from a user.
+        * @param parameters The parameters for the command.
+        * @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;
+
+       /** Register this object in the CommandParser
+        */
+       void RegisterService() CXX11_OVERRIDE;
+
+       /** Destructor
+        * Removes this command from the command parser
+        */
+       ~Command();
 };
 
 class CoreExport SplitCommand : public Command
@@ -252,5 +256,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..ac64dced284341805081c6e78c0b33927a1af9d3 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
@@ -58,6 +57,3 @@ class CoreExport ActionList
        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..905eb479e9ed02efe5eabb21f5458ea44b21d30d 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.
@@ -63,6 +62,3 @@ class CoreExport DLLManager : public classbase
        /** 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..c9bad7d
--- /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
+
+namespace Events
+{
+       class ModuleEventListener;
+       class ModuleEventProvider;
+}
+
+/** Provider of one or more cross-module events.
+ * Modules who wish to provide events for other modules create instances of this class and use
+ * one of the macros below to fire the event, passing the instance of the event provider class
+ * to the macro.
+ * Event providers are identified using a unique identifier string.
+ */
+class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
+{
+ public:
+       typedef std::vector<ModuleEventListener*> SubscriberList;
+
+       /** Constructor
+        * @param mod Module providing the event(s)
+        * @param eventid Identifier of the event or event group provided, must be unique
+        */
+       ModuleEventProvider(Module* mod, const std::string& eventid)
+               : ServiceProvider(mod, eventid, SERVICE_DATA)
+               , prov(mod, eventid)
+       {
+               prov.SetCaptureHook(this);
+       }
+
+       /** Get list of objects subscribed to this event
+        * @return List of subscribed objects
+        */
+       const SubscriberList& GetSubscribers() const { return prov->subscribers; }
+
+       friend class ModuleEventListener;
+
+ private:
+       void OnCapture() CXX11_OVERRIDE
+       {
+               // If someone else holds the list from now on, clear mine. See below for more info.
+               if (*prov != this)
+                       subscribers.clear();
+       }
+
+       /** Reference to the active provider for this event. In case multiple event providers
+        * exist for the same event, only one of them contains the list of subscribers.
+        * To handle the case when we are not the ones with the list, we get it from the provider
+        * where the dynref points to.
+        */
+       dynamic_reference_nocheck<ModuleEventProvider> prov;
+
+       /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
+        * exist with the same name and we are not the ones holding the list.
+        */
+       SubscriberList subscribers;
+};
+
+/** Base class for abstract classes describing cross-module events.
+ * Subscribers should NOT inherit directly from this class.
+ */
+class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
+{
+       /** Reference to the provider, can be NULL if none of the provider modules are loaded
+        */
+       dynamic_reference_nocheck<ModuleEventProvider> prov;
+
+       /** Called by the dynref when the event provider becomes available
+        */
+       void OnCapture() CXX11_OVERRIDE
+       {
+               prov->subscribers.push_back(this);
+       }
+
+ public:
+       /** Constructor
+        * @param mod Module subscribing
+        * @param eventid Identifier of the event to subscribe to
+        */
+       ModuleEventListener(Module* mod, const std::string& eventid)
+               : prov(mod, eventid)
+       {
+               prov.SetCaptureHook(this);
+               // If the dynamic_reference resolved at construction our capture handler wasn't called
+               if (prov)
+                       ModuleEventListener::OnCapture();
+       }
+
+       ~ModuleEventListener()
+       {
+               if (prov)
+                       stdalgo::erase(prov->subscribers, this);
+       }
+};
+
+/**
+ * Run the given hook provided by a module
+ *
+ * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
+ */
+#define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
+       const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
+       for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
+       { \
+               listenerclass* _t = static_cast<listenerclass*>(*_i); \
+               _t->func params ; \
+       } \
+} while (0);
+
+/**
+ * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY.
+ * If no module does that, result is set to MOD_RES_PASSTHRU.
+ *
+ * Example: ModResult MOD_RESULT;
+ * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
+ */
+#define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
+       result = MOD_RES_PASSTHRU; \
+       const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
+       for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
+       { \
+               listenerclass* _t = static_cast<listenerclass*>(*_i); \
+               result = _t->func params ; \
+               if (result != MOD_RES_PASSTHRU) \
+                       break; \
+       } \
+} while (0);
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..1da45cee632ff4ff63bd104db927c6e603112e55 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
         *
@@ -57,6 +67,10 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase
        /** Free the item */
        virtual void free(void* item) = 0;
 
+       /** Register this object in the ExtensionManager
+        */
+       void RegisterService() CXX11_OVERRIDE;
+
  protected:
        /** Get the item from the internal map */
        void* get_raw(const Extensible* container) const;
@@ -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();
+       virtual 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;
 };
 
-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,50 +172,57 @@ 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)
        {
-               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;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
 };
 
 class CoreExport LocalIntExt : public LocalExtItem
 {
  public:
-       LocalIntExt(const std::string& key, Module* owner);
+       LocalIntExt(const std::string& key, ExtensibleType exttype, Module* owner);
        virtual ~LocalIntExt();
        std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
        intptr_t get(const Extensible* container) const;
        intptr_t set(Extensible* container, intptr_t value);
+       void unset(Extensible* container) { set(container, 0); }
        void free(void* item);
 };
 
 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;
@@ -190,5 +231,3 @@ class CoreExport StringExtItem : public ExtensionItem
        void unset(Extensible* container);
        void free(void* item);
 };
-
-#endif
index 22a94c934cd3c288c4ea320e34dde0587d649437..ce571c3aeb303ad7d9fee7f9697e70780a7c6430 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);
+       virtual void OnLog(LogLevel loglevel, const std::string &type, const std::string &msg);
 };
-
-#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..bef1404
--- /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> >
+class flat_set : public detail::flat_map_base<T, Comp>
+{
+       typedef detail::flat_map_base<T, Comp> base_t;
+
+ public:
+       typedef typename base_t::iterator iterator;
+       typedef typename base_t::value_type value_type;
+
+       flat_set() { }
+
+       template <typename InputIterator>
+       flat_set(InputIterator first, InputIterator last)
+       {
+               this->insert(first, last);
+       }
+
+       flat_set(const flat_set& other)
+               : base_t(other)
+       {
+       }
+
+       std::pair<iterator, bool> insert(const value_type& x)
+       {
+               return this->insert_single(x);
+       }
+
+       template <typename InputIterator>
+       void insert(InputIterator first, InputIterator last)
+       {
+               for (; first != last; ++first)
+                       this->insert_single(*first);
+       }
+
+       void swap(flat_set& other)
+       {
+               base_t::vect.swap(other.vect);
+       }
+};
+
+template <typename T, typename Comp = std::less<T> >
+class flat_multiset : public detail::flat_map_base<T, Comp>
+{
+       typedef detail::flat_map_base<T, Comp> base_t;
+
+ public:
+       typedef typename base_t::iterator iterator;
+       typedef typename base_t::value_type value_type;
+
+       flat_multiset() { }
+
+       template <typename InputIterator>
+       flat_multiset(InputIterator first, InputIterator last)
+       {
+               this->insert(first, last);
+       }
+
+       flat_multiset(const flat_multiset& other)
+               : base_t(other)
+       {
+       }
+
+       iterator insert(const value_type& x)
+       {
+               return this->insert_multi(x);
+       }
+
+       template <typename InputIterator>
+       void insert(InputIterator first, InputIterator last)
+       {
+               for (; first != last; ++first)
+                       insert_multi(*first);
+       }
+
+       void swap(flat_multiset& other)
+       {
+               base_t::vect.swap(other.vect);
+       }
+};
+
+template <typename T, typename U, typename Comp = std::less<T> >
+class flat_map : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> >
+{
+       typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > base_t;
+
+ public:
+       typedef typename base_t::iterator iterator;
+       typedef typename base_t::key_type key_type;
+       typedef typename base_t::value_type value_type;
+       typedef U mapped_type;
+       typedef typename base_t::value_compare value_compare;
+
+       flat_map() { }
+
+       template <typename InputIterator>
+       flat_map(InputIterator first, InputIterator last)
+       {
+               insert(first, last);
+       }
+
+       flat_map(const flat_map& other)
+               : base_t(other)
+       {
+       }
+
+       std::pair<iterator, bool> insert(const value_type& x)
+       {
+               return this->insert_single(x);
+       }
+
+       template <typename InputIterator>
+       void insert(InputIterator first, InputIterator last)
+       {
+               for (; first != last; ++first)
+                       this->insert_single(*first);
+       }
+
+       void swap(flat_map& other)
+       {
+               base_t::vect.swap(other.vect);
+       }
+
+       mapped_type& operator[](const key_type& x)
+       {
+               return insert(std::make_pair(x, mapped_type())).first->second;
+       }
+
+       value_compare value_comp() const
+       {
+               return value_compare();
+       }
+};
+
+template <typename T, typename U, typename Comp = std::less<T> >
+class flat_multimap : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> >
+{
+       typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > base_t;
+
+ public:
+       typedef typename base_t::iterator iterator;
+       typedef typename base_t::value_type value_type;
+       typedef U mapped_type;
+       typedef typename base_t::value_compare value_compare;
+
+       flat_multimap() { }
+
+       template <typename InputIterator>
+       flat_multimap(InputIterator first, InputIterator last)
+       {
+               this->insert(first, last);
+       }
+
+       flat_multimap(const flat_multimap& other)
+               : base_t(other)
+       {
+       }
+
+       iterator insert(const value_type& x)
+       {
+               return this->insert_multi(x);
+       }
+
+       template <typename InputIterator>
+       void insert(InputIterator first, InputIterator last)
+       {
+               for (; first != last; ++first)
+                       this->insert_multi(*first);
+       }
+
+       void swap(flat_multimap& other)
+       {
+               base_t::vect.swap(other.vect);
+       }
+
+       value_compare value_comp() const
+       {
+               return value_compare();
+       }
+};
+
+} // namespace insp
diff --git a/include/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..1dd1b3b9835d699df41ac5f72d106aaa2c278caf 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,55 +64,49 @@ 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);
 
        /** This class returns true if two strings match.
         * Case sensitivity is ignored, and the RFC 'character set'
         * is adhered to
         */
-       struct CoreExport StrHashComp
+       struct StrHashComp
        {
                /** The operator () does the actual comparison in hash_map
                 */
-               bool operator()(const std::string& s1, const std::string& s2) const;
+               bool operator()(const std::string& s1, const std::string& s2) const
+               {
+                       return equals(s1, s2);
+               }
+       };
+
+       struct insensitive
+       {
+               size_t CoreExport operator()(const std::string &s) const;
+       };
+
+       struct insensitive_swo
+       {
+               bool CoreExport operator()(const std::string& a, const std::string& b) 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> {
-
+       struct CoreExport irc_char_traits : public std::char_traits<char>
+       {
                /** Check if two chars match.
                 * @param c1st First character
                 * @param c2nd Second character
@@ -144,7 +135,7 @@ namespace irc
                 * @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);
+               static 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
@@ -152,142 +143,83 @@ namespace irc
                 * @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);
+               static 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.
+       /** 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 Joined string.
         */
-       class CoreExport stringjoiner
-       {
-        private:
+       std::string CoreExport stringjoiner(const std::vector<std::string>& sequence, char separator = ' ');
 
-               /** Output string
+       /** 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
+        * false.
+        */
+       class CoreExport sepstream
+       {
+        protected:
+               /** Original string.
                 */
-               std::string joined;
-
+               std::string tokens;
+               /** Separator value
+                */
+               char sep;
+               /** Current string position
+                */
+               size_t pos;
+               /** If set then GetToken() can return an empty string
+                */
+               bool allow_empty;
         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
+               /** Create a sepstream and fill it with the provided data
                 */
-               stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end);
+               sepstream(const std::string &source, char separator, bool allowempty = false);
 
-               /** 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
+               /** 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
                 */
-               stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end);
+               bool GetToken(std::string& token);
 
-               /** 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
+               /** Fetch the entire remaining stream, without tokenizing
+                * @return The remaining part of the stream
                 */
-               stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end);
+               const std::string GetRemaining();
 
-               /** Get the joined sequence
-                * @return A reference to the joined string
+               /** Returns true if the end of the stream has been reached
+                * @return True if the end of the stream has been reached, otherwise false
                 */
-               std::string& GetJoined();
+               bool StreamEnd();
        };
 
-       /** irc::modestacker stacks mode sequences into a list.
-        * It can then reproduce this list, clamped to a maximum of MAXMODES
-        * values per line.
+       /** A derived form of sepstream, which seperates on commas
         */
-       class CoreExport modestacker
+       class CoreExport commasepstream : public sepstream
        {
-        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
+               /** Initialize with comma separator
                 */
-               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();
+               commasepstream(const std::string &source, bool allowempty = false) : sepstream(source, ',', allowempty)
+               {
+               }
+       };
 
-               /** 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.
+       /** A derived form of sepstream, which seperates on spaces
+        */
+       class CoreExport spacesepstream : public sepstream
+       {
+        public:
+               /** Initialize with space separator
                 */
-               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;
+               spacesepstream(const std::string &source, bool allowempty = false) : sepstream(source, ' ', allowempty)
+               {
                }
        };
 
@@ -303,47 +235,19 @@ namespace irc
         * list will be ":item". This is to allow for parsing 'source' fields
         * from data.
         */
-       class CoreExport tokenstream
+       class CoreExport tokenstream : private spacesepstream
        {
-        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.
@@ -357,76 +261,6 @@ namespace irc
                bool GetToken(long &token);
        };
 
-       /** 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.
-        */
-       class CoreExport sepstream
-       {
-        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;
-               /** Seperator value
-                */
-               char sep;
-        public:
-               /** Create a sepstream and fill it with the provided data
-                */
-               sepstream(const std::string &source, char seperator);
-
-               /** Destructor
-                */
-               virtual ~sepstream();
-
-               /** 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);
-
-               /** Fetch the entire remaining stream, without tokenizing
-                * @return The remaining part of the stream
-                */
-               virtual 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();
-       };
-
-       /** A derived form of sepstream, which seperates on commas
-        */
-       class CoreExport commasepstream : public sepstream
-       {
-        public:
-               /** Initialize with comma seperator
-                */
-               commasepstream(const std::string &source) : sepstream(source, ',')
-               {
-               }
-       };
-
-       /** A derived form of sepstream, which seperates on spaces
-        */
-       class CoreExport spacesepstream : public sepstream
-       {
-        public:
-               /** Initialize with space seperator
-                */
-               spacesepstream(const std::string &source) : sepstream(source, ' ')
-               {
-               }
-       };
-
        /** 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 +314,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..839cccb6ae9f3fffec2aebcddc18e0b43cd114d8 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 <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 "stdalgo.h"
 
 CoreExport extern InspIRCd* ServerInstance;
 
+/** Base class for manager classes that are still accessed using -> but are no longer pointers
+ */
+template <typename T>
+struct fakederef
+{
+       T* operator->()
+       {
+               return static_cast<T*>(this);
+       }
+};
+
+#include "config.h"
+#include "convto.h"
+#include "dynref.h"
+#include "consolecolors.h"
 #include "caller.h"
 #include "cull_list.h"
 #include "extensible.h"
+#include "fileutils.h"
 #include "numerics.h"
+#include "numeric.h"
 #include "uid.h"
+#include "server.h"
 #include "users.h"
 #include "channels.h"
 #include "timer.h"
@@ -98,99 +94,8 @@ CoreExport extern InspIRCd* ServerInstance;
 #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 +106,38 @@ class serverstats
   public:
        /** Number of accepted connections
         */
-       unsigned long statsAccept;
+       unsigned long Accept;
        /** Number of failed accepts
         */
-       unsigned long statsRefused;
+       unsigned long Refused;
        /** Number of unknown commands seen
         */
-       unsigned long statsUnknown;
+       unsigned long Unknown;
        /** Number of nickname collisions handled
         */
-       unsigned long statsCollisions;
+       unsigned long Collisions;
        /** Number of DNS queries sent out
         */
-       unsigned long statsDns;
+       unsigned long Dns;
        /** Number of good DNS replies received
         * NOTE: This may not tally to the number sent out,
         * due to timeouts and other latency issues.
         */
-       unsigned long statsDnsGood;
+       unsigned long DnsGood;
        /** Number of bad (negative) DNS replies received
         * NOTE: This may not tally to the number sent out,
         * due to timeouts and other latency issues.
         */
-       unsigned long statsDnsBad;
+       unsigned long DnsBad;
        /** Number of inbound connections seen
         */
-       unsigned long statsConnects;
+       unsigned long Connects;
        /** Total bytes of data transmitted
         */
-       unsigned long statsSent;
+       unsigned long Sent;
        /** Total bytes of data received
         */
-       unsigned long statsRecv;
+       unsigned long Recv;
 #ifdef _WIN32
        /** Cpu usage at last sample
        */
@@ -254,22 +159,16 @@ 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_HANDLER1(IsNickHandler, bool, const std::string&);
 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;
+DEFINE_HANDLER1(IsIdentHandler, bool, const std::string&);
+DEFINE_HANDLER1(IsChannelHandler, bool, const std::string&);
 
 /** The main class of the irc server.
  * This class contains instances of all the other classes in this software.
@@ -280,10 +179,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 +188,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,8 +197,15 @@ class CoreExport InspIRCd
         */
        char ReadBuffer[65535];
 
+       /** 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;
@@ -333,11 +216,7 @@ class CoreExport InspIRCd
 
        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
@@ -350,28 +229,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 +247,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 +263,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,15 +286,11 @@ 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
         */
@@ -443,11 +298,11 @@ class CoreExport InspIRCd
 
        /** User manager. Various methods and data associated with users.
         */
-       UserManager *Users;
+       UserManager Users;
 
        /** Channel list, a hash_map containing all channels XXX move to channel manager class
         */
-       chan_hash* chanlist;
+       chan_hash chanlist;
 
        /** List of the open ports
         */
@@ -461,13 +316,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,
@@ -499,24 +353,6 @@ class CoreExport InspIRCd
         */
        int BindPorts(FailedPortList &failed_ports);
 
-       /** Binds a socket on an already open file descriptor
-        * @param sockfd A valid file descriptor of an open socket
-        * @param port The port number to bind to
-        * @param addr The address to bind to (IP only)
-        * @param dolisten Should this port be listened on?
-        * @return True if the port was bound successfully
-        */
-       bool BindSocket(int sockfd, int port, const char* addr, bool dolisten = true);
-
-       /** 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
-        */
-       std::string GetServerDescription(const std::string& servername);
-
        /** 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
@@ -524,17 +360,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,38 +370,21 @@ 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);
-
-       /** Check we aren't running as root, and exit if we are
-        * @return Depending on the configuration, this function may never return
-        */
-       void CheckRoot();
-
-       /** 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);
+       chan_hash& GetChans() { return chanlist; }
 
        /** Return true if a channel name is valid
         * @param chname A channel name to verify
         * @return True if the name is valid
         */
-       caller2<bool, const char*, size_t> IsChannel;
+       caller1<bool, const std::string&> IsChannel;
 
        /** Return true if str looks like a server ID
-        * @param string to check against
+        * @param sid string to check against
         */
-       caller1<bool, const std::string&> IsSID;
-
-       /** Rehash the local server
-        */
-       caller1<void, const std::string&> Rehash;
+       static bool IsSID(const std::string& sid);
 
        /** Handles incoming signals after being set
         * @param signal the signal recieved
@@ -601,70 +409,25 @@ class CoreExport InspIRCd
         */
        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);
+       /** 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 const char* Format(const char* formatString, ...) CUSTOM_PRINTF(1, 2);
+       static const char* Format(va_list &vaList, const char* formatString) CUSTOM_PRINTF(2, 0);
 
        /** 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;
+       caller1<bool, const std::string&> 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;
-
-       /** 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);
-
-       /** 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
-        */
-       inline void AddCommand(Command *f)
-       {
-               Modules->AddService(*f);
-       }
-
-       /** 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);
-
-       /** 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);
+       caller1<bool, const std::string&> IsIdent;
 
        /** 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 +435,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 +445,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 +472,16 @@ 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);
 
        /** 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 +490,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
+        * @return True if the strings match, false 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).
-        */
-       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,86 +515,40 @@ 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;
 };
 
 ENTRYPOINT;
@@ -886,15 +562,18 @@ class CommandModule : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
        Version GetVersion()
        {
                return Version(cmd.name, VF_VENDOR|VF_CORE);
        }
 };
 
-#endif
+inline void stdalgo::culldeleter::operator()(classbase* item)
+{
+       if (item)
+               ServerInstance->GlobalCulls.AddItem(item);
+}
+
+#include "numericbuilder.h"
+#include "modules/whois.h"
+#include "modules/stats.h"
index c62c5a25099375193622b4331963256ef2e819ea..5c9c1059abe9faca68af318bb2997d8cc2e66032 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,12 @@ 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, long secs_from_now) : Timer(secs_from_now), sock(thesock), sfd(fd) { }
 
        /** Handle tick event
         */
-       virtual void Tick(time_t now);
+       virtual bool Tick(time_t now);
 };
 
 /**
@@ -102,30 +102,184 @@ 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;
+       };
+
+ 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();
+       StreamSocket() : iohook(NULL) { }
+       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; }
@@ -148,14 +302,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();
+       virtual 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
@@ -209,7 +371,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;
+       virtual void OnDataReady() CXX11_OVERRIDE = 0;
 
        /**
         * When an outbound connection fails, and the attempt times out, you
@@ -224,14 +386,10 @@ class CoreExport BufferedSocket : public StreamSocket
 
        virtual ~BufferedSocket();
  protected:
-       virtual void DoWrite();
+       void OnEventHandlerWrite() CXX11_OVERRIDE;
        BufferedSocketError BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned long timeout);
        BufferedSocketError BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip);
 };
 
-#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..ccc77da663156644b8723c34d6b998408ab07987 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 = 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..e99316b
--- /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 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.
+        */
+       IOHookProvider* const 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..f978e9c
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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;
+       /** 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;
+
+       /** 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
+        * @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");
+
+       /** 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
+        */
+       virtual void DisplayList(User* user, Channel* channel);
+
+       /** 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
+        */
+       virtual void DisplayEmptyList(User* user, Channel* channel);
+
+       /** 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
+        */
+       virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
+
+       /** Perform a rehash of this mode's configuration data
+        */
+       void DoRehash();
+
+       /** Handle the list mode.
+        * See mode.h
+        */
+       virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding);
+
+       /** 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);
+
+       /** 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..5d4a80d9f219756ea149a434d999cbe68f6df39b 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();
 
@@ -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..c952d09ae88f5859a7e8f3d6655383354ab19d0c 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
+uint64_t ConvToUInt64(const std::string& in);
+
+/**
+ * Represents a member of a channel.
+ * A Membership object is created when a user joins a channel, and destroyed when a user leaves
+ * (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 ConvToUInt64(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
index 1dab442d40ac6d993ae8c8787a89fc93c66e83a1..83b8f31be88cb596d6952f1c5fd317eb8ceffb8e 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);
+       virtual 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; }
+
+       /**
+        * Check whether this mode is a prefix mode
+        * @return non-NULL if this mode is a prefix mode, NULL otherwise
+        */
+       PrefixMode* IsPrefixMode();
+
        /**
-        * 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; }
+       const PrefixMode* IsPrefixMode() const;
+
        /**
-        * 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 handler inherits from ListModeBase
+        * @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise
         */
-       virtual unsigned int GetPrefixRank();
+       ListModeBase* IsListModeBase();
+
        /**
-        * 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; }
+       const ListModeBase* IsListModeBase() const;
+
        /**
-        * Returns the mode's parameter translation type
+        * Check whether this mode handler inherits from ParamModeBase
+        * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise
         */
-       inline TranslateType GetTranslateType() const { return m_paramtype; }
+       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 type
+        */
+       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, Modes::ChangeList& changelist);
+
+       /** Retrieves the level required to modify this mode.
+        * @param adding Whether the mode is being added or removed.
         */
-       virtual void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
+       inline unsigned int GetLevelRequired(bool adding) const
+       {
+               return adding ? ranktoset : ranktounset;
+       }
 
-       inline unsigned int GetLevelRequired() const { return levelrequired; }
+       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);
+
+       /**
+        * 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);
+
+
+       /**
+       * 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,7 +447,6 @@ 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);
 };
 
@@ -317,20 +460,9 @@ 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);
-};
-
 /**
  * The ModeWatcher class can be used to alter the behaviour of a mode implemented
  * by the core or by another module. To use ModeWatcher, derive a class from it,
@@ -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,147 @@ 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
+        */
+       ModeHandler* modehandlersbyid[MODETYPE_LAST][MODEID_MAX];
+
+       /** A map of mode handlers keyed by their name
+        */
+       ModeHandlerMap modehandlersbyname[MODETYPE_LAST];
+
+       /** Lists of mode handlers by type
         */
-       std::vector<ModeWatcher*> modewatchers[256];
-       /** Displays the current modes of a channel or user.
-        * Used by ModeParser::Process.
+       struct
+       {
+               /** List of mode handlers that inherit from ListModeBase
+                */
+               std::vector<ListModeBase*> list;
+
+               /** List of mode handlers that inherit from PrefixMode
+                */
+               std::vector<PrefixMode*> prefix;
+       } mhlist;
+
+       /** Mode watcher classes
         */
-       void DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text);
-       /** Displays the value of a list mode
-        * Used by ModeParser::Process.
+       ModeWatcherMap modewatchermap;
+
+       /** Last processed mode change
         */
-       void DisplayListModes(User* user, Channel* chan, std::string &mode_sequence);
+       Modes::ChangeList LastChangeList;
 
        /**
         * Attempts to apply a mode change to a user or channel
         */
-       ModeAction TryMode(User* user, User* targu, Channel* targc, bool adding, unsigned char mode, std::string &param, bool SkipACL);
+       ModeAction TryMode(User* user, User* targu, Channel* targc, Modes::Change& mcitem, bool SkipACL);
+
+       /** Returns a list of user or channel mode characters.
+        * Used for constructing the parts of the mode list in the 004 numeric.
+        * @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
+        */
+       std::string CreateModeList(ModeType mt, bool needparam = false);
+
+       /** Recreate the cached mode list that is displayed in the 004 numeric
+        * in Cached004ModeList.
+        * Called when a mode handler is added or removed.
+        */
+       void RecreateModeListFor004Numeric();
+
+       /** 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);
 
        /** The string representing the last set of modes to be parsed.
         * Use GetLastParse() to get this value, to be used for  display purposes.
         */
        std::string LastParse;
-       std::vector<std::string> LastParseParams;
-       std::vector<TranslateType> LastParseTranslate;
-
-       unsigned int sent[256];
 
-       unsigned int seq;
+       /** Cached mode list for use in 004 numeric
+        */
+       TR1NS::array<std::string, 3> Cached004ModeList;
 
  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'.
+       /** Initialize all built-in modes
         */
-       User* SanityChecks(User *user,const char *dest,Channel *chan,int status);
+       static void InitBuiltinModes();
+
+       static bool IsModeChar(char chr);
+
        /** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out.
         * E.g.
         *
@@ -474,13 +686,13 @@ class CoreExport ModeParser
         * 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; }
+       const std::string& GetLastParse() const { return LastParse; }
+
        /** 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 +708,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 +719,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 +777,26 @@ 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);
+       PrefixMode* FindPrefix(unsigned const char pfxletter);
 
-       /** Returns a list of mode characters which are usermodes.
-        * This is used in the 004 numeric when users connect.
+       /** Returns an array of modes:
+        * 1. User modes
+        * 2. Channel modes
+        * 3. Channel modes that require a parameter when set
+        * This is sent to users as the last part of the 004 numeric
         */
-       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();
+       const TR1NS::array<std::string, 3>& GetModeListFor004Numeric();
 
        /** Generates a list of modes, comma seperated by type:
         *  1; Listmodes EXCEPT those with a prefix
@@ -552,14 +804,68 @@ 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 const TR1NS::array<std::string, 3>& ModeParser::GetModeListFor004Numeric()
+{
+       return Cached004ModeList;
+}
+
+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..885c229
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace Modes
+{
+       struct Change;
+       class ChangeList;
+}
+
+/** A single mode to be changed
+ */
+struct Modes::Change
+{
+       bool adding;
+       ModeHandler* mh;
+       std::string param;
+
+       /**
+        * @param handler Mode handler
+        * @param add True if this mode is being set, false if removed
+        * @param parameter Mode parameter
+        */
+       Change(ModeHandler* handler, bool add, const std::string& parameter)
+               : adding(add)
+               , mh(handler)
+               , param(parameter)
+       {
+       }
+};
+
+/** A list of mode changes that can be applied on a Channel or User
+ */
+class Modes::ChangeList
+{
+ public:
+       typedef std::vector<Change> List;
+
+       /** Add a new mode to be changed to this ChangeList
+        * @param 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 4d4d0871fb07b18214d6de024b5fd55c97786b86..5244930d003f181c0e338fea984b4e8408d0a59f 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,
+
+       /** The module is a coremod and can be assumed to be loaded on all servers. */
+       VF_CORE = 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
 };
 
 /** Used to represent an event type, for user, channel or server
@@ -109,35 +115,33 @@ struct ModResult {
 /** InspIRCd major version.
  * 1.2 -> 102; 2.1 -> 201; 2.12 -> 212
  */
-#define INSPIRCD_VERSION_MAJ 200
+#define INSPIRCD_VERSION_MAJ 202
 /** 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 10
+#define INSPIRCD_VERSION_API 1
 
 /**
  * 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 IntModuleList& _handlers = ServerInstance->Modules->EventHandlers[I_ ## y]; \
+       for (IntModuleList::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 +153,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 IntModuleList& _handlers = ServerInstance->Modules->EventHandlers[I_ ## n]; \
+       for (IntModuleList::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 +210,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 +219,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,22 +227,21 @@ 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_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart,
+       I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo,
+       I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNick,
+       I_OnUserMessage, I_OnMode, I_OnSyncUser,
+       I_OnSyncChannel, I_OnDecodeMetaData, I_OnAcceptConnection, I_OnUserInit,
        I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine,
-       I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule,
+       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_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan,
-       I_OnDelBan, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
+       I_OnPostTopicChange, I_OnPostConnect,
+       I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
        I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnPostCommand, I_OnPostJoin,
-       I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
-       I_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO,
+       I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
+       I_OnText, I_OnPassCompare, I_OnNamesListItem, I_OnNumeric,
        I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP,
        I_END
 };
@@ -349,6 +253,11 @@ 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:
        /** File that this module was loaded from
         */
@@ -376,7 +285,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();
+       virtual CullResult cull() CXX11_OVERRIDE;
 
        /** Default destructor.
         * destroys a module class
@@ -387,6 +296,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 +393,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 +422,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
@@ -567,14 +475,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual void OnInfo(User* user);
 
-       /** Called whenever a /WHOIS is performed on a local user.
-        * The source parameter contains the details of the user who issued the WHOIS command, and
-        * the dest parameter contains the information of the user they are whoising.
-        * @param source The user issuing the WHOIS command
-        * @param dest The user who is being WHOISed
-        */
-       virtual void OnWhois(User* source, User* dest);
-
        /** Called whenever a user is about to invite another user into a channel, before any processing is done.
         * Returning 1 from this function stops the process immediately, causing no
         * output to be sent to the user by the core. If you do this you must produce your own numerics,
@@ -594,8 +494,10 @@ class CoreExport Module : public classbase, public usecountbase
         * @param dest The user being invited
         * @param channel The channel the user is being invited to
         * @param timeout The time the invite will expire (0 == never)
+        * @param notifyrank Rank required to get an invite announcement (if enabled)
+        * @param notifyexcepts List of users to not send the default NOTICE invite announcement to
         */
-       virtual void OnUserInvite(User* source,User* dest,Channel* channel, time_t timeout);
+       virtual void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts);
 
        /** Called whenever a user is about to PRIVMSG A user or a channel, before any processing is done.
         * Returning any nonzero value from this function stops the process immediately, causing no
@@ -611,30 +513,10 @@ class CoreExport Module : public classbase, public usecountbase
         * @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.
+        * @param msgtype The message type, MSG_PRIVMSG for PRIVMSGs, MSG_NOTICE for NOTICEs
         * @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);
+       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list, MessageType msgtype);
 
        /** 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
@@ -645,19 +527,16 @@ 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);
+       virtual ModResult OnUserPreNick(LocalUser* user, const std::string& newnick);
 
        /** Called after any PRIVMSG sent from a user.
         * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
@@ -668,25 +547,14 @@ class CoreExport Module : public classbase, public usecountbase
         * @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.
+        * @param msgtype The message type, MSG_PRIVMSG for PRIVMSGs, MSG_NOTICE for NOTICEs
         */
-       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);
+       virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list, MessageType msgtype);
 
        /** 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 difference between this event and 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
@@ -699,68 +567,47 @@ class CoreExport Module : public classbase, public usecountbase
        virtual void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
 
        /** 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".
+        * 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 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
+        * @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.
+        * @param output_mode Changed modes, including '+' and '-' characters, not including any parameters
         */
-       virtual void OnGetServerDescription(const std::string &servername,std::string &description);
+       virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags, const std::string& output_mode);
 
        /** Allows modules to synchronize data which relates to users during a netburst.
         * When this function is called, it will be called from the module which implements
-        * 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!
+        * the linking protocol. This currently is m_spanningtree.so.
+        * This function will be called for every user visible on your side
+        * of the burst, allowing you to for example set modes, etc.
         * @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!
+        * @param server The target of the burst
         */
-       virtual void OnSyncUser(User* user, Module* proto, void* opaque);
+       virtual void OnSyncUser(User* user, ProtocolServer& server);
 
        /** 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
+        * the linking protocol. This currently is m_spanningtree.so.
+        * This function will be called for every channel visible on your side of the burst,
+        * allowing you to for example set modes, etc.
         *
         * @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!
+        * @param server The target of the burst
         */
-       virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque);
+       virtual void OnSyncChannel(Channel* chan, ProtocolServer& server);
 
-       /* 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.
+       /** Allows modules to syncronize metadata not related to users or channels, over the network during a netburst.
+        * When the linking module has finished sending all data it wanted to send during a netburst, then
+        * this method is called. You should use the SendMetaData() function after you've
+        * correctly decided how the data should be represented, to send the data.
+        * @param server The target of the burst
         */
-       virtual void OnSyncNetwork(Module* proto, void* opaque);
+       virtual void OnSyncNetwork(ProtocolServer& server);
 
        /** 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.
@@ -770,43 +617,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        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
@@ -848,16 +658,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
@@ -870,7 +679,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
@@ -879,15 +688,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,
@@ -904,14 +713,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,
@@ -976,7 +777,7 @@ class CoreExport Module : public classbase, public usecountbase
         * @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
         */
-       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 std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line);
 
        /** Called when a user is first connecting, prior to starting DNS lookups, checking initial
         * connect class, or accepting any commands.
@@ -1020,15 +821,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
@@ -1079,14 +879,10 @@ class CoreExport Module : public classbase, public usecountbase
 
        /** Called on all /STATS commands
         * This method is triggered for all /STATS use, including stats symbols handled by the core.
-        * @param symbol the symbol provided to /STATS
-        * @param user the user issuing the /STATS command
-        * @param results A string_list to append results into. You should put all your results
-        * into this string_list, rather than displaying them directly, so that your handler will
-        * work when remote STATS queries are received.
+        * @param stats Context of the /STATS request, contains requesting user, list of answer rows etc.
         * @return 1 to block the /STATS from being processed by the core, 0 to allow it
         */
-       virtual ModResult OnStats(char symbol, User* user, string_list &results);
+       virtual ModResult OnStats(Stats::Context& stats);
 
        /** Called whenever a change of a local users displayed host is attempted.
         * Return 1 to deny the host change, or 0 to allow it.
@@ -1122,18 +918,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).
@@ -1146,14 +930,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.
@@ -1162,30 +938,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()
@@ -1195,48 +947,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.
@@ -1247,19 +957,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual ModResult OnSetAway(User* user, const std::string &awaymsg);
 
-       /** Called whenever a line of WHOIS output is sent to a user.
-        * You may change the numeric and the text of the output by changing
-        * the values numeric and text, but you cannot change the user the
-        * numeric is sent to. You may however change the user's User values.
-        * @param user The user the numeric is being sent to
-        * @param dest The user being WHOISed
-        * @param numeric The numeric of the line being sent
-        * @param text The text of the numeric, including any parameters
-        * @return nonzero to drop the line completely so that the user does not
-        * receive it, or zero to allow the line to be sent.
-        */
-       virtual ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text);
-
        /** Called at intervals for modules to garbage-collect any hashes etc.
         * Certain data types such as hash_map 'leak' buckets, which must be
         * tidied up and freed by copying into a new item every so often. This
@@ -1273,26 +970,36 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass);
 
+#ifdef INSPIRCD_ENABLE_TESTSUITE
        /** Add test suite hooks here. These are used for testing functionality of a module
         * via the --testsuite debugging parameter.
         */
        virtual void OnRunTestSuite();
+#endif
 
        /** 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.
+        * For example NAMESX, channel mode +u and +I, and UHNAMES.
+        * @param issuer The user who is going to receive the NAMES list being built
+        * @param item The channel member being considered for inclusion
+        * @param prefixes The prefix character(s) to display, initially set to the prefix char of the most powerful
+        * prefix mode the member has, can be changed
+        * @param nick The nick to display, initially set to the member's nick, can be changed
+        * @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 void OnNamesListItem(User* issuer, Membership* item, std::string &prefixes, std::string &nick);
+       virtual ModResult OnNamesListItem(User* issuer, Membership* item, std::string& prefixes, std::string& nick);
 
-       virtual ModResult OnNumeric(User* user, unsigned int numeric, const std::string &text);
+       virtual ModResult OnNumeric(User* user, const Numeric::Numeric& numeric);
 
        /** Called whenever a result from /WHO is about to be returned
         * @param source The user running the /WHO query
         * @param params The parameters to the /WHO query
         * @param user The user that this line of the query is about
-        * @param line The raw line to send; modifiable, if empty no line will be returned.
+        * @param memb The member shown in this line, NULL if no channel is in this line
+        * @param numeric Numeric to send; modifiable.
+        * @return MOD_RES_PASSTHRU to allow the line to be displayed, MOD_RES_DENY to hide it
         */
-       virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line);
+       virtual ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric);
 
        /** Called whenever a local user's IP is set for the first time, or when a local user's IP changes due to
         * a module like m_cgiirc changing it.
@@ -1301,195 +1008,23 @@ class CoreExport Module : public classbase, public usecountbase
        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
-        */
-       void CalcSize();
-
- public:
-       /** Default constructor.
-        * This method does not load any file into memory, you must use the LoadFile method
-        * after constructing the class this way.
-        */
-       FileReader();
-
-       /** 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.
-        */
-       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();
-};
-
 /** 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
         */
@@ -1501,9 +1036,23 @@ 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.
@@ -1513,6 +1062,23 @@ class CoreExport ModuleManager
        /** 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();
@@ -1539,12 +1105,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.
@@ -1553,7 +1113,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().
@@ -1585,6 +1145,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
         */
@@ -1604,25 +1169,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.
@@ -1637,6 +1195,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++)
@@ -1653,13 +1216,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 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
         */
-       const std::vector<std::string> GetAllModuleNames(int filter);
+       void DelReferent(ServiceProvider* service);
 };
 
 /** Do not mess with these functions unless you know the C preprocessor
@@ -1672,7 +1243,7 @@ class CoreExport ModuleManager
 #define MODULE_INIT_SYM_FN_2(x,y) MODULE_INIT_SYM_FN_1(x,y)
 #define MODULE_INIT_SYM_FN_1(x,y) inspircd_module_ ## x ## _ ## y
 
-#ifdef PURE_STATIC
+#ifdef INSPIRCD_STATIC
 
 struct AllCommandList {
        typedef Command* (*fn)(Module*);
@@ -1689,11 +1260,7 @@ struct AllModuleList {
 };
 
 #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
+       static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAME ".so");
 
 #else
 
@@ -1718,7 +1285,7 @@ struct AllModuleList {
                } \
                return TRUE; \
        } \
-       extern "C" DllExport const char inspircd_src_version[] = VERSION " r" REVISION;
+       extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION;
 
 #else
 
@@ -1727,11 +1294,9 @@ struct AllModuleList {
        { \
                return new y; \
        } \
-       extern "C" const char inspircd_src_version[] = VERSION " r" REVISION;
+       extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION;
 #endif
 
 #define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
 
 #endif
-
-#endif
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/cap.h b/include/modules/cap.h
new file mode 100644 (file)
index 0000000..86a60c4
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * 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;
+               void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
+       };
+
+       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;
+               }
+       };
+}
diff --git a/include/modules/dns.h b/include/modules/dns.h
new file mode 100644 (file)
index 0000000..f3bf459
--- /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)
+               {
+                       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..b590a57
--- /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)
+               : ModuleEventListener(mod, "event/exemption")
+       {
+       }
+
+ 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/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..b4b88be
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * 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>
+
+/** 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 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(const std::string& request_type, const std::string& uri,
+               HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata)
+               : 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;
+       }
+};
+
+/** 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..e03ee16
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 IRCv3
+{
+       class WriteNeighborsWithCap;
+}
+
+class IRCv3::WriteNeighborsWithCap : public User::ForEachNeighborHandler
+{
+       const Cap::Capability& cap;
+       const std::string& msg;
+
+       void Execute(LocalUser* user) CXX11_OVERRIDE
+       {
+               if (cap.get(user))
+                       user->Write(msg);
+       }
+
+ public:
+       WriteNeighborsWithCap(User* user, const std::string& message, const Cap::Capability& capability)
+               : cap(capability)
+               , msg(message)
+       {
+               user->ForEachNeighbor(*this, false);
+       }
+};
diff --git a/include/modules/ldap.h b/include/modules/ldap.h
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/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..0a7b19a
--- /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 parameterlist& params) = 0;
+};
diff --git a/include/modules/spanningtree.h b/include/modules/spanningtree.h
new file mode 100644 (file)
index 0000000..e71cdf9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 SpanningTreeEventListener : public Events::ModuleEventListener
+{
+ public:
+       SpanningTreeEventListener(Module* mod)
+               : ModuleEventListener(mod, "event/spanningtree")
+       {
+       }
+
+       /** Fired when a server finishes burst
+        * @param server Server that recently linked and finished burst
+        */
+       virtual void OnServerLink(const Server* server) { }
+
+        /** Fired when a server splits
+         * @param server Server that split
+         */
+       virtual void OnServerSplit(const Server* server) { }
+};
diff --git a/include/modules/sql.h b/include/modules/sql.h
new file mode 100644 (file)
index 0000000..f81a85a
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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
+
+/** 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->GetRealHost();
+               userinfo["ip"] = user->GetIPString();
+               userinfo["gecos"] = user->fullname;
+               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..d3372c5
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * 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;
+       }
+
+       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();
+       }
+};
+
+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)
+                       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;
+
+       /** 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..d2f6eab
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace Stats
+{
+       class Context;
+       class Row;
+}
+
+class Stats::Row : public Numeric::Numeric
+{
+ public:
+       Row(unsigned int num)
+               : Numeric(num)
+       {
+       }
+};
+
+class Stats::Context
+{
+       /** Source user of the STATS request
+        */
+       User* const source;
+
+       /** List of reply rows
+        */
+       std::vector<Row> rows;
+
+       /** Symbol indicating the type of this STATS request (usually a letter)
+        */
+       const char symbol;
+
+ public:
+       /** Constructor
+        * @param src Source user of the STATS request, can be a local or remote user
+        * @param sym Symbol (letter) indicating the type of the request
+        */
+       Context(User* src, char sym)
+               : source(src)
+               , symbol(sym)
+       {
+       }
+
+       /** Get the source user of the STATS request
+        * @return Source user of the STATS request
+        */
+       User* GetSource() const { return source; }
+
+       /** Get the list of reply rows
+        * @return List of rows generated as reply for the request
+        */
+       const std::vector<Row>& GetRows() const { return rows; }
+
+       /** Get the symbol (letter) indicating what type of STATS was requested
+        * @return Symbol specified by the requesting user
+        */
+       char GetSymbol() const { return symbol; }
+
+       /** Add a row to the reply list
+        * @param row Reply to add
+        */
+       void AddRow(const Row& row) { rows.push_back(row); }
+
+       template <typename T1>
+       void AddRow(unsigned int numeric, T1 p1)
+       {
+               Row n(numeric);
+               n.push(p1);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2, typename T3>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4, typename T5>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               n.push(p5);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               n.push(p5);
+               n.push(p6);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               n.push(p5);
+               n.push(p6);
+               n.push(p7);
+               AddRow(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
+       void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
+       {
+               Row n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               n.push(p5);
+               n.push(p6);
+               n.push(p7);
+               n.push(p8);
+               AddRow(n);
+       }
+};
diff --git a/include/modules/whois.h b/include/modules/whois.h
new file mode 100644 (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..8044fe5
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "numerics.h"
+
+namespace Numeric
+{
+       class Numeric;
+}
+
+class Numeric::Numeric
+{
+       /** Numeric number
+        */
+       unsigned int numeric;
+
+       /** Parameters of the numeric
+        */
+       std::vector<std::string> params;
+
+       /** Source server of the numeric, if NULL (the default) then it is the local server
+        */
+       Server* sourceserver;
+
+ public:
+       /** Constructor
+        * @param num Numeric number (RPL_*, ERR_*)
+        */
+       Numeric(unsigned int num)
+               : numeric(num)
+               , sourceserver(NULL)
+       {
+       }
+
+       /** Add a parameter to the numeric. The parameter will be converted to a string first with ConvToStr().
+        * @param x Parameter to add
+        */
+       template <typename T>
+       Numeric& push(const T& x)
+       {
+               params.push_back(ConvToStr(x));
+               return *this;
+       }
+
+       /** Set the source server of the numeric. The source server defaults to the local server.
+        * @param server Server to set as source
+        */
+       void SetServer(Server* server) { sourceserver = server; }
+
+       /** Get the source server of the numeric
+        * @return Source server or NULL if the source is the local server
+        */
+       Server* GetServer() const { return sourceserver; }
+
+       /** Get the number of the numeric as an unsigned integer
+        * @return Numeric number as an unsigned integer
+        */
+       unsigned int GetNumeric() const { return numeric; }
+
+       /** Get the parameters of the numeric
+        * @return Parameters of the numeric as a const vector of strings
+        */
+       const std::vector<std::string>& GetParams() const { return params; }
+
+       /** Get the parameters of the numeric
+        * @return Parameters of the numeric as a vector of strings
+        */
+       std::vector<std::string>& GetParams() { return params; }
+};
+
+namespace Numerics
+{
+       /** ERR_NOSUCHNICK numeric
+        */
+       class NoSuchNick : public Numeric::Numeric
+       {
+        public:
+               NoSuchNick(const std::string& nick)
+                       : Numeric(ERR_NOSUCHNICK)
+               {
+                       push(nick);
+                       push("No such nick/channel");
+               }
+       };
+}
diff --git a/include/numericbuilder.h b/include/numericbuilder.h
new file mode 100644 (file)
index 0000000..17aa9e0
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015-2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace Numeric
+{
+       class WriteNumericSink;
+       class WriteRemoteNumericSink;
+
+       template <char Sep, bool SendEmpty, typename Sink>
+       class GenericBuilder;
+
+       template <char Sep = ',', bool SendEmpty = false>
+       class Builder;
+
+       template <unsigned int NumStaticParams, bool SendEmpty, typename Sink>
+       class GenericParamBuilder;
+
+       template <unsigned int NumStaticParams, bool SendEmpty = false>
+       class ParamBuilder;
+}
+
+class Numeric::WriteNumericSink
+{
+       LocalUser* const user;
+
+ public:
+       WriteNumericSink(LocalUser* u)
+               : user(u)
+       {
+       }
+
+       void operator()(Numeric& numeric) const
+       {
+               user->WriteNumeric(numeric);
+       }
+};
+
+class Numeric::WriteRemoteNumericSink
+{
+       User* const user;
+
+ public:
+       WriteRemoteNumericSink(User* u)
+               : user(u)
+       {
+       }
+
+       void operator()(Numeric& numeric) const
+       {
+               user->WriteRemoteNumeric(numeric);
+       }
+};
+
+template <char Sep, bool SendEmpty, typename Sink>
+class Numeric::GenericBuilder
+{
+       Sink sink;
+       Numeric numeric;
+       const std::string::size_type max;
+
+       bool HasRoom(const std::string::size_type additional) const
+       {
+               return (numeric.GetParams().back().size() + additional <= max);
+       }
+
+ public:
+       GenericBuilder(Sink s, unsigned int num, bool addparam = true, size_t additionalsize = 0)
+               : sink(s)
+               , numeric(num)
+               , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 10)
+       {
+               if (addparam)
+                       numeric.push(std::string());
+       }
+
+       Numeric& GetNumeric() { return numeric; }
+
+       void Add(const std::string& entry)
+       {
+               if (!HasRoom(entry.size()))
+                       Flush();
+               numeric.GetParams().back().append(entry).push_back(Sep);
+       }
+
+       void Add(const std::string& entry1, const std::string& entry2)
+       {
+               if (!HasRoom(entry1.size() + entry2.size()))
+                       Flush();
+               numeric.GetParams().back().append(entry1).append(entry2).push_back(Sep);
+       }
+
+       void Flush()
+       {
+               std::string& data = numeric.GetParams().back();
+               if (IsEmpty())
+               {
+                       if (!SendEmpty)
+                               return;
+               }
+               else
+               {
+                       data.erase(data.size()-1);
+               }
+
+               sink(numeric);
+               data.clear();
+       }
+
+       bool IsEmpty() const { return (numeric.GetParams().back().empty()); }
+};
+
+template <char Sep, bool SendEmpty>
+class Numeric::Builder : public GenericBuilder<Sep, SendEmpty, WriteNumericSink>
+{
+ public:
+       Builder(LocalUser* user, unsigned int num, bool addparam = true, size_t additionalsize = 0)
+               : ::Numeric::GenericBuilder<Sep, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, addparam, additionalsize + user->nick.size())
+       {
+       }
+};
+
+template <unsigned int NumStaticParams, bool SendEmpty, typename Sink>
+class Numeric::GenericParamBuilder
+{
+       Sink sink;
+       Numeric numeric;
+       std::string::size_type currlen;
+       std::string::size_type max;
+
+       bool HasRoom(const std::string::size_type additional) const
+       {
+               return (currlen + additional <= max);
+       }
+
+ public:
+       GenericParamBuilder(Sink s, unsigned int num, size_t additionalsize)
+               : sink(s)
+               , numeric(num)
+               , currlen(0)
+               , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 10)
+       {
+       }
+
+       void AddStatic(const std::string& entry)
+       {
+               max -= (entry.length() + 1);
+               numeric.GetParams().push_back(entry);
+       }
+
+       void Add(const std::string& entry)
+       {
+               if (!HasRoom(entry.size()))
+                       Flush();
+
+               currlen += entry.size() + 1;
+               numeric.GetParams().push_back(entry);
+       }
+
+       void Flush()
+       {
+               if ((!SendEmpty) && (IsEmpty()))
+                       return;
+
+               sink(numeric);
+               currlen = 0;
+               numeric.GetParams().erase(numeric.GetParams().begin() + NumStaticParams, numeric.GetParams().end());
+       }
+
+       bool IsEmpty() const { return (numeric.GetParams().size() <= NumStaticParams); }
+};
+
+template <unsigned int NumStaticParams, bool SendEmpty>
+class Numeric::ParamBuilder : public GenericParamBuilder<NumStaticParams, SendEmpty, WriteNumericSink>
+{
+ public:
+       ParamBuilder(LocalUser* user, unsigned int num)
+               : ::Numeric::GenericParamBuilder<NumStaticParams, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, user->nick.size())
+       {
+       }
+};
index 4fce4cb6de53b746c9ef6a8e6798fdca00aa8a1b..2dc8d841489651d35c514740c81762e4ae01e9d8 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!
  * 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_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_SNOMASKIS                   = 8, // unrealircd
+       RPL_REDIR                       = 10,
+
+       RPL_MAP                         = 15, // ircu
+       RPL_ENDMAP                      = 17, // ircu
+       RPL_MAPUSERS                    = 18, // insp-specific
+
+       RPL_UMODEIS                     = 221,
+       RPL_RULES                       = 232, // unrealircd
+
+       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_UNAWAY                      = 305,
+       RPL_NOWAWAY                     = 306,
+
+       RPL_RULESTART                   = 308, // unrealircd
+       RPL_RULESEND                    = 309, // unrealircd
+
+       RPL_WHOISSERVER                 = 312,
+       RPL_WHOWASUSER                  = 314,
+
+       RPL_ENDOFWHO                    = 315,
+       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_WHOREPLY                    = 352,
+       RPL_NAMREPLY                    = 353,
+       RPL_LINKS                       = 364,
+       RPL_ENDOFLINKS                  = 365,
+       RPL_ENDOFNAMES                  = 366,
+       RPL_ENDOFWHOWAS                 = 369,
+
+       RPL_INFO                        = 371,
+       RPL_ENDOFINFO                   = 374,
+       RPL_MOTD                        = 372,
+       RPL_MOTDSTART                   = 375,
+       RPL_ENDOFMOTD                   = 376,
+
+       RPL_WHOWASIP                    = 379,
+
+       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_INVALIDCAPSUBCOMMAND        = 410, // ratbox/charybdis(?)
+       ERR_NOTEXTTOSEND                = 412,
+       ERR_UNKNOWNCOMMAND              = 421,
+       ERR_NOMOTD                      = 422,
+       ERR_NONICKNAMEGIVEN             = 431,
+       ERR_ERRONEUSNICKNAME            = 432,
+       ERR_NICKNAMEINUSE               = 433,
+       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_YOUREBANNEDCREEP            = 465,
+       ERR_UNKNOWNMODE                 = 472,
 
        /*
         * A quick side-rant about the next group of numerics..
@@ -131,31 +164,39 @@ enum Numerics
         *
         *  -- 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_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
+
+       ERR_RESTRICTED                  = 484,
 
-#endif
+       ERR_ALLMUSTSSL                  = 490, // unrealircd
+       ERR_NOOPERHOST                  = 491,
+       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_SYNTAX                      = 650, // insp-specific
+
+       ERR_CHANOPEN                    = 713,
+       ERR_KNOCKONCHAN                 = 714,
+
+       RPL_OTHERUMODEIS                = 803, // insp-specific
+       RPL_OTHERSNOMASKIS              = 804, // insp-specific
+
+       ERR_WORDFILTERED                = 936, // insp-specific, would be nice if we could get rid of this..
+       ERR_CANTUNLOADMODULE            = 972, // insp-specific
+       RPL_UNLOADEDMODULE              = 973, // insp-specific
+       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..b00082b
--- /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
+       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
+       {
+               T* mh = static_cast<T*>(this);
+               mh->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..2e512f11ae5a6f5906068eeff34405bebb2a0571 100644 (file)
@@ -18,8 +18,7 @@
  */
 
 
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
+#pragma once
 
 #include "hashcomp.h"
 
@@ -27,114 +26,121 @@ 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 gecos;
+               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 parameterlist& 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 parameterlist& 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 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 recieve.
         * @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..e54a379
--- /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 (GECOS) 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..8c7cc2e4e06bcdaf6206bc80d5e8d843011c024f 100644 (file)
@@ -22,8 +22,7 @@
  */
 
 
-#ifndef INSPIRCD_SOCKET_H
-#define INSPIRCD_SOCKET_H
+#pragma once
 
 #ifndef _WIN32
 
@@ -110,9 +109,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
@@ -120,24 +116,12 @@ namespace irc
                 * @return true if the conversion was successful, false if not.
                 */
                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
-                */
-               inline std::string satouser(const irc::sockets::sockaddrs& sa) { return sa.str(); }
        }
 }
 
+/** 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 +131,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..0187a043ed7168adb7cc3b06b4dcdbb4e969b6d6 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 OnEventHandlerWrite();
+
+       /** Called by the socket engine in case of an error event.
+        * The default implementation does nothing.
+        * @param errornum Error code
         */
-       virtual void HandleEvent(EventType et, int errornum = 0) = 0;
+       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;
+
+       /** Socket engine statistics: count of various events, bandwidth usage
+        */
+       static Statistics stats;
 
-       int MAX_DESCRIPTORS;
+       /** Look up the fd limit using rlimit. */
+       static void LookupMaxFds();
 
-       size_t indata;
-       size_t outdata;
-       time_t lastempty;
+       /** Terminates the program when the socket engine fails to initialize. */
+       static void InitError();
 
-       void UpdateStats(size_t len_in, size_t len_out);
+       static void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
 
-       virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask) = 0;
-       void SetEventMask(EventHandler* eh, int value);
-public:
+       /** Add an event handler to the base socket engine. AddFd(EventHandler*, int) should call this.
+        */
+       static bool AddFdRef(EventHandler* eh);
 
-       unsigned long TotalEvents;
-       unsigned long ReadEvents;
-       unsigned long WriteEvents;
-       unsigned long ErrorEvents;
+       static void DelFdRef(EventHandler* eh);
+
+       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,11 +484,11 @@ 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 to The remote IP address and port.
         * @param tolen The size of the to parameter.
         * @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 sockaddr *to, socklen_t tolen);
 
        /** Abstraction for BSD sockets connect(2).
         * This function should emulate its namesake system call exactly.
@@ -431,19 +497,19 @@ 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 Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen);
+       static int Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen);
 
        /** 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 +517,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 +550,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 +582,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..f446596
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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)));
+               }
+
+               /** 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());
+       }
+}
index f91e508c930597c0fa12bb4f2e59322370599d00..c760513f892411febae77d39241ac9c35178e185 100644 (file)
  */
 
 
-#ifndef TESTSUITE_H
-#define TESTSUITE_H
+#pragma once
+
+#ifdef INSPIRCD_ENABLE_TESTSUITE
 
 class TestSuite
 {
-       bool RealGenerateUIDTests();
  public:
        TestSuite();
        ~TestSuite();
index 4bf5a48f38399a94d367989f405f9bff213f5936..fec1bbb96f0d06ccac102e82d3cca44cafeeb93b 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.
         */
@@ -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..a597427e3c5c55a14ed0d7f8fd44eb7809622cb9 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(time_t 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
-
index 03593d40f45e616998a9509d37fad8b400d8c280..62ceb564e5ba242959462b87236e589375861526 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;
 
 #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
+typedef TR1NS::unordered_map<std::string, User*, irc::insensitive, irc::StrHashComp> user_hash;
+typedef TR1NS::unordered_map<std::string, Channel*, irc::insensitive, irc::StrHashComp> chan_hash;
 
-/** A list holding local users, this is the type of UserManager::local_users
+/** List of channels to consider when building the neighbor list of a user
  */
-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;
-
-/** Holds a complete list of all allow and deny tags from the configuration file (connection classes)
- */
-typedef std::vector<reference<ConnectClass> > ClassVector;
-
-/** Typedef for the list of user-channel records for a user
- */
-typedef 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;
 
@@ -150,7 +96,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 +110,7 @@ typedef XLineContainer::iterator ContainerIter;
  */
 typedef XLineLookup::iterator LookupIter;
 
-
-#endif
-
+namespace Stats
+{
+       class Context;
+}
index 17061bdee972a72f296459bfad966e93f735ccd2..772c8a7165fafb9b36e35db98b4885afa1be1add 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 TestSuite;
+
+class CoreExport UIDGenerator
+{
+       friend class TestSuite;
+
+       /** 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..531d50773a2f108e532ffd0de3ce18683d5c6a82 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
+       /** 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.
         */
-       unsigned int local_count;
+       void DoBackgroundUserStuff();
 
-       /** Map of global ip addresses for clone counting
-        * XXX - this should be private, but m_clones depends on it currently.
+       /** 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
         */
-       clonemap global_clones;
+       bool AllModulesReportReady(LocalUser* user);
 
-       /** Add a client to the system.
-        * This will create a new User, insert it into the user_hash,
-        * initialize it as not yet registered, and add it to the socket engine.
-        * @param socket The socket id (file descriptor) this user is on
-        * @param via The socket that this user connected using
+       /** Handle a client connection.
+        * Creates a new LocalUser object, inserts it into the appropriate containers,
+        * initializes it as not yet registered, and adds it to the socket engine.
+        *
+        * The new user may immediately be quit after being created, for example if the user limit
+        * is reached or if the user is banned.
+        * @param socket File descriptor of the connection
+        * @param via Listener socket that this user connected to
         * @param client The IP address and client port of the user
         * @param server The server IP address and port used by the user
-        * @return This function has no return value, but a call to AddClient may remove the user.
         */
        void AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 
-       /** Disconnect a user gracefully
+       /** Disconnect a user gracefully.
+        * When this method returns the user provided will be quit, but the User object will continue to be valid and will be deleted at the end of the current main loop iteration.
         * @param user The user to remove
         * @param quitreason The quit reason to show to normal users
-        * @param operreason The quit reason to show to opers
-        * @return Although this function has no return type, on exit the user provided will no longer exist.
+        * @param operreason The quit reason to show to opers, can be NULL if same as quitreason
         */
-       void QuitUser(User *user, const std::string &quitreason, const char* operreason = "");
+       void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL);
 
-       /** Add a user to the local clone map
+       /** Add a user to the clone map
         * @param user The user to add
         */
-       void AddLocalClone(User *user);
-
-       /** Add a user to the global 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 +131,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 +189,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..ef59646992a7a494c4bf117372f6b9696c412069 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,37 @@ 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 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 +286,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 +308,10 @@ 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 +319,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 +331,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 +345,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 +352,24 @@ 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;
-
-       /** This is true if the user matched an exception (E:Line). It is used to save time on ban checks.
-        */
-       unsigned int exempt:1;
-
-       /** has the user responded to their previous ping?
-        */
-       unsigned int lastping:1;
-
        /** What type of user is this? */
        const unsigned int usertype:2;
 
        /** Get client IP string from sockaddr, using static internal buffer
         * @return The IP string
         */
-       const char* GetIPString();
+       const std::string& GetIPString();
+
+       /** Retrieves this user's hostname.
+        * @param uncloak If true then return the real host; otherwise, the display host.
+        */
+       const std::string& GetHost(bool uncloak) const;
+
+       /** Retrieves this user's displayed hostname. */
+       const std::string& GetDisplayedHost() const;
+
+       /** Retrieves this user's real hostname. */
+       const std::string& GetRealHost() const;
 
        /** Get CIDR mask, using default range, for this user
         */
@@ -393,20 +378,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, bool recheck_eline = true);
 
        virtual void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true);
 
        /** 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, int 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 +407,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 +425,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
-        */
-       void SetNoticeMask(unsigned char sm, bool value);
-
-       /** Create a displayable mode string for this users umodes
-        * @param showparameters The mode string
+       /** Get the mode letters of modes set on the user as a string.
+        * @param includeparams True to get the parameters of the modes as well. Defaults to false.
+        * @return Mode letters of modes set on the user and optionally the parameters of those modes, if any.
+        * The returned string always begins with a '+' character. If the user has no modes set, "+" is returned.
         */
-       const char* FormatModes(bool showparameters = false);
+       std::string GetModeLetters(bool includeparams = false) const;
 
        /** Returns true if a specific mode is set
         * @param m The user mode
         * @return True if the mode is set
         */
-       bool IsModeSet(unsigned char m);
+       bool IsModeSet(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
@@ -491,17 +469,10 @@ class CoreExport User : public Extensible
        /** Returns true or false if a user can set a privileged user or channel mode.
         * This is done by looking up their oper type from User::oper, then referencing
         * this to their oper classes, and checking the modes they can set.
-        * @param mode The mode the check
-        * @param type ModeType (MODETYPE_CHANNEL or MODETYPE_USER).
+        * @param mh Mode to check
         * @return True if the user can set or unset this mode.
         */
-       virtual bool HasModePermission(unsigned char mode, ModeType type);
-
-       /** 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,24 +486,11 @@ 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
         */
@@ -563,9 +521,125 @@ class CoreExport User : public Extensible
         */
        void WriteServ(const char* text, ...) CUSTOM_PRINTF(2, 3);
 
-       void WriteNumeric(unsigned int numeric, const char* text, ...) CUSTOM_PRINTF(3, 4);
+       /** Sends a command to this user.
+        * @param command The command to be sent.
+        * @param text The message to send.
+        */
+       void WriteCommand(const char* command, const std::string& text);
+
+       /** Sends a server notice to this user.
+        * @param text The contents of the message to send.
+        */
+       void WriteNotice(const std::string& text) { this->WriteCommand("NOTICE", ":" + text); }
+
+       /** Send a NOTICE message from the local server to the user.
+        * @param text Text to send
+        */
+       virtual void WriteRemoteNotice(const std::string& text);
+
+       virtual void WriteRemoteNumeric(const Numeric::Numeric& numeric);
+
+       template <typename T1>
+       void WriteRemoteNumeric(unsigned int numeric, T1 p1)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               WriteRemoteNumeric(n);
+       }
+
+       template <typename T1, typename T2>
+       void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               WriteRemoteNumeric(n);
+       }
+
+       template <typename T1, typename T2, typename T3>
+       void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               WriteRemoteNumeric(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4>
+       void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               WriteRemoteNumeric(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4, typename T5>
+       void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               n.push(p5);
+               WriteRemoteNumeric(n);
+       }
+
+       void WriteNumeric(const Numeric::Numeric& numeric);
+
+       template <typename T1>
+       void WriteNumeric(unsigned int numeric, T1 p1)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               WriteNumeric(n);
+       }
+
+       template <typename T1, typename T2>
+       void WriteNumeric(unsigned int numeric, T1 p1, T2 p2)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               WriteNumeric(n);
+       }
+
+       template <typename T1, typename T2, typename T3>
+       void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               WriteNumeric(n);
+       }
+
+       template <typename T1, typename T2, typename T3, typename T4>
+       void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               WriteNumeric(n);
+       }
 
-       void WriteNumeric(unsigned int numeric, const std::string &text);
+       template <typename T1, typename T2, typename T3, typename T4, typename T5>
+       void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               n.push(p2);
+               n.push(p3);
+               n.push(p4);
+               n.push(p5);
+               WriteNumeric(n);
+       }
 
        /** Write text to this user, appending CR/LF and prepending :nick!user\@host of the user provided in the first parameter.
         * @param user The user to prepend the :nick!user\@host of
@@ -580,19 +654,6 @@ class CoreExport User : public Extensible
         */
        void WriteFrom(User *user, const char* text, ...) CUSTOM_PRINTF(3, 4);
 
-       /** 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);
-
-       /** 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);
-
        /** 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?
@@ -605,32 +666,15 @@ class CoreExport User : public Extensible
         */
        void WriteCommon(const char* text, ...) CUSTOM_PRINTF(2, 3);
 
-       /** 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);
-
-       /** 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);
-
-       /** 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
+       /** 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 std::string &LinePrefix, std::stringstream &TextStream);
-
-       /** Write to the user, routing the line if the user is remote.
-        */
-       virtual void SendText(const std::string& line) = 0;
-
-       /** Write to the user, routing the line if the user is remote.
-        */
-       void SendText(const char* text, ...) CUSTOM_PRINTF(2, 3);
+       void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true);
 
        /** Return true if the user shares at least one channel with another user
         * @param other The other user to compare the channel list against
@@ -638,32 +682,26 @@ 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,
@@ -672,53 +710,24 @@ class CoreExport User : public Extensible
         * @param gecos The user's new realname
         * @return True if the change succeeded, false if otherwise
         */
-       bool ChangeName(const char* gecos);
+       bool ChangeName(const std::string& gecos);
 
        /** Change a user's nick
-        * @param newnick The new nick
-        * @param force True if the change is being forced (should not be blocked by modes like +N)
+        * @param newnick The new nick. If equal to the users uuid, the nick change always succeeds.
+        * @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();
+       virtual CullResult cull() CXX11_OVERRIDE;
 };
 
 class CoreExport UserIOHandler : public StreamSocket
@@ -739,18 +748,14 @@ 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>
 {
  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
-        */
-       LocalUserList::iterator localuseriter;
-
        /** Stats counter for bytes inbound
         */
        unsigned int bytes_in;
@@ -777,11 +782,14 @@ 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.
         */
@@ -792,27 +800,40 @@ class CoreExport LocalUser : public User, public InviteBase
         */
        int GetServerPort();
 
+       /** 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;
+
+       /** has the user responded to their previous ping?
+        */
+       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;
+
        /** Used by PING checking code
         */
        time_t nping;
 
+       /** 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 ZLines 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.
@@ -825,39 +846,18 @@ 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, bool recheck_eline = true) 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, bool recheck_eline = true) 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
-        */
-       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);
+       void Write(const std::string& text) CXX11_OVERRIDE;
+       void Write(const char*, ...) CXX11_OVERRIDE CUSTOM_PRINTF(2, 3);
 
-       /** 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
+       /** 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
         */
-       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,7 +865,7 @@ 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 HasPermission(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
@@ -875,39 +875,48 @@ class CoreExport LocalUser : public User, public InviteBase
         * @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, bool noisy = false) 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();
 };
 
-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;
+       }
+
+       virtual CullResult cull() CXX11_OVERRIDE;
+       virtual const std::string& GetFullHost() CXX11_OVERRIDE;
+       virtual const std::string& GetFullRealHost() CXX11_OVERRIDE;
 };
 
 /* Faster than dynamic_cast */
@@ -926,42 +935,20 @@ 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 (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)
+{
+       modes[mh->GetId()] = value;
+}
index 2a49d8b80efe1c49230137a847a4838a049485be..c2ede29dfb458c2904761fa8f0734d8d8b615a56 100644 (file)
@@ -20,8 +20,7 @@
  */
 
 
-#ifndef XLINE_H
-#define XLINE_H
+#pragma once
 
 /** 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
@@ -101,16 +100,16 @@ class CoreExport XLine : public classbase
         * line. Usually a line in the form 'expiring Xline 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)
+        * for its Displayable() method).
         */
-       virtual const char* Displayable() = 0;
+       virtual const std::string& Displayable() = 0;
 
        /** Called when the xline has just been added.
         */
@@ -177,9 +176,7 @@ class CoreExport KLine : public XLine
 
        virtual void Apply(User* u);
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       virtual const std::string& Displayable();
 
        virtual bool IsBurstable();
 
@@ -225,9 +222,7 @@ class CoreExport GLine : public XLine
 
        virtual void Apply(User* u);
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       virtual const std::string& Displayable();
 
        /** Ident mask (ident part only)
         */
@@ -269,11 +264,9 @@ class CoreExport ELine : public XLine
 
        virtual void Unset();
 
-       virtual void DisplayExpiry();
-
        virtual void OnAdd();
 
-       virtual const char* Displayable();
+       virtual const std::string& Displayable();
 
        /** Ident mask (ident part only)
         */
@@ -314,9 +307,7 @@ class CoreExport ZLine : public XLine
 
        virtual void Apply(User* u);
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       virtual const std::string& Displayable();
 
        /** IP mask (no ident part)
         */
@@ -351,9 +342,7 @@ class CoreExport QLine : public XLine
 
        virtual void Apply(User* u);
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       virtual const std::string& Displayable();
 
        /** Nickname mask
         */
@@ -531,10 +520,7 @@ class CoreExport XLineManager
         * will be expired and removed before the list is displayed.
         * @param type The type of stats to show
         * @param numeric The numeric to give to each result line
-        * @param user The username making the query
-        * @param results The string_list to receive the results
+        * @param stats Stats context
         */
-       void InvokeStats(const std::string &type, int numeric, User* user, string_list &results);
+       void InvokeStats(const std::string& type, unsigned int numeric, Stats::Context& stats);
 };
-
-#endif
index 49506dd3be8fe35c05251bad55f477c582880e9b..99355efa40f1b9843781f0290dcd1c9c450a4b96 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 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,21 +48,16 @@ run;
 exit 0;
 
 sub run() {
-       my $build = $ENV{BUILDPATH};
-       mkdir $build;
-       chdir $build or die "Could not open build directory: $!";
+       mkdir BUILDPATH;
+       chdir BUILDPATH or die "Could not open build directory: $!";
        unlink 'include';
-       symlink "$ENV{SOURCEPATH}/include", 'include';
+       symlink "${\SOURCEPATH}/include", 'include';
        mkdir $_ for qw/bin modules obj/;
-# BSD make has a horribly annoying bug resulting in an extra chdir of the make process
-# Create symlinks to work around it
-       symlink "../$_", "obj/$_" for qw/bin modules obj/;
 
-       $build = getcwd();
        open MAKE, '>real.mk' or die "Could not write real.mk: $!";
-       chdir "$ENV{SOURCEPATH}/src";
+       chdir "${\SOURCEPATH}/src";
 
-       if ($ENV{PURE_STATIC}) {
+       if ($ENV{INSPIRCD_STATIC}) {
                run_static();
        } else {
                run_dynamic();
@@ -59,7 +66,6 @@ sub run() {
 }
 
 sub run_dynamic() {
-       my $build = $ENV{BUILDPATH};
        print MAKE <<END;
 # DO NOT EDIT THIS FILE
 # It is autogenerated by make/calcdep.pl, and will be overwritten
@@ -71,56 +77,56 @@ 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);
+       my(@core_deps, @modlist);
        for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, "threadengines/threadengine_pthread.cpp") {
                my $out = find_output $file;
                dep_cpp $file, $out, 'gen-o';
-               next if $file =~ m#^socketengines/# && $file ne "socketengines/$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
+.PHONY: all bad-target inspircd modules
 
 END
 }
@@ -141,14 +147,14 @@ all: inspircd
 
 END
        my(@deps, @srcs);
-       for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, <commands/*.cpp>,
+       for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, <coremods/*.cpp>, <coremods/core_*/*.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";
+                       mkdir "${\BUILDPATH}/obj/$1";
                }
                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";
                push @deps, $out;
                push @srcs, $file;
        }
@@ -158,10 +164,10 @@ END
        print MAKE <<END;
 
 obj/ld-extra.cmd: $core_src
-       \@\$(SOURCEPATH)/make/unit-cc.pl gen-ld\$(VERBOSE) \$\@ \$^ \$>
+       \@\$(SOURCEPATH)/make/unit-cc.pl gen-ld \$\@ \$^ \$>
 
 bin/inspircd: $core_mk obj/ld-extra.cmd
-       \@\$(SOURCEPATH)/make/unit-cc.pl static-ld\$(VERBOSE) \$\@ \$^ \$>
+       \@\$(SOURCEPATH)/make/unit-cc.pl static-ld \$\@ \$^ \$>
 
 inspircd: bin/inspircd
 
@@ -173,11 +179,11 @@ END
 sub find_output {
        my($file, $static) = @_;
        my($path,$base) = $file =~ m#^((?:.*/)?)([^/]+)\.cpp# or die "Bad file $file";
-       if ($path eq 'modules/' || $path eq 'commands/') {
+       if ($path eq 'modules/' || $path eq 'coremods/') {
                return $static ? "obj/$base.o" : "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 +205,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 +231,19 @@ 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';
-       }
+       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 +255,8 @@ 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";
+               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..baf67eb38fbf304eaa3b782b05b7044ccb069280 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(catfile);
+
+use make::common;
+use make::console;
+
+use constant CONFIGURE_DIRECTORY     => '.configure';
+use constant CONFIGURE_CACHE_FILE    => catfile(CONFIGURE_DIRECTORY, 'cache.cfg');
+use constant CONFIGURE_CACHE_VERSION => '1';
+use constant CONFIGURE_ERROR_PIPE    => $ENV{INSPIRCD_VERBOSE} ? '' : '1>/dev/null 2>/dev/null';
+
+our @EXPORT = qw(CONFIGURE_CACHE_FILE
+                 CONFIGURE_CACHE_VERSION
+                 cmd_clean
+                 cmd_help
+                 cmd_update
+                 run_test
+                 test_file
+                 test_header
+                 write_configure_cache
+                 get_compiler_info
+                 find_compiler
+                 parse_templates);
+
+sub __get_socketengines {
+       my @socketengines;
+       foreach (<src/socketengines/socketengine_*.cpp>) {
+               s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
+               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 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 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..0d3c1b3
--- /dev/null
@@ -0,0 +1,159 @@
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+#   Copyright (C) 2014-2017 Peter Powell <petpow@saberuk.com>
+#
+# This file is part of InspIRCd.  InspIRCd is free software: you can
+# redistribute it and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+package make::console;
+
+BEGIN {
+       require 5.10.0;
+}
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use Class::Struct         qw(struct);
+use Exporter              qw(import);
+use File::Path            qw(mkpath);
+use File::Spec::Functions qw(rel2abs);
+
+our @EXPORT = qw(command
+                 execute_command
+                 print_format
+                 print_error
+                 print_warning
+                 prompt_bool
+                 prompt_dir
+                 prompt_string);
+
+my %FORMAT_CODES = (
+       DEFAULT   => "\e[0m",
+       BOLD      => "\e[1m",
+       UNDERLINE => "\e[4m",
+
+       RED    => "\e[1;31m",
+       GREEN  => "\e[1;32m",
+       YELLOW => "\e[1;33m",
+       BLUE   => "\e[1;34m"
+);
+
+my %commands;
+
+struct 'command' => {
+       'callback'    => '$',
+       'description' => '$',
+};
+
+sub __console_format($$) {
+       my ($name, $data) = @_;
+       return $data unless -t STDOUT;
+       return $FORMAT_CODES{uc $name} . $data . $FORMAT_CODES{DEFAULT};
+}
+
+sub print_format($;$) {
+       my $message = shift;
+       my $stream = shift // *STDOUT;
+       while ($message =~ /(<\|(\S+)\s(.*?)\|>)/) {
+               my $formatted = __console_format $2, $3;
+               $message =~ s/\Q$1\E/$formatted/;
+       }
+       print { $stream } $message;
+}
+
+sub print_error {
+       print_format "<|RED Error:|> ", *STDERR;
+       for my $line (@_) {
+               print_format "$line\n", *STDERR;
+       }
+       exit 1;
+}
+
+sub print_warning {
+       print_format "<|YELLOW Warning:|> ", *STDERR;
+       for my $line (@_) {
+               print_format "$line\n", *STDERR;
+       }
+}
+
+sub prompt_bool($$$) {
+       my ($interactive, $question, $default) = @_;
+       my $answer = prompt_string($interactive, $question, $default ? 'y' : 'n');
+       return $answer =~ /y/i;
+}
+
+sub prompt_dir($$$;$) {
+       my ($interactive, $question, $default, $create_now) = @_;
+       my ($answer, $create);
+       do {
+               $answer = rel2abs(prompt_string($interactive, $question, $default));
+               $create = prompt_bool($interactive && !-d $answer, "$answer does not exist. Create it?", 'y');
+               if ($create && $create_now) {
+                       unless (create_directory $answer, 0750) {
+                               print_warning "unable to create $answer: $!\n";
+                               $create = 0;
+                       }
+               }
+       } while (!$create);
+       return $answer;
+}
+
+sub prompt_string($$$) {
+       my ($interactive, $question, $default) = @_;
+       return $default unless $interactive;
+       print_format "$question\n";
+       print_format "[<|GREEN $default|>] => ";
+       chomp(my $answer = <STDIN>);
+       say '';
+       return $answer ? $answer : $default;
+}
+
+sub command($$$) {
+       my ($name, $description, $callback) = @_;
+       $commands{$name} = command->new;
+       $commands{$name}->callback($callback);
+       $commands{$name}->description($description);
+}
+
+sub command_alias($$) {
+       my ($source, $target) = @_;
+       command $source, undef, sub(@) {
+               execute_command $target, @_;
+       };
+}
+
+sub execute_command(@) {
+       my $command = defined $_[0] ? lc shift : 'help';
+       if ($command eq 'help') {
+               print_format "<|GREEN Usage:|> $0 <<|UNDERLINE COMMAND|>> [<|UNDERLINE OPTIONS...|>]\n\n";
+               print_format "<|GREEN Commands:|>\n";
+               for my $key (sort keys %commands) {
+                       next unless defined $commands{$key}->description;
+                       my $name = sprintf "%-15s", $key;
+                       my $description = $commands{$key}->description;
+                       print_format "  <|BOLD $name|> # $description\n";
+               }
+               exit 0;
+       } elsif (!$commands{$command}) {
+               print_error "no command called <|BOLD $command|> exists!",
+                       "See <|BOLD $0 help|> for a list of commands.";
+       } else {
+               return $commands{$command}->callback->(@_);
+       }
+}
+
+1;
diff --git a/make/directive.pm b/make/directive.pm
new file mode 100644 (file)
index 0000000..2e9e7ed
--- /dev/null
@@ -0,0 +1,271 @@
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+#   Copyright (C) 2016 Peter Powell <petpow@saberuk.com>
+#
+# This file is part of InspIRCd.  InspIRCd is free software: you can
+# redistribute it and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+package make::directive;
+
+BEGIN {
+       require 5.10.0;
+}
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use File::Basename qw(basename);
+use Exporter       qw(import);
+
+use make::configure;
+use make::console;
+
+use constant DIRECTIVE_ERROR_PIPE => $ENV{INSPIRCD_VERBOSE} ? '' : '2>/dev/null';
+
+our @EXPORT = qw(get_directive
+                 execute_functions);
+
+sub get_directive($$;$)
+{
+       my ($file, $property, $default) = @_;
+       open(my $fh, $file) or return $default;
+
+       my $value = '';
+       while (<$fh>) {
+               if ($_ =~ /^\/\* \$(\S+): (.+) \*\/$/ || $_ =~ /^\/\/\/ \$(\S+): (.+)/) {
+                       next unless $1 eq $property;
+                       $value .= ' ' . execute_functions($file, $1, $2);
+               }
+       }
+       close $fh;
+
+       # Strip all extraneous whitespace.
+       $value =~ s/^\s+|\s+$//g;
+       return $value || $default;
+}
+
+sub execute_functions($$$) {
+       my ($file, $name, $line) = @_;
+
+       # NOTE: we have to use 'our' instead of 'my' here because of a Perl bug.
+       for (our @parameters = (); $line =~ /([a-z_]+)\((?:\s*"([^"]*)(?{push @parameters, $2})"\s*)*\)/; undef @parameters) {
+               my $sub = make::directive->can("__function_$1");
+               print_error "unknown $name directive '$1' in $file!" unless $sub;
+
+               # Call the subroutine and replace the function.
+               my $result = $sub->($file, @parameters);
+               if (defined $result) {
+                       $line = $` . $result . $';
+                       next;
+               }
+
+               # If the subroutine returns undef then it is a sign that we should
+               # disregard the rest of the line and stop processing it.
+               $line = $`;
+       }
+
+       return $line;
+}
+
+sub __environment {
+       my ($prefix, $suffix) = @_;
+       $suffix =~ s/[-.]/_/g;
+       $suffix =~ s/[^A-Za-z0-9_]//g;
+       return $prefix . uc $suffix;
+}
+
+sub __error {
+       my ($file, @message) = @_;
+       push @message, '';
+
+       # If we have package details then suggest to the user that they check
+       # that they have the packages installed.=
+       my $dependencies = get_directive($file, 'PackageInfo');
+       if (defined $dependencies) {
+               my @packages = sort grep { /^\S+$/ } split /\s/, $dependencies;
+               push @message, 'You should make sure you have the following packages installed:';
+               for (@packages) {
+                       push @message, " * $_";
+               }
+       } else {
+               push @message, 'You should make sure that you have all of the required dependencies';
+               push @message, 'for this module installed.';
+       }
+       push @message, '';
+
+       # If we have author information then tell the user to report the bug
+       # to them. Otherwise, assume it is a bundled module and tell the user
+       # to report it to the InspIRCd issue tracker.
+       my $author = get_directive($file, 'ModAuthor');
+       if (defined $author) {
+               push @message, 'If you believe this error to be a bug then you can try to contact the';
+               push @message, 'author of this module:';
+               my $author_mail = get_directive($file, 'ModAuthorMail');
+               if (defined $author_mail) {
+                       push @message, " * $author <$author_mail>";
+               } else {
+                       push @message, " * $author";
+               }
+       } else {
+               push @message, 'If you believe this error to be a bug then you can file a bug report';
+               push @message, 'at https://github.com/inspircd/inspircd/issues';
+       }
+       push @message, '';
+
+       push @message, 'If you would like help with fixing this problem then visit our IRC';
+       push @message, 'channel at irc.inspircd.org #InspIRCd for support.';
+       push @message, '';
+
+       print_error @message;
+}
+
+sub __function_error {
+       my ($file, @messages) = @_;
+       __error $file, @messages;
+}
+
+sub __function_execute {
+       my ($file, $command, $environment, $defaults) = @_;
+
+       # Try to execute the command...
+       chomp(my $result = `$command ${\DIRECTIVE_ERROR_PIPE}`);
+       unless ($?) {
+               print_format "Execution of `<|GREEN $command|>` succeeded: <|BOLD $result|>\n";
+               return $result;
+       }
+
+       # If looking up with pkg-config fails then check the environment...
+       if (defined $environment && $environment ne '') {
+               $environment = __environment 'INSPIRCD_', $environment;
+               if (defined $ENV{$environment}) {
+                       print_format "Execution of `<|GREEN $command|>` failed; using the environment: <|BOLD $ENV{$environment}|>\n";
+                       return $ENV{$environment};
+               }
+       }
+
+       # If all else fails then look for the defaults..
+       if (defined $defaults) {
+               print_format "Execution of `<|GREEN $command|>` failed; using the defaults: <|BOLD $defaults|>\n";
+               return $defaults;
+       }
+
+       # Executing the command failed and we don't have any defaults so give up. 
+       __error $file, "`<|GREEN $command|>` exited with a non-zero exit code!";
+}
+
+sub __function_find_compiler_flags {
+       my ($file, $name, $defaults) = @_;
+
+       # Try to look up the compiler flags with pkg-config...
+       chomp(my $flags = `pkg-config --cflags $name ${\DIRECTIVE_ERROR_PIPE}`);
+       unless ($?) {
+               print_format "Found the compiler flags for <|GREEN ${\basename $file, '.cpp'}|> using pkg-config: <|BOLD $flags|>\n";
+               return $flags;
+       }
+
+       # If looking up with pkg-config fails then check the environment...
+       my $key = __environment 'INSPIRCD_CXXFLAGS_', $name;
+       if (defined $ENV{$key}) {
+               print_format "Found the compiler flags for <|GREEN ${\basename $file, '.cpp'}|> using the environment: <|BOLD $ENV{$key}|>\n";
+               return $ENV{$key};
+       }
+
+       # If all else fails then look for the defaults..
+       if (defined $defaults) {
+               print_format "Found the compiler flags for <|GREEN ${\basename $file, '.cpp'}|> using the defaults: <|BOLD $defaults|>\n";
+               return $defaults;
+       }
+
+       # We can't find it via pkg-config, via the environment, or via the defaults so give up.
+       __error $file, "unable to find the compiler flags for <|GREEN ${\basename $file, '.cpp'}|>!";
+}
+
+sub __function_find_linker_flags {
+       my ($file, $name, $defaults) = @_;
+
+       # Try to look up the linker flags with pkg-config...
+       chomp(my $flags = `pkg-config --libs $name ${\DIRECTIVE_ERROR_PIPE}`);
+       unless ($?) {
+               print_format "Found the linker flags for <|GREEN ${\basename $file, '.cpp'}|> using pkg-config: <|BOLD $flags|>\n";
+               return $flags;
+       }
+
+       # If looking up with pkg-config fails then check the environment...
+       my $key = __environment 'INSPIRCD_CXXFLAGS_', $name;
+       if (defined $ENV{$key}) {
+               print_format "Found the linker flags for <|GREEN ${\basename $file, '.cpp'}|> using the environment: <|BOLD $ENV{$key}|>\n";
+               return $ENV{$key};
+       }
+
+       # If all else fails then look for the defaults..
+       if (defined $defaults) {
+               print_format "Found the linker flags for <|GREEN ${\basename $file, '.cpp'}|> using the defaults: <|BOLD $defaults|>\n";
+               return $defaults;
+       }
+
+       # We can't find it via pkg-config, via the environment, or via the defaults so give up.
+       __error $file, "unable to find the linker flags for <|GREEN ${\basename $file, '.cpp'}|>!";
+}
+
+sub __function_require_system {
+       my ($file, $name, $minimum, $maximum) = @_;
+       my ($system, $version);
+
+       # Linux is special and can be compared by distribution names.
+       if ($^O eq 'linux' && $name ne 'linux') {
+               chomp($system = lc `lsb_release --id --short 2>/dev/null`);
+               chomp($version = lc `lsb_release --release --short 2>/dev/null`);
+       }
+
+       # Gather information on the system if we don't have it already.
+       chomp($system ||= lc `uname -s 2>/dev/null`);
+       chomp($version ||= lc `uname -r 2>/dev/null`);
+
+       # We only care about the important bit of the version number so trim the rest.
+       $version =~ s/^(\d+\.\d+).+/$1/;
+
+       # Check whether the current system is suitable.
+       return undef if $name ne $system;
+       return undef if defined $minimum && $version < $minimum;
+       return undef if defined $maximum && $version > $maximum;
+
+       # Requirement directives don't change anything directly.
+       return "";
+}
+
+sub __function_require_version {
+       my ($file, $name, $minimum, $maximum) = @_;
+
+       # If pkg-config isn't installed then we can't do anything here.
+       if (system "pkg-config --exists $name ${\DIRECTIVE_ERROR_PIPE}") {
+               print_warning "unable to look up the version of $name using pkg-config!";
+               return undef;
+       }
+
+       # Check with pkg-config whether we have the required version.
+       return undef if defined $minimum && system "pkg-config --atleast-version $minimum $name";
+       return undef if defined $maximum && system "pkg-config --max-version $maximum $name";
+
+       # Requirement directives don't change anything directly.
+       return "";
+}
+
+sub __function_warning {
+       my ($file, @messages) = @_;
+       print_warning @messages;
+}
+
+1;
diff --git a/make/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..c1e8bd0
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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@.@VERSION_MINOR@"
+#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_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..f34345cea54fd4abc05edfc8af79a96cbd4bfdcd 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 (!("--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,13 +302,13 @@ sub dev_screenvaldebug(@)
        # If we are still alive here.. Try starting the IRCd..
        print "Starting InspIRCd in `screen`, type `screen -r` when the ircd crashes to view the valgrind and gdb output and get a backtrace.\n";
        print "Once you're inside the screen session press ^C + d to re-detach from the session\n";
-       exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
+       exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=30), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
        die "Failed to start screen: $!\n";
 }
 
 sub cmd_stop()
 {
-       if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return 0; }
+       if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return GENERIC_EXIT_SUCCESS; }
        # Get to here, we have something to kill.
        my $pid = getprocessid();
        print "Stopping InspIRCd (pid: $pid)...\n";
@@ -299,12 +318,12 @@ sub cmd_stop()
                sleep 1;
                if (getstatus() == 0) {
                        print "InspIRCd Stopped.\n";
-                       return;
+                       return GENERIC_EXIT_SUCCESS;
                }
        }
        print "InspIRCd not dying quietly -- forcing kill\n";
        kill KILL => $pid;
-       return 0;
+       return GENERIC_EXIT_SUCCESS;
 }
 
 ###
@@ -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..d43a3b4
--- /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 irc://irc.inspircd.org/inspircd.
+
+Bug reports and feature requests can be filed at https://github.com/inspircd/inspircd/issues.
diff --git a/make/template/inspircd.1 b/make/template/inspircd.1
new file mode 100644 (file)
index 0000000..eb1453d
--- /dev/null
@@ -0,0 +1,104 @@
+.\"
+.\" InspIRCd -- Internet Relay Chat Daemon
+.\"
+.\"   Copyright (C) 2014 Peter Powell <petpow@saberuk.com>
+.\"
+.\" This file is part of InspIRCd.  InspIRCd is free software: you can
+.\" redistribute it and/or modify it under the terms of the GNU General Public
+.\" License as published by the Free Software Foundation, version 2.
+.\"
+.\" This program is distributed in the hope that it will be useful, but WITHOUT
+.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+.\" FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+.\" details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
+.\"
+
+
+.TH "InspIRCd" "1" "June 2014" "InspIRCd @VERSION_FULL@" "InspIRCd Manual"
+
+.SH "NAME"
+\t\fBInspIRCd\fR - \fIthe\fR stable, high-performance and modular Internet Relay Chat Daemon
+.BR
+
+.SH "SYNOPSIS"
+\t\fBinspircd\fR [--config <file>] [--debug] [--nofork] [--nolog] [--runasroot] [--version] 
+
+.SH "OPTIONS"
+.TP
+.B "--config <file>"
+.br
+Sets the path to the main configuration file. Defaults to \fI@CONFIG_DIR@/inspircd.conf\fR.
+.TP
+.B "--debug"
+.br
+Log verbosely to the standard output stream.
+.TP
+.B "--nofork"
+.br
+Don't fork into the background after starting up.
+.TP
+.B "--nolog"
+.br
+Don't write to log files.
+.TP
+.B "--runasroot"
+.br
+Allow the server to start as root (not recommended).
+.TP
+.B "--version"
+.br
+Displays the InspIRCd version and exits.
+
+.SH "EXIT STATUS"
+.TP
+.B "0 (EXIT_STATUS_NOERROR)"
+.br
+The server exited cleanly.
+.TP
+.B "1 (EXIT_STATUS_DIE)"
+.br
+The server exited because the DIE command was executed.
+.TP
+.B "2 (EXIT_STATUS_CONFIG)"
+.br
+The server exited because of a configuration file error.
+.TP
+.B "3 (EXIT_STATUS_LOG)"
+.br
+The server exited because of a log file error.
+.TP
+.B "4 (EXIT_STATUS_FORK)"
+.br
+The server exited because it was unable to fork into the background.
+.TP
+.B "5 (EXIT_STATUS_ARGV)"
+.br
+The server exited because an invalid argument was passed to it on the command line.
+.TP
+.B "6 (EXIT_STATUS_PID)"
+.br
+The server exited because it was unable to write to the PID file.
+.TP
+.B "7 (EXIT_STATUS_SOCKETENGINE)"
+.br
+The server exited because it was unable to initialize the @SOCKETENGINE@ socket engine.
+.TP
+.B "8 (EXIT_STATUS_ROOT)"
+.br
+The server exited because the user tried to start as root without \fI--runasroot\fR.
+.TP
+.B "9 (EXIT_STATUS_MODULE)"
+.br
+The server exited because it was unable to load a module on first run.
+.TP
+.B "10 (EXIT_STATUS_SIGTERM)"
+.br
+The server exited because it received SIGTERM.
+
+.SH "SUPPORT"
+IRC support for InspIRCd can be found at irc://irc.inspircd.org/inspircd.
+
+Bug reports and feature requests can be filed at https://github.com/inspircd/inspircd/issues.
diff --git a/make/template/inspircd.service b/make/template/inspircd.service
new file mode 100644 (file)
index 0000000..c05e618
--- /dev/null
@@ -0,0 +1,35 @@
+%platform linux
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+#   Copyright (C) 2014 Peter Powell <petpow@saberuk.com>
+#
+# This file is part of InspIRCd.  InspIRCd is free software: you can
+# redistribute it and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+[Unit]
+After=network.target
+Description=InspIRCd - Internet Relay Chat Daemon
+Requires=network.target
+
+[Service]
+ExecReload=@SCRIPT_DIR@/inspircd rehash
+ExecStart=@SCRIPT_DIR@/inspircd start
+ExecStop=@SCRIPT_DIR@/inspircd stop
+PIDFile=@DATA_DIR@/inspircd.pid
+Restart=on-failure
+Type=forking
+
+[Install]
+WantedBy=multi-user.target
index 23daa7efc02a0d2069cc70f694b56958f1870571..ff464a2283f996140296b7e756691b2cb340db0b 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 = 
+CORECXXFLAGS = -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -pipe -Iinclude -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -Wshadow
+LDLIBS = -lstdc++
 CORELDFLAGS = -rdynamic -L. $(LDFLAGS)
 PICLDFLAGS = -fPIC -shared -rdynamic $(LDFLAGS)
 BASE = "$(DESTDIR)@BASE_DIR@"
 CONPATH = "$(DESTDIR)@CONFIG_DIR@"
+MANPATH = "$(DESTDIR)@MANUAL_DIR@"
 MODPATH = "$(DESTDIR)@MODULE_DIR@"
 LOGPATH = "$(DESTDIR)@LOG_DIR@"
 DATPATH = "$(DESTDIR)@DATA_DIR@"
 BINPATH = "$(DESTDIR)@BINARY_DIR@"
+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
+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
+endif
 
-GCC6=@GCC6@
-@IFEQ $(GCC6) true
-  CXXFLAGS += -fno-delete-null-pointer-checks
-@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
 FOOTER = finishmessage
 
-CXXFLAGS += -Iinclude
+MAKEFLAGS += --no-print-directory
 
-@GNU_ONLY MAKEFLAGS += --no-print-directory
+SOURCEPATH = $(shell /bin/pwd)
 
-@GNU_ONLY SOURCEPATH = $(shell /bin/pwd)
-@BSD_ONLY SOURCEPATH != /bin/pwd
+ifndef INSPIRCD_VERBOSE
+  MAKEFLAGS += --silent
+endif
 
-@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
+ifdef INSPIRCD_STATIC
+  CORECXXFLAGS += -DINSPIRCD_STATIC
+endif
 
-@IFDEF PURE_STATIC
-  CXXFLAGS += -DPURE_STATIC
-@ENDIF
+# Add the users CPPFLAGS/CXXFLAGS to the base ones to allow them to
+# override things like -Wfatal-errors if they wish to.
+CORECXXFLAGS += $(CPPFLAGS) $(CXXFLAGS)
 
-@DO_EXPORT RUNCC RUNLD CXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
-@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC SPLIT_CC
+export BUILDPATH
+export CORECXXFLAGS
+export CORELDFLAGS
+export CXX
+export INSPIRCD_STATIC
+export INSPIRCD_VERBOSE
+export LDLIBS
+export PICLDFLAGS
+export SOCKETENGINE
+export SOURCEPATH
 
 # Default target
 TARGET = all
 
-@IFDEF M
+ifdef INSPIRCD_MODULE
     HEADER = mod-header
     FOOTER = mod-footer
-    @BSD_ONLY TARGET = modules/${M:S/.so$//}.so
-    @GNU_ONLY TARGET = modules/$(M:.so=).so
-@ENDIF
+    TARGET = modules/$(INSPIRCD_MODULE:.so=).so
+endif
 
-@IFDEF T
+ifdef INSPIRCD_TARGET
     HEADER =
     FOOTER = target
-    TARGET = $(T)
-@ENDIF
+    TARGET = $(INSPIRCD_TARGET)
+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,10 +177,10 @@ debug-header:
        @echo "*************************************"
 
 mod-header:
-@IFDEF PURE_STATIC
+ifdef INSPIRCD_STATIC
        @echo 'Cannot build single modules in pure-static build'
        @exit 1
-@ENDIF
+endif
        @echo 'Building single module:'
 
 mod-footer: target
@@ -229,18 +221,30 @@ install: target
        @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(DATPATH)
        @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(LOGPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(BINPATH)
-       @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/aliases
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/modules
+       @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/services
+       @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MANPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MODPATH)
+       @-$(INSTALL) -d -m $(INSTMODE_DIR) $(SCRPATH)
        [ "$(BUILDPATH)/bin/" -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) "$(BUILDPATH)/bin/inspircd" $(BINPATH)
-@IFNDEF PURE_STATIC
+ifndef INSPIRCD_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
+endif
+       -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/inspircd $(SCRPATH) 2>/dev/null
+       -$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(SCRPATH)/.gdbargs 2>/dev/null
+ifeq ($(SYSTEM), darwin)
+       -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/org.inspircd.plist $(SCRPATH) 2>/dev/null
+endif
+ifeq ($(SYSTEM), linux)
+       -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.service $(SCRPATH) 2>/dev/null
+endif
+       -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.1 $(MANPATH) 2>/dev/null
+       -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd-genssl.1 $(MANPATH) 2>/dev/null
+       -$(INSTALL) -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
-       -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
+       -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/services/*.example $(CONPATH)/examples/services
+       -$(INSTALL) -m $(INSTMODE_LIB) *.pem $(CONPATH) 2>/dev/null
        @echo ""
        @echo "*************************************"
        @echo "*        INSTALL COMPLETE!          *"
@@ -251,15 +255,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 +273,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 +298,11 @@ help:
        @echo 'Use: ${MAKE} [flags] [targets]'
        @echo ''
        @echo 'Flags:'
-       @echo ' V=1       Show the full command being executed instead of "BUILD: dns.cpp"'
-       @echo ' D=1       Enable debug build, for module development or crash tracing'
-       @echo ' D=2       Enable debug build with optimizations, for detailed backtraces'
-       @echo ' DESTDIR=  Specify a destination root directory (for tarball creation)'
-       @echo ' -j <N>    Run a parallel build using N jobs'
+       @echo ' INSPIRCD_VERBOSE=1  Show the full command being executed instead of "BUILD: dns.cpp"'
+       @echo ' INSPIRCD_DEBUG=1    Enable debug build, for module development or crash tracing'
+       @echo ' INSPIRCD_DEBUG=2    Enable debug build with optimizations, for detailed backtraces'
+       @echo ' DESTDIR=            Specify a destination root directory (for tarball creation)'
+       @echo ' -j <N>              Run a parallel build using N jobs'
        @echo ''
        @echo 'Targets:'
        @echo ' all       Complete build of InspIRCd, without installing (default)'
@@ -310,10 +310,10 @@ help:
        @echo '           Currently installs to ${BASE}'
        @echo ' debug     Compile a debug build. Equivalent to "make D=1 all"'
        @echo ''
-       @echo ' M=m_foo   Builds a single module (cmd_foo also works here)'
-       @echo ' T=target  Builds a user-specified target, such as "inspircd" or "modules"'
-       @echo '           Other targets are specified by their path in the build directory'
-       @echo '           Multiple targets may be separated by a space'
+       @echo ' INSPIRCD_MODULE=m_foo   Builds a single module (core_foo also works here)'
+       @echo ' INSPIRCD_TARGET=target  Builds a user-specified target, such as "inspircd" or "modules"'
+       @echo '                         Other targets are specified by their path in the build directory'
+       @echo '                         Multiple targets may be separated by a space'
        @echo ''
        @echo ' clean     Cleans object files produced by the compile'
        @echo ' distclean Cleans all generated files (build, configure, run, etc)'
@@ -322,4 +322,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/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..1cf6cf86601f2cd0d1b34001d4f7f07148c7d94a 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);
@@ -65,10 +56,25 @@ if ($type eq 'gen-ld') {
 }
 exit 1;
 
+sub message($$$) {
+       my ($type, $file, $command) = @_;
+       if ($ENV{INSPIRCD_VERBOSE}) {
+               print "$command\n";
+       } else {
+               print_format "\t<|GREEN $type:|>\t\t$file\n";
+       }
+}
+
+sub rpath($) {
+       my $message = shift;
+       $message =~ s/-L(\S+)/-Wl,-rpath,$1 -L$1/g unless defined $ENV{INSPIRCD_DISABLE_RPATH};
+       return $message;
+}
+
 sub do_static_find {
        my @flags;
        for my $file (@ARGV) {
-               push @flags, getlinkerflags($file);
+               push @flags, rpath(get_directive($file, 'LinkerFlags', ''));
        }
        open F, '>', $out;
        print F join ' ', @flags;
@@ -77,32 +83,37 @@ sub do_static_find {
 }
 
 sub do_static_link {
-       my $execstr = "$ENV{RUNLD} -o $out $ENV{CORELDFLAGS}";
+       my $execstr = "$ENV{CXX} -o $out $ENV{CORELDFLAGS}";
+       my $link_flags = '';
        for (@ARGV) {
                if (/\.cmd$/) {
                        open F, '<', $_;
                        my $libs = <F>;
                        chomp $libs;
-                       $execstr .= ' '.$libs;
+                       $link_flags .= ' '.$libs;
                        close F;
                } else {
                        $execstr .= ' '.$_;
                }
        }
-       $execstr .= ' '.$ENV{LDLIBS};
-       print "$execstr\n" if $verbose;
+       $execstr .= ' '.$ENV{LDLIBS}.' '.$link_flags;
+       message 'LINK', $out, $execstr;
        exec $execstr;
 }
 
 sub do_core_link {
-       my $execstr = "$ENV{RUNLD} -o $out $ENV{CORELDFLAGS} @_ $ENV{LDLIBS}";
-       print "$execstr\n" if $verbose;
+       my $execstr = "$ENV{CXX} -o $out $ENV{CORELDFLAGS} @_ $ENV{LDLIBS}";
+       message 'LINK', $out, $execstr;
        exec $execstr;
 }
 
 sub do_link_dir {
-       my $execstr = "$ENV{RUNLD} -o $out $ENV{PICLDFLAGS} @_";
-       print "$execstr\n" if $verbose;
+       my ($dir, $link_flags) = (shift, '');
+       for my $file (<$dir/*.cpp>) {
+               $link_flags .= rpath(get_directive($file, 'LinkerFlags', '')) . ' ';
+       }
+       my $execstr = "$ENV{CXX} -o $out $ENV{PICLDFLAGS} $link_flags @_";
+       message 'LINK', $out, $execstr;
        exec $execstr;
 }
 
@@ -111,27 +122,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 bc46194082c4ec724d6e4108d03789ea40941f40..3e4eee9c95fcd86d24a11dcf7f60a4bbff49e458 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..0ff3fbe4c303256865110bd050492abdeb7c6de2 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;
@@ -174,35 +192,35 @@ void Extensible::doUnhookExtensions(const std::vector<reference<ExtensionItem> >
        }
 }
 
-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);
        }
        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, ConvToInt(value));
+}
+
 intptr_t LocalIntExt::get(const Extensible* container) const
 {
        return reinterpret_cast<intptr_t>(get_raw(container));
@@ -265,7 +298,8 @@ void LocalIntExt::free(void*)
 {
 }
 
-StringExtItem::StringExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+StringExtItem::StringExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+       : ExtensionItem(Key, exttype, mod)
 {
 }
 
@@ -312,4 +346,3 @@ ModuleException::ModuleException(const std::string &message, Module* who)
        : CoreException(message, who ? who->ModuleSourceFile : "A Module")
 {
 }
-
index 9f1eafd0cb1f1e9654a8d3890310a46ac98af26a..1edc57693dfeb5f1345b1ee95edaa6b958bb3206 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");
+       ChanModeReference inviteonlymode(NULL, "inviteonly");
+       ChanModeReference keymode(NULL, "key");
+       ChanModeReference limitmode(NULL, "limit");
 }
 
-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;
+       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;
+               this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
        }
-}
-
-void Channel::SetModeParam(ModeHandler* mode, const std::string& parameter)
-{
-       SetModeParam(mode->GetModeChar(), parameter);
-}
 
-std::string Channel::GetModeParameter(char mode)
-{
-       CustomModeList::iterator n = custom_mode_params.find(mode);
-       if (n != custom_mode_params.end())
-               return n->second;
-       return "";
-}
+       // 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(ModeHandler* mode)
-{
-       CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
-       if (n != custom_mode_params.end())
-               return n->second;
-       return "";
+       FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
 }
 
-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));
-
-       return CMD_SUCCESS;
-}
+       std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
+       if (!ret.second)
+               return NULL;
 
-long Channel::GetUserCounter()
-{
-       return userlist.size();
+       Membership* memb = new(ret.first->second) Membership(user, this);
+       return memb;
 }
 
-Membership* Channel::AddUser(User* user)
+void Channel::DelUser(User* user)
 {
-       Membership* memb = new Membership(user, this);
-       userlist[user] = memb;
-       return memb;
+       MemberMap::iterator it = userlist.find(user);
+       if (it != userlist.end())
+               DelUser(it);
 }
 
-void Channel::DelUser(User* user)
+void Channel::CheckDestroy()
 {
-       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 +135,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 +148,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 +160,184 @@ 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 = ConvToInt(user->oper->getConfig("maxchans"));
+                       // If not set, use 2.0's <channels:opers>, if that's not set either, use limit from CC
+                       if (!opermaxchans && 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);
+
+       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 (!Ptr)
+       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 no module returned MOD_RES_DENY or MOD_RES_ALLOW (which is the case
+                       // most of the time) then proceed to check channel modes +k, +i, +l and bans,
+                       // in this order.
+                       // If a module explicitly allowed the join (by returning MOD_RES_ALLOW),
+                       // then this entire section is skipped
+                       if (MOD_RESULT == MOD_RES_PASSTHRU)
+                       {
+                               std::string ckey = chan->GetModeParameter(keymode);
                                if (!ckey.empty())
                                {
-                                       FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key ? key : ""));
-                                       if (!MOD_RESULT.check((key && ckey == key) || can_bypass))
+                                       FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, key));
+                                       if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, key)))
                                        {
                                                // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
-                                               user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
+                                               user->WriteNumeric(ERR_BADCHANNELKEY, chan->name, "Cannot join channel (Incorrect channel key)");
                                                return NULL;
                                        }
                                }
 
-                               if (Ptr->IsModeSet('i'))
+                               if (chan->IsModeSet(inviteonlymode))
                                {
-                                       FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
-                                       if (!MOD_RESULT.check(invited))
+                                       FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
+                                       if (MOD_RESULT != MOD_RES_ALLOW)
                                        {
-                                               user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
+                                               user->WriteNumeric(ERR_INVITEONLYCHAN, chan->name, "Cannot join channel (Invite only)");
                                                return NULL;
                                        }
                                }
 
-                               std::string limit = Ptr->GetModeParameter('l');
+                               std::string limit = chan->GetModeParameter(limitmode);
                                if (!limit.empty())
                                {
-                                       FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
-                                       if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
+                                       FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
+                                       if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()))))
                                        {
-                                               user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
+                                               user->WriteNumeric(ERR_CHANNELISFULL, chan->name, "Cannot join channel (Channel is full)");
                                                return NULL;
                                        }
                                }
 
-                               if (Ptr->IsBanned(user) && !can_bypass)
+                               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());
+       this->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", this->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()))
+       if ((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());
+               this->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", this->name.c_str(), ms.c_str());
        }
 
-       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 +348,15 @@ 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);
+       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 +376,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 +395,14 @@ 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);
+       const ListModeBase::ModeList* bans = banlm->GetList(this);
+       if (bans)
        {
-               if (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 (CheckBan(user, it->mask))
                                return MOD_RES_DENY;
                }
        }
@@ -487,150 +410,76 @@ 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)
+bool Channel::PartUser(User* user, std::string& reason)
 {
-       if (!user)
-               return;
+       MemberMap::iterator membiter = userlist.find(user);
 
-       Membership* memb = GetUser(user);
+       if (membiter == userlist.end())
+               return false;
 
-       if (memb)
-       {
-               CUList except_list;
-               FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
+       Membership* memb = membiter->second;
+       CUList except_list;
+       FOREACH_MOD(OnUserPart, (memb, reason, except_list));
 
-               WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
+       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);
-       }
+       // 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->DelUser(user);
+       return true;
 }
 
-void Channel::KickUser(User *src, User *user, const char* reason)
+void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& 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);
+       Membership* memb = victimiter->second;
+       CUList except_list;
+       FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
 
-               user->chans.erase(this);
-               this->RemoveAllPrefixes(user);
-       }
+       User* victim = memb->user;
+       WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
 
-       this->DelUser(user);
+       victim->chans.erase(memb);
+       this->DelUser(victimiter);
 }
 
 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));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteChannel(user, 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;
+       const std::string message = ":" + user->GetFullHost() + " " + text;
 
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+       for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
        {
                if (IS_LOCAL(i->first))
-                       i->first->Write(out);
+                       i->first->Write(message);
        }
 }
 
 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));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteChannelWithServ(ServName, 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;
+       const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
 
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+       for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
        {
                if (IS_LOCAL(i->first))
-                       i->first->Write(out);
+                       i->first->Write(message);
        }
 }
 
@@ -638,43 +487,23 @@ void Channel::WriteChannelWithServ(const std::string& ServName, const std::strin
  * for the sender (for privmsg etc) */
 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, 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->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteAllExceptSender(user, serversource, status, textbuffer);
 }
 
 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!text)
-               return;
-
-       int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
-       va_end(argsPtr);
-
-       this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       textbuffer = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + textbuffer;
+       this->RawWriteAllExcept(user, serversource, status, except_list, textbuffer);
 }
 
 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
 {
-       char tb[MAXBUF];
-
-       snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
-
-       this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
+       const std::string message = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + text;
+       this->RawWriteAllExcept(user, serversource, status, except_list, message);
 }
 
 void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
@@ -682,11 +511,11 @@ void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CULi
        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()))
                {
@@ -706,188 +535,64 @@ void Channel::WriteAllExceptSender(User* user, bool serversource, char status, c
        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 showkey)
 {
-       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();
+                       scratch.push_back(n + 65);
+
+                       ParamModeBase* pm = mh->IsParameterMode();
+                       if (!pm)
+                               continue;
+
                        if (n == 'k' - 65 && !showkey)
                        {
-                               extparam = "<key>";
+                               sparam += " <key>";
                        }
                        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;
+       scratch += sparam;
+       return scratch.c_str();
 }
 
-/* compile a userlist of a channel into a string, each nick seperated by
- * spaces and op, voice etc status shown as @ and +, and send it to 'user'
- */
-void Channel::UserList(User *user)
+void Channel::WriteNotice(const std::string& text)
 {
-       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;
-}
-
-void Channel::ResetMaxBans()
-{
-       this->maxbans = 0;
+       std::string rawmsg = "NOTICE ";
+       rawmsg.append(this->name).append(" :").append(text);
+       WriteChannelWithServ(ServerInstance->Config->ServerName, rawmsg);
 }
 
 /* returns the status character for a given user on a channel, e.g. @ for op,
  * % 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 +604,50 @@ 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)
-{
-       if ((timeout != 0) && (ServerInstance->Time() >= timeout))
-               // Expired, don't bother
-               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;
-       }
-}
index b245a15527e7a25077f855ceae2df3c0be33e52c..250ad9c69b0d716dc6cee3b2758fec1bede6b6e7 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
@@ -82,5 +80,3 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr
 
        return mask == mask2;
 }
-
-
index 76dfc06ce05eabf887794b76981ef8410c36f992..3b3329261c1a0af2a551060a7aee95a7ab4d0390 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 std::vector<std::string>& 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();
-
                        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
+
+                       CmdResult result = handler->Handle(new_parameters, user);
+                       if (localuser)
                        {
-                               return true;
+                               // Run the OnPostCommand hook with the last parameter (original line) being empty
+                               // to indicate that the command had more targets in its original form.
+                               item.clear();
+                               FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, item));
                        }
                }
        }
-       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 +118,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 std::vector<std::string>& parameters, User* user, Command** cmd)
 {
-       Commandtable::iterator n = cmdlist.find(commandname);
+       CommandMap::iterator n = cmdlist.find(commandname);
 
        if (n != cmdlist.end())
        {
@@ -174,6 +150,8 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std::
 
                        if (bOkay)
                        {
+                               if (cmd)
+                                       *cmd = n->second;
                                return n->second->Handle(parameters,user);
                        }
                }
@@ -181,7 +159,7 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std::
        return CMD_INVALID;
 }
 
-bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
+void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
 {
        std::vector<std::string> command_p;
        irc::tokenstream tokens(cmd);
@@ -196,23 +174,22 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
        if (command[0] == ':')
                tokens.GetToken(command);
 
-       while (tokens.GetToken(token) && (command_p.size() <= MAXPARAMETERS))
+       while (tokens.GetToken(token))
                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
@@ -222,13 +199,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));
                if (MOD_RESULT == MOD_RES_DENY)
-                       return true;
+                       return;
 
                /*
                 * This double lookup is in case a module (abbreviation) wishes to change a command.
@@ -237,17 +213,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):
@@ -256,32 +234,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 std::vector<std::string>::iterator lastkeep = command_p.begin() + (handler->max_params - 1);
+               // Iterator to the first excess parameter
+               const std::vector<std::string>::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 (std::vector<std::string>::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());
        }
 
        /*
@@ -291,104 +258,140 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
        ModResult MOD_RESULT;
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
        if (MOD_RESULT == MOD_RES_DENY)
-               return true;
+               return;
 
        /* activity resets the ping pending timer */
        user->nping = 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))
                {
                        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;
+                       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;
                }
        }
-       if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
+
+       if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->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_UNKNOWNCOMMAND, command, "Unknown command");
                }
                else
                {
-                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
-                                                                               user->nick.c_str(), command.c_str());
+                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "This command has been disabled.");
                }
 
                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;
+                               command.c_str(), user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
+               return;
        }
 
-       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));
                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(command_p, user);
 
-               FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, user, result,cmd));
-               return do_more;
+               FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, cmd));
        }
 }
 
 void CommandParser::RemoveCommand(Command* x)
 {
-       Commandtable::iterator n = cmdlist.find(x->name);
+       CommandMap::iterator n = cmdlist.find(x->name);
        if (n != cmdlist.end() && n->second == x)
                cmdlist.erase(n);
 }
 
+CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+       : ServiceProvider(mod, cmd, SERVICE_COMMAND)
+       , flags_needed(0)
+       , min_params(minpara)
+       , max_params(maxpara)
+       , use_count(0)
+       , disabled(false)
+       , works_before_reg(false)
+       , allow_empty_last_param(true)
+       , Penalty(1)
+{
+}
+
+CommandBase::~CommandBase()
+{
+}
+
+void CommandBase::EncodeParameter(std::string& parameter, int index)
+{
+}
+
+RouteDescriptor CommandBase::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       return ROUTE_LOCALONLY;
+}
+
+Command::Command(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+       : CommandBase(mod, cmd, minpara, maxpara)
+       , force_manual_route(false)
+{
+}
+
 Command::~Command()
 {
-       ServerInstance->Parser->RemoveCommand(this);
+       ServerInstance->Parser.RemoveCommand(this);
 }
 
-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(std::string &buffer,LocalUser *user)
+{
+       if (buffer.empty())
+               return;
 
-       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);
+       ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), buffer.c_str());
+       ProcessCommand(user,buffer);
 }
 
 bool CommandParser::AddCommand(Command *f)
@@ -406,88 +409,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 std::vector<std::string>& 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:
+                       // If no custom translator was given, fall through
+               }
                case TR_TEXT:
                default:
                        /* Do nothing */
-                       dest = source;
+                       dest.append(item);
                break;
        }
-
-       return translations;
 }
index 5bf2aeb03a420e024289d0478ed05b2fe4419195..c72a5dc73b09a11255839dca6811508656f95d44 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)
-{
-       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++)
-       {
-               if ((InspIRCd::MatchCIDR(u->second->MakeHost(), mask, ascii_case_insensitive_map)) ||
-                   (InspIRCd::MatchCIDR(u->second->MakeHostIP(), mask, 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 G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),mask.c_str(),percent);
-               return true;
-       }
-       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)
 {
@@ -127,7 +31,7 @@ CmdResult SplitCommand::Handle(const std::vector<std::string>& parms, User* 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 in command (uuid=%s)!", u->uuid.c_str());
        return CMD_INVALID;
 }
 
@@ -145,4 +49,3 @@ CmdResult SplitCommand::HandleServer(const std::vector<std::string>&, FakeUser*)
 {
        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 8438f8c..0000000
+++ /dev/null
@@ -1,410 +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 [ohurmMiaplf]";
-       }
-       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++)
-                       {
-                               /* None of this applies if we WHO ourselves */
-                               if (user != i->first)
-                               {
-                                       /* opers only, please */
-                                       if (opt_viewopersonly && !IS_OPER(i->first))
-                                               continue;
-
-                                       /* If we're not inside the channel, hide +i users */
-                                       if (i->first->IsModeSet('i') && !inside && !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..2af796b21c771c3532781d881c7470ec652809f2 100644 (file)
@@ -91,7 +91,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 +119,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 +136,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 +173,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 +199,13 @@ struct Parser
                if (name.empty())
                        throw CoreException("Empty tag name");
 
-               std::vector<KeyVal>* items;
-               std::set<std::string> seen;
+               ConfigItems* items;
                tag = ConfigTag::create(name, current.filename, current.line, items);
 
-               while (kv(items, seen));
+               while (kv(items))
+               {
+                       // Do nothing here (silences a GCC warning).
+               }
 
                if (name == mandatory_tag)
                {
@@ -197,21 +219,21 @@ struct Parser
                }
                else if (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")
                {
-                       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")
                {
-                       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");
@@ -223,9 +245,9 @@ struct Parser
                {
                        std::string format = tag->getString("format");
                        if (format == "xml")
-                               flags |= FLAG_USE_XML;
+                               flags &= ~FLAG_USE_COMPAT;
                        else if (format == "compat")
-                               flags &= ~FLAG_USE_XML;
+                               flags |= FLAG_USE_COMPAT;
                        else if (!format.empty())
                                throw CoreException("Unknown configuration format " + format);
                }
@@ -297,7 +319,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 +330,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 +342,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 +363,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 +401,23 @@ 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, 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)
+long ConfigTag::getInt(const std::string &key, long def, long min, long max)
 {
        std::string result;
        if(!readString(key, result))
@@ -446,18 +431,41 @@ long ConfigTag::getInt(const std::string &key, long def)
        switch (toupper(*res_tail))
        {
                case 'K':
-                       res= res* 1024;
+                       res = res * 1024;
                        break;
                case 'M':
-                       res= res* 1024 * 1024;
+                       res = res * 1024 * 1024;
                        break;
                case 'G':
-                       res= res* 1024 * 1024 * 1024;
+                       res = res * 1024 * 1024 * 1024;
                        break;
        }
+
+       CheckRange(key, res, def, min, max);
        return res;
 }
 
+void ConfigTag::CheckRange(const std::string& key, long& res, long def, long min, long max)
+{
+       if (res < min || res > max)
+       {
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <%s:%s> value of %ld is not between %ld and %ld; set to %ld.",
+                       tag.c_str(), key.c_str(), res, min, max, def);
+               res = def;
+       }
+}
+
+long ConfigTag::getDuration(const std::string& key, long def, long min, long max)
+{
+       std::string duration;
+       if (!readString(key, duration))
+               return def;
+
+       long ret = InspIRCd::Duration(duration);
+       CheckRange(key, ret, def, min, max);
+       return ret;
+}
+
 double ConfigTag::getFloat(const std::string &key, double def)
 {
        std::string result;
@@ -477,7 +485,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 +495,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 +507,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..168bdd09be8d2345018426e47cf4da41eb02592e 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->getInt("maxnick", 30))
+       , ChanMax(tag->getInt("maxchan", 64))
+       , MaxModes(tag->getInt("maxmodes", 20))
+       , IdentMax(tag->getInt("maxident", 10))
+       , MaxQuit(tag->getInt("maxquit", 255))
+       , MaxTopic(tag->getInt("maxtopic", 307))
+       , MaxKick(tag->getInt("maxkick", 255))
+       , MaxGecos(tag->getInt("maxgecos", 128))
+       , MaxAway(tag->getInt("maxaway", 200))
+       , MaxLine(tag->getInt("maxline", 512))
+       , MaxHost(tag->getInt("maxhost", 64))
 {
-       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()
-{
-       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)
+static ConfigTag* CreateEmptyTag()
 {
-       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());
+       ConfigItems* items;
+       return ConfigTag::create("empty", "<auto>", 0, items);
 }
 
-template<typename T, typename V>
-static void range(T& value, V min, V max, V def, const char* msg)
+ServerConfig::ServerConfig()
+       : EmptyTag(CreateEmptyTag())
+       , Limits(EmptyTag)
+       , Paths(EmptyTag)
+       , RawLog(false)
+       , NoSnoticeStack(false)
 {
-       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;
 }
 
-
-static void ValidIP(const std::string& ip, const std::string& key)
+ServerConfig::~ServerConfig()
 {
-       irc::sockets::sockaddrs dummy;
-       if (!irc::sockets::aptosa(ip, 0, dummy))
-               throw CoreException("The value of "+key+" is not an IP address");
+       delete EmptyTag;
 }
 
 static void ValidHost(const std::string& p, const std::string& msg)
@@ -133,83 +92,29 @@ static void ValidHost(const std::string& p, const std::string& msg)
                throw CoreException("The value of "+msg+" is not a valid hostname");
 }
 
-bool ServerConfig::ApplyDisabledCommands(const std::string& data)
+bool ServerConfig::ApplyDisabledCommands()
 {
-       std::stringstream dcmds(data);
-       std::string thiscmd;
-
-       /* Enable everything first */
-       for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
+       // Enable everything first.
+       const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+       for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
                x->second->Disable(false);
 
-       /* Now disable all the ones which the user wants disabled */
-       while (dcmds >> thiscmd)
+       // Now disable the commands specified in the config.
+       std::string command;
+       irc::spacesepstream commandlist(ConfValue("disabled")->getString("commands"));
+       while (commandlist.GetToken(command))
        {
-               Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
-               if (cm != ServerInstance->Parser->cmdlist.end())
+               Command* handler = ServerInstance->Parser.GetHandler(command);
+               if (!handler)
                {
-                       cm->second->Disable(true);
-               }
-       }
-       return true;
-}
-
-static void FindDNS(std::string& server)
-{
-       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);
+                       ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Unable to disable the %s command as it does not exist!", command.c_str());
+                       continue;
                }
 
-               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", LOG_DEBUG, "The %s command has been disabled", command.c_str());
+               handler->Disable(true);
        }
-
-       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";
+       return true;
 }
 
 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
@@ -250,14 +155,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 +183,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 +206,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;
                        }
@@ -322,9 +223,9 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
        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;
        }
@@ -404,8 +305,8 @@ 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))
                        {
@@ -428,6 +329,15 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
                        me->maxchans = tag->getInt("maxchans", me->maxchans);
                        me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
                        me->limit = tag->getInt("limit", me->limit);
+                       me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
+
+                       std::string ports = tag->getString("port");
+                       if (!ports.empty())
+                       {
+                               irc::portparser portrange(ports, false);
+                               while (int port = portrange.GetToken())
+                                       me->ports.insert(port);
+                       }
 
                        ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
                        if (oldMask != oldBlocksByMask.end())
@@ -445,88 +355,80 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
 
 /** Represents a deprecated configuration tag.
  */
-struct Deprecated
+struct DeprecatedConfig
 {
-       /** Tag name
-        */
-       const char* tag;
-       /** Tag value
-        */
-       const char* value;
-       /** Reason for deprecation
-        */
-       const char* reason;
+       /** Tag name. */
+       std::string tag;
+
+       /** Attribute key. */
+       std::string key;
+
+       /** Attribute value. */
+       std::string value;
+
+       /** Reason for deprecation. */
+       std::string 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."},
+static const DeprecatedConfig ChangedConfig[] = {
+       { "bind",        "transport",   "",                 "has been moved to <bind:ssl> as of 2.0" },
+       { "die",         "value",       "",                 "you need to reread your config" },
+       { "gnutls",      "starttls",    "",                 "has been replaced with m_starttls as of 3.0" },
+       { "link",        "autoconnect", "",                 "2.0+ does not use this attribute - define <autoconnect> tags instead" },
+       { "link",        "transport",   "",                 "has been moved to <link:ssl> as of 2.0" },
+       { "module",      "name",        "m_chanprotect.so", "has been replaced with m_customprefix as of 3.0" },
+       { "module",      "name",        "m_halfop.so",      "has been replaced with m_customprefix as of 3.0" },
+       { "options",     "cyclehosts",  "",                 "has been replaced with m_hostcycle as of 3.0" },
+       { "performance", "nouserdns",   "",                 "has been moved to <connect:resolvehostnames> as of 3.0" }
 };
 
 void ServerConfig::Fill()
 {
        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");
+               ServerName = server->getString("name", "irc.example.com");
                ValidHost(ServerName, "<server:name>");
-               if (!sid.empty() && !ServerInstance->IsSID(sid))
+
+               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());
+                       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")->getInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
+       CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
        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", "");
+       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);
        DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
        UserStats = security->getString("userstats");
-       CustomVersion = security->getString("customversion", Network + " IRCd");
+       CustomVersion = security->getString("customversion");
        HideSplits = security->getBool("hidesplits");
        HideBans = security->getBool("hidebans");
        HideWhoisServer = security->getString("hidewhois");
@@ -534,52 +436,26 @@ void ServerConfig::Fill()
        HideULineKills = security->getBool("hideulinekills");
        RestrictBannedUsers = security->getBool("restrictbannedusers", true);
        GenericOper = security->getBool("genericoper");
-       NoUserDns = ConfValue("performance")->getBool("nouserdns");
        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->getInt("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);
+       OperMaxChans = ConfValue("channels")->getInt("opers");
        c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
        c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
-       Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
-       Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
-       Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
-       Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
-       Limits.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);
+       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 +466,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,30 +474,25 @@ void ServerConfig::Fill()
        ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
        ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
 
-       memset(DisabledUModes, 0, sizeof(DisabledUModes));
+       DisabledUModes.reset();
        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')))
+               // Complain when the character is not a valid mode character.
+               if (!ModeParser::IsModeChar(*p))
                        throw CoreException("Invalid usermode " + std::string(1, *p) + " was found.");
-               DisabledUModes[*p - 'A'] = 1;
+               DisabledUModes.set(*p - 'A');
        }
 
-       memset(DisabledCModes, 0, sizeof(DisabledCModes));
+       DisabledCModes.reset();
        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')))
+               if (!ModeParser::IsModeChar(*p))
                        throw CoreException("Invalid chanmode " + std::string(1, *p) + " was found.");
-               DisabledCModes[*p - 'A'] = 1;
+               DisabledCModes.set(*p - 'A');
        }
 
-       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")
@@ -675,12 +527,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 +539,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 +548,26 @@ 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++)
+               for (int index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++)
                {
-                       std::string dummy;
-                       ConfigTagList tags = ConfTags(ChangedConfig[Index].tag);
+                       std::string value;
+                       ConfigTagList tags = ConfTags(ChangedConfig[index].tag);
                        for(ConfigIter i = tags.first; i != tags.second; ++i)
                        {
-                               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";
+                               if (i->second->readString(ChangedConfig[index].key, value, true)
+                                       && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value))
+                               {
+                                       errstr << "Your configuration contains a deprecated value: <"  << ChangedConfig[index].tag;
+                                       if (ChangedConfig[index].value.empty())
+                                       {
+                                               errstr << ':' << ChangedConfig[index].key;
+                                       }
+                                       else
+                                       {
+                                               errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\"";
+                                       }
+                                       errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")" << std::endl;
+                               }
                        }
                }
 
@@ -721,7 +579,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 +589,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 +601,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 +616,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 +631,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);
        }
@@ -783,15 +645,11 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
                ConfigTag *tag = (*it)->config;
                // Make sure our connection class allows motd colors
                if(!tag->getBool("allowmotdcolors"))
-                     continue;
+                       continue;
 
                ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
                if (file != this->Files.end())
-                     InspIRCd::ProcessColors(file->second);
-
-               file = this->Files.find(tag->getString("rules", "rules"));
-               if (file != this->Files.end())
-                     InspIRCd::ProcessColors(file->second);
+                       InspIRCd::ProcessColors(file->second);
        }
 
        /* No old configuration -> initial boot, nothing more to do here */
@@ -813,20 +671,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 +687,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 +695,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 +751,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 +761,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 +794,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 +806,33 @@ 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);
+               ChanModeReference ban(NULL, "ban");
+               static_cast<ListModeBase*>(*ban)->DoRehash();
+               Config->ApplyDisabledCommands();
                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());
+                       }
+               }
+
+               // 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..a1319eb
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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>]";
+}
+
+/** Handle /INVITE
+ */
+CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       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))
+                               timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[2]);
+                       else if (parameters.size() > 3)
+                               timeout = ConvToInt(parameters[3]);
+               }
+
+               if ((!c) || (!u) || (u->registered != REG_ALL))
+               {
+                       user->WriteNumeric(Numerics::NoSuchNick(c ? parameters[0] : parameters[1]));
+                       return CMD_FAILURE;
+               }
+
+               // Verify channel timestamp if the INVITE is coming from a remote server
+               if (!IS_LOCAL(user))
+               {
+                       // Remote INVITE commands must carry a channel timestamp
+                       if (parameters.size() < 3)
+                               return CMD_INVALID;
+
+                       // Drop the invite if our channel TS is lower
+                       time_t RemoteTS = ConvToInt(parameters[2]);
+                       if (c->age < RemoteTS)
+                               return CMD_FAILURE;
+               }
+
+               if ((IS_LOCAL(user)) && (!c->HasUser(user)))
+               {
+                       user->WriteNumeric(ERR_NOTONCHANNEL, 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;
+                               }
+                       }
+               }
+
+               if (IS_LOCAL(u))
+               {
+                       invapi.Create(IS_LOCAL(u), c, timeout);
+                       u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
+               }
+
+               if (IS_LOCAL(user))
+               {
+                       user->WriteNumeric(RPL_INVITING, u->nick, c->name);
+                       if (u->IsAway())
+                               user->WriteNumeric(RPL_AWAY, u->nick, u->awaymsg);
+               }
+
+               char prefix = 0;
+               unsigned int minrank = 0;
+               switch (ServerInstance->Config->AnnounceInvites)
+               {
+                       case ServerConfig::INVITE_ANNOUNCE_OPS:
+                       {
+                               prefix = '@';
+                               minrank = OP_VALUE;
+                               break;
+                       }
+                       case ServerConfig::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 (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
+                       c->WriteAllExcept(user, true, prefix, excepts, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
+       }
+       else if (IS_LOCAL(user))
+       {
+               // pinched from ircu - invite with not enough parameters shows channels
+               // youve been invited to but haven't joined yet.
+               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 std::vector<std::string>& 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..06e203e
--- /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(const std::vector<std::string>& parameters, LocalUser *user)
+{
+       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_NOSUCHCHANNEL, parameters[0], "Invalid channel name");
+       return CMD_FAILURE;
+}
diff --git a/src/coremods/core_channel/cmd_kick.cpp b/src/coremods/core_channel/cmd_kick.cpp
new file mode 100644 (file)
index 0000000..420ed2b
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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 (const std::vector<std::string>& parameters, User *user)
+{
+       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 ((!u) || (!c) || (u->registered != REG_ALL))
+       {
+               user->WriteNumeric(Numerics::NoSuchNick(c ? parameters[1] : parameters[0]));
+               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 std::vector<std::string>& 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..21fe43c
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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"
+
+CommandNames::CommandNames(Module* parent)
+       : SplitCommand(parent, "NAMES", 0, 0)
+       , secretmode(parent, "secret")
+       , privatemode(parent, "private")
+       , invisiblemode(parent, "invisible")
+{
+       syntax = "{<channel>{,<channel>}}";
+}
+
+/** Handle /NAMES
+ */
+CmdResult CommandNames::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+{
+       Channel* c;
+
+       if (!parameters.size())
+       {
+               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::NoSuchNick(parameters[0]));
+       return CMD_FAILURE;
+}
+
+void CommandNames::SendNames(LocalUser* user, Channel* chan, bool show_invisible)
+{
+       Numeric::Builder<' '> reply(user, RPL_NAMREPLY, false, chan->name.size() + 3);
+       Numeric::Numeric& numeric = reply.GetNumeric();
+       if (chan->IsModeSet(secretmode))
+               numeric.push(std::string(1, '@'));
+       else if (chan->IsModeSet(privatemode))
+               numeric.push(std::string(1, '*'));
+       else
+               numeric.push(std::string(1, '='));
+
+       numeric.push(chan->name);
+       numeric.push(std::string());
+
+       std::string prefixlist;
+       std::string nick;
+       const Channel::MemberMap& members = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator i = members.begin(); i != members.end(); ++i)
+       {
+               if ((!show_invisible) && (i->first->IsModeSet(invisiblemode)))
+               {
+                       // Member is invisible and we are not supposed to show them
+                       continue;
+               }
+
+               Membership* const memb = i->second;
+
+               prefixlist.clear();
+               char prefix = memb->GetPrefixChar();
+               if (prefix)
+                       prefixlist.push_back(prefix);
+               nick = i->first->nick;
+
+               ModResult res;
+               FIRST_MOD_RESULT(OnNamesListItem, res, (user, memb, prefixlist, nick));
+
+               // See if a module wants us to exclude this user from NAMES
+               if (res == MOD_RES_DENY)
+                       continue;
+
+               reply.Add(prefixlist, nick);
+       }
+
+       reply.Flush();
+       user->WriteNumeric(RPL_ENDOFNAMES, chan->name, "End of /NAMES list.");
+}
diff --git a/src/coremods/core_channel/cmd_topic.cpp b/src/coremods/core_channel/cmd_topic.cpp
new file mode 100644 (file)
index 0000000..835ac82
--- /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(const std::vector<std::string>& parameters, LocalUser* user)
+{
+       Channel* c = ServerInstance->FindChan(parameters[0]);
+       if (!c)
+       {
+               user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+               return CMD_FAILURE;
+       }
+
+       if (parameters.size() == 1)
+       {
+               if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
+               {
+                       user->WriteNumeric(Numerics::NoSuchNick(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/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp
new file mode 100644 (file)
index 0000000..3af8096
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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"
+
+class CoreModChannel : public Module, public CheckExemption::EventListener
+{
+       Invite::APIImpl invapi;
+       CommandInvite cmdinvite;
+       CommandJoin cmdjoin;
+       CommandKick cmdkick;
+       CommandNames cmdnames;
+       CommandTopic cmdtopic;
+       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)
+               , invapi(this)
+               , cmdinvite(this, invapi)
+               , cmdjoin(this)
+               , cmdkick(this)
+               , cmdnames(this)
+               , cmdtopic(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
+               Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan };
+               if (optionstag->getBool("invitebypassmodes", true))
+                       ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation));
+               else
+               {
+                       for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++)
+                               ServerInstance->Modules.Detach(events[i], this);
+               }
+
+               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;
+               }
+               exemptions.swap(exempts);
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               // 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(',');
+
+                       buffer.append(iter->second);
+                       buffer.push_back(':');
+                       buffer.append(ConvToStr(iter->first));
+               }
+       }
+
+       void OnPostJoin(Membership* memb) CXX11_OVERRIDE
+       {
+               Channel* const chan = memb->chan;
+               LocalUser* const localuser = IS_LOCAL(memb->user);
+               if (localuser)
+               {
+                       // Remove existing invite, if any
+                       invapi.Remove(localuser, chan);
+
+                       if (chan->topicset)
+                               Topic::ShowTopic(localuser, chan);
+
+                       // Show all members of the channel, including invisible (+i) users
+                       cmdnames.SendNames(localuser, chan, true);
+               }
+       }
+
+       ModResult OnCheckKey(User* user, Channel* chan, const std::string& keygiven) CXX11_OVERRIDE
+       {
+               // Hook only runs when being invited bypasses +bkl
+               return IsInvited(user, chan);
+       }
+
+       ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               // Hook only runs when being invited bypasses +bkl
+               return IsInvited(user, chan);
+       }
+
+       ModResult OnCheckLimit(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               // Hook only runs when being invited bypasses +bkl
+               return IsInvited(user, chan);
+       }
+
+       ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               // Hook always runs
+               return IsInvited(user, chan);
+       }
+
+       void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+       {
+               invapi.RemoveAll(user);
+       }
+
+       void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
+       {
+               // Make sure the channel won't appear in invite lists from now on, don't wait for cull to unset the ext
+               invapi.RemoveAll(chan);
+       }
+
+       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);
+       }
+
+       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..19a9848
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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"
+#include "modules/exemption.h"
+
+namespace Topic
+{
+       void ShowTopic(LocalUser* user, Channel* chan);
+}
+
+namespace Invite
+{
+       class APIImpl;
+}
+
+/** Handle /INVITE.
+ */
+class CommandInvite : public Command
+{
+       Invite::APIImpl& invapi;
+
+ public:
+       /** Constructor for invite.
+        */
+       CommandInvite(Module* parent, 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(const std::vector<std::string>& parameters, User*user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** 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(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** Handle /NAMES.
+ */
+class CommandNames : public SplitCommand
+{
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+       UserModeReference invisiblemode;
+
+ 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(const std::vector<std::string>& parameters, LocalUser* user);
+
+       /** Spool the NAMES list for a given channel to the given user
+        * @param user User to spool the NAMES list to
+        * @param chan Channel whose nicklist to send
+        * @param show_invisible True to show invisible (+i) members to the user, false to omit them from the list
+        */
+       void SendNames(LocalUser* user, Channel* chan, bool show_invisible);
+};
+
+/** Handle /KICK.
+ */
+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(const std::vector<std::string>& parameters, User *user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
diff --git a/src/coremods/core_channel/invite.cpp b/src/coremods/core_channel/invite.cpp
new file mode 100644 (file)
index 0000000..7ac662e
--- /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, ConvToInt(exptime));
+       }
+}
+
+Invite::Invite::Invite(LocalUser* u, Channel* c)
+       : user(u)
+       , chan(c)
+       , expiretimer(NULL)
+{
+}
+
+Invite::Invite::~Invite()
+{
+       delete expiretimer;
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::~ %p", (void*) this);
+}
+
+void Invite::Invite::Serialize(SerializeFormat format, bool show_chans, std::string& out)
+{
+       if (show_chans)
+               out.append(this->chan->name);
+       else
+               out.append((format == FORMAT_USER) ? user->nick : user->uuid);
+       out.push_back(' ');
+
+       if (expiretimer)
+               out.append(ConvToStr(expiretimer->GetTrigger()));
+       else
+               out.push_back('0');
+       out.push_back(' ');
+}
+
+InviteExpireTimer::InviteExpireTimer(Invite::Invite* invite, time_t timeout)
+       : Timer(timeout)
+       , inv(invite)
+{
+}
+
+bool InviteExpireTimer::Tick(time_t currtime)
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "InviteExpireTimer::Tick(): expired %p", (void*) inv);
+       apiimpl->Destruct(inv);
+       return false;
+}
diff --git a/src/coremods/core_channel/invite.h b/src/coremods/core_channel/invite.h
new file mode 100644 (file)
index 0000000..2a99ec2
--- /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(store);
+       }
+
+       void free(void* item) CXX11_OVERRIDE
+       {
+               Store<T>* store = static_cast<Store<T>*>(item);
+               for (typename Store<T>::List::iterator i = store->invites.begin(); i != store->invites.end(); )
+               {
+                       Invite* inv = *i;
+                       // Destructing the Invite invalidates the iterator, so move it now
+                       ++i;
+                       RemoveInvite(inv, (ExtType != ExtensionItem::EXT_USER), (ExtType == ExtensionItem::EXT_USER));
+               }
+
+               delete store;
+       }
+
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
+       {
+               if (format == FORMAT_NETWORK)
+                       return std::string();
+
+               std::string ret;
+               Store<T>* store = static_cast<Store<T>*>(item);
+               for (typename insp::intrusive_list<Invite, T>::iterator i = store->invites.begin(); i != store->invites.end(); ++i)
+               {
+                       Invite* inv = *i;
+                       inv->Serialize(format, (ExtType == ExtensionItem::EXT_USER), ret);
+               }
+               if (!ret.empty())
+                       ret.erase(ret.length()-1);
+               return ret;
+       }
+
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
+       {
+               if ((ExtType != ExtensionItem::EXT_CHANNEL) && (format != FORMAT_NETWORK))
+                       UnserializeInvite(static_cast<LocalUser*>(container), value);
+       }
+};
+
+class Invite::APIImpl : public APIBase
+{
+       ExtItem<LocalUser, ExtensionItem::EXT_USER> userext;
+       ExtItem<Channel, ExtensionItem::EXT_CHANNEL> chanext;
+
+ public:
+       APIImpl(Module* owner);
+
+       void Create(LocalUser* user, Channel* chan, time_t timeout) CXX11_OVERRIDE;
+       Invite* Find(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+       bool Remove(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+       const List* GetList(LocalUser* user) CXX11_OVERRIDE;
+
+       void RemoveAll(LocalUser* user) { userext.unset(user); }
+       void RemoveAll(Channel* chan) { chanext.unset(chan); }
+       void Destruct(Invite* inv, bool remove_chan = true, bool remove_user = true);
+       void Unserialize(LocalUser* user, const std::string& value);
+};
diff --git a/src/coremods/core_dns.cpp b/src/coremods/core_dns.cpp
new file mode 100644 (file)
index 0000000..1040bb0
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+ * 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 recieved to/from the nameserver
+ */
+class Packet : public Query
+{
+       static bool IsValidName(const std::string& name)
+       {
+               return (name.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") == std::string::npos);
+       }
+
+       void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
+       {
+               if (pos + name.length() + 2 > output_size)
+                       throw Exception("Unable to pack name");
+
+               ServerInstance->Logs->Log(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 (!IsValidName(record.rdata))
+                                       throw Exception("Invalid name"); // XXX: Causes the request to time out
+
+                               break;
+                       }
+                       case QUERY_TXT:
+                       {
+                               if (pos + rdlength > input_size)
+                                       throw Exception("Unable to unpack txt resource record");
+
+                               record.rdata = std::string(reinterpret_cast<const char *>(input + pos), rdlength);
+                               pos += rdlength;
+
+                               if (record.rdata.find_first_of("\r\n\0", 0, 3) != std::string::npos)
+                                       throw Exception("Invalid character in txt record");
+
+                               break;
+                       }
+                       default:
+                               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.sa, this->myserver.sa_size()) != len)
+                       throw Exception("DNS: Unable to send query");
+
+               // Add timer for timeout
+               ServerInstance->Timers.AddTimer(req);
+       }
+
+       void RemoveRequest(DNS::Request* req) 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.sa.sa_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.sa.sa_family == AF_INET)
+                                       sourceaddr = "0.0.0.0";
+                               else if (myserver.sa.sa_family == AF_INET6)
+                                       sourceaddr = "::";
+                       }
+                       irc::sockets::aptosa(sourceaddr, sourceport, bindto);
+
+                       if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
+                       {
+                               /* Failed to bind */
+                               ServerInstance->Logs->Log(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.sa.sa_family != myserver.sa.sa_family)
+                               ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Nameserver address family differs from source address family - hostnames might not resolve");
+               }
+               else
+               {
+                       ServerInstance->Logs->Log(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->getInt("sourceport", 0, 0, 65535);
+
+               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("DNS support", 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..4ec8869
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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)
+       {
+               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.sa.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->sa.sa_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)
+       {
+               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 (!DNS || !user->MyClass->resolvehostnames)
+               {
+                       user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
+                       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..f79ebc0
--- /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 (const std::vector<std::string>& parameters, User *user)
+{
+       if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+               return CMD_SUCCESS;
+       user->WriteRemoteNumeric(RPL_ADMINME, InspIRCd::Format("Administrative info for %s", ServerInstance->Config->ServerName.c_str()));
+       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("E-Mail   - %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..a7a622f
--- /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 (const std::vector<std::string>&, User *user)
+{
+       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..e84daec
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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[] = {
+       "                   -/\\- \2InspIRCd\2 -\\/-",
+       "                 November 2002 - Present",
+       " ",
+       "\2Core Developers\2:",
+       "    Attila Molnar,          Attila,     <attilamolnar@hush.com>",
+       "    Peter Powell,           SaberUK,    <petpow@saberuk.com>",
+       " ",
+       "\2Former Developers\2:",
+       "    Oliver Lupton,          Om,         <om@inspircd.org>",
+       "    John Brooks,            Special,    <special@inspircd.org>",
+       "    Dennis Friis,           peavey,     <peavey@inspircd.org>",
+       "    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>",
+       " ",
+       "\2Founding Developers\2:",
+       "    Craig Edwards,          Brain,      <brain@inspircd.org>",
+       "    Craig McLure,           Craig,      <craig@inspircd.org>",
+       "    Robin Burchell,         w00t,       <w00t@inspircd.org>",
+       " ",
+       "\2Active Contributors\2:",
+       "    Adam",
+       " ",
+       "\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",
+       "   jackmcbarn      ChrisTX         Shawn          Shutter",
+       " ",
+       "\2Thanks To\2:",
+       "   Asmo            Brik            fraggeln       genius3000",
+       "   Sheogorath",
+       " ",
+       " 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->WriteRemoteNumeric(RPL_INFO, lines[i++]);
+       FOREACH_MOD(OnInfo, (user));
+       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..47dd2dc
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 (const std::vector<std::string>& parameters, User *user)
+{
+       // 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() || 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");
+                       int pos = 0;
+                       for (int mult = 2; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
+                               if (!(V.Flags & mult))
+                                       flags[pos] = '-';
+
+#ifdef INSPIRCD_STATIC
+                       user->WriteRemoteNumeric(RPL_MODLIST, m->ModuleSourceFile, INSPIRCD_VERSION, flags, V.description);
+#else
+                       std::string srcrev = m->ModuleDLLManager->GetVersion();
+                       user->WriteRemoteNumeric(RPL_MODLIST, m->ModuleSourceFile, srcrev.empty() ? "*" : srcrev, flags, V.description);
+#endif
+               }
+               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..cfb083e
--- /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 (const std::vector<std::string>& parameters, User *user)
+{
+       if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+       {
+               // Give extra penalty if a non-oper queries the /MOTD of a remote server
+               LocalUser* localuser = IS_LOCAL(user);
+               if ((localuser) && (!user->IsOper()))
+                       localuser->CommandFloodPenalty += 2000;
+               return CMD_SUCCESS;
+       }
+
+       ConfigTag* tag = 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 = ServerInstance->Config->Files.find(motd_name);
+       if (motd == ServerInstance->Config->Files.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..6755e58
--- /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 (const std::vector<std::string>& parameters, User *user)
+{
+       if (parameters.size() > 0 && 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..9ec0108
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 (const std::vector<std::string>&, User *user)
+{
+       std::string version = ServerInstance->GetVersionString((user->IsOper()));
+       user->WriteNumeric(RPL_VERSION, version);
+       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..bd51907
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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"
+
+RouteDescriptor ServerTargetCommand::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       // Parameter must be a server name, not a nickname or uuid
+       if ((!parameters.empty()) && (parameters[0].find('.') != std::string::npos))
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
+
+class CoreModInfo : public Module
+{
+       CommandAdmin cmdadmin;
+       CommandCommands cmdcommands;
+       CommandInfo cmdinfo;
+       CommandModules cmdmodules;
+       CommandMotd cmdmotd;
+       CommandTime cmdtime;
+       CommandVersion cmdversion;
+
+ public:
+       CoreModInfo()
+               : cmdadmin(this), cmdcommands(this), cmdinfo(this), cmdmodules(this), cmdmotd(this), cmdtime(this), cmdversion(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               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");
+       }
+
+       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..ecfeb4f
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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 std::vector<std::string>& parameters);
+};
+
+/** 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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /MOTD.
+ */
+class CommandMotd : public ServerTargetCommand
+{
+ public:
+       /** 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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, User* user);
+};
diff --git a/src/coremods/core_ison.cpp b/src/coremods/core_ison.cpp
new file mode 100644 (file)
index 0000000..f1733ba
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+class CommandIson : public SplitCommand
+{
+ public:
+       /** Constructor for ison.
+        */
+       CommandIson(Module* parent)
+               : SplitCommand(parent, "ISON", 1)
+       {
+               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(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+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(const std::vector<std::string>& parameters, LocalUser* user)
+{
+       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;
+}
+
+
+COMMAND_INIT(CommandIson)
diff --git a/src/coremods/core_list.cpp b/src/coremods/core_list.cpp
new file mode 100644 (file)
index 0000000..6a62d12
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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
+{
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+
+ public:
+       /** Constructor for list.
+        */
+       CommandList(Module* parent)
+               : Command(parent,"LIST", 0, 0)
+               , secretmode(creator, "secret")
+               , privatemode(creator, "private")
+       {
+               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(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(RPL_LISTSTART, "Channel", "Users Name");
+
+       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);
+               }
+       }
+
+       const bool has_privs = user->HasPrivPermission("channels/auspex");
+       const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
+
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+       {
+               Channel* const chan = i->second;
+
+               // attempt to match a glob pattern
+               long users = chan->GetUserCounter();
+
+               bool too_few = (minusers && (users <= minusers));
+               bool too_many = (maxusers && (users >= maxusers));
+
+               if (too_many || too_few)
+                       continue;
+
+               if (match_name_topic)
+               {
+                       if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
+                               continue;
+               }
+
+               // if the channel is not private/secret, OR the user is on the channel anyway
+               bool n = (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;
+}
+
+
+COMMAND_INIT(CommandList)
diff --git a/src/coremods/core_loadmodule.cpp b/src/coremods/core_loadmodule.cpp
new file mode 100644 (file)
index 0000000..fde1495
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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(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(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:
+       /** Constructor for unloadmodule.
+        */
+       CommandUnloadmodule(Module* parent)
+               : Command(parent,"UNLOADMODULE", 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(const std::vector<std::string>& parameters, User* user);
+};
+
+CmdResult CommandUnloadmodule::Handle(const std::vector<std::string>& parameters, User* user)
+{
+       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;
+       }
+
+       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);
+       }
+};
+
+MODULE_INIT(CoreModLoadModule)
diff --git a/src/coremods/core_lusers.cpp b/src/coremods/core_lusers.cpp
new file mode 100644 (file)
index 0000000..7f1903d
--- /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(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();
+       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)
+       {
+               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("LUSERS", VF_VENDOR | VF_CORE);
+       }
+};
+
+MODULE_INIT(ModuleLusers)
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..5fe6435
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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)
+       : Command(parent, "DIE", 1)
+{
+       flags_needed = 'o';
+       syntax = "<server>";
+}
+
+static void QuitAll()
+{
+       const std::string quitmsg = "Server shutdown";
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       while (!list.empty())
+               ServerInstance->Users.QuitUser(list.front(), quitmsg);
+}
+
+void DieRestart::SendError(const std::string& message)
+{
+       const std::string unregline = "ERROR :" + message;
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               LocalUser* user = *i;
+               if (user->registered == REG_ALL)
+               {
+                       user->WriteNotice(message);
+               }
+               else
+               {
+                       // Unregistered connections receive ERROR, not a NOTICE
+                       user->Write(unregline);
+               }
+       }
+}
+
+/** Handle /DIE
+ */
+CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       if (DieRestart::CheckPass(user, parameters[0], "diepass"))
+       {
+               {
+                       std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
+                       ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, diebuf);
+                       DieRestart::SendError(diebuf);
+               }
+
+               QuitAll();
+               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..8a453f7
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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)
+{
+       flags_needed = 'o';
+       syntax = "<nickname> <reason>";
+       TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
+}
+
+
+/** Handle /KILL
+ */
+CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       /* 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 (!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);
+       }
+
+       if ((!ServerInstance->Config->HideULineKills) || (!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(user) || IS_LOCAL(target))
+               ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "%s KILL: %s :%s!%s!%s (%s)",
+                               IS_LOCAL(user) && IS_LOCAL(target) ? "LOCAL" : "REMOTE",
+                               target->nick.c_str(),
+                               ServerInstance->Config->ServerName.c_str(), user->GetDisplayedHost().c_str(), user->nick.c_str(),
+                               parameters[1].c_str());
+
+       if (IS_LOCAL(target))
+       {
+               target->Write(":%s KILL %s :%s",
+                               ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
+                               target->nick.c_str(),
+                               parameters[1].c_str());
+
+               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 std::vector<std::string>& 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, 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..0322a05
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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(const std::vector<std::string>& parameters, LocalUser *user)
+{
+       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());
+       ServerInstance->Logs->Log("OPER", LOG_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;
+}
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..5ce38eb
--- /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 (const std::vector<std::string>& parameters, User *user)
+{
+       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..6c19329
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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)
+       : Command(parent, "RESTART", 1, 1)
+{
+       flags_needed = 'o';
+       syntax = "<server>";
+}
+
+CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Restart: %s", user->nick.c_str());
+       if (DieRestart::CheckPass(user, parameters[0], "restartpass"))
+       {
+               ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
+
+               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..a6b2abd
--- /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 "core_oper.h"
+
+namespace DieRestart
+{
+       bool CheckPass(User* user, const std::string& inputpass, const char* confentry)
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("power");
+               // The hash method for *BOTH* the die and restart passwords
+               const std::string hash = tag->getString("hash");
+               const std::string correctpass = tag->getString(confentry,  ServerInstance->Config->ServerName);
+               return ServerInstance->PassCompare(user, correctpass, inputpass, hash);
+       }
+}
+
+class CoreModOper : public Module
+{
+       CommandDie cmddie;
+       CommandKill cmdkill;
+       CommandOper cmdoper;
+       CommandRehash cmdrehash;
+       CommandRestart cmdrestart;
+
+ public:
+       CoreModOper()
+               : cmddie(this), cmdkill(this), cmdoper(this), cmdrehash(this), cmdrestart(this)
+       {
+       }
+
+       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..338a369
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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
+{
+       /** Checks a die or restart password
+        * @param user The user executing /DIE or /RESTART
+        * @param inputpass The password given by the user
+        * @param confkey The name of the key in the power tag containing the correct password
+        * @return True if the given password was correct, false if it was not
+        */
+       bool CheckPass(User* user, const std::string& inputpass, const char* confkey);
+
+       /** Send an ERROR to unregistered users and a NOTICE to all registered local users
+        * @param message Message to send
+        */
+       void SendError(const std::string& message);
+}
+
+/** Handle /DIE.
+ */
+class CommandDie : public Command
+{
+ public:
+       /** Constructor for die.
+        */
+       CommandDie(Module* parent);
+
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /KILL.
+ */
+class CommandKill : public Command
+{
+       std::string lastuuid;
+       std::string killreason;
+
+ public:
+       /** Constructor for kill.
+        */
+       CommandKill(Module* parent);
+
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+
+       void EncodeParameter(std::string& param, int index);
+};
+
+/** Handle /OPER.
+ */
+class CommandOper : public SplitCommand
+{
+ public:
+       /** Constructor for oper.
+        */
+       CommandOper(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(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** Handle /REHASH.
+ */
+class CommandRehash : public Command
+{
+ public:
+       /** Constructor for rehash.
+        */
+       CommandRehash(Module* parent);
+
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+/** Handle /RESTART
+ */
+class CommandRestart : public Command
+{
+ public:
+       /** Constructor for restart.
+        */
+       CommandRestart(Module* parent);
+
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
diff --git a/src/coremods/core_privmsg.cpp b/src/coremods/core_privmsg.cpp
new file mode 100644 (file)
index 0000000..449097a
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * 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"
+
+namespace
+{
+       const char* MessageTypeString[] = { "PRIVMSG", "NOTICE" };
+}
+
+class MessageCommandBase : public Command
+{
+       ChanModeReference moderatedmode;
+       ChanModeReference noextmsgmode;
+
+       /** Send a PRIVMSG or NOTICE message to all local users from the given user
+        * @param user User sending the message
+        * @param msg The message to send
+        * @param mt Type of the message (MSG_PRIVMSG or MSG_NOTICE)
+        */
+       static void SendAll(User* user, const std::string& msg, MessageType mt);
+
+ public:
+       MessageCommandBase(Module* parent, MessageType mt)
+               : Command(parent, MessageTypeString[mt], 2, 2)
+               , 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 HandleMessage(const std::vector<std::string>& parameters, User* user, MessageType mt);
+
+       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]);
+       }
+};
+
+void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt)
+{
+       const std::string message = ":" + user->GetFullHost() + " " + MessageTypeString[mt] + " $* :" + msg;
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               if ((*i)->registered == REG_ALL)
+                       (*i)->Write(message);
+       }
+}
+
+CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& parameters, User* user, MessageType mt)
+{
+       User *dest;
+       Channel *chan;
+       CUList except_list;
+
+       LocalUser* localuser = IS_LOCAL(user);
+       if (localuser)
+               localuser->idle_lastmsg = ServerInstance->Time();
+
+       if (CommandParser::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, mt));
+               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(OnText, (user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
+               if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL))
+               {
+                       SendAll(user, text, mt);
+               }
+               FOREACH_MOD(OnUserMessage, (user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list, mt));
+               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 (localuser && chan->GetPrefixValue(user) < VOICE_VALUE)
+                       {
+                               if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
+                               {
+                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
+                                       return CMD_FAILURE;
+                               }
+
+                               if (chan->IsModeSet(moderatedmode))
+                               {
+                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m)");
+                                       return CMD_FAILURE;
+                               }
+
+                               if (ServerInstance->Config->RestrictBannedUsers)
+                               {
+                                       if (chan->IsBanned(user))
+                                       {
+                                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
+                                               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, mt));
+                       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(ERR_NOTEXTTOSEND, "No text to send");
+                               return CMD_FAILURE;
+                       }
+
+                       FOREACH_MOD(OnText, (user,chan,TYPE_CHANNEL,text,status,except_list));
+
+                       if (status)
+                       {
+                               chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), text);
+                       }
+                       else
+                       {
+                               chan->WriteAllExcept(user, false, status, except_list, "%s %s :%s", MessageTypeString[mt], chan->name.c_str(), text);
+                       }
+
+                       FOREACH_MOD(OnUserMessage, (user,chan, TYPE_CHANNEL, text, status, except_list, mt));
+               }
+               else
+               {
+                       /* no such nick/channel */
+                       user->WriteNumeric(Numerics::NoSuchNick(target));
+                       return CMD_FAILURE;
+               }
+               return CMD_SUCCESS;
+       }
+
+       const char* destnick = parameters[0].c_str();
+
+       if (localuser)
+       {
+               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->GetName().c_str(), targetserver + 1))
+                       {
+                               /* Incorrect server for user */
+                               user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+                               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(ERR_NOTEXTTOSEND, "No text to send");
+                       return CMD_FAILURE;
+               }
+
+               if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
+               {
+                       /* auto respond with aweh msg */
+                       user->WriteNumeric(RPL_AWAY, dest->nick, dest->awaymsg);
+               }
+
+               ModResult MOD_RESULT;
+
+               std::string temp = parameters[1];
+               FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list, mt));
+               if (MOD_RESULT == MOD_RES_DENY)
+                       return CMD_FAILURE;
+
+               const char* text = temp.c_str();
+
+               FOREACH_MOD(OnText, (user, dest, TYPE_USER, text, 0, except_list));
+
+               if (IS_LOCAL(dest))
+               {
+                       // direct write, same server
+                       dest->WriteFrom(user, "%s %s :%s", MessageTypeString[mt], dest->nick.c_str(), text);
+               }
+
+               FOREACH_MOD(OnUserMessage, (user, dest, TYPE_USER, text, 0, except_list, mt));
+       }
+       else
+       {
+               /* no such nick/channel */
+               user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+               return CMD_FAILURE;
+       }
+       return CMD_SUCCESS;
+}
+
+template<MessageType MT>
+class CommandMessage : public MessageCommandBase
+{
+ public:
+       CommandMessage(Module* parent)
+               : MessageCommandBase(parent, MT)
+       {
+       }
+
+       CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+       {
+               return HandleMessage(parameters, user, MT);
+       }
+};
+
+class ModuleCoreMessage : public Module
+{
+       CommandMessage<MSG_PRIVMSG> CommandPrivmsg;
+       CommandMessage<MSG_NOTICE> CommandNotice;
+
+ public:
+       ModuleCoreMessage()
+               : CommandPrivmsg(this), CommandNotice(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleCoreMessage)
diff --git a/src/coremods/core_reloadmodule.cpp b/src/coremods/core_reloadmodule.cpp
new file mode 100644 (file)
index 0000000..ffd4f9b
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * 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;
+
+class CommandReloadmodule : public Command
+{
+       Events::ModuleEventProvider evprov;
+ public:
+       /** Constructor for reloadmodule.
+        */
+       CommandReloadmodule(Module* parent)
+               : Command(parent, "RELOADMODULE", 1)
+               , evprov(parent, "event/reloadmodule")
+       {
+               reloadevprov = &evprov;
+               flags_needed = 'o';
+               syntax = "<modulename>";
+       }
+
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+namespace ReloadModule
+{
+
+class DataKeeper
+{
+       /** Data we save for each mode and extension provided by the module
+        */
+       struct ProviderInfo
+       {
+               std::string itemname;
+               union
+               {
+                       ModeHandler* mh;
+                       ExtensionItem* extitem;
+               };
+
+               ProviderInfo(ModeHandler* mode)
+                       : itemname(mode->name)
+                       , mh(mode)
+               {
+               }
+
+               ProviderInfo(ExtensionItem* ei)
+                       : itemname(ei->name)
+                       , extitem(ei)
+               {
+               }
+       };
+
+       struct InstanceData
+       {
+               /** Position of the ModeHandler or ExtensionItem that the serialized data belongs to
+                */
+               size_t index;
+
+               /** Serialized data
+                */
+               std::string serialized;
+
+               InstanceData(size_t Index, const std::string& Serialized)
+                       : index(Index)
+                       , serialized(Serialized)
+               {
+               }
+       };
+
+       struct ModesExts
+       {
+               /** Mode data for the object, one entry per mode set by the module being reloaded
+                */
+               std::vector<InstanceData> modelist;
+
+               /** Extensions for the object, one entry per extension set by the module being reloaded
+                */
+               std::vector<InstanceData> extlist;
+
+               bool empty() const { return ((modelist.empty()) && (extlist.empty())); }
+
+               void swap(ModesExts& other)
+               {
+                       modelist.swap(other.modelist);
+                       extlist.swap(other.extlist);
+               }
+       };
+
+       struct OwnedModesExts : public ModesExts
+       {
+               /** User uuid or channel name
+                */
+               std::string owner;
+
+               OwnedModesExts(const std::string& Owner)
+                       : owner(Owner)
+               {
+               }
+       };
+
+       // Data saved for each channel
+       struct ChanData : public OwnedModesExts
+       {
+               /** Type of data stored for each member who has any affected modes or extensions set
+                */
+               typedef OwnedModesExts MemberData;
+
+               /** List of data (modes and extensions) about each member
+                */
+               std::vector<MemberData> memberdatalist;
+
+               ChanData(Channel* chan)
+                       : OwnedModesExts(chan->name)
+               {
+               }
+       };
+
+       // Data saved for each user
+       typedef OwnedModesExts UserData;
+
+       /** Module being reloaded
+        */
+       Module* mod;
+
+       /** Stores all user and channel modes provided by the module
+        */
+       std::vector<ProviderInfo> handledmodes[2];
+
+       /** Stores all extensions provided by the module
+        */
+       std::vector<ProviderInfo> handledexts;
+
+       /** Stores all of the module data related to users
+        */
+       std::vector<UserData> userdatalist;
+
+       /** Stores all of the module data related to channels and memberships
+        */
+       std::vector<ChanData> chandatalist;
+
+       /** Data attached by modules
+        */
+       ReloadModule::CustomData moddata;
+
+       void SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdatalist);
+       void SaveMemberData(Channel* chan, std::vector<ChanData::MemberData>& memberdatalist);
+       static void SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata);
+
+       void CreateModeList(ModeType modetype);
+       void DoSaveUsers();
+       void DoSaveChans();
+
+       /** Link previously saved extension names to currently available ExtensionItems
+        */
+       void LinkExtensions();
+
+       /** Link previously saved mode names to currently available ModeHandlers
+        * @param modetype Type of the modes to look for
+        */
+       void LinkModes(ModeType modetype);
+
+       void DoRestoreUsers();
+       void DoRestoreChans();
+       void DoRestoreModules();
+
+       /** Restore previously saved modes and extensions on an Extensible.
+        * The extensions are set directly on the extensible, the modes are added into the provided mode change list.
+        * @param data Data to unserialize from
+        * @param extensible Object to restore
+        * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+        * (for Channels and Memberships).
+        * @param modechange Mode change to populate with the modes
+        */
+       void RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange);
+
+       /** Restore all previously saved extensions on an Extensible
+        * @param list List of extensions and their serialized data to restore
+        * @param extensible Target Extensible
+        */
+       void RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible);
+
+       /** Restore all previously saved modes on a User, Channel or Membership
+        * @param list List of modes to restore
+        * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+        * @param modechange Mode change to populate with the modes
+        */
+       void RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange);
+
+       /** Restore all modes and extensions of all members on a channel
+        * @param chan Channel whose members are being restored
+        * @param memberdata Data to restore
+        * @param modechange Mode change to populate with prefix modes
+        */
+       void RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange);
+
+       /** Verify that a service which had its data saved is available and owned by the module that owned it previously
+        * @param service Service descriptor
+        * @param type Human-readable type of the service for log messages
+        */
+       void VerifyServiceProvider(const ProviderInfo& service, const char* type);
+
+ public:
+       /** Save module state
+        * @param currmod Module whose data to save
+        */
+       void Save(Module* currmod);
+
+       /** Restore module state
+        * @param newmod Newly loaded instance of the module which had its data saved
+        */
+       void Restore(Module* newmod);
+
+       /** Handle reload failure
+        */
+       void Fail();
+};
+
+void DataKeeper::DoSaveUsers()
+{
+       ModesExts currdata;
+
+       const user_hash& users = ServerInstance->Users->GetUsers();
+       for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+       {
+               User* const user = i->second;
+
+               // Serialize user modes
+               for (size_t j = 0; j < handledmodes[MODETYPE_USER].size(); j++)
+               {
+                       ModeHandler* mh = handledmodes[MODETYPE_USER][j].mh;
+                       if (user->IsModeSet(mh))
+                               currdata.modelist.push_back(InstanceData(j, mh->GetUserParameter(user)));
+               }
+
+               // Serialize all extensions attached to the User
+               SaveExtensions(user, currdata.extlist);
+
+               // Add to list if the user has any modes or extensions set that we are interested in, otherwise we don't
+               // have to do anything with this user when restoring
+               if (!currdata.empty())
+               {
+                       userdatalist.push_back(UserData(user->uuid));
+                       userdatalist.back().swap(currdata);
+               }
+       }
+}
+
+void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdata)
+{
+       const Extensible::ExtensibleStore& setexts = extensible->GetExtList();
+
+       // Position of the extension saved in the handledexts list
+       size_t index = 0;
+       for (std::vector<ProviderInfo>::const_iterator i = handledexts.begin(); i != handledexts.end(); ++i, index++)
+       {
+               ExtensionItem* const item = i->extitem;
+               Extensible::ExtensibleStore::const_iterator it = setexts.find(item);
+               if (it == setexts.end())
+                       continue;
+
+               std::string value = item->serialize(FORMAT_INTERNAL, extensible, it->second);
+               // If the serialized value is empty the extension won't be saved and restored
+               if (!value.empty())
+                       extdata.push_back(InstanceData(index, value));
+       }
+}
+
+void DataKeeper::SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata)
+{
+       const ListModeBase::ModeList* list = lm->GetList(chan);
+       if (!list)
+               return;
+
+       for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+       {
+               const ListModeBase::ListItem& listitem = *i;
+               currdata.modelist.push_back(InstanceData(index, listitem.mask));
+       }
+}
+
+void DataKeeper::DoSaveChans()
+{
+       ModesExts currdata;
+       std::vector<OwnedModesExts> currmemberdata;
+
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+       {
+               Channel* const chan = i->second;
+
+               // Serialize channel modes
+               for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+               {
+                       ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+                       ListModeBase* lm = mh->IsListModeBase();
+                       if (lm)
+                               SaveListModes(chan, lm, j, currdata);
+                       else if (chan->IsModeSet(mh))
+                               currdata.modelist.push_back(InstanceData(j, chan->GetModeParameter(mh)));
+               }
+
+               // Serialize all extensions attached to the Channel
+               SaveExtensions(chan, currdata.extlist);
+
+               // Serialize all extensions attached to and all modes set on all members of the channel
+               SaveMemberData(chan, currmemberdata);
+
+               // Same logic as in DoSaveUsers() plus we consider the modes and extensions of all members
+               if ((!currdata.empty()) || (!currmemberdata.empty()))
+               {
+                       chandatalist.push_back(ChanData(chan));
+                       chandatalist.back().swap(currdata);
+                       chandatalist.back().memberdatalist.swap(currmemberdata);
+               }
+       }
+}
+
+void DataKeeper::SaveMemberData(Channel* chan, std::vector<OwnedModesExts>& memberdatalist)
+{
+       ModesExts currdata;
+       const Channel::MemberMap& users = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+       {
+               Membership* const memb = i->second;
+
+               for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+               {
+                       ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+                       const PrefixMode* const pm = mh->IsPrefixMode();
+                       if ((pm) && (memb->HasMode(pm)))
+                               currdata.modelist.push_back(InstanceData(j, memb->user->uuid)); // Need to pass the user's uuid to the mode parser to set the mode later
+               }
+
+               SaveExtensions(memb, currdata.extlist);
+
+               // Same logic as in DoSaveUsers()
+               if (!currdata.empty())
+               {
+                       memberdatalist.push_back(OwnedModesExts(memb->user->uuid));
+                       memberdatalist.back().swap(currdata);
+               }
+       }
+}
+
+void DataKeeper::RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange)
+{
+       for (std::vector<ChanData::MemberData>::const_iterator i = memberdatalist.begin(); i != memberdatalist.end(); ++i)
+       {
+               const ChanData::MemberData& md = *i;
+               User* const user = ServerInstance->FindUUID(md.owner);
+               if (!user)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone (while processing %s)", md.owner.c_str(), chan->name.c_str());
+                       continue;
+               }
+
+               Membership* const memb = chan->GetUser(user);
+               if (!memb)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Member %s is no longer on channel %s", md.owner.c_str(), chan->name.c_str());
+                       continue;
+               }
+
+               RestoreObj(md, memb, MODETYPE_CHANNEL, modechange);
+       }
+}
+
+void DataKeeper::CreateModeList(ModeType modetype)
+{
+       const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes->GetModes(modetype);
+       for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
+       {
+               ModeHandler* mh = i->second;
+               if (mh->creator == mod)
+                       handledmodes[modetype].push_back(ProviderInfo(mh));
+       }
+}
+
+void DataKeeper::Save(Module* currmod)
+{
+       this->mod = currmod;
+
+       const ExtensionManager::ExtMap& allexts = ServerInstance->Extensions.GetExts();
+       for (ExtensionManager::ExtMap::const_iterator i = allexts.begin(); i != allexts.end(); ++i)
+       {
+               ExtensionItem* ext = i->second;
+               if (ext->creator == mod)
+                       handledexts.push_back(ProviderInfo(ext));
+       }
+
+       CreateModeList(MODETYPE_USER);
+       DoSaveUsers();
+
+       CreateModeList(MODETYPE_CHANNEL);
+       DoSaveChans();
+
+       FOREACH_MOD_CUSTOM(*reloadevprov, ReloadModule::EventListener, OnReloadModuleSave, (mod, this->moddata));
+
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Saved data about %lu users %lu chans %lu modules", (unsigned long)userdatalist.size(), (unsigned long)chandatalist.size(), (unsigned long)moddata.list.size());
+}
+
+void DataKeeper::VerifyServiceProvider(const ProviderInfo& service, const char* type)
+{
+       const ServiceProvider* sp = service.extitem;
+       if (!sp)
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is no longer available", type, service.itemname.c_str());
+       else if (sp->creator != mod)
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is now handled by %s", type, service.itemname.c_str(), (sp->creator ? sp->creator->ModuleSourceFile.c_str() : "<core>"));
+}
+
+void DataKeeper::LinkModes(ModeType modetype)
+{
+       std::vector<ProviderInfo>& list = handledmodes[modetype];
+       for (std::vector<ProviderInfo>::iterator i = list.begin(); i != list.end(); ++i)
+       {
+               ProviderInfo& item = *i;
+               item.mh = ServerInstance->Modes->FindMode(item.itemname, modetype);
+               VerifyServiceProvider(item, (modetype == MODETYPE_USER ? "User mode" : "Channel mode"));
+       }
+}
+
+void DataKeeper::LinkExtensions()
+{
+       for (std::vector<ProviderInfo>::iterator i = handledexts.begin(); i != handledexts.end(); ++i)
+       {
+               ProviderInfo& item = *i;
+               item.extitem = ServerInstance->Extensions.GetItem(item.itemname);
+               VerifyServiceProvider(item.extitem, "Extension");
+       }
+}
+
+void DataKeeper::Restore(Module* newmod)
+{
+       this->mod = newmod;
+
+       // Find the new extension items
+       LinkExtensions();
+       LinkModes(MODETYPE_USER);
+       LinkModes(MODETYPE_CHANNEL);
+
+       // Restore
+       DoRestoreUsers();
+       DoRestoreChans();
+       DoRestoreModules();
+
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore finished");
+}
+
+void DataKeeper::Fail()
+{
+       this->mod = NULL;
+
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore failed, notifying modules");
+       DoRestoreModules();
+}
+
+void DataKeeper::RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange)
+{
+       RestoreExtensions(data.extlist, extensible);
+       RestoreModes(data.modelist, modetype, modechange);
+}
+
+void DataKeeper::RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible)
+{
+       for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               const InstanceData& id = *i;
+               handledexts[id.index].extitem->unserialize(FORMAT_INTERNAL, extensible, id.serialized);
+       }
+}
+
+void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange)
+{
+       for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               const InstanceData& id = *i;
+               modechange.push_add(handledmodes[modetype][id.index].mh, id.serialized);
+       }
+}
+
+void DataKeeper::DoRestoreUsers()
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
+       Modes::ChangeList modechange;
+
+       for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
+       {
+               const UserData& userdata = *i;
+               User* const user = ServerInstance->FindUUID(userdata.owner);
+               if (!user)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
+                       continue;
+               }
+
+               RestoreObj(userdata, user, MODETYPE_USER, modechange);
+               ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
+               modechange.clear();
+       }
+}
+
+void DataKeeper::DoRestoreChans()
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring channel data");
+       Modes::ChangeList modechange;
+
+       for (std::vector<ChanData>::const_iterator i = chandatalist.begin(); i != chandatalist.end(); ++i)
+       {
+               const ChanData& chandata = *i;
+               Channel* const chan = ServerInstance->FindChan(chandata.owner);
+               if (!chan)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel %s not found", chandata.owner.c_str());
+                       continue;
+               }
+
+               RestoreObj(chandata, chan, MODETYPE_CHANNEL, modechange);
+               // Process the mode change before applying any prefix modes
+               ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+               modechange.clear();
+
+               // Restore all member data
+               RestoreMemberData(chan, chandata.memberdatalist, modechange);
+               ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+               modechange.clear();
+       }
+}
+
+void DataKeeper::DoRestoreModules()
+{
+       for (ReloadModule::CustomData::List::iterator i = moddata.list.begin(); i != moddata.list.end(); ++i)
+       {
+               ReloadModule::CustomData::Data& data = *i;
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Calling module data handler %p", (void*)data.handler);
+               data.handler->OnReloadModuleRestore(mod, data.data);
+       }
+}
+
+} // namespace ReloadModule
+
+class ReloadAction : public HandlerBase0<void>
+{
+       Module* const mod;
+       const std::string uuid;
+       const std::string passedname;
+
+ public:
+       ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
+               : mod(m)
+               , uuid(uid)
+               , passedname(passedmodname)
+       {
+       }
+
+       void Call()
+       {
+               ReloadModule::DataKeeper datakeeper;
+               datakeeper.Save(mod);
+
+               DLLManager* dll = mod->ModuleDLLManager;
+               std::string name = mod->ModuleSourceFile;
+               ServerInstance->Modules->DoSafeUnload(mod);
+               ServerInstance->GlobalCulls.Apply();
+               delete dll;
+               bool result = ServerInstance->Modules->Load(name);
+
+               if (result)
+               {
+                       Module* newmod = ServerInstance->Modules->Find(name);
+                       datakeeper.Restore(newmod);
+               }
+               else
+                       datakeeper.Fail();
+
+               ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded", passedname.c_str(), result ? "" : "un");
+               User* user = ServerInstance->FindUUID(uuid);
+               if (user)
+                       user->WriteNumeric(RPL_LOADEDMODULE, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
+
+               ServerInstance->GlobalCulls.AddItem(this);
+       }
+};
+
+CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       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;
+       }
+}
+
+COMMAND_INIT(CommandReloadmodule)
diff --git a/src/coremods/core_stats.cpp b/src/coremods/core_stats.cpp
new file mode 100644 (file)
index 0000000..7c34849
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * 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"
+
+#ifdef _WIN32
+#include <psapi.h>
+#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
+#endif
+
+/** Handle /STATS.
+ */
+class CommandStats : public Command
+{
+       void DoStats(Stats::Context& stats);
+ 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 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) && (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 = ServerInstance->Config->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(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 */
+                       {
+                               stats.AddRow(249, "Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
+                               stats.AddRow(249, "Signals:          "+ConvToStr(R.ru_nsignals));
+                               stats.AddRow(249, "Page faults:      "+ConvToStr(R.ru_majflt));
+                               stats.AddRow(249, "Swaps:            "+ConvToStr(R.ru_nswap));
+                               stats.AddRow(249, "Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
+
+                               float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
+                                       + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
+                               float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
+                               float per = (n_eaten / n_elapsed) * 100;
+
+                               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':
+               {
+                       ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
+                       for(ConfigIter i = tags.first; i != tags.second; ++i)
+                       {
+                               ConfigTag* tag = i->second;
+                               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 (const std::vector<std::string>& parameters, User *user)
+{
+       if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
+       {
+               // Give extra penalty if a non-oper does /STATS <remoteserver>
+               LocalUser* localuser = IS_LOCAL(user);
+               if ((localuser) && (!user->IsOper()))
+                       localuser->CommandFloodPenalty += 2000;
+               return CMD_SUCCESS;
+       }
+       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;
+}
+
+COMMAND_INIT(CommandStats)
diff --git a/src/coremods/core_stub.cpp b/src/coremods/core_stub.cpp
new file mode 100644 (file)
index 0000000..71b0dda
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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"
+
+
+/** Handle /CONNECT.
+ */
+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 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.
+                */
+               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(const std::vector<std::string>& parameters, User* user)
+       {
+               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(const std::vector<std::string>& parameters, User* user)
+       {
+               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 = "<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(const std::vector<std::string>& parameters, User* user)
+       {
+               user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
+               return CMD_FAILURE;
+       }
+};
+
+class CoreModStub : public Module
+{
+       CommandConnect cmdconnect;
+       CommandLinks cmdlinks;
+       CommandServer cmdserver;
+       CommandSquit cmdsquit;
+
+ public:
+       CoreModStub()
+               : cmdconnect(this), cmdlinks(this), cmdserver(this), cmdsquit(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the stub commands CONNECT, LINKS, SERVER and SQUIT", 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..fb720a5
--- /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_user.h"
+
+CommandAway::CommandAway(Module* parent)
+       : Command(parent, "AWAY", 0, 0)
+{
+       syntax = "[<message>]";
+}
+
+/** 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, "You have been marked as being away");
+       }
+       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, "You are no longer marked as being away");
+       }
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandAway::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_mode.cpp b/src/coremods/core_user/cmd_mode.cpp
new file mode 100644 (file)
index 0000000..2b26526
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandMode::CommandMode(Module* parent)
+       : Command(parent, "MODE", 1)
+       , seq(0)
+{
+       syntax = "<target> <modes> {<mode-parameters>}";
+       memset(&sent, 0, sizeof(sent));
+}
+
+CmdResult CommandMode::Handle(const std::vector<std::string>& parameters, User* user)
+{
+       const std::string& target = parameters[0];
+       Channel* targetchannel = ServerInstance->FindChan(target);
+       User* targetuser = NULL;
+       if (!targetchannel)
+       {
+               if (IS_LOCAL(user))
+                       targetuser = ServerInstance->FindNickOnly(target);
+               else
+                       targetuser = ServerInstance->FindNick(target);
+       }
+
+       if ((!targetchannel) && (!targetuser))
+       {
+               user->WriteNumeric(Numerics::NoSuchNick(target));
+               return CMD_FAILURE;
+       }
+       if (parameters.size() == 1)
+       {
+               this->DisplayCurrentModes(user, targetuser, targetchannel);
+               return CMD_SUCCESS;
+       }
+
+       // Populate a temporary Modes::ChangeList with the parameters
+       Modes::ChangeList changelist;
+       ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
+       ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
+
+       ModResult MOD_RESULT;
+       FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
+
+       ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
+       if (IS_LOCAL(user))
+       {
+               if (MOD_RESULT == MOD_RES_PASSTHRU)
+               {
+                       if ((targetuser) && (user != targetuser))
+                       {
+                               // Local users may only change the modes of other users if a module explicitly allows it
+                               user->WriteNumeric(ERR_USERSDONTMATCH, "Can't change mode for other users");
+                               return CMD_FAILURE;
+                       }
+
+                       // This is a mode change by a local user and modules didn't explicitly allow/deny.
+                       // Ensure access checks will happen for each mode being changed.
+                       flags |= ModeParser::MODE_CHECKACCESS;
+               }
+               else if (MOD_RESULT == MOD_RES_DENY)
+                       return CMD_FAILURE; // Entire mode change denied by a module
+       }
+       else
+               flags |= ModeParser::MODE_LOCALONLY;
+
+       if (IS_LOCAL(user))
+               ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
+       else
+               ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
+
+       if ((ServerInstance->Modes.GetLastParse().empty()) && (targetchannel) && (parameters.size() == 2))
+       {
+               /* Special case for displaying the list for listmodes,
+                * e.g. MODE #chan b, or MODE #chan +b without a parameter
+                */
+               this->DisplayListModes(user, targetchannel, parameters[1]);
+       }
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandMode::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
+
+void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
+{
+       seq++;
+
+       for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
+       {
+               unsigned char mletter = *i;
+               if (mletter == '+')
+                       continue;
+
+               ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
+               if (!mh || !mh->IsListMode())
+                       return;
+
+               /* Ensure the user doesnt request the same mode twice,
+                * so they can't flood themselves off out of idiocy.
+                */
+               if (sent[mletter] == seq)
+                       continue;
+
+               sent[mletter] = seq;
+               ServerInstance->Modes.ShowListModeList(user, chan, mh);
+       }
+}
+
+static std::string GetSnomasks(const User* user)
+{
+       ModeHandler* const snomask = ServerInstance->Modes.FindMode('s', MODETYPE_USER);
+       std::string snomaskstr = snomask->GetUserParameter(user);
+       // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
+       // In the former case output a "+", not an empty string.
+       if (snomaskstr.empty())
+               snomaskstr.push_back('+');
+       return snomaskstr;
+}
+
+void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
+{
+       if (targetchannel)
+       {
+               // Display channel's current mode string
+               user->WriteNumeric(RPL_CHANNELMODEIS, targetchannel->name, (std::string("+") + targetchannel->ChanModes(targetchannel->HasUser(user))));
+               user->WriteNumeric(RPL_CHANNELCREATED, targetchannel->name, (unsigned long)targetchannel->age);
+       }
+       else
+       {
+               if (targetuser == user)
+               {
+                       // Display user's current mode string
+                       user->WriteNumeric(RPL_UMODEIS, targetuser->GetModeLetters());
+                       if (targetuser->IsOper())
+                               user->WriteNumeric(RPL_SNOMASKIS, GetSnomasks(targetuser), "Server notice mask");
+               }
+               else if (user->HasPrivPermission("users/auspex"))
+               {
+                       // Querying the modes of another user.
+                       // We cannot use RPL_UMODEIS because that's only for showing the user's own modes.
+                       user->WriteNumeric(RPL_OTHERUMODEIS, targetuser->nick, targetuser->GetModeLetters());
+                       if (targetuser->IsOper())
+                               user->WriteNumeric(RPL_OTHERSNOMASKIS, targetuser->nick, GetSnomasks(targetuser), "Server notice mask");
+               }
+               else
+               {
+                       user->WriteNumeric(ERR_USERSDONTMATCH, "Can't view modes for other users");
+               }
+       }
+}
diff --git a/src/coremods/core_user/cmd_nick.cpp b/src/coremods/core_user/cmd_nick.cpp
new file mode 100644 (file)
index 0000000..cedafbb
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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(const std::vector<std::string>& parameters, LocalUser* user)
+{
+       std::string oldnick = user->nick;
+       std::string newnick = parameters[0];
+
+       // anything except the initial NICK gets a flood penalty
+       if (user->registered == REG_ALL)
+               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)
+       {
+               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))
+                       {
+                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
+                               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..4da2787
--- /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 (const std::vector<std::string>& parameters, User *user)
+{
+       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::NoSuchNick(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 std::vector<std::string>& 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..f9a4e1f
--- /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 (const std::vector<std::string>& parameters, User *user)
+{
+       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 std::vector<std::string>& 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..8bf3466
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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"
+
+CommandUser::CommandUser(Module* parent)
+       : SplitCommand(parent, "USER", 4, 4)
+{
+       works_before_reg = true;
+       Penalty = 0;
+       syntax = "<username> <localhost> <remotehost> <GECOS>";
+}
+
+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]))
+               {
+                       /*
+                        * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
+                        *  -- Craig, and then w00t.
+                        */
+                       user->WriteNumeric(ERR_NEEDMOREPARAMS, name, "Your username is not valid");
+                       return CMD_FAILURE;
+               }
+               else
+               {
+                       user->ChangeIdent(parameters[0]);
+                       user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
+                       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/core_user.cpp b/src/coremods/core_user/core_user.cpp
new file mode 100644 (file)
index 0000000..a2ffc00
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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(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, "You may not reregister");
+                       return CMD_FAILURE;
+               }
+               user->password = parameters[0];
+
+               return CMD_SUCCESS;
+       }
+};
+
+/** Handle /PING.
+ */
+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 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)
+       {
+               user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
+               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(const std::vector<std::string>& parameters, 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;
+       }
+};
+
+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;
+       CommandMode cmdmode;
+       CommandNick cmdnick;
+       CommandPart cmdpart;
+       CommandPass cmdpass;
+       CommandPing cmdping;
+       CommandPong cmdpong;
+       CommandQuit cmdquit;
+       CommandUser cmduser;
+
+ public:
+       CoreModUser()
+               : cmdaway(this), cmdmode(this), cmdnick(this), cmdpart(this), cmdpass(this), cmdping(this)
+               , cmdpong(this), cmdquit(this), cmduser(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, MODE, NICK, PART, PASS, PING, PONG, QUIT 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..72ba678
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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 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
+{
+ 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(const std::vector<std::string>& parameters, User *user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+class CommandMode : public Command
+{
+       unsigned int sent[256];
+       unsigned int seq;
+
+       /** Show the list of one or more list modes to a user.
+        * @param user User to send to.
+        * @param chan Channel whose lists to show.
+        * @param mode_sequence Mode letters to show the lists of.
+        */
+       void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
+
+       /** Show the current modes of a channel or a user to a user.
+        * @param user User to show the modes to.
+        * @param targetuser User whose modes to show. NULL if showing the modes of a channel.
+        * @param targetchannel Channel whose modes to show. NULL if showing the modes of a user.
+        */
+       void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
+
+ public:
+       /** Constructor for mode.
+        */
+       CommandMode(Module* parent);
+
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /NICK.
+ */
+class CommandNick : public 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(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** 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(const std::vector<std::string>& parameters, User *user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** 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(const std::vector<std::string>& parameters, User*user);
+
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** 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(const std::vector<std::string>& parameters, LocalUser *user);
+
+       /** Run the OnUserRegister hook if the user has sent both NICK and USER. Called after an unregistered user
+        * successfully executes the USER or the NICK command.
+        * @param user User to inspect and possibly pass to the OnUserRegister hook
+        * @return CMD_FAILURE if OnUserRegister was called and it returned MOD_RES_DENY, CMD_SUCCESS in every other case
+        * (i.e. if the hook wasn't fired because the user still needs to send NICK/USER or if it was fired and finished with
+        * a non-MOD_RES_DENY result).
+        */
+       static CmdResult CheckRegister(LocalUser* user);
+};
diff --git a/src/coremods/core_userhost.cpp b/src/coremods/core_userhost.cpp
new file mode 100644 (file)
index 0000000..7ee093c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+class CommandUserhost : public Command
+{
+       UserModeReference hideopermode;
+
+ public:
+       /** Constructor for userhost.
+        */
+       CommandUserhost(Module* parent)
+               : Command(parent,"USERHOST", 1)
+               , hideopermode(parent, "hideoper")
+       {
+               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(const std::vector<std::string>& parameters, User *user);
+};
+
+CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       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;
+}
+
+COMMAND_INIT(CommandUserhost)
diff --git a/src/coremods/core_wallops.cpp b/src/coremods/core_wallops.cpp
new file mode 100644 (file)
index 0000000..0210df8
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+ public:
+       /** Constructor for wallops.
+        */
+       CommandWallops(Module* parent)
+               : Command(parent, "WALLOPS", 1, 1)
+               , wallopsmode(parent, "wallops", 'w')
+       {
+               flags_needed = 'o';
+               syntax = "<any-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(const std::vector<std::string>& parameters, User *user);
+
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       {
+               return ROUTE_BROADCAST;
+       }
+};
+
+CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       std::string wallop("WALLOPS :");
+       wallop.append(parameters[0]);
+
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               User* t = *i;
+               if (t->IsModeSet(wallopsmode))
+                       t->WriteFrom(user, wallop);
+       }
+
+       return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandWallops)
diff --git a/src/coremods/core_who.cpp b/src/coremods/core_who.cpp
new file mode 100644 (file)
index 0000000..196bf04
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * 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.
+ */
+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;
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+       UserModeReference hidechansmode;
+       UserModeReference invisiblemode;
+
+       Membership* get_first_visible_channel(User* source, User* u)
+       {
+               for (User::ChanList::iterator i = u->chans.begin(); i != u->chans.end(); ++i)
+               {
+                       Membership* memb = *i;
+
+                       /* XXX move the +I check into m_hidechans */
+                       bool has_modes = memb->chan->IsModeSet(secretmode) || memb->chan->IsModeSet(privatemode) || u->IsModeSet(hidechansmode);
+                       if (source == u || !has_modes || memb->chan->HasUser(source))
+                               return memb;
+               }
+               return NULL;
+       }
+
+ public:
+       /** Constructor for who.
+        */
+       CommandWho(Module* parent)
+               : Command(parent, "WHO", 1)
+               , secretmode(parent, "secret")
+               , privatemode(parent, "private")
+               , hidechansmode(parent, "hidechans")
+               , invisiblemode(parent, "invisible")
+       {
+               syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
+       }
+
+       void SendWhoLine(User* user, const std::vector<std::string>& parms, Membership* memb, User* u, std::vector<Numeric::Numeric>& whoresults);
+       /** Handle command.
+        * @param parameters The parameters to the command
+        * @param user The user issuing the command
+        * @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);
+};
+
+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->GetRealHost(), 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 = InspIRCd::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->GetDisplayedHost(), 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->GetName(), matchtext);
+
+               return match;
+       }
+}
+
+bool CommandWho::CanView(Channel* chan, User* user)
+{
+       /* 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(secretmode) && !chan->IsModeSet(privatemode))
+               return true;
+
+       return false;
+}
+
+void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, Membership* memb, User* u, std::vector<Numeric::Numeric>& whoresults)
+{
+       if (!memb)
+               memb = get_first_visible_channel(user, u);
+
+       Numeric::Numeric wholine(RPL_WHOREPLY);
+       wholine.push(memb ? memb->chan->name : "*").push(u->ident);
+       wholine.push(u->GetHost(opt_showrealhost));
+       if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+               wholine.push(ServerInstance->Config->HideWhoisServer);
+       else
+               wholine.push(u->server->GetName());
+
+       wholine.push(u->nick);
+
+       std::string param;
+       /* away? */
+       if (u->IsAway())
+       {
+               param.push_back('G');
+       }
+       else
+       {
+               param.push_back('H');
+       }
+
+       /* oper? */
+       if (u->IsOper())
+       {
+               param.push_back('*');
+       }
+
+       if (memb)
+       {
+               char prefix = memb->GetPrefixChar();
+               if (prefix)
+                       param.push_back(prefix);
+       }
+
+       wholine.push(param);
+       wholine.push("0 ");
+       wholine.GetParams().back().append(u->fullname);
+
+       ModResult res;
+       FIRST_MOD_RESULT(OnSendWhoLine, res, (user, parms, u, memb, wholine));
+       if (res != MOD_RES_DENY)
+               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;
+
+       std::vector<Numeric::Numeric> whoresults;
+
+       /* Change '0' into '*' so the wildcard matcher can grok it */
+       std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]);
+
+       // WHO flags count as a wildcard
+       bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos));
+
+       if (parameters.size() > 1)
+       {
+               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? */
+       Channel* ch = ServerInstance->FindChan(matchtext);
+
+       if (ch)
+       {
+               if (CanView(ch,user))
+               {
+                       bool inside = ch->HasUser(user);
+
+                       /* who on a channel. */
+                       const Channel::MemberMap& cu = ch->GetUsers();
+                       for (Channel::MemberMap::const_iterator i = cu.begin(); i != cu.end(); ++i)
+                       {
+                               /* None of this applies if we WHO ourselves */
+                               if (user != i->first)
+                               {
+                                       /* opers only, please */
+                                       if (opt_viewopersonly && !i->first->IsOper())
+                                               continue;
+
+                                       /* If we're not inside the channel, hide +i users */
+                                       if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex"))
+                                               continue;
+                               }
+
+                               SendWhoLine(user, parameters, i->second, i->first, whoresults);
+                       }
+               }
+       }
+       else
+       {
+               /* Match against wildcard of nick, server or host */
+               if (opt_viewopersonly)
+               {
+                       /* Showing only opers */
+                       const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+                       for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+                       {
+                               User* oper = *i;
+
+                               if (whomatch(user, oper, matchtext.c_str()))
+                               {
+                                       if (!user->SharesChannelWith(oper))
+                                       {
+                                               if (usingwildcards && (oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
+                                                       continue;
+                                       }
+
+                                       SendWhoLine(user, parameters, NULL, oper, whoresults);
+                               }
+                       }
+               }
+               else
+               {
+                       const user_hash& users = ServerInstance->Users->GetUsers();
+                       for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+                       {
+                               if (whomatch(user, i->second, matchtext.c_str()))
+                               {
+                                       if (!user->SharesChannelWith(i->second))
+                                       {
+                                               if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
+                                                       continue;
+                                       }
+
+                                       SendWhoLine(user, parameters, NULL, i->second, whoresults);
+                               }
+                       }
+               }
+       }
+       /* Send the results out */
+       for (std::vector<Numeric::Numeric>::const_iterator n = whoresults.begin(); n != whoresults.end(); ++n)
+               user->WriteNumeric(*n);
+       user->WriteNumeric(RPL_ENDOFWHO, (*parameters[0].c_str() ? parameters[0] : "*"), "End of /WHO list.");
+
+       // Penalize the user a bit for large queries
+       // (add one unit of penalty per 200 results)
+       if (IS_LOCAL(user))
+               IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
+       return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandWho)
diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp
new file mode 100644 (file)
index 0000000..c2fac75
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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"
+
+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, unsigned long signon, unsigned long idle);
+       void SendChanList(WhoisContextImpl& whois);
+
+ public:
+       /** 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 = "<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(const std::vector<std::string>& parameters, LocalUser* user);
+       CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target);
+};
+
+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), 319, false, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1)
+       {
+               GetNumeric().push(whois.GetTarget()->nick).push(std::string());
+       }
+};
+
+class WhoisChanList
+{
+       const ServerConfig::OperSpyWhoisState spywhois;
+       WhoisChanListNumericBuilder num;
+       WhoisChanListNumericBuilder spynum;
+       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)
+               : spywhois(whois.GetSource()->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE)
+               , num(whois)
+               , spynum(whois)
+       {
+       }
+
+       void AddVisible(Membership* memb)
+       {
+               AddMember(memb, num);
+       }
+
+       void AddHidden(Membership* memb)
+       {
+               if (spywhois == ServerConfig::SPYWHOIS_NONE)
+                       return;
+               AddMember(memb, (spywhois == ServerConfig::SPYWHOIS_SPLITMSG ? spynum : num));
+       }
+
+       void Flush(WhoisContextImpl& whois)
+       {
+               num.Flush();
+               if (!spynum.IsEmpty())
+                       whois.SendLine(336, "is on private/secret channels:");
+               spynum.Flush();
+       }
+};
+
+void CommandWhois::SendChanList(WhoisContextImpl& whois)
+{
+       WhoisChanList chanlist(whois);
+
+       User* const target = whois.GetTarget();
+       for (User::ChanList::iterator i = target->chans.begin(); i != target->chans.end(); ++i)
+       {
+               Membership* memb = *i;
+               Channel* c = memb->chan;
+               /* If the target is the sender, neither +p nor +s is set, or
+                * the channel contains the user, it is not a spy channel
+                */
+               if ((whois.IsSelfWhois()) || ((!c->IsModeSet(privatemode)) && (!c->IsModeSet(secretmode))) || (c->HasUser(whois.GetSource())))
+                       chanlist.AddVisible(memb);
+               else
+                       chanlist.AddHidden(memb);
+       }
+
+       chanlist.Flush(whois);
+}
+
+void CommandWhois::DoWhois(LocalUser* user, User* dest, unsigned long signon, unsigned long idle)
+{
+       WhoisContextImpl whois(user, dest, lineevprov);
+
+       whois.SendLine(311, dest->ident, dest->GetDisplayedHost(), '*', dest->fullname);
+       if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+       {
+               whois.SendLine(378, 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->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+       {
+               whois.SendLine(312, ServerInstance->Config->HideWhoisServer, ServerInstance->Config->Network);
+       }
+       else
+       {
+               whois.SendLine(312, dest->server->GetName(), dest->server->GetDesc());
+       }
+
+       if (dest->IsAway())
+       {
+               whois.SendLine(301, dest->awaymsg);
+       }
+
+       if (dest->IsOper())
+       {
+               if (ServerInstance->Config->GenericOper)
+                       whois.SendLine(313, "is an IRC operator");
+               else
+                       whois.SendLine(313, InspIRCd::Format("is %s %s on %s", (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"), dest->oper->name.c_str(), ServerInstance->Config->Network.c_str()));
+       }
+
+       if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+       {
+               if (dest->IsModeSet(snomaskmode))
+               {
+                       whois.SendLine(379, InspIRCd::Format("is using modes %s %s", dest->GetModeLetters().c_str(), snomaskmode->GetUserParameter(dest).c_str()));
+               }
+               else
+               {
+                       whois.SendLine(379, 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 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))
+       {
+               whois.SendLine(317, idle, signon, "seconds idle, signon time");
+       }
+
+       whois.SendLine(318, "End of /WHOIS list.");
+}
+
+CmdResult CommandWhois::HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target)
+{
+       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 = ConvToInt(parameters.back());
+       DoWhois(localuser, target, target->signon, idle);
+
+       return CMD_SUCCESS;
+}
+
+CmdResult CommandWhois::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+{
+       User *dest;
+       int userindex = 0;
+       unsigned long idle = 0, 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 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
+                */
+               LocalUser* localuser = IS_LOCAL(dest);
+               if (localuser && (ServerInstance->Config->HideWhoisServer.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;
+}
+
+COMMAND_INIT(CommandWhois)
diff --git a/src/coremods/core_whowas.cpp b/src/coremods/core_whowas.cpp
new file mode 100644 (file)
index 0000000..af1f997
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * 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"
+
+CommandWhowas::CommandWhowas( Module* parent)
+       : Command(parent, "WHOWAS", 1)
+{
+       syntax = "<nick>{,<nick>}";
+       Penalty = 2;
+}
+
+CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
+{
+       /* 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->gecos);
+
+                       if (user->HasPrivPermission("users/auspex"))
+                               user->WriteNumeric(RPL_WHOWASIP, parameters[0], InspIRCd::Format("was connecting from *@%s", u->host.c_str()));
+
+                       std::string signon = InspIRCd::TimeString(u->signon);
+                       bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
+                       user->WriteNumeric(RPL_WHOISSERVER, parameters[0], (hide_server ? ServerInstance->Config->HideWhoisServer : 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())
+       , gecos(user->fullname)
+       , signon(user->signon)
+{
+}
+
+WhoWas::Nick::Nick(const std::string& nickname)
+       : addtime(ServerInstance->Time())
+       , nick(nickname)
+{
+}
+
+WhoWas::Nick::~Nick()
+{
+       stdalgo::delete_all(entries);
+}
+
+class ModuleWhoWas : public Module
+{
+       CommandWhowas cmd;
+
+ public:
+       ModuleWhoWas() : 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->getInt("groupsize", 10, 0, 10000);
+               unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
+               unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
+
+               cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("WHOWAS", 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..26b4989
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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 = "<ident@host> [<duration> :<reason>]";
+}
+
+/** 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->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 = InspIRCd::Duration(parameters[1]);
+               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 = InspIRCd::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->WriteNotice("*** E-Line for " + target + " already exists");
+               }
+       }
+       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->WriteNotice("*** E-Line " + target + " not found in list, try /stats e");
+               }
+       }
+
+       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..49932ba
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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 = "<ident@host> [<duration> :<reason>]";
+}
+
+/** 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->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 = InspIRCd::Duration(parameters[1]);
+               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 = InspIRCd::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->WriteNotice("** G-Line for " + target + " already exists");
+               }
+
+       }
+       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->WriteNotice("*** G-Line " + target + " not found in list, try /stats g.");
+               }
+       }
+
+       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..db8862d
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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 = "<ident@host> [<duration> :<reason>]";
+}
+
+/** 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->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 = InspIRCd::Duration(parameters[1]);
+               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 = InspIRCd::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->WriteNotice("*** K-Line for " + target + " already exists");
+               }
+       }
+       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->WriteNotice("*** K-Line " + target + " not found in list, try /stats k.");
+               }
+       }
+
+       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..6dc0da9
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 = "<nick> [<duration> :<reason>]";
+}
+
+CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       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 = InspIRCd::Duration(parameters[1]);
+               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 = InspIRCd::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->WriteNotice("*** Q-Line for " + parameters[0] + " already exists");
+               }
+       }
+       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->WriteNotice("*** Q-Line " + parameters[0] + " not found in list, try /stats q.");
+                       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..af9d54a
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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 (const std::vector<std::string>& parameters, User *user)
+{
+       std::string target = parameters[0];
+
+       if (parameters.size() >= 3)
+       {
+               if (target.find('!') != std::string::npos)
+               {
+                       user->WriteNotice("*** You cannot include a nickname in a zline, a zline 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 = InspIRCd::Duration(parameters[1]);
+               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 = InspIRCd::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->WriteNotice("*** Z-Line for " + std::string(ipaddr) + " already exists");
+               }
+       }
+       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->WriteNotice("*** Z-Line " + target + " not found in list, try /stats Z.");
+                       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..d6c8047
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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);
+
+       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', "\2WARNING\2: %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)
+       {
+       }
+
+       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..5b34e7a
--- /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)
+               {
+                       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 eline.
+        */
+       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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /GLINE.
+ */
+class CommandGline : public Command
+{
+ public:
+       /** Constructor for gline.
+        */
+       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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /KLINE.
+ */
+class CommandKline : public Command
+{
+ public:
+       /** Constructor for kline.
+        */
+       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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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 qline.
+        */
+       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(const std::vector<std::string>& parameters, User* user);
+};
+
+/** 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 zline.
+        */
+       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(const std::vector<std::string>& parameters, User* user);
+};
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 25178cfa111c7f287461e9fd4053e7f70e22cb0a..f138b04d1dd9f30d49a3eb48fd56058ab0b0ec28 100644 (file)
@@ -22,7 +22,7 @@
 
 
 #include "inspircd.h"
-#include "dynamic.h"
+
 #ifndef _WIN32
 #include <dlfcn.h>
 #else
@@ -83,7 +83,7 @@ std::string DLLManager::GetVersion()
        const char* srcver = (char*)dlsym(h, "inspircd_src_version");
        if (srcver)
                return srcver;
-       return "Unversioned module";
+       return "";
 }
 
 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..aa06759a4d91e910b92732c37d6923a405fb964c 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.
+
+
+/**
+ * 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_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
+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
 };
 
-/* 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)
+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();
+       for (; *n1 && *n2; n1++, n2++)
+               if (national_case_insensitive_map[*n1] != national_case_insensitive_map[*n2])
+                       return false;
+       return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
+}
+
+bool irc::insensitive_swo::operator()(const std::string& a, const std::string& b) const
 {
-       if (n)
+       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);
+
+       for (std::string::size_type i = 0; i < maxsize; i++)
        {
-               for (char* t = n; *t; t++)
-                       *t = national_case_insensitive_map[(unsigned char)*t];
+               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;
        }
+       return (asize < bsize);
 }
 
-#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
-
+size_t irc::insensitive::operator()(const std::string &s) const
 {
        /* XXX: NO DATA COPIES! :)
         * The hash function here is practically
@@ -146,25 +161,6 @@ void nspace::strlower(char *n)
        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;
-}
-
-bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const
-{
-       const unsigned char* n1 = (const unsigned char*)s1.c_str();
-       const unsigned char* n2 = (const unsigned char*)s2.c_str();
-       for (; *n1 && *n2; n1++, n2++)
-               if (national_case_insensitive_map[*n1] != national_case_insensitive_map[*n2])
-                       return false;
-       return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
-}
-
 /******************************************************
  *
  * This is the implementation of our special irc::string
@@ -217,68 +213,30 @@ const char* irc::irc_char_traits::find(const char* s1, int  n, char c)
        return (n >= 0) ? s1 : NULL;
 }
 
-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()
+irc::tokenstream::tokenstream(const std::string &source) : spacesepstream(source)
 {
 }
 
 bool irc::tokenstream::GetToken(std::string &token)
 {
-       std::string::iterator lsp = last_starting_position;
-
-       while (n != tokens.end())
-       {
-               /** Skip multi space, converting "  " into " "
-                */
-               while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
-                       n++;
+       bool first = !pos;
 
-               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());
-                       return true;
-               }
-
-               last_pushed = false;
+       if (!spacesepstream::GetToken(token))
+               return false;
 
-               if ((*n == ' ') || (n+1 == tokens.end()))
+       /* This is the last parameter */
+       if (token[0] == ':' && !first)
+       {
+               token.erase(token.begin());
+               if (!StreamEnd())
                {
-                       /* 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();
+                       token += ' ';
+                       token += GetRemaining();
                }
-
-               n++;
+               pos = tokens.length() + 1;
        }
-       token.clear();
-       return false;
-}
 
-bool irc::tokenstream::GetToken(irc::string &token)
-{
-       std::string stdstring;
-       bool returnval = GetToken(stdstring);
-       token = assign(stdstring);
-       return returnval;
+       return true;
 }
 
 bool irc::tokenstream::GetToken(int &token)
@@ -297,182 +255,59 @@ bool irc::tokenstream::GetToken(long &token)
        return returnval;
 }
 
-irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
+irc::sepstream::sepstream(const std::string& source, char separator, bool allowempty)
+       : tokens(source), sep(separator), pos(0), allow_empty(allowempty)
 {
-       last_starting_position = tokens.begin();
-       n = tokens.begin();
 }
 
 bool irc::sepstream::GetToken(std::string &token)
 {
-       std::string::iterator lsp = last_starting_position;
-
-       while (n != tokens.end())
-       {
-               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;
-}
-
-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 (this->StreamEnd())
        {
-               hexbuf[i++] = hex[raw[j] / 16];
-               hexbuf[i++] = hex[raw[j] % 16];
-       }
-       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("");
-}
-
-void irc::modestacker::Push(char modeletter, const std::string &parameter)
-{
-       *(sequence.begin()) += modeletter;
-       sequence.push_back(parameter);
-}
-
-void irc::modestacker::Push(char modeletter)
-{
-       this->Push(modeletter,"");
-}
-
-void irc::modestacker::PushPlus()
-{
-       this->Push('+',"");
-}
-
-void irc::modestacker::PushMinus()
-{
-       this->Push('-',"");
-}
-
-int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
-{
-       if (sequence.empty())
-       {
-               return 0;
+               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()
+std::string irc::stringjoiner(const std::vector<std::string>& sequence, char separator)
 {
+       std::string joined;
+       if (sequence.empty())
+               return joined; // nothing to do here
+
+       for (std::vector<std::string>::const_iterator i = sequence.begin(); i != sequence.end(); ++i)
+               joined.append(*i).push_back(separator);
+       joined.erase(joined.end()-1);
        return joined;
 }
 
@@ -528,10 +363,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 +383,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..7e992c4a6dc7898318b2302f454f637c47fd71e1 100644 (file)
@@ -22,8 +22,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);
+       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);
-
-       if (iter == this->Users->clientlist->end())
+       if (iter == this->Users->clientlist.end())
                return NULL;
 
        return iter->second;
@@ -101,66 +52,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();
@@ -216,7 +127,8 @@ void InspIRCd::StripColor(std::string &sentence)
                else
                        seq = 0;
 
-               if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31)))
+               // Strip all control codes too except \001 for CTCP
+               if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
                        i = sentence.erase(i);
                else
                        ++i;
@@ -281,47 +193,35 @@ void InspIRCd::ProcessColors(file_cache& input)
 }
 
 /* true for valid channel name, false else */
-bool IsChannelHandler::Call(const char *chname, size_t max)
+bool IsChannelHandler::Call(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 IsNickHandler::Call(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 +229,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 +239,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 IsIdentHandler::Call(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,7 +266,7 @@ bool IsIdentHandler::Call(const char* n)
        return true;
 }
 
-bool IsSIDHandler::Call(const std::string &str)
+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
@@ -377,66 +276,22 @@ bool IsSIDHandler::Call(const std::string &str)
                         ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
 }
 
-/* open the proper logfile */
-bool InspIRCd::OpenLog(char**, int)
-{
-       if (!Config->cmdline.writelog) return true; // Skip opening default log if -nolog
-
-       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 (!startup)
-       {
-               return false;
-       }
-
-       FileWriter* fw = new FileWriter(startup);
-       FileLogStream *f = new FileLogStream((Config->cmdline.forcedebug ? DEBUG : DEFAULT), fw);
-
-       this->Logs->AddLogType("*", f, true);
-
-       return true;
-}
-
 void InspIRCd::CheckRoot()
 {
 #ifndef _WIN32
        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.
  */
-long InspIRCd::Duration(const std::string &str)
+unsigned long InspIRCd::Duration(const std::string &str)
 {
        unsigned char multiplier = 0;
        long total = 0;
@@ -476,31 +331,44 @@ long InspIRCd::Duration(const std::string &str)
        return total + subtotal;
 }
 
-bool InspIRCd::ULine(const std::string& sserver)
+const char* InspIRCd::Format(va_list &vaList, const char* formatString)
 {
-       if (sserver.empty())
-               return true;
+       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 &formatBuffer[0];
 }
 
-bool InspIRCd::SilentULine(const std::string& sserver)
+const char* 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;
+       const char* 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,27 +382,15 @@ std::string InspIRCd::TimeString(time_t curtime)
        else if (timeinfo->tm_year + 1900 < 1000)
                timeinfo->tm_year = 0;
 
-       return std::string(asctime(timeinfo),24);
-}
+       // This is the default format used by asctime without the terminating new line.
+       if (!format)
+               format = "%a %b %d %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)
@@ -574,28 +430,3 @@ void GenRandomHandler::Call(char *output, size_t max)
                output[i] = random();
 #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..80d1df75d12117334606cd0560c3dd0dc63a3352 100644 (file)
@@ -26,9 +26,7 @@
  */
 
 
-/* $Core */
 #include "inspircd.h"
-#include "inspircd_version.h"
 #include <signal.h>
 
 #ifndef _WIN32
 #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,28 +71,26 @@ 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
 };
 
+#ifdef INSPIRCD_ENABLE_TESTSUITE
+/** True if we have been told to run the testsuite from the commandline,
+ * rather than entering the mainloop.
+ */
+static int do_testsuite = 0;
+#endif
+
 template<typename T> static void DeleteZero(T*&n)
 {
        T* t = n;
@@ -109,125 +100,29 @@ 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");
-       }
-
        GlobalCulls.Apply();
        Modules->UnloadAll();
 
        /* 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()
@@ -263,8 +158,8 @@ bool InspIRCd::DaemonSeed()
        // Do not use QuickExit 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)
        {
@@ -287,13 +182,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
@@ -304,7 +199,7 @@ void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
 #ifndef _WIN32
        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 +207,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)
+        IsIdent(&HandleIsIdent)
 {
        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_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;
@@ -427,28 +279,26 @@ InspIRCd::InspIRCd(int argc, char** argv) :
        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       },
                { "runasroot",  no_argument,            &do_root,       1       },
                { "version",    no_argument,            &do_version,    1       },
+#ifdef INSPIRCD_ENABLE_TESTSUITE
                { "testsuite",  no_argument,            &do_testsuite,  1       },
+#endif
                { 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;
+                               ConfigFileName = ServerInstance->Config->Paths.PrependConfig(optarg);
                        break;
                        case 0:
                                /* getopt_long_only() set an int variable, just keep going */
@@ -458,19 +308,21 @@ 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] [--debug] [--config <config>]" << std::endl <<
+                                       std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version]" << std::endl;
                                Exit(EXIT_STATUS_ARGV);
                        break;
                }
        }
 
+#ifdef INSPIRCD_ENABLE_TESTSUITE
        if (do_testsuite)
                do_nofork = do_debug = true;
+#endif
 
        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 +336,22 @@ 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);
 
        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 +359,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 +391,32 @@ 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();
+       ModeParser::InitBuiltinModes();
 
-       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 +425,9 @@ 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();
+       Config->ApplyDisabledCommands();
 
        if (!pl.empty())
        {
@@ -613,13 +437,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 +451,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 +465,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 +474,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,67 +495,52 @@ 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->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
                        this->QuickExit(0);
                }
 
-               // 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->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
                        this->QuickExit(0);
                }
 
-               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->Logs->Log("STARTUP", LOG_DEFAULT, "setgid(%d) failed (wrong group?): %s", g->gr_gid, strerror(errno));
                        this->QuickExit(0);
                }
        }
 
+       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->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
                        this->QuickExit(0);
                }
 
-               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->Logs->Log("STARTUP", LOG_DEFAULT, "setuid(%d) failed (wrong user?): %s", u->pw_uid, strerror(errno));
                        this->QuickExit(0);
                }
        }
@@ -760,15 +569,17 @@ void InspIRCd::UpdateTime()
 #endif
 }
 
-int InspIRCd::Run()
+void InspIRCd::Run()
 {
+#ifdef INSPIRCD_ENABLE_TESTSUITE
        /* See if we're supposed to be running the test suite rather than entering the mainloop */
-       if (Config->cmdline.TestSuite)
+       if (do_testsuite)
        {
                TestSuite* ts = new TestSuite;
                delete ts;
-               Exit(0);
+               return;
        }
+#endif
 
        UpdateTime();
        time_t OLDTIME = TIME.tv_sec;
@@ -783,7 +594,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,18 +614,18 @@ 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
 
@@ -827,21 +638,25 @@ int InspIRCd::Run()
                        {
                                SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)(TIME.tv_sec - OLDTIME));
                        }
-\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 +668,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 +681,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..9bfc6a73e33104bf0ff64d1df9c4b80000aa98e4 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,7 +45,7 @@ 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)
@@ -66,7 +64,7 @@ BufferedSocketError BufferedSocket::BeginConnect(const std::string &ipaddr, int
        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!");
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
                return I_ERR_CONNECT;
        }
 
@@ -92,13 +90,13 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs&
 
        if (bind.sa.sa_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.sa, dest.sa_size()) == -1)
        {
                if (errno != EINPROGRESS)
                        return I_ERR_CONNECT;
@@ -106,13 +104,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 +120,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 +146,79 @@ bool StreamSocket::GetNextLine(std::string& line, char delim)
        std::string::size_type i = recvq.find(delim);
        if (i == std::string::npos)
                return false;
-       line = recvq.substr(0, i);
-       // TODO is this the most efficient way to split?
-       recvq = recvq.substr(i + 1);
+       line.assign(recvq, 0, i);
+       recvq.erase(0, i + 1);
        return true;
 }
 
-void StreamSocket::DoRead()
+int StreamSocket::HookChainRead(IOHook* hook, std::string& rq)
 {
-       if (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 +226,56 @@ static const int MYIOV_MAX = IOV_MAX < 128 ? IOV_MAX : 128;
 
 void StreamSocket::DoWrite()
 {
-       if (sendq.empty())
+       if (getSendQSize() == 0)
                return;
-       if (!error.empty() || fd < 0 || fd == INT_MAX)
+       if (!error.empty() || fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET", 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 +284,25 @@ void StreamSocket::DoWrite()
                        }
 
                        int rv_max = 0;
-                       iovec* iovecs = new iovec[bufcount];
-                       for(int i=0; i < bufcount; i++)
+                       int rv;
                        {
-                               iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
-                               iovecs[i].iov_len = sendq[i].length();
-                               rv_max += sendq[i].length();
+                               SocketEngine::IOVector iovecs[MYIOV_MAX];
+                               size_t j = 0;
+                               for (SendQueue::const_iterator i = sq.begin(), end = i+bufcount; i != end; ++i, j++)
+                               {
+                                       const SendQueue::Element& elem = *i;
+                                       iovecs[j].iov_base = const_cast<char*>(elem.data());
+                                       iovecs[j].iov_len = elem.length();
+                                       rv_max += elem.length();
+                               }
+                               rv = SocketEngine::WriteV(this, iovecs, bufcount);
                        }
-                       int rv = writev(fd, iovecs, bufcount);
-                       delete[] iovecs;
 
-                       if (rv == (int)sendq_len)
+                       if (rv == (int)sq.bytes())
                        {
                                // it's our lucky day, everything got written out. Fast cleanup.
                                // This won't ever happen if the number of buffers got capped.
-                               sendq_len = 0;
-                               sendq.clear();
+                               sq.clear();
                        }
                        else if (rv > 0)
                        {
@@ -368,20 +312,19 @@ void StreamSocket::DoWrite()
                                        // it's going to block now
                                        eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
                                }
-                               sendq_len -= rv;
-                               while (rv > 0 && !sendq.empty())
+                               while (rv > 0 && !sq.empty())
                                {
-                                       std::string& front = sendq.front();
+                                       const SendQueue::Element& front = sq.front();
                                        if (front.length() <= (size_t)rv)
                                        {
                                                // this string got fully written out
                                                rv -= front.length();
-                                               sendq.pop_front();
+                                               sq.pop_front();
                                        }
                                        else
                                        {
                                                // stopped in the middle of this string
-                                               front = front.substr(rv);
+                                               sq.erase_front(rv);
                                                rv = 0;
                                        }
                                }
@@ -407,38 +350,38 @@ 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
 }
 
 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 +397,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..b5949273806b61a537dfb94227f25a8404f077d3 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,19 @@ std::string Base64ToBin(const std::string& data_str, const char* table)
        }
        return rv;
 }
+
+bool InspIRCd::TimingSafeCompare(const std::string& one, const std::string& two)
+{
+       if (one.length() != two.length())
+               return false;
+
+       unsigned int diff = 0;
+       for (std::string::const_iterator i = one.begin(), j = two.begin(); i != one.end(); ++i, ++j)
+       {
+               unsigned char a = static_cast<unsigned char>(*i);
+               unsigned char b = static_cast<unsigned char>(*j);
+               diff |= a ^ b;
+       }
+
+       return (diff == 0);
+}
index ae11c3b48238b3155cfb2b52c9de0f2dac97ecb2..4ec6c2b0642453b7dfc31b9a563d7bec46e817d4 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);
 
        if (this->fd == -1)
@@ -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,55 +106,32 @@ 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));
        }
 }
 
-/* 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)
@@ -156,7 +164,7 @@ void ListenSocket::AcceptInternal()
                }
        }
 
-       ServerInstance->SE->NonBlocking(incomingSockfd);
+       SocketEngine::NonBlocking(incomingSockfd);
 
        ModResult res;
        FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server));
@@ -171,29 +179,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..23f98bf
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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, const std::string &ctag)
+       : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST),
+       listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
+       configtag(ctag)
+       , extItem("listbase_mode_" + name + "_list", 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(configtag);
+
+       limitlist oldlimits = chanlimits;
+       chanlimits.clear();
+
+       for (ConfigIter i = tags.first; i != tags.second; i++)
+       {
+               // For each <banlist> tag
+               ConfigTag* c = i->second;
+               ListLimit limit(c->getString("chan"), 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.
+       chanlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE));
+
+       // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
+       if (oldlimits == chanlimits)
+               return;
+
+       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::TellListTooLong(User* source, Channel* channel, std::string& parameter)
+{
+       source->WriteNumeric(ERR_BANLISTFULL, channel->name, parameter, "Channel ban list is full");
+}
+
+void ListModeBase::TellAlreadyOnList(User*, Channel*, std::string&)
+{
+}
+
+void ListModeBase::TellNotSet(User*, Channel*, std::string&)
+{
+}
index 89b2be019721289c241b4839bb591820215b1b84..6fa972c739228621579a4c3ef5e9bd74ac9337fa 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()
@@ -82,43 +83,43 @@ void LogManager::OpenFileLogs()
                }
                std::string type = tag->getString("type");
                std::string level = tag->getString("level");
-               int loglevel = DEFAULT;
+               LogLevel loglevel = LOG_DEFAULT;
                if (level == "rawio")
                {
-                       loglevel = RAWIO;
+                       loglevel = LOG_RAWIO;
                        ServerInstance->Config->RawLog = true;
                }
                else if (level == "debug")
                {
-                       loglevel = DEBUG;
+                       loglevel = LOG_DEBUG;
                }
                else if (level == "verbose")
                {
-                       loglevel = VERBOSE;
+                       loglevel = LOG_VERBOSE;
                }
                else if (level == "default")
                {
-                       loglevel = DEFAULT;
+                       loglevel = LOG_DEFAULT;
                }
                else if (level == "sparse")
                {
-                       loglevel = SPARSE;
+                       loglevel = LOG_SPARSE;
                }
                else if (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, static_cast<unsigned int>(tag->getInt("flush", 20, 1, INT_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..98b0f98540449f4543628c7620da7b5d54310014 100644 (file)
 
 
 #include "inspircd.h"
+#include "builtinmodes.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 +51,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 "";
 }
@@ -123,11 +97,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 +116,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 +126,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 +135,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 +261,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 +270,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 +283,87 @@ 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;
+               ModeWatcher* mw = i->second;
+               if (mw->GetModeType() == type)
+               {
+                       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 (IS_LOCAL(user) && !IS_OPER(user))
+       if (IS_LOCAL(user) && !user->IsOper())
        {
-               char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
-               if (disabled[modechar - 'A'])
+               const std::bitset<64>& disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
+               if (disabled.test(modechar - 'A'))
                {
-                       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);
+                       user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - %s mode %c has been locked by the administrator",
+                               type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
                        return MODEACTION_DENY;
                }
        }
 
-       if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
+       if ((adding) && (IS_LOCAL(user)) && (mh->NeedsOper()) && (!user->HasModePermission(mh)))
        {
                /* It's an oper only mode, and they don't have access to it. */
-               if (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 set %s mode %c",
+                                       user->oper->name.c_str(), 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 set %s mode %c",
+                                       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 (endindex > parameters.size())
+               endindex = parameters.size();
 
-       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];
-
-       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 +378,166 @@ 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 unknown mode char to me");
                        continue;
                }
 
                std::string parameter;
-               int pcnt = mh->GetNumParams(adding);
-               if (pcnt && param_at == parameters.size())
-               {
-                       /* No parameter, continue to the next mode */
-                       mh->OnParameterMissing(user, targetuser, targetchannel);
-                       continue;
-               }
-               else if (pcnt)
-               {
+               if ((mh->NeedsParam(adding)) && (param_at < endindex))
                        parameter = parameters[param_at++];
-                       /* Make sure the user isn't trying to slip in an invalid parameter */
-                       if ((parameter.empty()) || (parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
+
+               changelist.push(mh, adding, parameter);
+       }
+}
+
+static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
+{
+       // An empty parameter is never acceptable
+       if (item.param.empty())
+       {
+               item.mh->OnParameterMissing(user, targetuser, targetchannel);
+               return false;
+       }
+
+       // The parameter cannot begin with a ':' character or contain a space
+       if ((item.param[0] == ':') || (item.param.find(' ') != std::string::npos))
+               return false;
+
+       return true;
+}
+
+// Returns true if we should apply a merged mode, false if we should skip it
+static bool ShouldApplyMergedMode(Channel* chan, Modes::Change& item)
+{
+       ModeHandler* mh = item.mh;
+       if ((!chan) || (!chan->IsModeSet(mh)) || (mh->IsListMode()))
+               // Mode not set here or merge is not applicable, apply the incoming mode
+               return true;
+
+       // Mode handler decides
+       std::string ours = chan->GetModeParameter(mh);
+       return mh->ResolveModeConflict(item.param, ours, chan);
+}
+
+void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+{
+       // Call ProcessSingle until the entire list is processed, but at least once to ensure
+       // LastParse and LastChangeList are cleared
+       unsigned int processed = 0;
+       do
+       {
+               unsigned int n = ProcessSingle(user, targetchannel, targetuser, changelist, flags, processed);
+               processed += n;
+       }
+       while (processed < changelist.size());
+}
+
+unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
+{
+       LastParse.clear();
+       LastChangeList.clear();
+
+       unsigned int modes_processed = 0;
+       std::string output_mode;
+       std::string output_parameters;
+
+       char output_pm = '\0'; // current output state, '+' or '-'
+       Modes::ChangeList::List& list = changelist.getlist();
+       for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
+       {
+               modes_processed++;
+
+               Modes::Change& item = *i;
+               ModeHandler* mh = item.mh;
+
+               // If the mode is supposed to have a parameter then we first take a look at item.param
+               // and, if we were asked to, also handle mode merges now
+               if (mh->NeedsParam(item.adding))
+               {
+                       // Skip the mode if the parameter does not pass basic validation
+                       if (!IsModeParamValid(user, targetchannel, targetuser, item))
+                               continue;
+
+                       // If this is a merge and we won we don't apply this mode
+                       if ((flags & MODE_MERGE) && (!ShouldApplyMergedMode(targetchannel, item)))
                                continue;
-                       if (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 ? '+' : '-';
+               char needed_pm = item.adding ? '+' : '-';
                if (needed_pm != output_pm)
                {
                        output_pm = needed_pm;
                        output_mode.append(1, output_pm);
                }
-               output_mode.append(1, modechar);
+               output_mode.push_back(mh->GetModeChar());
 
-               if (pcnt)
+               if (!item.param.empty())
                {
-                       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);
+                       output_parameters.push_back(' ');
+                       output_parameters.append(item.param);
                }
+               LastChangeList.push(mh, item.adding, item.param);
 
-               if ( (output_mode.length() + output_parameters.str().length() > 450)
+               if ((output_mode.length() + output_parameters.length() > 450)
                                || (output_mode.length() > 100)
-                               || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes))
+                               || (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes))
                {
                        /* mode sequence is getting too long */
                        break;
                }
        }
 
-       LastParseParams[0] = output_mode;
-
        if (!output_mode.empty())
        {
                LastParse = targetchannel ? targetchannel->name : targetuser->nick;
                LastParse.append(" ");
                LastParse.append(output_mode);
-               LastParse.append(output_parameters.str());
+               LastParse.append(output_parameters);
 
                if (targetchannel)
-               {
-                       targetchannel->WriteChannel(user, "MODE %s", LastParse.c_str());
-                       FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, LastParseParams, LastParseTranslate));
-               }
+                       targetchannel->WriteChannel(user, "MODE " + LastParse);
                else
-               {
-                       targetuser->WriteFrom(user, "MODE %s", LastParse.c_str());
-                       FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, LastParseParams, LastParseTranslate));
-               }
-       }
-       else if (targetchannel && parameters.size() == 2)
-       {
-               /* Special case for displaying the list for listmodes,
-                * e.g. MODE #chan b, or MODE #chan +b without a parameter
-                */
-               this->DisplayListModes(user, targetchannel, mode_sequence);
+                       targetuser->WriteFrom(user, "MODE " + LastParse);
+
+               FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags, output_mode));
        }
+
+       return modes_processed;
 }
 
-void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence)
+void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh)
 {
-       seq++;
-
-       for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
        {
-               unsigned char mletter = *letter;
-               if (mletter == '+')
-                       continue;
-
-               /* Ensure the user doesnt request the same mode twice,
-                * so they cant flood themselves off out of idiocy.
-                */
-               if (sent[mletter] == seq)
-                       continue;
-
-               sent[mletter] = seq;
-
-               ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL);
-
-               if (!mh || !mh->IsListMode())
-                       return;
-
                ModResult MOD_RESULT;
-               FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, 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 +545,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 +581,79 @@ 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("Invalid letter for mode " + mh->name);
 
        /* 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("Invalid prefix for mode " + mh->name);
 
-       if (mh->GetPrefix() && FindPrefix(mh->GetPrefix()))
-               return false;
+               if (FindPrefix(pm->GetPrefix()))
+                       throw ModuleException("Prefix already exists for mode " + mh->name);
+       }
 
-       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("Letter is already in use for mode " + mh->name);
 
-       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;
+       if (!modehandlersbyname[mh->GetModeType()].insert(std::make_pair(mh->name, mh)).second)
+               throw ModuleException("Mode name already in use: " + mh->name);
+
+       // 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());
+
+       RecreateModeListFor004Numeric();
 }
 
 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 +662,106 @@ 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()));
 
+       RecreateModeListFor004Numeric();
        return true;
 }
 
-ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
+ModeHandler* ModeParser::FindMode(const std::string& modename, 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;
+       ModeHandlerMap& mhmap = modehandlersbyname[mt];
+       ModeHandlerMap::const_iterator it = mhmap.find(modename);
+       if (it != mhmap.end())
+               return it->second;
 
-       return modehandlers[pos];
+       return NULL;
 }
 
-std::string ModeParser::UserModeList()
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
 {
-       char modestr[256];
-       int pointer = 0;
+       if (!ModeParser::IsModeChar(modeletter))
+               return NULL;
 
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
-       {
-               unsigned char pos = (mode-65) | MASK_USER;
+       return modehandlers[mt][modeletter-65];
+}
 
-               if (modehandlers[pos])
-                       modestr[pointer++] = mode;
-       }
-       modestr[pointer++] = 0;
-       return modestr;
+PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
+{
+       ModeHandler* mh = FindMode(modeletter, MODETYPE_CHANNEL);
+       if (!mh)
+               return NULL;
+       return mh->IsPrefixMode();
 }
 
-std::string ModeParser::ChannelModeList()
+std::string ModeParser::CreateModeList(ModeType mt, bool needparam)
 {
-       char modestr[256];
-       int pointer = 0;
+       std::string modestr;
 
        for (unsigned char mode = 'A'; mode <= 'z'; mode++)
        {
-               unsigned char pos = (mode-65) | MASK_CHANNEL;
-
-               if (modehandlers[pos])
-                       modestr[pointer++] = mode;
+               ModeHandler* mh = modehandlers[mt][mode-65];
+               if ((mh) && ((!needparam) || (mh->NeedsParam(true))))
+                       modestr.push_back(mode);
        }
-       modestr[pointer++] = 0;
+
        return modestr;
 }
 
-std::string ModeParser::ParaModeList()
+void ModeParser::RecreateModeListFor004Numeric()
 {
-       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;
+       Cached004ModeList[0] = CreateModeList(MODETYPE_USER);
+       Cached004ModeList[1] = CreateModeList(MODETYPE_CHANNEL);
+       Cached004ModeList[2] = CreateModeList(MODETYPE_CHANNEL, true);
 }
 
-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 +770,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 +811,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 +821,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,103 +841,68 @@ 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);
+       }
+}
+
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
+{
+       const Channel::MemberMap& userlist = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
+       {
+               if (i->second->HasMode(this))
+                       changelist.push_remove(this, i->first->nick);
        }
 }
 
 struct builtin_modes
 {
-       ModeChannelSecret s;
-       ModeChannelPrivate p;
-       ModeChannelModerated m;
-       ModeChannelTopicOps t;
+       SimpleChannelModeHandler s;
+       SimpleChannelModeHandler p;
+       SimpleChannelModeHandler m;
+       SimpleChannelModeHandler t;
 
-       ModeChannelNoExternal n;
-       ModeChannelInviteOnly i;
+       SimpleChannelModeHandler n;
+       SimpleChannelModeHandler i;
        ModeChannelKey k;
        ModeChannelLimit l;
 
@@ -975,45 +910,47 @@ struct builtin_modes
        ModeChannelOp o;
        ModeChannelVoice v;
 
-       ModeUserWallops uw;
-       ModeUserInvisible ui;
+       SimpleUserModeHandler ui;
        ModeUserOperator uo;
        ModeUserServerNoticeMask us;
 
-       void init(ModeParser* modes)
-       {
-               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);
+       builtin_modes()
+               : s(NULL, "secret", 's')
+               , p(NULL, "private", 'p')
+               , m(NULL, "moderated", 'm')
+               , t(NULL, "topiclock", 't')
+               , n(NULL, "noextmsg", 'n')
+               , i(NULL, "inviteonly", 'i')
+               , ui(NULL, "invisible", 'i')
+       {
+       }
+
+       void init()
+       {
+               ServiceProvider* modes[] = { &s, &p, &m, &t, &n, &i, &k, &l, &b, &o, &v,
+                                                                        &ui, &uo, &us };
+               ServerInstance->Modules->AddServices(modes, sizeof(modes)/sizeof(ServiceProvider*));
        }
 };
 
 static builtin_modes static_modes;
 
+void ModeParser::InitBuiltinModes()
+{
+       static_modes.init();
+       static_modes.b.DoRehash();
+}
+
+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 e45f191..0000000
+++ /dev/null
@@ -1,179 +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 :Channel ban list for %s is full (maximum entries for this channel is %ld)",user->nick.c_str(), chan->name.c_str(), 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;
-}
-
index 400333fced175133e4b1cdac3e9b1e2921c9a4af..980b3215a4e504fad6dc65577476a882c404686e 100644 (file)
 
 
 #include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_k.h"
+#include "builtinmodes.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)
+ModeChannelKey::ModeChannelKey()
+       : ParamMode<ModeChannelKey, LocalStringExt>(NULL, "key", 'k', PARAM_ALWAYS)
 {
 }
 
 ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
 {
-       bool exists = channel->IsModeSet('k');
+       const std::string* key = ext.get(channel);
+       bool exists = (key != NULL);
        if (IS_LOCAL(source))
        {
                if (exists == adding)
                        return MODEACTION_DENY;
-               if (exists && (parameter != channel->GetModeParameter('k')))
+               if (exists && (parameter != *key))
                {
                        /* Key is currently set and the correct key wasnt given */
                        return MODEACTION_DENY;
                }
        } else {
-               if (exists && adding && parameter == channel->GetModeParameter('k'))
+               if (exists && adding && parameter == *key)
                {
                        /* 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;
-
+       channel->SetMode(this, adding);
        if (adding)
        {
-               std::string ckey;
-               ckey.assign(parameter, 0, 32);
-               parameter = ckey;
-               channel->SetModeParam('k', parameter);
+               if (parameter.length() > maxkeylen)
+                       parameter.erase(maxkeylen);
+               ext.set(channel, parameter);
        }
        else
-       {
-               channel->SetModeParam('k', "");
-       }
+               ext.unset(channel);
+
        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;
+}
index 001d058bbfab9c6b2132f2e90706a63a2b19284e..d61b2597b40444da2574c176796293ee784c0402 100644 (file)
 
 
 #include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_l.h"
+#include "builtinmodes.h"
 
-ModeChannelLimit::ModeChannelLimit() : ParamChannelModeHandler(NULL, "limit", 'l')
+ModeChannelLimit::ModeChannelLimit()
+       : ParamMode<ModeChannelLimit, LocalIntExt>(NULL, "limit", 'l')
 {
 }
 
@@ -35,13 +33,17 @@ bool ModeChannelLimit::ResolveModeConflict(std::string &their_param, const std::
        return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
 }
 
-bool ModeChannelLimit::ParamValidate(std::string &parameter)
+ModeAction ModeChannelLimit::OnSet(User* user, Channel* chan, std::string& parameter)
 {
-       int limit = atoi(parameter.c_str());
-
+       int limit = ConvToInt(parameter);
        if (limit < 0)
-               return false;
+               return MODEACTION_DENY;
+
+       ext.set(chan, limit);
+       return MODEACTION_ALLOW;
+}
 
-       parameter = ConvToStr(limit);
-       return true;
+void ModeChannelLimit::SerializeParam(Channel* chan, intptr_t n, std::string& out)
+{
+       out += ConvToStr(n);
 }
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;
-}
index a5f590ba03dc03c8091a276c326fe2431c6f8bd3..9d50e571b670d56d2f231f42ea71997c9852346e 100644 (file)
 
 
 #include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_o.h"
+#include "builtinmodes.h"
 
 ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE, MODETYPE_USER)
 {
@@ -32,7 +29,7 @@ ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE
 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))
+       if (!source->server->IsULine() && !source->IsOper())
                return MODEACTION_DENY;
 
        /* Not even opers can GIVE the +o mode, only take it away */
@@ -40,14 +37,13 @@ ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, st
                return MODEACTION_DENY;
 
        /* Set the bitfields.
-        * Note that oper status is only given in cmd_oper.cpp
+        * 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.empty() ? source->server.c_str() : source->nick.c_str());
+       ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(), source->nick.c_str());
        dest->UnOper();
 
        return MODEACTION_ALLOW;
index 1b782ae853dbd74444c61e5a087a71b2823c5b63..ffad21662283dc08b19e8df893f6a8e19457d472 100644 (file)
 
 
 #include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_s.h"
+#include "builtinmodes.h"
 
 ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
 {
@@ -32,41 +29,116 @@ ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomas
 
 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());
+               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->modes[UM_SNOMASK] != false)
+               if (dest->IsModeSet(this))
                {
-                       dest->modes[UM_SNOMASK] = false;
+                       dest->SetMode(this, false);
+                       dest->snomasks.reset();
                        return MODEACTION_ALLOW;
                }
        }
 
-       /* Allow the change */
+       // Mode not set and trying to unset, deny
        return MODEACTION_DENY;
 }
 
-std::string ModeUserServerNoticeMask::GetUserParameter(User* user)
+std::string ModeUserServerNoticeMask::GetUserParameter(const User* user) const
 {
-       std::string masks = user->FormatNoticeMasks();
-       if (masks.length())
-               masks = "+" + masks;
-       return masks;
+       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->WriteServ("NOTICE %s :*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.",
-                       user->nick.c_str());
+       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 unknown snomask char to me");
+                                               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;
+}
index 050f41c758379e7581fea0054287a3532d885c7e..644d2140f614435dbc9e51f286fc34fb22ded050 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
 #include "exitcodes.h"
 #include <iostream>
 
 #include <dirent.h>
 #endif
 
-#ifndef PURE_STATIC
+#ifndef INSPIRCD_STATIC
 
-bool ModuleManager::Load(const std::string& filename, bool defer)
+bool ModuleManager::Load(const std::string& modname, bool defer)
 {
        /* Don't allow people to specify paths for modules, it doesn't work as expected */
-       if (filename.find('/') != std::string::npos)
+       if (modname.find('/') != std::string::npos)
        {
-               LastModuleError = "You can't load modules with a path: " + filename;
+               LastModuleError = "You can't load modules with a path: " + modname;
                return false;
        }
 
-       char modfile[MAXBUF];
-       snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename.c_str());
+       const std::string filename = ExpandModName(modname);
+       const std::string moduleFile = ServerInstance->Config->Paths.PrependModule(filename);
 
-       if (!ServerConfig::FileExists(modfile))
+       if (!FileSystem::FileExists(moduleFile))
        {
                LastModuleError = "Module file could not be found: " + filename;
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+               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", DEFAULT, LastModuleError);
+               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                return false;
        }
 
        Module* newmod = NULL;
-       DLLManager* newhandle = new DLLManager(modfile);
+       DLLManager* newhandle = new DLLManager(moduleFile.c_str());
+       ServiceList newservices;
+       if (!defer)
+               this->NewServices = &newservices;
 
        try
        {
                newmod = newhandle->CallInit();
+               this->NewServices = NULL;
 
                if (newmod)
                {
@@ -72,30 +71,39 @@ bool ModuleManager::Load(const std::string& filename, bool defer)
                        newmod->dying = false;
                        Modules[filename] = newmod;
                        std::string version = newhandle->GetVersion();
+                       if (version.empty())
+                               version.assign("unknown");
                        if (defer)
                        {
-                               ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)",
+                               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", DEFAULT,"New module introduced: %s (Module version %s)%s",
+                               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", DEFAULT, LastModuleError);
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                        delete newhandle;
                        return false;
                }
        }
        catch (CoreException& modexcept)
        {
+               this->NewServices = NULL;
+
                // failure in module constructor
                if (newmod)
                {
@@ -105,109 +113,41 @@ bool ModuleManager::Load(const std::string& filename, bool defer)
                else
                        delete newhandle;
                LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+               ServerInstance->Logs->Log("MODULE", LOG_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));
+       FOREACH_MOD(OnLoadModule, (newmod));
+       PrioritizeHooks();
+       ServerInstance->ISupport.Build();
        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()
+void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
 {
-       ModCount = 0;
-
        std::cout << std::endl << "Loading core commands";
        fflush(stdout);
 
-       DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
+       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, "cmd_*.so", ascii_case_insensitive_map))
+                       if (InspIRCd::Match(entry->d_name, "core_*.so", ascii_case_insensitive_map))
                        {
                                std::cout << ".";
                                fflush(stdout);
 
+                               this->NewServices = &servicemap[entry->d_name];
+
                                if (!Load(entry->d_name, true))
                                {
-                                       ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
+                                       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);
                                }
@@ -216,57 +156,6 @@ void ModuleManager::LoadAll()
                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
index cea40c7a32364e75a807866f7979e6730c88a40c..4de111b63ae44e303b906f069915e609a52f5d52 100644 (file)
  */
 
 
-#define MODNAME cmd_all
+#define MODNAME "core_all"
 
 #include "inspircd.h"
 #include "exitcodes.h"
 #include <iostream>
 
-#ifdef PURE_STATIC
+#ifdef INSPIRCD_STATIC
 
 typedef std::map<std::string, AllModuleList*> modmap;
 static std::vector<AllCommandList::fn>* cmdlist = NULL;
@@ -58,7 +58,6 @@ class AllModule : public Module
                        {
                                Command* c = (*i)(this);
                                cmds.push_back(c);
-                               ServerInstance->AddCommand(c);
                        }
                }
                catch (...)
@@ -70,11 +69,10 @@ class AllModule : public Module
 
        ~AllModule()
        {
-               for(std::vector<Command*>::iterator i = cmds.begin(); i != cmds.end(); ++i)
-                       delete *i;
+               stdalgo::delete_all(cmds);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("All commands", VF_VENDOR|VF_CORE);
        }
@@ -82,12 +80,18 @@ class AllModule : public Module
 
 MODULE_INIT(AllModule)
 
-bool ModuleManager::Load(const std::string& name, bool defer)
+bool ModuleManager::Load(const std::string& inputname, bool defer)
 {
+       const std::string name = ExpandModName(inputname);
        modmap::iterator it = modlist->find(name);
        if (it == modlist->end())
                return false;
        Module* mod = NULL;
+
+       ServiceList newservices;
+       if (!defer)
+               this->NewServices = &newservices;
+
        try
        {
                mod = (*it->second->init)();
@@ -95,147 +99,50 @@ bool ModuleManager::Load(const std::string& name, bool defer)
                mod->ModuleDLLManager = NULL;
                mod->dying = false;
                Modules[name] = mod;
+               this->NewServices = NULL;
                if (defer)
                {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s", name.c_str());
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s", name.c_str());
                        return true;
                }
                else
                {
+                       ConfigStatus confstatus;
+
+                       AttachAll(mod);
+                       AddServices(newservices);
                        mod->init();
+                       mod->ReadConfig(confstatus);
                }
        }
        catch (CoreException& modexcept)
        {
+               this->NewServices = NULL;
+
                if (mod)
                        DoSafeUnload(mod);
-               ServerInstance->Logs->Log("MODULE", DEFAULT, "Unable to load " + name + ": " + modexcept.GetReason());
+               ServerInstance->Logs->Log("MODULE", LOG_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));
+       FOREACH_MOD(OnLoadModule, (mod));
+       PrioritizeHooks();
+       ServerInstance->ISupport.Build();
        return true;
 }
 
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
+void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
 {
-       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++)
+       for (modmap::const_iterator i = modlist->begin(); i != modlist->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)
+               const std::string modname = i->first;
+               if (InspIRCd::Match(modname, "core_*.so", ascii_case_insensitive_map))
                {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
+                       this->NewServices = &servicemap[modname];
+                       Load(modname, true);
                }
        }
+       this->NewServices = NULL;
 }
 
 #endif
index 79a33e6172599f5f618627c58e3413fc902991b9..de2fe96b3c59d6489607033bc2d7632ea615f658 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,24 +52,6 @@ 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) { }
-
-void Event::Send()
-{
-       FOREACH_MOD(I_OnEvent,OnEvent(*this));
-}
-
 // These declarations define the behavours of the base class Module (which does nothing at all)
 
 Module::Module() { }
@@ -85,100 +63,101 @@ 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::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::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, const std::string&) { DetachEvent(I_OnMode); }
+void           Module::OnOper(User*, const std::string&) { DetachEvent(I_OnOper); }
+void           Module::OnPostOper(User*, const std::string&, const std::string &) { DetachEvent(I_OnPostOper); }
+void           Module::OnInfo(User*) { DetachEvent(I_OnInfo); }
+ModResult      Module::OnUserPreInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserPreInvite); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&, MessageType) { DetachEvent(I_OnUserPreMessage); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnUserPreNick(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&, std::vector<std::string>&, LocalUser*, bool, const std::string&) { DetachEvent(I_OnPreCommand); return MOD_RES_PASSTHRU; }
+void           Module::OnPostCommand(Command*, const std::vector<std::string>&, LocalUser*, CmdResult, const std::string&) { DetachEvent(I_OnPostCommand); }
+void           Module::OnUserInit(LocalUser*) { DetachEvent(I_OnUserInit); }
+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::OnStats(Stats::Context&) { DetachEvent(I_OnStats); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { DetachEvent(I_OnChangeLocalUserHost); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { DetachEvent(I_OnChangeLocalUserGECOS); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnPreTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPreTopicChange); return MOD_RES_PASSTHRU; }
+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::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&, MessageType) { DetachEvent(I_OnUserMessage); }
+void           Module::OnUserInvite(User*, User*, Channel*, time_t, unsigned int, CUList&) { DetachEvent(I_OnUserInvite); }
+void           Module::OnPostTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPostTopicChange); }
+void           Module::OnSyncUser(User*, ProtocolInterface::Server&) { DetachEvent(I_OnSyncUser); }
+void           Module::OnSyncChannel(Channel*, ProtocolInterface::Server&) { DetachEvent(I_OnSyncChannel); }
+void           Module::OnSyncNetwork(ProtocolInterface::Server&) { DetachEvent(I_OnSyncNetwork); }
+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::OnChangeName(User*, const std::string&) { DetachEvent(I_OnChangeName); }
+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); }
+ModResult      Module::OnSetAway(User*, const std::string &) { DetachEvent(I_OnSetAway); return MOD_RES_PASSTHRU; }
+void           Module::OnBuildNeighborList(User*, IncludeChanList&, std::map<User*,bool>&) { DetachEvent(I_OnBuildNeighborList); }
+void           Module::OnGarbageCollect() { DetachEvent(I_OnGarbageCollect); }
+ModResult      Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { DetachEvent(I_OnSetConnectClass); return MOD_RES_PASSTHRU; }
+void           Module::OnText(User*, void*, int, const std::string&, char, CUList&) { DetachEvent(I_OnText); }
+ModResult      Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { DetachEvent(I_OnNamesListItem); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnNumeric(User*, 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; }
+ModResult      Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, Numeric::Numeric&) { DetachEvent(I_OnSendWhoLine); return MOD_RES_PASSTHRU; }
+void           Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
+
+#ifdef INSPIRCD_ENABLE_TESTSUITE
 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*) { }
+#endif
+
+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);
+}
 
-ModuleManager::ModuleManager() : ModCount(0)
+void ServiceProvider::DisableAutoRegister()
+{
+       if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+               stdalgo::erase(*ServerInstance->Modules->NewServices, this);
+}
+
+ModuleManager::ModuleManager()
 {
 }
 
@@ -188,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);
@@ -197,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)
@@ -212,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)
@@ -254,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)
@@ -284,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)
                        {
@@ -324,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);
@@ -331,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;
        }
 
@@ -345,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++;
@@ -389,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()
@@ -427,40 +436,109 @@ void ModuleManager::UnloadAll()
        }
 }
 
-std::string& ModuleManager::LastError()
+namespace
 {
-       return LastModuleError;
+       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();
+                       // 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);
+
+       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);
+               }
+       }
+
+       ConfigStatus confstatus;
+
+       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();
+                       mod->ReadConfig(confstatus);
+               }
+               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;
+
+       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)
@@ -468,10 +546,11 @@ 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));
                        }
+                       dynamic_reference_base::reset_all();
                        return;
                }
                default:
-                       throw ModuleException("Cannot add unknown service type");
+                       item.RegisterService();
        }
 }
 
@@ -482,17 +561,11 @@ 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:
@@ -518,85 +591,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();
+       resolve();
 }
 
-void dynamic_reference_base::lookup()
+void dynamic_reference_base::resolve()
 {
-       if (!*this)
-               throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
-}
-
-dynamic_reference_base::operator bool()
-{
-       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;
@@ -604,181 +658,21 @@ Module* ModuleManager::Find(const std::string &name)
                return modfind->second;
 }
 
-const std::vector<std::string> ModuleManager::GetAllModuleNames(int filter)
-{
-       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)
+void ModuleManager::AddReferent(const std::string& name, ServiceProvider* service)
 {
-       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 a36c39bc83cd3e6d078d311b4a11313df543a44f..1f77afa76f5ca347260f4ab70ef4b186f9a7bffa 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/// $CompilerFlags: find_compiler_flags("geoip" "")
+/// $LinkerFlags: find_linker_flags("geoip" "-lGeoIP")
+
+/// $PackageInfo: require_system("centos" "7.0") GeoIP-devel pkgconfig
+/// $PackageInfo: require_system("darwin") geoip pkg-config
+/// $PackageInfo: require_system("debian") libgeoip-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libgeoip-dev pkg-config
 
 #include "inspircd.h"
 #include "xline.h"
 
+// Fix warnings about the use of commas at end of enumerator lists on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-pedantic"
+#endif
+
 #include <GeoIP.h>
 
 #ifdef _WIN32
 # pragma comment(lib, "GeoIP.lib")
 #endif
 
-/* $ModDesc: Provides a way to restrict users by country using GeoIP lookup */
-/* $LinkerFlags: -lGeoIP */
-
 class ModuleGeoIP : public Module
 {
        LocalStringExt ext;
@@ -37,7 +48,7 @@ class ModuleGeoIP : public Module
 
        std::string* SetExt(LocalUser* user)
        {
-               const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString());
+               const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString().c_str());
                if (!c)
                        c = "UNK";
 
@@ -47,21 +58,20 @@ class ModuleGeoIP : public Module
        }
 
  public:
-       ModuleGeoIP() : ext("geoip_cc", this), gi(NULL)
+       ModuleGeoIP()
+               : ext("geoip_cc", ExtensionItem::EXT_USER, this)
+               , gi(NULL)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                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_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)
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
                {
                        LocalUser* user = *i;
                        if ((user->registered == REG_ALL) && (!ext.get(user)))
@@ -77,12 +87,12 @@ class ModuleGeoIP : public Module
                        GeoIP_delete(gi);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_VENDOR);
        }
 
-       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
                std::string* cc = ext.get(user);
                if (!cc)
@@ -99,14 +109,16 @@ class ModuleGeoIP : public Module
                return MOD_RES_DENY;
        }
 
-       ModResult OnStats(char symbol, User* user, string_list &out)
+       ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
        {
-               if (symbol != 'G')
+               if (stats.GetSymbol() != 'G')
                        return MOD_RES_PASSTHRU;
 
                unsigned int unknown = 0;
                std::map<std::string, unsigned int> results;
-               for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
                {
                        std::string* cc = ext.get(*i);
                        if (cc)
@@ -115,18 +127,16 @@ class ModuleGeoIP : public Module
                                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));
+                       stats.AddRow(801, "GeoIPSTATS " + i->first + " " + ConvToStr(i->second));
                }
 
                if (unknown)
-                       out.push_back(p + "Unknown " + ConvToStr(unknown));
+                       stats.AddRow(801, "GeoIPSTATS Unknown " + ConvToStr(unknown));
 
                return MOD_RES_DENY;
        }
 };
 
 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..f3ace54
--- /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 (scope == "base")
+                       searchscope = LDAP_SCOPE_BASE;
+               else if (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 (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("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..3490f7fa60f66291d0a8d19ba4faf5ee2bfbe234 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
@@ -88,7 +97,7 @@ struct RQueueItem
        RQueueItem(SQLQuery* Q, MySQLresult* R) : q(Q), r(R) {}
 };
 
-typedef std::map<std::string, SQLConnection*> ConnMap;
+typedef insp::flat_map<std::string, SQLConnection*> ConnMap;
 typedef std::deque<QQueueItem> QueryQueue;
 typedef std::deque<RQueueItem> ResultQueue;
 
@@ -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();
+       void OnNotify();
 };
 
 #if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
@@ -184,21 +193,17 @@ class MySQLresult : public SQLResult
 
        }
 
-       ~MySQLresult()
-       {
-       }
-
-       virtual int Rows()
+       int Rows()
        {
                return rows;
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       void GetCols(std::vector<std::string>& result)
        {
                result.assign(colnames.begin(), colnames.end());
        }
 
-       virtual SQLEntry GetValue(int row, int column)
+       SQLEntry GetValue(int row, int column)
        {
                if ((row >= 0) && (row < rows) && (column >= 0) && (column < (int)fieldlists[row].size()))
                {
@@ -207,7 +212,7 @@ class MySQLresult : public SQLResult
                return SQLEntry();
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQLEntries& result)
        {
                if (currentrow < rows)
                {
@@ -258,6 +263,12 @@ class SQLConnection : public SQLProvider
                bool rv = mysql_real_connect(connection, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), port, NULL, 0);
                if (!rv)
                        return rv;
+
+               // Enable character set settings
+               std::string charset = config->getString("charset");
+               if ((!charset.empty()) && (mysql_set_character_set(connection, charset.c_str())))
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not set character set to \"%s\"", charset.c_str());
+
                std::string initquery;
                if (config->readString("initialquery", initquery))
                {
@@ -381,12 +392,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,7 +409,7 @@ ModuleSQL::~ModuleSQL()
        }
 }
 
-void ModuleSQL::OnRehash(User* user)
+void ModuleSQL::ReadConfig(ConfigStatus& status)
 {
        ConnMap conns;
        ConfigTagList tags = ServerInstance->Config->ConfTags("database");
index ac247548ac35c303a805734c483f112bb0c3f3f5..09aba7de9fef96af570b3bd8ab6903a7d85d3274 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,10 +62,10 @@ 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);
 };
 
 struct QueueItem
@@ -97,12 +100,12 @@ class PgSQLresult : public SQLResult
                PQclear(res);
        }
 
-       virtual int Rows()
+       int Rows()
        {
                return rows;
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       void GetCols(std::vector<std::string>& result)
        {
                result.resize(PQnfields(res));
                for(unsigned int i=0; i < result.size(); i++)
@@ -111,7 +114,7 @@ class PgSQLresult : public SQLResult
                }
        }
 
-       virtual SQLEntry GetValue(int row, int column)
+       SQLEntry GetValue(int row, int column)
        {
                char* v = PQgetvalue(res, row, column);
                if (!v || PQgetisnull(res, row, column))
@@ -120,7 +123,7 @@ class PgSQLresult : public SQLResult
                return SQLEntry(std::string(v, PQgetlength(res, row, column)));
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQLEntries& result)
        {
                if (currentrow >= PQntuples(res))
                        return false;
@@ -152,12 +155,12 @@ class SQLConn : public SQLProvider, public EventHandler
        {
                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();
                ServerInstance->Modules->DelService(*this);
@@ -180,18 +183,19 @@ class SQLConn : public SQLProvider, public EventHandler
                }
        }
 
-       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 +246,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 +261,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:
@@ -350,17 +354,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 +390,7 @@ restart:
                }
        }
 
-       void submit(SQLQuery *req, const std::string& q)
+       void submit(SQLQuery *req, const std::string& q) CXX11_OVERRIDE
        {
                if (qinprog.q.empty())
                {
@@ -399,7 +403,7 @@ restart:
                }
        }
 
-       void submit(SQLQuery *req, const std::string& q, const ParamL& p)
+       void submit(SQLQuery *req, const std::string& q, const ParamL& p) CXX11_OVERRIDE
        {
                std::string res;
                unsigned int param = 0;
@@ -413,14 +417,10 @@ restart:
                                {
                                        std::string parm = p[param++];
                                        std::vector<char> buffer(parm.length() * 2 + 1);
-#ifdef PGSQL_HAS_ESCAPECONN
                                        int error;
                                        size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
                                        if (error)
-                                               ServerInstance->Logs->Log("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);
                                }
                        }
@@ -428,7 +428,7 @@ restart:
                submit(req, res);
        }
 
-       void submit(SQLQuery *req, const std::string& q, const ParamM& p)
+       void submit(SQLQuery *req, const std::string& q, const ParamM& p) CXX11_OVERRIDE
        {
                std::string res;
                for(std::string::size_type i = 0; i < q.length(); i++)
@@ -448,14 +448,10 @@ restart:
                                {
                                        std::string parm = it->second;
                                        std::vector<char> buffer(parm.length() * 2 + 1);
-#ifdef PGSQL_HAS_ESCAPECONN
                                        int error;
                                        size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
                                        if (error)
-                                               ServerInstance->Logs->Log("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);
                                }
                        }
@@ -488,7 +484,7 @@ restart:
 
        void Close()
        {
-               ServerInstance->SE->DelFd(this);
+               SocketEngine::DelFd(this);
 
                if(sql)
                {
@@ -505,25 +501,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();
        }
@@ -564,7 +552,7 @@ class ModulePgSQL : public Module
                connections.clear();
        }
 
-       void OnUnloadModule(Module* mod)
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
        {
                SQLerror err(SQL_BAD_DBID);
                for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
@@ -592,16 +580,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 +605,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..7a888ed722b77b0523ec321a44372b71d76d03b8 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,23 +60,20 @@ 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")
                        ref.regextype = std::regex::basic;
                else if(regextype == "ere")
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..c7af2c23af4ba53318b845c9b2fd13517b1d37a4 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
 {
@@ -48,16 +58,12 @@ class SQLite3Result : public SQLResult
        {
        }
 
-       ~SQLite3Result()
-       {
-       }
-
-       virtual int Rows()
+       int Rows()
        {
                return rows;
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQLEntries& result)
        {
                if (currentrow < rows)
                {
@@ -72,7 +78,7 @@ class SQLite3Result : public SQLResult
                }
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       void GetCols(std::vector<std::string>& result)
        {
                result.assign(columns.begin(), columns.end());
        }
@@ -80,7 +86,6 @@ class SQLite3Result : public SQLResult
 
 class SQLConn : public SQLProvider
 {
- private:
        sqlite3* conn;
        reference<ConfigTag> config;
 
@@ -93,7 +98,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"));
                }
        }
 
@@ -154,13 +159,13 @@ class SQLConn : public SQLProvider
                sqlite3_finalize(stmt);
        }
 
-       virtual void submit(SQLQuery* query, const std::string& q)
+       void submit(SQLQuery* query, const std::string& q)
        {
                Query(query, q);
                delete query;
        }
 
-       virtual void submit(SQLQuery* query, const std::string& q, const ParamL& p)
+       void submit(SQLQuery* query, const std::string& q, const ParamL& p)
        {
                std::string res;
                unsigned int param = 0;
@@ -181,7 +186,7 @@ class SQLConn : public SQLProvider
                submit(query, res);
        }
 
-       virtual void submit(SQLQuery* query, const std::string& q, const ParamM& p)
+       void submit(SQLQuery* query, const std::string& q, const ParamM& p)
        {
                std::string res;
                for(std::string::size_type i = 0; i < q.length(); i++)
@@ -211,23 +216,10 @@ class SQLConn : public SQLProvider
 
 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,7 +235,7 @@ class ModuleSQLite3 : public Module
                conns.clear();
        }
 
-       void ReadConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ClearConns();
                ConfigTagList tags = ServerInstance->Config->ConfTags("database");
@@ -257,12 +249,7 @@ class ModuleSQLite3 : public Module
                }
        }
 
-       void OnRehash(User* user)
-       {
-               ReadConf();
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("sqlite3 provider", VF_VENDOR);
        }
index 2f4acf3f05edbb14454642940bf70fa37ae2759a..50c847ee4f7e87c4df3208d0b20379c56a5649f4 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__ < 6
+#  pragma GCC diagnostic ignored "-pedantic"
+# else
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# 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 */
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
 
-#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
+#ifndef GNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION LIBGNUTLS_VERSION
 #endif
 
-// These don't exist in older GnuTLS versions
-#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 7))
-#define GNUTLS_NEW_PRIO_API
-#endif
+// Check if the GnuTLS library is at least version major.minor.patch
+#define INSPIRCD_GNUTLS_HAS_VERSION(major, minor, patch) (GNUTLS_VERSION_NUMBER >= ((major << 16) | (minor << 8) | patch))
 
-#if(GNUTLS_VERSION_MAJOR < 2)
-typedef gnutls_certificate_credentials_t gnutls_certificate_credentials;
-typedef gnutls_dh_params_t gnutls_dh_params;
+#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);
-
-#ifdef GNUTLS_NEW_PRIO_API
-               if (priority)
-                       gnutls_priority_deinit(priority);
+#if (!INSPIRCD_GNUTLS_HAS_VERSION(2, 0, 0))
+typedef gnutls_certificate_credentials_t gnutls_certificate_credentials;
+typedef gnutls_dh_params_t gnutls_dh_params;
 #endif
-       }
-};
 
-static reference<SSLConfig> currconf;
+enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN };
 
-static SSLConfig* GetSessionConfig(gnutls_session_t session);
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
+#define INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+#define GNUTLS_NEW_CERT_CALLBACK_API
+typedef gnutls_retr2_st cert_cb_last_param_type;
+#else
+typedef gnutls_retr_st cert_cb_last_param_type;
+#endif
 
-#if(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
+
+static Module* thismod;
 
 class RandGen : public HandlerBase2<void, char*, size_t>
 {
  public:
-       RandGen() {}
        void Call(char* buffer, size_t len)
        {
 #ifdef GNUTLS_HAS_RND
@@ -143,749 +125,670 @@ 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)
-{
-       issl_session* session = reinterpret_cast<issl_session*>(gnutls_transport_get_ptr(sess));
-       return session->config;
-}
-
-class CommandStartTLS : public SplitCommand
+namespace GnuTLS
 {
- 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 (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 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();
-
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
-               starttls.enabled = Conf->getBool("starttls", true);
+               std::vector<gnutls_x509_crt_t> certs;
 
-               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");
+                               }
 
+                               trustedca = certlist;
+                               crl = CRL;
+                       }
+               }
+       };
 
-       void On005Numeric(std::string &output)
+       class DataReader
        {
-               if (!sslports.empty())
-                       output.append(" SSL=" + sslports);
-               if (starttls.enabled)
-                       output.append(" STARTTLS");
-       }
+               int retval;
+#ifdef INSPIRCD_GNUTLS_HAS_RECV_PACKET
+               gnutls_packet_t packet;
 
-       void OnHookIO(StreamSocket* user, ListenSocket* lsb)
-       {
-               if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
+        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()];
-
-               gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
-               session->socket = user;
-               session->config = currconf;
-
-               #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);
 
-               if (me_server)
-                       gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+               void appendto(std::string& recvq)
+               {
+                       // Copy data from ReadBuffer to recvq
+                       recvq.append(buffer, retval);
+               }
+#endif
 
-               Handshake(session, user);
-       }
+               int ret() const { return retval; }
+       };
 
-       void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+       class Profile : public refcountbase
        {
-               issl_session* session = &sessions[user->GetFd()];
+               /** Name of this profile
+                */
+               const std::string name;
 
-               /* For STARTTLS: Don't try and init a session on a socket that already has a session */
-               if (session->sess)
-                       return;
+               /** X509 certificate(s) and key
+                */
+               X509Credentials x509cred;
 
-               InitSession(user, true);
-       }
+               /** The minimum length in bits for the DH prime to be accepted as a client
+                */
+               unsigned int min_dh_bits;
 
-       void OnStreamSocketConnect(StreamSocket* user)
-       {
-               InitSession(user, false);
-       }
+               /** Hashing algorithm to use when generating certificate fingerprints
+                */
+               Hash hash;
 
-       void OnStreamSocketClose(StreamSocket* user)
-       {
-               CloseSession(&sessions[user->GetFd()]);
-       }
+               /** Priorities for ciphers, compression methods, etc.
+                */
+               Priority priority;
 
-       int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
-       {
-               issl_session* session = &sessions[user->GetFd()];
+               /** Rough max size of records to send
+                */
+               const unsigned int outrecsize;
 
-               if (!session->sess)
+               /** True to request a client certificate as a server
+                */
+               const bool requestclientcert;
+
+               Profile(const std::string& profilename, const std::string& certstr, const std::string& keystr,
+                               std::auto_ptr<DHParams>& DH, unsigned int mindh, const std::string& hashstr,
+                               const std::string& priostr, std::auto_ptr<X509CertList>& CA, std::auto_ptr<X509CRL>& CRL,
+                               unsigned int recsize, bool Requestclientcert)
+                       : name(profilename)
+                       , x509cred(certstr, keystr)
+                       , min_dh_bits(mindh)
+                       , hash(hashstr)
+                       , priority(priostr)
+                       , outrecsize(recsize)
+                       , requestclientcert(Requestclientcert)
                {
-                       CloseSession(session);
-                       user->SetError("No SSL session");
-                       return -1;
+                       x509cred.SetDH(DH);
+                       x509cred.SetCA(CA, CRL);
                }
 
-               if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
+               static std::string ReadFile(const std::string& filename)
                {
-                       // The handshake isn't finished, try to finish it.
+                       FileReader reader(filename);
+                       std::string ret = reader.GetString();
+                       if (ret.empty())
+                               throw Exception("Cannot read file " + filename);
+                       return ret;
+               }
 
-                       if(!Handshake(session, user))
+               static std::string GetPrioStr(const std::string& profilename, ConfigTag* tag)
+               {
+                       // Use default priority string if this tag does not specify one
+                       std::string priostr = GnuTLS::Priority::GetDefault();
+                       bool found = tag->readString("priority", priostr);
+                       // If the prio string isn't set in the config don't be strict about the default one because it doesn't work on all versions of GnuTLS
+                       if (!tag->getBool("strictpriority", found))
                        {
-                               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:
+               static reference<Profile> Create(const std::string& profilename, ConfigTag* tag)
                {
-                       char* buffer = ServerInstance->GetReadBuffer();
-                       size_t bufsiz = ServerInstance->Config->NetBufferSize;
-                       int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
-                       if (ret > 0)
-                       {
-                               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
+                       std::string certstr = ReadFile(tag->getString("certfile", "cert.pem"));
+                       std::string keystr = ReadFile(tag->getString("keyfile", "key.pem"));
+
+                       std::auto_ptr<DHParams> dh = DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem")));
+
+                       std::string priostr = GetPrioStr(profilename, tag);
+                       unsigned int mindh = tag->getInt("mindhbits", 1024);
+                       std::string hashstr = tag->getString("hash", "md5");
+
+                       // Load trusted CA and revocation list, if set
+                       std::auto_ptr<X509CertList> ca;
+                       std::auto_ptr<X509CRL> crl;
+                       std::string filename = tag->getString("cafile");
+                       if (!filename.empty())
                        {
-                               user->SetError(gnutls_strerror(ret));
-                               CloseSession(session);
-                               return -1;
+                               ca.reset(new X509CertList(ReadFile(filename)));
+
+                               filename = tag->getString("crlfile");
+                               if (!filename.empty())
+                                       crl.reset(new X509CRL(ReadFile(filename)));
                        }
-               }
-               else if (session->status == ISSL_CLOSING)
-                       return -1;
 
-               return 0;
-       }
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+                       // If cork support is available outrecsize represents the (rough) max amount of data we give GnuTLS while corked
+                       unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512);
+#else
+                       unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512, 16384);
+#endif
 
-       int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
-       {
-               issl_session* session = &sessions[user->GetFd()];
+                       const bool requestclientcert = tag->getBool("requestclientcert", true);
 
-               if (!session->sess)
-               {
-                       CloseSession(session);
-                       user->SetError("No SSL session");
-                       return -1;
+                       return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl, outrecsize, requestclientcert);
                }
 
-               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;
+       reference<GnuTLS::Profile> profile;
+#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);
+                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 
-                       return true;
+                       return 1;
                }
        }
 
-       void OnUserConnect(LocalUser* user)
+       void VerifyCertificate()
        {
-               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());
-                       }
-               }
-       }
-
-       void CloseSession(issl_session* session)
-       {
-               if (session->sess)
-               {
-                       gnutls_bye(session->sess, GNUTLS_SHUT_WR);
-                       gnutls_deinit(session->sess);
-               }
-               session->socket = NULL;
-               session->sess = NULL;
-               session->cert = NULL;
-               session->status = ISSL_NONE;
-               session->config = NULL;
-       }
-
-       void VerifyCertificate(issl_session* session, StreamSocket* user)
-       {
-               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 +796,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 +819,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 +837,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, profile->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 +875,509 @@ 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)
        {
-               if (starttls.enabled)
-                       capHandler.HandleEvent(ev);
+               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, const reference<GnuTLS::Profile>& sslprofile)
+               : SSLIOHook(hookprov)
+               , sess(NULL)
+               , status(ISSL_NONE)
+               , profile(sslprofile)
+#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);
+               profile->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 < profile->GetOutgoingRecordSize()))
+                       {
+                               const StreamSocket::SendQueue::Element& elem = sendq.front();
+                               gbuffersize += elem.length();
+                               ret = gnutls_record_send(this->sess, elem.data(), elem.length());
+                               if (ret < 0)
+                               {
+                                       CloseSession();
+                                       return -1;
+                               }
+                               sendq.pop_front();
+                       }
+               }
+#else
+               int ret = 0;
+
+               while (!sendq.empty())
+               {
+                       FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
+                       const StreamSocket::SendQueue::Element& buffer = sendq.front();
+                       ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length()));
+
+                       if (ret <= 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() { return profile; }
+       bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st)
+{
+#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 refcountbase, public IOHookProvider
+{
+       reference<GnuTLS::Profile> profile;
+
+ public:
+       GnuTLSIOHookProvider(Module* mod, reference<GnuTLS::Profile>& prof)
+               : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+               , profile(prof)
+       {
+               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, profile);
+       }
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile);
+       }
+};
+
+class ModuleSSLGnuTLS : public Module
+{
+       typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
+
+       // First member of the class, gets constructed first and destructed last
+       GnuTLS::Init libinit;
+       RandGen randhandler;
+       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
+                       {
+                               reference<GnuTLS::Profile> profile(GnuTLS::Profile::Create(defname, tag));
+                               newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+                       }
+               }
+
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       if (tag->getString("provider") != "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<GnuTLS::Profile> profile;
+                       try
+                       {
+                               profile = GnuTLS::Profile::Create(name, tag);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+                       }
+
+                       newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
+               }
+
+               // New profiles are ok, begin using them
+               // Old profiles are deleted when their refcount drops to zero
+               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 = &randhandler;
+       }
+
+       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 = &ServerInstance->HandleGenRandom;
+       }
+
+       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
+       {
+               return Version("Provides SSL support for clients", 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..4e0032f
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * 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 : public refcountbase
+       {
+               /** Name of this profile
+                */
+               const std::string name;
+
+               X509Credentials x509cred;
+
+               /** Ciphersuites to use
+                */
+               Ciphersuites ciphersuites;
+
+               /** Curves accepted for use in ECDHE and in the peer's end-entity certificate
+                */
+               Curves curves;
+
+               Context serverctx;
+               Context clientctx;
+
+               DHParams dhparams;
+
+               X509CertList cacerts;
+
+               X509CRL crl;
+
+               /** Hashing algorithm to use when generating certificate fingerprints
+                */
+               Hash hash;
+
+               /** Rough max size of records to send
+                */
+               const unsigned int outrecsize;
+
+               Profile(const std::string& profilename, const std::string& certstr, const std::string& keystr,
+                               const std::string& dhstr, unsigned int mindh, const std::string& hashstr,
+                               const std::string& ciphersuitestr, const std::string& curvestr,
+                               const std::string& castr, const std::string& crlstr,
+                               unsigned int recsize,
+                               CTRDRBG& ctrdrbg,
+                               int minver, int maxver,
+                               bool requestclientcert
+                               )
+                       : name(profilename)
+                       , x509cred(certstr, keystr)
+                       , ciphersuites(ciphersuitestr)
+                       , curves(curvestr)
+                       , serverctx(ctrdrbg, MBEDTLS_SSL_IS_SERVER)
+                       , clientctx(ctrdrbg, MBEDTLS_SSL_IS_CLIENT)
+                       , cacerts(castr, true)
+                       , crl(crlstr)
+                       , hash(hashstr)
+                       , outrecsize(recsize)
+               {
+                       serverctx.SetX509CertAndKey(x509cred);
+                       clientctx.SetX509CertAndKey(x509cred);
+                       clientctx.SetMinDHBits(mindh);
+
+                       if (!ciphersuites.empty())
+                       {
+                               serverctx.SetCiphersuites(ciphersuites);
+                               clientctx.SetCiphersuites(ciphersuites);
+                       }
+
+                       if (!curves.empty())
+                       {
+                               serverctx.SetCurves(curves);
+                               clientctx.SetCurves(curves);
+                       }
+
+                       serverctx.SetVersion(minver, maxver);
+                       clientctx.SetVersion(minver, maxver);
+
+                       if (!dhstr.empty())
+                       {
+                               dhparams.set(dhstr);
+                               serverctx.SetDHParams(dhparams);
+                       }
+
+                       clientctx.SetOptionalVerifyCert();
+                       clientctx.SetCA(cacerts, crl);
+                       // The default for servers is to not request a client certificate from the peer
+                       if (requestclientcert)
+                       {
+                               serverctx.SetOptionalVerifyCert();
+                               serverctx.SetCA(cacerts, crl);
+                       }
+               }
+
+               static std::string ReadFile(const std::string& filename)
+               {
+                       FileReader reader(filename);
+                       std::string ret = reader.GetString();
+                       if (ret.empty())
+                               throw Exception("Cannot read file " + filename);
+                       return ret;
+               }
+
+        public:
+               static reference<Profile> Create(const std::string& profilename, ConfigTag* tag, CTRDRBG& ctr_drbg)
+               {
+                       const std::string certstr = ReadFile(tag->getString("certfile", "cert.pem"));
+                       const std::string keystr = ReadFile(tag->getString("keyfile", "key.pem"));
+                       const std::string dhstr = ReadFile(tag->getString("dhfile", "dhparams.pem"));
+
+                       const std::string ciphersuitestr = tag->getString("ciphersuites");
+                       const std::string curvestr = tag->getString("curves");
+                       unsigned int mindh = tag->getInt("mindhbits", 2048);
+                       std::string hashstr = tag->getString("hash", "sha256");
+
+                       std::string crlstr;
+                       std::string castr = tag->getString("cafile");
+                       if (!castr.empty())
+                       {
+                               castr = ReadFile(castr);
+                               crlstr = tag->getString("crlfile");
+                               if (!crlstr.empty())
+                                       crlstr = ReadFile(crlstr);
+                       }
+
+                       int minver = tag->getInt("minver");
+                       int maxver = tag->getInt("maxver");
+                       unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512, 16384);
+                       const bool requestclientcert = tag->getBool("requestclientcert", true);
+                       return new Profile(profilename, certstr, keystr, dhstr, mindh, hashstr, ciphersuitestr, curvestr, castr, crlstr, outrecsize, ctr_drbg, minver, maxver, requestclientcert);
+               }
+
+               /** Set up the given session with the settings in this profile
+                */
+               void SetupClientSession(mbedtls_ssl_context* sess)
+               {
+                       mbedtls_ssl_setup(sess, clientctx.GetConf());
+               }
+
+               void SetupServerSession(mbedtls_ssl_context* sess)
+               {
+                       mbedtls_ssl_setup(sess, serverctx.GetConf());
+               }
+
+               const std::string& GetName() const { return name; }
+               X509Credentials& GetX509Credentials() { return x509cred; }
+               unsigned int GetOutgoingRecordSize() const { return outrecsize; }
+               const Hash& GetHash() const { return hash; }
+       };
+}
+
+class mbedTLSIOHook : public SSLIOHook
+{
+       enum Status
+       {
+               ISSL_NONE,
+               ISSL_HANDSHAKING,
+               ISSL_HANDSHAKEN
+       };
+
+       mbedtls_ssl_context sess;
+       Status status;
+       reference<mbedTLS::Profile> profile;
+
+       void CloseSession()
+       {
+               if (status == ISSL_NONE)
+                       return;
+
+               mbedtls_ssl_close_notify(&sess);
+               mbedtls_ssl_free(&sess);
+               certificate = NULL;
+               status = ISSL_NONE;
+       }
+
+       // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+       int Handshake(StreamSocket* sock)
+       {
+               int ret = mbedtls_ssl_handshake(&sess);
+               if (ret == 0)
+               {
+                       // Change the seesion state
+                       this->status = ISSL_HANDSHAKEN;
+
+                       VerifyCertificate();
+
+                       // Finish writing, if any left
+                       SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+
+                       return 1;
+               }
+
+               this->status = ISSL_HANDSHAKING;
+               if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+               {
+                       SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                       return 0;
+               }
+               else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+               {
+                       SocketEngine::ChangeEventMask(sock, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+                       return 0;
+               }
+
+               sock->SetError("Handshake Failed - " + mbedTLS::ErrorToString(ret));
+               CloseSession();
+               return -1;
+       }
+
+       // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+       int PrepareIO(StreamSocket* sock)
+       {
+               if (status == ISSL_HANDSHAKEN)
+                       return 1;
+               else if (status == ISSL_HANDSHAKING)
+               {
+                       // The handshake isn't finished, try to finish it
+                       return Handshake(sock);
+               }
+
+               CloseSession();
+               sock->SetError("No SSL session");
+               return -1;
+       }
+
+       void VerifyCertificate()
+       {
+               this->certificate = new ssl_cert;
+               const mbedtls_x509_crt* const cert = mbedtls_ssl_get_peer_cert(&sess);
+               if (!cert)
+               {
+                       certificate->error = "No client certificate sent";
+                       return;
+               }
+
+               // If there is a certificate we can always generate a fingerprint
+               certificate->fingerprint = profile->GetHash().hash(cert->raw.p, cert->raw.len);
+
+               // At this point mbedTLS verified the cert already, we just need to check the results
+               const uint32_t flags = mbedtls_ssl_get_verify_result(&sess);
+               if (flags == 0xFFFFFFFF)
+               {
+                       certificate->error = "Internal error during verification";
+                       return;
+               }
+
+               if (flags == 0)
+               {
+                       // Verification succeeded
+                       certificate->trusted = true;
+               }
+               else
+               {
+                       // Verification failed
+                       certificate->trusted = false;
+                       if ((flags & MBEDTLS_X509_BADCERT_EXPIRED) || (flags & MBEDTLS_X509_BADCERT_FUTURE))
+                               certificate->error = "Not activated, or expired certificate";
+               }
+
+               certificate->unknownsigner = (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED);
+               certificate->revoked = (flags & MBEDTLS_X509_BADCERT_REVOKED);
+               certificate->invalid = ((flags & MBEDTLS_X509_BADCERT_BAD_KEY) || (flags & MBEDTLS_X509_BADCERT_BAD_MD) || (flags & MBEDTLS_X509_BADCERT_BAD_PK));
+
+               GetDNString(&cert->subject, certificate->dn);
+               GetDNString(&cert->issuer, certificate->issuer);
+       }
+
+       static void GetDNString(const mbedtls_x509_name* x509name, std::string& out)
+       {
+               char buf[512];
+               const int ret = mbedtls_x509_dn_gets(buf, sizeof(buf), x509name);
+               if (ret <= 0)
+                       return;
+
+               out.assign(buf, ret);
+       }
+
+       static int Pull(void* userptr, unsigned char* buffer, size_t size)
+       {
+               StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr);
+               if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+                       return MBEDTLS_ERR_SSL_WANT_READ;
+
+               const int ret = SocketEngine::Recv(sock, reinterpret_cast<char*>(buffer), size, 0);
+               if (ret < (int)size)
+               {
+                       SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+                       if ((ret == -1) && (SocketEngine::IgnoreError()))
+                               return MBEDTLS_ERR_SSL_WANT_READ;
+               }
+               return ret;
+       }
+
+       static int Push(void* userptr, const unsigned char* buffer, size_t size)
+       {
+               StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr);
+               if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+                       return MBEDTLS_ERR_SSL_WANT_WRITE;
+
+               const int ret = SocketEngine::Send(sock, buffer, size, 0);
+               if (ret < (int)size)
+               {
+                       SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+                       if ((ret == -1) && (SocketEngine::IgnoreError()))
+                               return MBEDTLS_ERR_SSL_WANT_WRITE;
+               }
+               return ret;
+       }
+
+ public:
+       mbedTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool isserver, mbedTLS::Profile* sslprofile)
+               : SSLIOHook(hookprov)
+               , status(ISSL_NONE)
+               , profile(sslprofile)
+       {
+               mbedtls_ssl_init(&sess);
+               if (isserver)
+                       profile->SetupServerSession(&sess);
+               else
+                       profile->SetupClientSession(&sess);
+
+               mbedtls_ssl_set_bio(&sess, reinterpret_cast<void*>(sock), Push, Pull, NULL);
+
+               sock->AddIOHook(this);
+               Handshake(sock);
+       }
+
+       void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               CloseSession();
+       }
+
+       int OnStreamSocketRead(StreamSocket* sock, std::string& recvq) CXX11_OVERRIDE
+       {
+               // Finish handshake if needed
+               int prepret = PrepareIO(sock);
+               if (prepret <= 0)
+                       return prepret;
+
+               // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
+               char* const readbuf = ServerInstance->GetReadBuffer();
+               const size_t readbufsize = ServerInstance->Config->NetBufferSize;
+               int ret = mbedtls_ssl_read(&sess, reinterpret_cast<unsigned char*>(readbuf), readbufsize);
+               if (ret > 0)
+               {
+                       recvq.append(readbuf, ret);
+
+                       // Schedule a read if there is still data in the mbedTLS buffer
+                       if (mbedtls_ssl_get_bytes_avail(&sess) > 0)
+                               SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_READ);
+                       return 1;
+               }
+               else if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+               {
+                       SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ);
+                       return 0;
+               }
+               else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+               {
+                       SocketEngine::ChangeEventMask(sock, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+                       return 0;
+               }
+               else if (ret == 0)
+               {
+                       sock->SetError("Connection closed");
+                       CloseSession();
+                       return -1;
+               }
+               else // error or MBEDTLS_ERR_SSL_CLIENT_RECONNECT which we treat as an error
+               {
+                       sock->SetError(mbedTLS::ErrorToString(ret));
+                       CloseSession();
+                       return -1;
+               }
+       }
+
+       int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
+       {
+               // Finish handshake if needed
+               int prepret = PrepareIO(sock);
+               if (prepret <= 0)
+                       return prepret;
+
+               // Session is ready for transferring application data
+               while (!sendq.empty())
+               {
+                       FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
+                       const StreamSocket::SendQueue::Element& buffer = sendq.front();
+                       int ret = mbedtls_ssl_write(&sess, reinterpret_cast<const unsigned char*>(buffer.data()), buffer.length());
+                       if (ret == (int)buffer.length())
+                       {
+                               // Wrote entire record, continue sending
+                               sendq.pop_front();
+                       }
+                       else if (ret > 0)
+                       {
+                               sendq.erase_front(ret);
+                               SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+                               return 0;
+                       }
+                       else if (ret == 0)
+                       {
+                               sock->SetError("Connection closed");
+                               CloseSession();
+                               return -1;
+                       }
+                       else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+                       {
+                               SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+                               return 0;
+                       }
+                       else if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+                       {
+                               SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ);
+                               return 0;
+                       }
+                       else
+                       {
+                               sock->SetError(mbedTLS::ErrorToString(ret));
+                               CloseSession();
+                               return -1;
+                       }
+               }
+
+               SocketEngine::ChangeEventMask(sock, FD_WANT_NO_WRITE);
+               return 1;
+       }
+
+       void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
+       {
+               if (!IsHandshakeDone())
+                       return;
+               out.append(mbedtls_ssl_get_version(&sess)).push_back('-');
+
+               // All mbedTLS ciphersuite names currently begin with "TLS-" which provides no useful information so skip it, but be prepared if it changes
+               const char* const ciphersuitestr = mbedtls_ssl_get_ciphersuite(&sess);
+               const char prefix[] = "TLS-";
+               unsigned int skip = sizeof(prefix)-1;
+               if (strncmp(ciphersuitestr, prefix, sizeof(prefix)-1))
+                       skip = 0;
+               out.append(ciphersuitestr + skip);
+       }
+
+       bool GetServerName(std::string& out) const CXX11_OVERRIDE
+       {
+               // TODO: Implement SNI support.
+               return false;
+       }
+
+       bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+class mbedTLSIOHookProvider : public refcountbase, public IOHookProvider
+{
+       reference<mbedTLS::Profile> profile;
+
+ public:
+       mbedTLSIOHookProvider(Module* mod, mbedTLS::Profile* prof)
+               : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+               , profile(prof)
+       {
+               ServerInstance->Modules->AddService(*this);
+       }
+
+       ~mbedTLSIOHookProvider()
+       {
+               ServerInstance->Modules->DelService(*this);
+       }
+
+       void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+       {
+               new mbedTLSIOHook(this, sock, true, profile);
+       }
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new mbedTLSIOHook(this, sock, false, profile);
+       }
+};
+
+class ModuleSSLmbedTLS : public Module
+{
+       typedef std::vector<reference<mbedTLSIOHookProvider> > ProfileList;
+
+       mbedTLS::Entropy entropy;
+       mbedTLS::CTRDRBG ctr_drbg;
+       ProfileList profiles;
+
+       void ReadProfiles()
+       {
+               // First, store all profiles in a new, temporary container. If no problems occur, swap the two
+               // containers; this way if something goes wrong we can go back and continue using the current profiles,
+               // avoiding unpleasant situations where no new SSL connections are possible.
+               ProfileList newprofiles;
+
+               ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+               if (tags.first == tags.second)
+               {
+                       // No <sslprofile> tags found, create a profile named "mbedtls" from settings in the <mbedtls> block
+                       const std::string defname = "mbedtls";
+                       ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <mbedtls> tag");
+
+                       try
+                       {
+                               reference<mbedTLS::Profile> profile(mbedTLS::Profile::Create(defname, tag, ctr_drbg));
+                               newprofiles.push_back(new mbedTLSIOHookProvider(this, profile));
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+                       }
+               }
+
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       if (tag->getString("provider") != "mbedtls")
+                               continue;
+
+                       std::string name = tag->getString("name");
+                       if (name.empty())
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+                               continue;
+                       }
+
+                       reference<mbedTLS::Profile> profile;
+                       try
+                       {
+                               profile = mbedTLS::Profile::Create(name, tag, ctr_drbg);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+                       }
+
+                       newprofiles.push_back(new mbedTLSIOHookProvider(this, profile));
+               }
+
+               // New profiles are ok, begin using them
+               // Old profiles are deleted when their refcount drops to zero
+               profiles.swap(newprofiles);
+       }
+
+ public:
+       void init() CXX11_OVERRIDE
+       {
+               char verbuf[16]; // Should be at least 9 bytes in size
+               mbedtls_version_get_string(verbuf);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "mbedTLS lib version %s module was compiled for " MBEDTLS_VERSION_STRING, verbuf);
+
+               if (!ctr_drbg.Seed(entropy))
+                       throw ModuleException("CTR DRBG seed failed");
+               ReadProfiles();
+       }
+
+       void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
+       {
+               if (param != "ssl")
+                       return;
+
+               try
+               {
+                       ReadProfiles();
+               }
+               catch (ModuleException& ex)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
+               }
+       }
+
+       void OnCleanup(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 aee7a5e3428a86afc8e2817c21fea3566aed3365..9b7e608a2b32fb4fe7dd55f33365d29dad09d06a 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 "ssl.h"
 
 #ifdef _WIN32
 # pragma comment(lib, "ssleay32.lib")
 # pragma comment(lib, "libeay32.lib")
-# undef MAX_DESCRIPTORS
-# define MAX_DESCRIPTORS 10000
 #endif
 
-/* $ModDesc: Provides SSL support for clients */
-
-/* $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 */
-
+#if ((OPENSSL_VERSION_NUMBER >= 0x10000000L) && (!(defined(OPENSSL_NO_ECDH))))
+// OpenSSL 0.9.8 includes some ECC support, but it's unfinished. Enable only for 1.0.0 and later.
+#define INSPIRCD_OPENSSL_ENABLE_ECDH
+#endif
 
-class ModuleSSLOpenSSL;
+// BIO is opaque in OpenSSL 1.1 but the access API does not exist in 1.0 and older.
+#if ((defined LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L))
+# define BIO_get_data(BIO) BIO->ptr
+# define BIO_set_data(BIO, VALUE) BIO->ptr = VALUE;
+# define BIO_set_init(BIO, VALUE) BIO->init = VALUE;
+#else
+# define INSPIRCD_OPENSSL_OPAQUE_BIO
+#endif
 
 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
 
 static bool SelfSigned = false;
-
-#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_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));
+                       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);
+               }
 
-               // 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);
-       }
+               ~Context()
+               {
+                       SSL_CTX_free(ctx);
+               }
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
-       void SetupECDH(ConfigTag* tag)
-       {
-               std::string curvename = tag->getString("ecdhcurve", "prime256v1");
-               if (curvename.empty())
-                       return;
+               bool SetDH(DHParams& dh)
+               {
+                       ERR_clear_error();
+                       return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
+               }
 
-               int nid = OBJ_sn2nid(curvename.c_str());
-               if (nid == 0)
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+               void SetECDH(const std::string& curvename)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unknown curve: \"%s\"", curvename.c_str());
-                       return;
+                       int nid = OBJ_sn2nid(curvename.c_str());
+                       if (nid == 0)
+                               throw Exception("Unknown curve: " + curvename);
+
+                       EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
+                       if (!eckey)
+                               throw Exception("Unable to create EC key object");
+
+                       ERR_clear_error();
+                       bool ret = (SSL_CTX_set_tmp_ecdh(ctx, eckey) >= 0);
+                       EC_KEY_free(eckey);
+                       if (!ret)
+                               throw Exception("Couldn't set ECDH parameters");
                }
+#endif
 
-               EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
-               if (!eckey)
+               bool SetCiphers(const std::string& ciphers)
                {
-                       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_cipher_list(ctx, ciphers.c_str());
                }
 
-               ERR_clear_error();
-               if (SSL_CTX_set_tmp_ecdh(ctx, eckey) < 0)
+               bool SetCerts(const std::string& filename)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set ECDH parameters");
-                       ERR_print_errors_cb(error_callback, this);
+                       ERR_clear_error();
+                       return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
                }
 
-               EC_KEY_free(eckey);
-       }
-#endif
+               bool SetPrivateKey(const std::string& filename)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
+               }
 
-#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 SetCA(const std::string& filename)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
+               }
 
-               if ((where & SSL_CB_HANDSHAKE_START) && (session.status == ISSL_OPEN))
+               long GetDefaultContextOptions() const
                {
-                       // 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);
+                       return ctx_options;
                }
-       }
 
-       bool CheckRenego(StreamSocket* sock, issl_session* session)
-       {
-               if (session->status != ISSL_NONE)
-                       return true;
+               long SetRawContextOptions(long setoptions, long clearoptions)
+               {
+                       // Clear everything
+                       SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
 
-               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
+                       // 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);
+               }
 
- public:
+               void SetVerifyCert()
+               {
+                       SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+               }
 
-       ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
-       {
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-               opensslmod = this;
-#endif
-               sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+               SSL* CreateServerSession()
+               {
+                       SSL* sess = SSL_new(ctx);
+                       SSL_set_accept_state(sess); // Act as server
+                       return sess;
+               }
 
-               /* Global SSL library initialization*/
-               SSL_library_init();
-               SSL_load_error_strings();
+               SSL* CreateClientSession()
+               {
+                       SSL* sess = SSL_new(ctx);
+                       SSL_set_connect_state(sess); // Act as client
+                       return sess;
+               }
+       };
 
-               /* Build our SSL contexts:
-                * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
+       class Profile : public refcountbase
+       {
+               /** Name of this profile
                 */
-               ctx = SSL_CTX_new( SSLv23_server_method() );
-               clictx = SSL_CTX_new( SSLv23_client_method() );
+               const std::string name;
 
-               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);
+               /** DH parameters in use
+                */
+               DHParams dh;
 
-               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);
+               /** OpenSSL makes us have two contexts, one for servers and one for clients
+                */
+               Context ctx;
+               Context clictx;
 
-               SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
-               SSL_CTX_set_session_cache_mode(clictx, SSL_SESS_CACHE_OFF);
+               /** Digest to use when generating fingerprints
+                */
+               const EVP_MD* digest;
 
-               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
+               /** Last error, set by error_callback()
+                */
+               std::string lasterr;
 
-               ctx_options = SSL_CTX_set_options(ctx, opts);
-               clictx_options = SSL_CTX_set_options(clictx, opts);
-       }
+               /** True if renegotiations are allowed, false if not
+                */
+               const bool allowrenego;
 
-       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);
-       }
+               /** Rough max size of records to send
+                */
+               const unsigned int outrecsize;
 
-       void OnHookIO(StreamSocket* user, ListenSocket* lsb)
-       {
-               if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
+               static int error_callback(const char* str, size_t len, void* u)
                {
-                       /* Hook the user with our module */
-                       user->AddIOHook(this);
+                       Profile* profile = reinterpret_cast<Profile*>(u);
+                       profile->lasterr = std::string(str, len - 1);
+                       return 0;
                }
-       }
-
-       void OnRehash(User* user)
-       {
-               sslports.clear();
-
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
 
-#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))
-               {
-                       SSL_CTX_set_info_callback(ctx, NULL);
-                       SSL_CTX_set_info_callback(clictx, NULL);
-               }
-               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");
+                       long clearoptions = tag->getInt(ctxname + "clearoptions");
+#ifdef SSL_OP_NO_COMPRESSION
+                       if (!tag->getBool("compression", false)) // Disable compression by default
+                               setoptions |= SSL_OP_NO_COMPRESSION;
 #endif
-
-               if (Conf->getBool("showports", true))
-               {
-                       sslports = Conf->getString("advertisedports");
-                       if (!sslports.empty())
-                               return;
-
-                       for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+                       if (!tag->getBool("sslv3", false)) // Disable SSLv3 by default
+                               setoptions |= SSL_OP_NO_SSLv3;
+                       if (!tag->getBool("tlsv1", true))
+                               setoptions |= SSL_OP_NO_TLSv1;
+
+                       if (!setoptions && !clearoptions)
+                               return; // Nothing to do
+
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Setting %s %s context options, default: %ld set: %ld clear: %ld", name.c_str(), ctxname.c_str(), ctx.GetDefaultContextOptions(), setoptions, clearoptions);
+                       long final = context.SetRawContextOptions(setoptions, clearoptions);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "%s %s context options: %ld", name.c_str(), ctxname.c_str(), final);
+               }
+
+        public:
+               Profile(const std::string& profilename, ConfigTag* tag)
+                       : name(profilename)
+                       , dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "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->getInt("outrecsize", 2048, 512, 16384))
+               {
+                       if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
+                               throw Exception("Couldn't set DH parameters");
+
+                       std::string hash = tag->getString("hash", "md5");
+                       digest = EVP_get_digestbyname(hash.c_str());
+                       if (digest == NULL)
+                               throw Exception("Unknown hash type " + hash);
+
+                       std::string ciphers = tag->getString("ciphers");
+                       if (!ciphers.empty())
                        {
-                               ListenSocket* port = ServerInstance->ports[i];
-                               if (port->bind_tag->getString("ssl") != "openssl")
-                                       continue;
-
-                               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());
-
-                               if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+                               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");
+#ifdef INSPIRCD_OPENSSL_ENABLE_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());
                        }
+
+                       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;
+       reference<OpenSSL::Profile> profile;
+
+       // 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, profile->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_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_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 (profile->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, const reference<OpenSSL::Profile>& sslprofile)
+               : SSLIOHook(hookprov)
+               , sess(session)
+               , status(ISSL_NONE)
+               , data_to_write(false)
+               , profile(sslprofile)
        {
-               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];
+               // Finish handshake if needed
+               int prepret = PrepareIO(user);
+               if (prepret <= 0)
+                       return prepret;
 
-               if (!session->sess)
-               {
-                       CloseSession(session);
-                       return -1;
-               }
-
-               session->data_to_write = true;
-
-               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, profile->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); }
+};
+
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
+{
+       OpenSSLIOHook* hook = static_cast<OpenSSLIOHook*>(SSL_get_ex_data(ssl, exdataindex));
+       hook->SSLInfoCallback(where, rc);
+}
+
+static int OpenSSL::BIOMethod::write(BIO* bio, const char* buffer, int size)
+{
+       BIO_clear_retry_flags(bio);
+
+       StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
+       if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+       {
+               // Writes blocked earlier, don't retry syscall
+               BIO_set_retry_write(bio);
+               return -1;
+       }
+
+       int ret = SocketEngine::Send(sock, buffer, size, 0);
+       if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+       {
+               // Blocked, set retry flag for OpenSSL
+               SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+               BIO_set_retry_write(bio);
+       }
+
+       return ret;
+}
+
+static int OpenSSL::BIOMethod::read(BIO* bio, char* buffer, int size)
+{
+       BIO_clear_retry_flags(bio);
+
+       StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
+       if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+       {
+               // Reads blocked earlier, don't retry syscall
+               BIO_set_retry_read(bio);
+               return -1;
+       }
+
+       int ret = SocketEngine::Recv(sock, buffer, size, 0);
+       if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+       {
+               // Blocked, set retry flag for OpenSSL
+               SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+               BIO_set_retry_read(bio);
+       }
+
+       return ret;
+}
+
+class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider
+{
+       reference<OpenSSL::Profile> profile;
+
+ public:
+       OpenSSLIOHookProvider(Module* mod, reference<OpenSSL::Profile>& prof)
+               : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+               , profile(prof)
+       {
+               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(), profile);
+       }
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new OpenSSLIOHook(this, sock, profile->CreateClientSession(), profile);
+       }
+};
+
+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)
-                       {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
-                               session->status = ISSL_HANDSHAKING;
-                               return true;
-                       }
-                       else if (err == SSL_ERROR_WANT_WRITE)
+                       try
                        {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
-                               session->status = ISSL_HANDSHAKING;
-                               return true;
+                               reference<OpenSSL::Profile> profile(new OpenSSL::Profile(defname, tag));
+                               newprofiles.push_back(new OpenSSLIOHookProvider(this, profile));
                        }
-                       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 (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<OpenSSL::Profile> profile;
+                       try
+                       {
+                               profile = new OpenSSL::Profile(name, tag);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+                       }
 
-                       return true;
+                       newprofiles.push_back(new OpenSSLIOHookProvider(this, profile));
                }
-               else if (ret == 0)
-               {
-                       CloseSession(session);
-               }
-               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
+               SSL_library_init();
+               SSL_load_error_strings();
+#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 "\"", SSLeay_version(SSLEAY_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_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_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 for clients", VF_VENDOR);
+       }
+};
 
 MODULE_INIT(ModuleSSLOpenSSL)
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..2a38bd3a674f16a57b2ae4f6a04f74d88731c10b 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the ability to abbreviate commands a-la BBC BASIC keywords. */
-
 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);
        }
 
-       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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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(420, "Ambiguous abbreviation and too many possible matches.");
                                        return MOD_RES_DENY;
                                }
 
@@ -79,16 +67,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(420, 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..bdb242938de2d62f1c6b7ab33b04d9570ac40324 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,25 +54,26 @@ 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()
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy");
                AllowBots = fantasy->getBool("allowbots", false);
-               std::string fpre = fantasy->getString("prefix", "!");
-               fprefix = fpre.empty() ? '!' : fpre[0];
+               fprefix = fantasy->getString("prefix", "!", 1, ServerInstance->Config->Limits.MaxLine);
 
                Aliases.clear();
                ConfigTagList tags = ServerInstance->Config->ConfTags("alias");
@@ -85,8 +81,8 @@ class ModuleAlias : public Module
                {
                        ConfigTag* tag = i->second;
                        Alias a;
-                       std::string aliastext = tag->getString("text");
-                       a.AliasedCommand = aliastext.c_str();
+                       a.AliasedCommand = tag->getString("text");
+                       std::transform(a.AliasedCommand.begin(), a.AliasedCommand.end(), a.AliasedCommand.begin(), ::toupper);
                        tag->readString("replace", a.ReplaceFormat, true);
                        a.RequiredNick = tag->getString("requires");
                        a.ULineOnly = tag->getBool("uline");
@@ -94,25 +90,16 @@ class ModuleAlias : public Module
                        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:
-
-       void init()
+       ModuleAlias()
+               : botmode(this, "bot")
        {
-               ReadAliases();
-               Implementation eventlist[] = { I_OnPreCommand, I_OnRehash, I_OnUserMessage };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual ~ModuleAlias()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides aliases of commands.", VF_VENDOR);
        }
@@ -142,10 +129,8 @@ 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)
+       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
        {
-               std::multimap<irc::string, Alias>::iterator i, upperbound;
-
                /* If theyre not registered yet, we dont want
                 * to know.
                 */
@@ -153,19 +138,16 @@ 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 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,29 +156,27 @@ 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)
+       void OnUserMessage(User *user, void *dest, int target_type, const std::string &text, char status, const CUList &exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
-               if (target_type != TYPE_CHANNEL)
+               if ((target_type != TYPE_CHANNEL) || (msgtype != 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;
                }
@@ -207,87 +187,67 @@ class ModuleAlias : public Module
                // text is like "!moo cows bite me", we want "!moo" first
                irc::spacesepstream ss(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());
+               scommand.erase(0, fprefix.size());
 
-               std::multimap<irc::string, Alias>::iterator i = Aliases.find(fcommand);
-
-               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(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, 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 an IRC operator as soon as possible.");
                                return 1;
                        }
                }
@@ -298,7 +258,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 +267,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];
@@ -329,32 +289,37 @@ class ModuleAlias : public Module
                                        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);
                        }
@@ -367,23 +332,18 @@ class ModuleAlias : public Module
                std::string command, token;
 
                ss.GetToken(command);
-               while (ss.GetToken(token) && (pars.size() <= MAXPARAMETERS))
+               while (ss.GetToken(token))
                {
                        pars.push_back(token);
                }
-               ServerInstance->Parser->CallHandler(command, pars, user);
+               ServerInstance->Parser.CallHandler(command, pars, user);
        }
 
-       virtual void OnRehash(User* user)
-       {
-               ReadAliases();
-       }
-
-       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_OnUserMessage, PRIORITY_AFTER, linkmod);
        }
 };
 
index 08a5f542aeab68ac8e36e6b469cc615e015d1989..6a4db18227def74a42c5c3c8b8f0e546acf2ed72 100644 (file)
@@ -19,8 +19,6 @@
 
 #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:
@@ -36,19 +34,12 @@ class ModuleAllowInvite : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(ni);
-               Implementation eventlist[] = { I_OnUserPreInvite, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('A');
+               tokens["EXTBAN"].push_back('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 +47,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,11 +60,7 @@ 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);
        }
index 38ae4b254f7c7853ab30d99d7c0d5975e8ffa02b..73c0fa994f0443e2819d6b7df83c063db4b24863 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)
        {
-               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;
@@ -52,7 +47,6 @@ class CommandAlltime : public Command
        }
 };
 
-
 class Modulealltime : public Module
 {
        CommandAlltime mycommand;
@@ -62,16 +56,7 @@ 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);
        }
index 2a8edb9d49f1a464eb26fe597817073b44b1ba18..7acbd2fff856a11e0ea081dbb60faa17eb9bc553 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/exemption.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)
-       {
-               levelrequired = OP_VALUE;
-       }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       AuditoriumMode(Module* Creator) : SimpleChannelModeHandler(Creator, "auditorium", 'u')
        {
-               if (channel->IsModeSet(this) == adding)
-                       return MODEACTION_DENY;
-               channel->SetMode(this, adding);
-               return MODEACTION_ALLOW;
+               ranktoset = ranktounset = OP_VALUE;
        }
 };
 
 class ModuleAuditorium : public Module
 {
- private:
+       CheckExemption::EventProvider exemptionprov;
        AuditoriumMode aum;
        bool OpsVisible;
        bool OpsCanSee;
        bool OperCanSee;
- public:
-       ModuleAuditorium() : aum(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(aum);
-
-               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()
+               : exemptionprov(this)
+               , aum(this)
        {
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("auditorium");
                OpsVisible = tag->getBool("opvisible");
@@ -78,7 +55,7 @@ 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);
        }
@@ -89,7 +66,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 +82,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(User* 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 +107,45 @@ 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 OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
        {
                BuildExcept(memb, excepts);
        }
 
-       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
        {
                BuildExcept(memb, excepts);
        }
 
-       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
        {
                BuildExcept(memb, excepts);
        }
 
-       void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
+       void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
        {
-               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,17 +153,15 @@ class ModuleAuditorium : public Module
                }
        }
 
-       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+       ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, 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;
        }
 };
 
index 0c0e8f579a1e458094b758557332904db4c4d9b3..257c3647cbe48fb578ff4958c4f21b6eacec3d45 100644 (file)
@@ -19,9 +19,7 @@
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +w channel mode, autoop list */
+#include "listmode.h"
 
 /** Handles +w channel mode
  */
@@ -30,21 +28,17 @@ class AutoOpList : public ListModeBase
  public:
        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)
@@ -53,13 +47,12 @@ class AutoOpList : public ListModeBase
                if (pos == 0 || pos == std::string::npos)
                        return adding ? MOD_RES_DENY : MOD_RES_PASSTHRU;
                unsigned int mylevel = channel->GetPrefixValue(source);
-               std::string mid = parameter.substr(0, pos);
-               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(415, mid, InspIRCd::Format("Cannot find prefix mode '%s' for autoop", mid.c_str()));
                        return MOD_RES_DENY;
                }
                else if (!mh)
@@ -68,10 +61,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,62 +74,42 @@ 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);
        }
index 1811f743de59e5dd7fc64124b1d66ba5ccb9cb32..b29b397475e5e304fb2e79b76e85e3713258a666 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 */
@@ -49,85 +46,63 @@ 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"] = "e";
        }
 
-       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[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[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);
        }
index 1b9e361bf408dc12fd0697f354323388941a3545..5202051f326cc1fd3d4f9857b9aec87b2986e29d 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)
        {
                /* 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,9 +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 :Channel ban list for %s is full (maximum entries for this channel is %ld)", source->nick.c_str(), channel->name.c_str(), channel->name.c_str(), maxbans);
+                               source->WriteNumeric(ERR_BANLISTFULL, channel->name, InspIRCd::Format("Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), maxbans));
                                return false;
                        }
 
@@ -89,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;
                                }
                        }
@@ -144,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;
                                        }
                                }
@@ -195,8 +199,7 @@ class BanRedirect : public ModeWatcher
 
                                                for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
                                                {
-                                                       /* Ugly as fuck */
-                                                       if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str())))
+                                                       if ((irc::equals(redir->targetchan, mask[CHAN])) && (irc::equals(redir->banmask, param)))
                                                        {
                                                                redirects->erase(redir);
 
@@ -224,61 +227,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)
                {
@@ -322,19 +306,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() >= atoi(destlimit.c_str())))
                                                {
-                                                       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 are 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 are banned)");
+                                                       user->WriteNumeric(470, chan->name, redir->targetchan, "You are banned from this channel, so you are automatically transferred to the redirected channel.");
                                                        nofollow = true;
-                                                       Channel::JoinUser(user, redir->targetchan.c_str(), false, "", false, ServerInstance->Time());
+                                                       Channel::JoinUser(user, redir->targetchan);
                                                        nofollow = false;
                                                        return MOD_RES_DENY;
                                                }
@@ -345,14 +329,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..1698b07
--- /dev/null
@@ -0,0 +1,987 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Most of the code in this file is taken from
+ * http://openwall.com/crypt/crypt_blowfish-1.3.tar.gz
+ */
+
+/*
+ * The crypt_blowfish homepage is:
+ *
+ *     http://www.openwall.com/crypt/
+ *
+ * This code comes from John the Ripper password cracker, with reentrant
+ * and crypt(3) interfaces added, but optimizations specific to password
+ * cracking removed.
+ *
+ * Written by Solar Designer <solar at openwall.com> in 1998-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain.  In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * It is my intent that you should be able to use this on your system,
+ * as part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose.  I would appreciate
+ * it if you give credit where it is due and keep your modifications in
+ * the public domain as well, but I don't require that in order to let
+ * you place this code and any modifications you make under a license
+ * of your choice.
+ *
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
+ * "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
+ * some of his ideas.  The password hashing algorithm was designed by David
+ * Mazieres <dm at lcs.mit.edu>.  For information on the level of
+ * compatibility for bcrypt hash prefixes other than "$2b$", please refer to
+ * the comments in BF_set_key() below and to the included crypt(3) man page.
+ *
+ * There's a paper on the algorithm that explains its design decisions:
+ *
+ *     http://www.usenix.org/events/usenix99/provos.html
+ *
+ * Some of the tricks in BF_ROUND might be inspired by Eric Young's
+ * Blowfish library (I can't be sure if I would think of something if I
+ * hadn't seen his code).
+ */
+
+#include <string.h>
+
+#ifdef __i386__
+#define BF_SCALE                       1
+#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
+#define BF_SCALE                       1
+#else
+#define BF_SCALE                       0
+#endif
+
+typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
+
+/* Number of Blowfish rounds, this is also hardcoded into a few places */
+#define BF_N                           16
+
+typedef BF_word BF_key[BF_N + 2];
+
+typedef struct {
+       BF_word S[4][0x100];
+       BF_key P;
+} BF_ctx;
+
+/*
+ * Magic IV for 64 Blowfish encryptions that we do at the end.
+ * The string is "OrpheanBeholderScryDoubt" on big-endian.
+ */
+static BF_word BF_magic_w[6] = {
+       0x4F727068, 0x65616E42, 0x65686F6C,
+       0x64657253, 0x63727944, 0x6F756274
+};
+
+/*
+ * P-box and S-box tables initialized with digits of Pi.
+ */
+static BF_ctx BF_init_state = {
+       {
+               {
+                       0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+                       0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+                       0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+                       0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+                       0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+                       0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+                       0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+                       0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+                       0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+                       0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+                       0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+                       0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+                       0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+                       0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+                       0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+                       0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+                       0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+                       0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+                       0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+                       0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+                       0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+                       0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+                       0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+                       0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+                       0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+                       0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+                       0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+                       0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+                       0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+                       0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+                       0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+                       0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+                       0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+                       0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+                       0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+                       0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+                       0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+                       0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+                       0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+                       0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+                       0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+                       0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+                       0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+                       0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+                       0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+                       0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+                       0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+                       0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+                       0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+                       0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+                       0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+                       0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+                       0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+                       0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+                       0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+                       0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+                       0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+                       0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+                       0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+                       0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+                       0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+                       0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+                       0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+                       0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+               }, {
+                       0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+                       0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+                       0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+                       0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+                       0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+                       0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+                       0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+                       0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+                       0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+                       0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+                       0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+                       0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+                       0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+                       0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+                       0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+                       0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+                       0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+                       0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+                       0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+                       0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+                       0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+                       0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+                       0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+                       0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+                       0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+                       0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+                       0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+                       0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+                       0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+                       0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+                       0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+                       0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+                       0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+                       0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+                       0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+                       0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+                       0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+                       0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+                       0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+                       0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+                       0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+                       0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+                       0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+                       0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+                       0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+                       0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+                       0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+                       0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+                       0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+                       0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+                       0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+                       0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+                       0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+                       0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+                       0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+                       0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+                       0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+                       0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+                       0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+                       0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+                       0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+                       0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+                       0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+                       0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+               }, {
+                       0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+                       0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+                       0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+                       0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+                       0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+                       0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+                       0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+                       0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+                       0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+                       0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+                       0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+                       0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+                       0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+                       0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+                       0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+                       0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+                       0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+                       0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+                       0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+                       0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+                       0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+                       0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+                       0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+                       0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+                       0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+                       0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+                       0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+                       0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+                       0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+                       0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+                       0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+                       0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+                       0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+                       0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+                       0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+                       0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+                       0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+                       0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+                       0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+                       0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+                       0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+                       0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+                       0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+                       0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+                       0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+                       0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+                       0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+                       0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+                       0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+                       0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+                       0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+                       0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+                       0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+                       0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+                       0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+                       0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+                       0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+                       0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+                       0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+                       0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+                       0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+                       0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+                       0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+                       0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+               }, {
+                       0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+                       0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+                       0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+                       0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+                       0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+                       0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+                       0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+                       0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+                       0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+                       0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+                       0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+                       0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+                       0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+                       0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+                       0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+                       0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+                       0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+                       0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+                       0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+                       0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+                       0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+                       0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+                       0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+                       0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+                       0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+                       0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+                       0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+                       0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+                       0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+                       0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+                       0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+                       0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+                       0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+                       0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+                       0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+                       0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+                       0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+                       0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+                       0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+                       0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+                       0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+                       0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+                       0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+                       0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+                       0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+                       0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+                       0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+                       0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+                       0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+                       0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+                       0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+                       0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+                       0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+                       0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+                       0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+                       0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+                       0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+                       0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+                       0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+                       0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+                       0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+                       0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+                       0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+                       0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+               }
+       }, {
+               0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+               0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+               0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+               0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+               0x9216d5d9, 0x8979fb1b
+       }
+};
+
+static unsigned char BF_itoa64[64 + 1] =
+       "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+static unsigned char BF_atoi64[0x60] = {
+       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
+       54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
+       64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
+       64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+       43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
+};
+
+#define BF_safe_atoi64(dst, src) \
+{ \
+       tmp = (unsigned char)(src); \
+       if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
+       tmp = BF_atoi64[tmp]; \
+       if (tmp > 63) return -1; \
+       (dst) = tmp; \
+}
+
+static int BF_decode(BF_word *dst, const char *src, int size)
+{
+       unsigned char *dptr = (unsigned char *)dst;
+       unsigned char *end = dptr + size;
+       const unsigned char *sptr = (const unsigned char *)src;
+       unsigned int tmp, c1, c2, c3, c4;
+
+       do {
+               BF_safe_atoi64(c1, *sptr++);
+               BF_safe_atoi64(c2, *sptr++);
+               *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+               if (dptr >= end) break;
+
+               BF_safe_atoi64(c3, *sptr++);
+               *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
+               if (dptr >= end) break;
+
+               BF_safe_atoi64(c4, *sptr++);
+               *dptr++ = ((c3 & 0x03) << 6) | c4;
+       } while (dptr < end);
+
+       return 0;
+}
+
+static void BF_encode(char *dst, const BF_word *src, int size)
+{
+       const unsigned char *sptr = (const unsigned char *)src;
+       const unsigned char *end = sptr + size;
+       unsigned char *dptr = (unsigned char *)dst;
+       unsigned int c1, c2;
+
+       do {
+               c1 = *sptr++;
+               *dptr++ = BF_itoa64[c1 >> 2];
+               c1 = (c1 & 0x03) << 4;
+               if (sptr >= end) {
+                       *dptr++ = BF_itoa64[c1];
+                       break;
+               }
+
+               c2 = *sptr++;
+               c1 |= c2 >> 4;
+               *dptr++ = BF_itoa64[c1];
+               c1 = (c2 & 0x0f) << 2;
+               if (sptr >= end) {
+                       *dptr++ = BF_itoa64[c1];
+                       break;
+               }
+
+               c2 = *sptr++;
+               c1 |= c2 >> 6;
+               *dptr++ = BF_itoa64[c1];
+               *dptr++ = BF_itoa64[c2 & 0x3f];
+       } while (sptr < end);
+}
+
+static void BF_swap(BF_word *x, int count)
+{
+       static int endianness_check = 1;
+       char *is_little_endian = (char *)&endianness_check;
+       BF_word tmp;
+
+       if (*is_little_endian)
+       do {
+               tmp = *x;
+               tmp = (tmp << 16) | (tmp >> 16);
+               *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
+       } while (--count);
+}
+
+#if BF_SCALE
+/* Architectures which can shift addresses left by 2 bits with no extra cost */
+#define BF_ROUND(L, R, N) \
+       tmp1 = L & 0xFF; \
+       tmp2 = L >> 8; \
+       tmp2 &= 0xFF; \
+       tmp3 = L >> 16; \
+       tmp3 &= 0xFF; \
+       tmp4 = L >> 24; \
+       tmp1 = data.ctx.S[3][tmp1]; \
+       tmp2 = data.ctx.S[2][tmp2]; \
+       tmp3 = data.ctx.S[1][tmp3]; \
+       tmp3 += data.ctx.S[0][tmp4]; \
+       tmp3 ^= tmp2; \
+       R ^= data.ctx.P[N + 1]; \
+       tmp3 += tmp1; \
+       R ^= tmp3;
+#else
+/* Architectures with no complicated addressing modes supported */
+#define BF_INDEX(S, i) \
+       (*((BF_word *)(((unsigned char *)S) + (i))))
+#define BF_ROUND(L, R, N) \
+       tmp1 = L & 0xFF; \
+       tmp1 <<= 2; \
+       tmp2 = L >> 6; \
+       tmp2 &= 0x3FC; \
+       tmp3 = L >> 14; \
+       tmp3 &= 0x3FC; \
+       tmp4 = L >> 22; \
+       tmp4 &= 0x3FC; \
+       tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
+       tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
+       tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
+       tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
+       tmp3 ^= tmp2; \
+       R ^= data.ctx.P[N + 1]; \
+       tmp3 += tmp1; \
+       R ^= tmp3;
+#endif
+
+/*
+ * Encrypt one block, BF_N is hardcoded here.
+ */
+#define BF_ENCRYPT \
+       L ^= data.ctx.P[0]; \
+       BF_ROUND(L, R, 0); \
+       BF_ROUND(R, L, 1); \
+       BF_ROUND(L, R, 2); \
+       BF_ROUND(R, L, 3); \
+       BF_ROUND(L, R, 4); \
+       BF_ROUND(R, L, 5); \
+       BF_ROUND(L, R, 6); \
+       BF_ROUND(R, L, 7); \
+       BF_ROUND(L, R, 8); \
+       BF_ROUND(R, L, 9); \
+       BF_ROUND(L, R, 10); \
+       BF_ROUND(R, L, 11); \
+       BF_ROUND(L, R, 12); \
+       BF_ROUND(R, L, 13); \
+       BF_ROUND(L, R, 14); \
+       BF_ROUND(R, L, 15); \
+       tmp4 = R; \
+       R = L; \
+       L = tmp4 ^ data.ctx.P[BF_N + 1];
+
+#define BF_body() \
+       L = R = 0; \
+       ptr = data.ctx.P; \
+       do { \
+               ptr += 2; \
+               BF_ENCRYPT; \
+               *(ptr - 2) = L; \
+               *(ptr - 1) = R; \
+       } while (ptr < &data.ctx.P[BF_N + 2]); \
+\
+       ptr = data.ctx.S[0]; \
+       do { \
+               ptr += 2; \
+               BF_ENCRYPT; \
+               *(ptr - 2) = L; \
+               *(ptr - 1) = R; \
+       } while (ptr < &data.ctx.S[3][0xFF]);
+
+static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
+    unsigned char flags)
+{
+       const char *ptr = key;
+       unsigned int bug, i, j;
+       BF_word safety, sign, diff, tmp[2];
+
+/*
+ * There was a sign extension bug in older revisions of this function.  While
+ * we would have liked to simply fix the bug and move on, we have to provide
+ * a backwards compatibility feature (essentially the bug) for some systems and
+ * a safety measure for some others.  The latter is needed because for certain
+ * multiple inputs to the buggy algorithm there exist easily found inputs to
+ * the correct algorithm that produce the same hash.  Thus, we optionally
+ * deviate from the correct algorithm just enough to avoid such collisions.
+ * While the bug itself affected the majority of passwords containing
+ * characters with the 8th bit set (although only a percentage of those in a
+ * collision-producing way), the anti-collision safety measure affects
+ * only a subset of passwords containing the '\xff' character (not even all of
+ * those passwords, just some of them).  This character is not found in valid
+ * UTF-8 sequences and is rarely used in popular 8-bit character encodings.
+ * Thus, the safety measure is unlikely to cause much annoyance, and is a
+ * reasonable tradeoff to use when authenticating against existing hashes that
+ * are not reliably known to have been computed with the correct algorithm.
+ *
+ * We use an approach that tries to minimize side-channel leaks of password
+ * information - that is, we mostly use fixed-cost bitwise operations instead
+ * of branches or table lookups.  (One conditional branch based on password
+ * length remains.  It is not part of the bug aftermath, though, and is
+ * difficult and possibly unreasonable to avoid given the use of C strings by
+ * the caller, which results in similar timing leaks anyway.)
+ *
+ * For actual implementation, we set an array index in the variable "bug"
+ * (0 means no bug, 1 means sign extension bug emulation) and a flag in the
+ * variable "safety" (bit 16 is set when the safety measure is requested).
+ * Valid combinations of settings are:
+ *
+ * Prefix "$2a$": bug = 0, safety = 0x10000
+ * Prefix "$2b$": bug = 0, safety = 0
+ * Prefix "$2x$": bug = 1, safety = 0
+ * Prefix "$2y$": bug = 0, safety = 0
+ */
+       bug = (unsigned int)flags & 1;
+       safety = ((BF_word)flags & 2) << 15;
+
+       sign = diff = 0;
+
+       for (i = 0; i < BF_N + 2; i++) {
+               tmp[0] = tmp[1] = 0;
+               for (j = 0; j < 4; j++) {
+                       tmp[0] <<= 8;
+                       tmp[0] |= (unsigned char)*ptr; /* correct */
+                       tmp[1] <<= 8;
+                       tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
+/*
+ * Sign extension in the first char has no effect - nothing to overwrite yet,
+ * and those extra 24 bits will be fully shifted out of the 32-bit word.  For
+ * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
+ * extension in tmp[1] occurs.  Once this flag is set, it remains set.
+ */
+                       if (j)
+                               sign |= tmp[1] & 0x80;
+                       if (!*ptr)
+                               ptr = key;
+                       else
+                               ptr++;
+               }
+               diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
+
+               expanded[i] = tmp[bug];
+               initial[i] = BF_init_state.P[i] ^ tmp[bug];
+       }
+
+/*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result.  If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm.  Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases.  Let's check for this.
+ */
+       diff |= diff >> 16; /* still zero iff exact match */
+       diff &= 0xffff; /* ditto */
+       diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
+       sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
+       sign &= ~diff & safety; /* action needed? */
+
+/*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key.  (The choice of 16 is arbitrary, but
+ * let's stick to it now.  It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop.  By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+       initial[0] ^= sign;
+}
+
+static const unsigned char flags_by_subtype[26] =
+       {2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
+
+static char *BF_crypt(const char *key, const char *setting,
+       char *output, int size,
+       BF_word min)
+{
+       struct {
+               BF_ctx ctx;
+               BF_key expanded_key;
+               union {
+                       BF_word salt[4];
+                       BF_word output[6];
+               } binary;
+       } data;
+       BF_word L, R;
+       BF_word tmp1, tmp2, tmp3, tmp4;
+       BF_word *ptr;
+       BF_word count;
+       int i;
+
+       if (size < 7 + 22 + 31 + 1) {
+               return NULL;
+       }
+
+       if (setting[0] != '$' ||
+           setting[1] != '2' ||
+           setting[2] < 'a' || setting[2] > 'z' ||
+           !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
+           setting[3] != '$' ||
+           setting[4] < '0' || setting[4] > '3' ||
+           setting[5] < '0' || setting[5] > '9' ||
+           (setting[4] == '3' && setting[5] > '1') ||
+           setting[6] != '$') {
+               return NULL;
+       }
+
+       count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
+       if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
+               return NULL;
+       }
+       BF_swap(data.binary.salt, 4);
+
+       BF_set_key(key, data.expanded_key, data.ctx.P,
+           flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
+
+       memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
+
+       L = R = 0;
+       for (i = 0; i < BF_N + 2; i += 2) {
+               L ^= data.binary.salt[i & 2];
+               R ^= data.binary.salt[(i & 2) + 1];
+               BF_ENCRYPT;
+               data.ctx.P[i] = L;
+               data.ctx.P[i + 1] = R;
+       }
+
+       ptr = data.ctx.S[0];
+       do {
+               ptr += 4;
+               L ^= data.binary.salt[(BF_N + 2) & 3];
+               R ^= data.binary.salt[(BF_N + 3) & 3];
+               BF_ENCRYPT;
+               *(ptr - 4) = L;
+               *(ptr - 3) = R;
+
+               L ^= data.binary.salt[(BF_N + 4) & 3];
+               R ^= data.binary.salt[(BF_N + 5) & 3];
+               BF_ENCRYPT;
+               *(ptr - 2) = L;
+               *(ptr - 1) = R;
+       } while (ptr < &data.ctx.S[3][0xFF]);
+
+       do {
+               int done;
+
+               for (i = 0; i < BF_N + 2; i += 2) {
+                       data.ctx.P[i] ^= data.expanded_key[i];
+                       data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
+               }
+
+               done = 0;
+               do {
+                       BF_body();
+                       if (done)
+                               break;
+                       done = 1;
+
+                       tmp1 = data.binary.salt[0];
+                       tmp2 = data.binary.salt[1];
+                       tmp3 = data.binary.salt[2];
+                       tmp4 = data.binary.salt[3];
+                       for (i = 0; i < BF_N; i += 4) {
+                               data.ctx.P[i] ^= tmp1;
+                               data.ctx.P[i + 1] ^= tmp2;
+                               data.ctx.P[i + 2] ^= tmp3;
+                               data.ctx.P[i + 3] ^= tmp4;
+                       }
+                       data.ctx.P[16] ^= tmp1;
+                       data.ctx.P[17] ^= tmp2;
+               } while (1);
+       } while (--count);
+
+       for (i = 0; i < 6; i += 2) {
+               L = BF_magic_w[i];
+               R = BF_magic_w[i + 1];
+
+               count = 64;
+               do {
+                       BF_ENCRYPT;
+               } while (--count);
+
+               data.binary.output[i] = L;
+               data.binary.output[i + 1] = R;
+       }
+
+       memcpy(output, setting, 7 + 22 - 1);
+       output[7 + 22 - 1] = BF_itoa64[(int)
+               BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
+
+/* This has to be bug-compatible with the original implementation, so
+ * only encode 23 of the 24 bytes. :-) */
+       BF_swap(data.binary.output, 6);
+       BF_encode(&output[7 + 22], data.binary.output, 23);
+       output[7 + 22 + 31] = '\0';
+
+       return output;
+}
+
+static int _crypt_output_magic(const char *setting, char *output, int size)
+{
+       if (size < 3)
+               return -1;
+
+       output[0] = '*';
+       output[1] = '0';
+       output[2] = '\0';
+
+       if (setting[0] == '*' && setting[1] == '0')
+               output[1] = '1';
+
+       return 0;
+}
+
+/*
+ * Please preserve the runtime self-test.  It serves two purposes at once:
+ *
+ * 1. We really can't afford the risk of producing incompatible hashes e.g.
+ * when there's something like gcc bug 26587 again, whereas an application or
+ * library integrating this code might not also integrate our external tests or
+ * it might not run them after every build.  Even if it does, the miscompile
+ * might only occur on the production build, but not on a testing build (such
+ * as because of different optimization settings).  It is painful to recover
+ * from incorrectly-computed hashes - merely fixing whatever broke is not
+ * enough.  Thus, a proactive measure like this self-test is needed.
+ *
+ * 2. We don't want to leave sensitive data from our actual password hash
+ * computation on the stack or in registers.  Previous revisions of the code
+ * would do explicit cleanups, but simply running the self-test after hash
+ * computation is more reliable.
+ *
+ * The performance cost of this quick self-test is around 0.6% at the "$2a$08"
+ * setting.
+ */
+static char *_crypt_blowfish_rn(const char *key, const char *setting,
+       char *output, int size)
+{
+       const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+       const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
+       static const char * const test_hashes[2] =
+               {"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
+               "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
+       const char *test_hash = test_hashes[0];
+       char *retval;
+       const char *p;
+       int ok;
+       struct {
+               char s[7 + 22 + 1];
+               char o[7 + 22 + 31 + 1 + 1 + 1];
+       } buf;
+
+/* Hash the supplied password */
+       _crypt_output_magic(setting, output, size);
+       retval = BF_crypt(key, setting, output, size, 16);
+
+/*
+ * Do a quick self-test.  It is important that we make both calls to BF_crypt()
+ * from the same scope such that they likely use the same stack locations,
+ * which makes the second call overwrite the first call's sensitive data on the
+ * stack and makes it more likely that any alignment related issues would be
+ * detected by the self-test.
+ */
+       memcpy(buf.s, test_setting, sizeof(buf.s));
+       if (retval) {
+               unsigned int flags = flags_by_subtype[
+                   (unsigned int)(unsigned char)setting[2] - 'a'];
+               test_hash = test_hashes[flags & 1];
+               buf.s[2] = setting[2];
+       }
+       memset(buf.o, 0x55, sizeof(buf.o));
+       buf.o[sizeof(buf.o) - 1] = 0;
+       p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
+
+       ok = (p == buf.o &&
+           !memcmp(p, buf.s, 7 + 22) &&
+           !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
+
+       {
+               const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
+               BF_key ae, ai, ye, yi;
+               BF_set_key(k, ae, ai, 2); /* $2a$ */
+               BF_set_key(k, ye, yi, 4); /* $2y$ */
+               ai[0] ^= 0x10000; /* undo the safety (for comparison) */
+               ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
+                   !memcmp(ae, ye, sizeof(ae)) &&
+                   !memcmp(ai, yi, sizeof(ai));
+       }
+
+       if (ok)
+               return retval;
+
+/* Should not happen */
+       _crypt_output_magic(setting, output, size);
+       /* pretend we don't support this hash type */
+       return NULL;
+}
+
+static char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
+       const char *input, int size, char *output, int output_size)
+{
+       if (size < 16 || output_size < 7 + 22 + 1 ||
+           (count && (count < 4 || count > 31)) ||
+           prefix[0] != '$' || prefix[1] != '2' ||
+           (prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
+               if (output_size > 0) output[0] = '\0';
+               return NULL;
+       }
+
+       if (!count) count = 5;
+
+       output[0] = '$';
+       output[1] = '2';
+       output[2] = prefix[2];
+       output[3] = '$';
+       output[4] = '0' + count / 10;
+       output[5] = '0' + count % 10;
+       output[6] = '$';
+
+       BF_encode(&output[7], (const BF_word *)input, 16);
+       output[7 + 22] = '\0';
+
+       return output;
+}
+
+// Start inspircd-specific code
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+class BCryptProvider : public HashProvider
+{
+ private:
+       std::string Salt()
+       {
+               char entropy[16];
+               for (unsigned int i = 0; i < sizeof(entropy); ++i)
+                       entropy[i] = ServerInstance->GenRandomInt(0xFF);
+
+               char salt[32];
+               if (!_crypt_gensalt_blowfish_rn("$2a$", rounds, entropy, sizeof(entropy), salt, sizeof(salt)))
+                       throw ModuleException("Could not generate salt - this should never happen");
+
+               return salt;
+       }
+
+ public:
+       unsigned int rounds;
+
+       std::string Generate(const std::string& data, const std::string& salt)
+       {
+               char hash[64];
+               _crypt_blowfish_rn(data.c_str(), salt.c_str(), hash, sizeof(hash));
+               return hash;
+       }
+
+       std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+       {
+               return Generate(data, Salt());
+       }
+
+       bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+       {
+               std::string ret = Generate(input, hash);
+               if (ret.empty())
+                       return false;
+
+               if (ret == hash)
+                       return true;
+               return false;
+       }
+
+       std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+       {
+               return raw;
+       }
+
+       BCryptProvider(Module* parent)
+               : HashProvider(parent, "bcrypt", 60)
+               , rounds(10)
+       {
+       }
+};
+
+class ModuleBCrypt : public Module
+{
+       BCryptProvider bcrypt;
+
+ public:
+       ModuleBCrypt() : bcrypt(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* conf = ServerInstance->Config->ConfValue("bcrypt");
+               bcrypt.rounds = conf->getInt("rounds", 10, 1);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Implements bcrypt hashing", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleBCrypt)
index be861447f36effcab132852d2f8e32836fe45387..7d97069f540f9ad2b07d7980a97fa3158c492d03 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,13 +35,13 @@ 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)
        {
        }
 };
@@ -55,46 +53,35 @@ class ModuleBlockAmsg : public Module
        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);
        }
 
-       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", -1);
                std::string act = tag->getString("action");
 
-               if(act == "notice")
+               if (act == "notice")
                        action = IBLOCK_NOTICE;
-               else if(act == "noticeopers")
+               else if (act == "noticeopers")
                        action = IBLOCK_NOTICEOPERS;
-               else if(act == "silent")
+               else if (act == "silent")
                        action = IBLOCK_SILENT;
-               else if(act == "kill")
+               else if (act == "kill")
                        action = IBLOCK_KILL;
                else
                        action = IBLOCK_KILLOPERS;
        }
 
-       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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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 != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == user->chans.size())))
                        {
                                // Block it...
-                               if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
+                               if (action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
                                        ServerInstance->SNO->WriteToSnoMask('a', "%s had an /amsg or /ame denied", user->nick.c_str());
 
-                               if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
+                               if (action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
                                        ServerInstance->Users->QuitUser(user, "Attempted to global message (/amsg or /ame)");
-                               else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
-                                       user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) denied", user->nick.c_str());
+                               else if (action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
+                                       user->WriteNotice("Global message (/amsg or /ame) denied");
 
                                return MOD_RES_DENY;
                        }
 
-                       if(m)
+                       if (m)
                        {
                                // If there's already a BlockedMessage allocated, use it.
                                m->message = parameters[1];
-                               m->target = parameters[0].c_str();
+                               m->target = parameters[0];
                                m->sent = ServerInstance->Time();
                        }
                        else
                        {
-                               m = new BlockedMessage(parameters[1], parameters[0].c_str(), ServerInstance->Time());
+                               m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time());
                                blockamsg.set(user, m);
                        }
                }
@@ -169,5 +147,4 @@ class ModuleBlockAmsg : public Module
        }
 };
 
-
 MODULE_INIT(ModuleBlockAmsg)
index 7146ee068d421b0cf83525117eaad13355cd7b09..dc0bdf974fced22bc137c3e0f1fe79bd4e2e60b1 100644 (file)
@@ -21,8 +21,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support to block all-CAPS channel messages and notices */
+#include "modules/exemption.h"
 
 
 /** Handles the +B channel mode
@@ -35,35 +34,25 @@ class BlockCaps : public SimpleChannelModeHandler
 
 class ModuleBlockCAPS : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        BlockCaps bc;
-       int percent;
+       unsigned int percent;
        unsigned int minlen;
        char capsmap[256];
-public:
-
-       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)
        {
-               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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (target_type == TYPE_CHANNEL)
                {
@@ -71,33 +60,25 @@ public:
                                return MOD_RES_PASSTHRU;
 
                        Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"blockcaps");
+                       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;
-
-                               for (std::string::iterator i = text.begin(); i != text.end(); i++)
-                               {
-                                       /* Smart fix for suggestion from Jobe, ignore CTCP ACTION (part of /ME) */
-                                       if (*actstr && *i == *actstr++ && act != -1)
-                                       {
-                                               act++;
-                                               continue;
-                                       }
-                                       else
-                                               act = -1;
+                               std::string::size_type caps = 0;
+                               unsigned int offset = 0;
+                               // Ignore the beginning of the text if it's a CTCP ACTION (/me)
+                               if (!text.compare(0, 8, "\1ACTION ", 8))
+                                       offset = 8;
 
+                               for (std::string::const_iterator i = text.begin() + offset; i != text.end(); ++i)
                                        caps += capsmap[(unsigned char)*i];
-                               }
-                               if ( ((caps*100)/(int)text.length()) >= percent )
+
+                               if (((caps * 100) / text.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 +86,18 @@ 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);
+               percent = tag->getInt("percent", 100, 1, 100);
+               minlen = tag->getInt("minlen", 1, 1, ServerInstance->Config->Limits.MaxLine);
                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()
-       {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support to block all-CAPS channel messages and notices", VF_VENDOR);
        }
index 3cc01b4c05bcc842f98f121e41da040ec5b366fa..175c3b793918b45cbc2c58629e81966f73bfb526 100644 (file)
@@ -22,8 +22,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +c to block color */
+#include "modules/exemption.h"
 
 /** Handles the +c channel mode
  */
@@ -35,36 +34,32 @@ class BlockColor : public SimpleChannelModeHandler
 
 class ModuleBlockColor : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        BlockColor bc;
  public:
 
-       ModuleBlockColor() : bc(this)
+       ModuleBlockColor()
+               : exemptionprov(this)
+               , bc(this)
        {
        }
 
-       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)
-       {
-               ServerInstance->AddExtBanChar('c');
-       }
-
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
                {
                        Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"blockcolor");
+                       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++)
                                {
@@ -76,7 +71,7 @@ class ModuleBlockColor : public Module
                                                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());
+                                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send colors to channel (+c set)");
                                                        return MOD_RES_DENY;
                                                break;
                                        }
@@ -86,16 +81,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..e0236bc17c4a8756b3a949bb537ee88b126c23df 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides user mode +B to mark the user as a bot */
-
 /** Handles user mode +B
  */
 class BotMode : public SimpleUserModeHandler
@@ -31,40 +29,28 @@ class BotMode : public SimpleUserModeHandler
        BotMode(Module* Creator) : SimpleUserModeHandler(Creator, "bot", 'B') { }
 };
 
-class ModuleBotMode : public Module
+class ModuleBotMode : public Module, public Whois::EventListener
 {
        BotMode bm;
  public:
        ModuleBotMode()
-               : bm(this)
+               : Whois::EventListener(this)
+               , bm(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(bm);
-               Implementation eventlist[] = { I_OnWhois };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleBotMode()
-       {
-       }
-
-       virtual Version GetVersion()
+       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(335, "is a bot on " + ServerInstance->Config->Network);
                }
        }
-
 };
 
-
 MODULE_INIT(ModuleBotMode)
index 2df6d7af0cdb46c9a3c42b160bbe266ce69057aa..d191a9fc7da0d2e94435577be4433790d259000a 100644 (file)
 
 #include "inspircd.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 +56,7 @@ class callerid_data
        {
                std::ostringstream oss;
                oss << lastnotify;
-               for (std::set<User*>::const_iterator i = accepting.begin(); i != accepting.end(); ++i)
+               for (UserSet::const_iterator i = accepting.begin(); i != accepting.end(); ++i)
                {
                        User* u = *i;
                        // Encode UIDs.
@@ -56,18 +69,26 @@ 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
        {
-               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)
        {
+               if (format == FORMAT_NETWORK)
+                       return;
+
                void* old = get_raw(container);
                if (old)
                        this->free(old);
@@ -81,11 +102,8 @@ struct CallerIDExtInfo : public ExtensionItem
 
                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)
                                {
@@ -112,21 +130,18 @@ struct CallerIDExtInfo : public ExtensionItem
                callerid_data* dat = static_cast<callerid_data*>(item);
 
                // We need to walk the list of users on our accept list, and remove ourselves from their wholistsme.
-               for (std::set<User *>::iterator it = dat->accepting.begin(); it != dat->accepting.end(); it++)
+               for (callerid_data::UserSet::iterator it = dat->accepting.begin(); it != dat->accepting.end(); ++it)
                {
                        callerid_data *targ = this->get(*it, false);
 
                        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;
        }
@@ -140,6 +155,28 @@ public:
 
 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;
@@ -148,42 +185,21 @@ public:
        {
                allow_empty_last_param = false;
                syntax = "*|(+|-)<nick>[,(+|-)<nick> ...]";
-               TRANSLATE2(TR_CUSTOM, TR_END);
+               TRANSLATE1(TR_CUSTOM);
        }
 
-       virtual void EncodeParameter(std::string& parameter, int index)
+       void EncodeParameter(std::string& parameter, int index)
        {
-               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.
@@ -193,54 +209,58 @@ public:
         */
        CmdResult Handle(const std::vector<std::string> &parameters, User* user)
        {
-               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)
        {
-               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 +268,10 @@ public:
                callerid_data* dat = extInfo.get(user, false);
                if (dat)
                {
-                       for (std::set<User*>::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
-                               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 +280,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 +293,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,43 +303,35 @@ 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
 {
-private:
        CommandAccept cmd;
        User_g myumode;
 
@@ -339,17 +351,13 @@ 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();
@@ -360,53 +368,39 @@ public:
        {
        }
 
-       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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implementation of callerid, usermode +g, /accept", VF_COMMON | VF_VENDOR);
        }
 
-       virtual void On005Numeric(std::string& output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output += " CALLERID=g";
+               tokens["CALLERID"] = "g";
        }
 
-       ModResult PreText(User* user, User* dest, std::string& text)
+       ModResult OnUserPreMessage(User* user, void* voiddest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
-               if (!dest->IsModeSet('g') || (user == dest))
+               if (!IS_LOCAL(user) || target_type != TYPE_USER)
                        return MOD_RES_PASSTHRU;
 
-               if (operoverride && IS_OPER(user))
+               User* dest = static_cast<User*>(voiddest);
+               if (!dest->IsModeSet(myumode) || (user == dest))
                        return MOD_RES_PASSTHRU;
 
-               callerid_data* dat = cmd.extInfo.get(dest, true);
-               std::set<User*>::iterator i = dat->accepting.find(user);
+               if (operoverride && user->IsOper())
+                       return MOD_RES_PASSTHRU;
 
-               if (i == dat->accepting.end())
+               callerid_data* dat = cmd.extInfo.get(dest, true);
+               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 umode +g. Use /ACCEPT +%s to allow.",
+                                               user->nick.c_str()));
                                dat->lastnotify = now;
                        }
                        return MOD_RES_DENY;
@@ -414,43 +408,31 @@ public:
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
-       {
-               if (IS_LOCAL(user) && target_type == TYPE_USER)
-                       return PreText(user, (User*)dest, text);
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
-       {
-               if (IS_LOCAL(user) && target_type == TYPE_USER)
-                       return PreText(user, (User*)dest, text);
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       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");
                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..86de15d9559f0dbebddfac1a78d4a73ddc776632 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 */
+namespace Cap
+{
+       class ManagerImpl;
+}
 
-/*
-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
+static Cap::ManagerImpl* managerimpl;
+
+class Cap::ManagerImpl : public Cap::Manager, public ReloadModule::EventListener
 {
- public:
-       LocalIntExt reghold;
-       CommandCAP (Module* mod) : Command(mod, "CAP", 1),
-               reghold("CAP_REGHOLD", mod)
+       /** 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;
+
+               CapModData* capmoddata = new CapModData;
+               cd.add(this, capmoddata);
 
-                       CapEvent Data(creator, user, CapEvent::CAPEVENT_REQ);
+               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;
 
-                       // tokenize the input into a nice list of requested caps
-                       std::string cap_;
-                       irc::spacesepstream cap_stream(parameters[1]);
+                       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;
 
-                       reghold.set(user, 1);
-                       Data.Send();
+               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;
 
-                       std::string Result;
-                       if (Data.wanted.size() > 0)
-                               Result = irc::stringjoiner(" ", Data.wanted, 0, Data.wanted.size() - 1).GetJoined();
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
 
-                       user->WriteServ("CAP %s %s :%s", user->nick.c_str(), subcommand.c_str(), Result.c_str());
+               // Fire the event first so modules can still see who is using the cap which is being unregistered
+               FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, false));
+
+               // Turn off the cap for all users
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+               {
+                       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)
 {
-       CommandCAP cmd;
+}
+
+std::string Cap::ExtItem::serialize(SerializeFormat format, const Extensible* container, void* item) const
+{
+       std::string ret;
+       // XXX: Cast away the const because IS_LOCAL() doesn't handle it
+       LocalUser* user = IS_LOCAL(const_cast<User*>(static_cast<const User*>(container)));
+       if ((format == FORMAT_NETWORK) || (!user))
+               return ret;
+
+       // List requested caps
+       managerimpl->HandleList(ret, user, false, false);
+
+       // Serialize cap protocol version. If building a human-readable string append a new token, otherwise append only a single character indicating the version.
+       Protocol protocol = managerimpl->GetProtocol(user);
+       if (format == FORMAT_USER)
+               ret.append("capversion=3.");
+       else if (!ret.empty())
+               ret.erase(ret.length()-1);
+
+       if (protocol == CAP_302)
+               ret.push_back('2');
+       else
+               ret.push_back('1');
+
+       return ret;
+}
+
+void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+       if (format == FORMAT_NETWORK)
+               return;
+
+       LocalUser* user = IS_LOCAL(static_cast<User*>(container));
+       if (!user)
+               return; // Can't happen
+
+       // Process the cap protocol version which is a single character at the end of the serialized string
+       const char verchar = *value.rbegin();
+       if (verchar == '2')
+               managerimpl->Set302Protocol(user);
+
+       // Remove the version indicator from the string passed to HandleReq
+       std::string caplist(value, 0, value.size()-1);
+       managerimpl->HandleReq(user, caplist);
+}
+
+class CommandCap : public SplitCommand
+{
+       Events::ModuleEventProvider evprov;
+       Cap::ManagerImpl manager;
+
+       static void DisplayResult(LocalUser* user, std::string& result)
+       {
+               if (*result.rbegin() == ' ')
+                       result.erase(result.end()-1);
+               user->WriteCommand("CAP", result);
+       }
+
  public:
-       ModuleCAP()
-               : cmd(this)
+       LocalIntExt holdext;
+
+       CommandCap(Module* mod)
+               : SplitCommand(mod, "CAP", 1)
+               , evprov(mod, "event/cap")
+               , manager(mod, evprov)
+               , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
        {
+               works_before_reg = true;
        }
 
-       void init()
+       CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(cmd.reghold);
+               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;
+
+                       std::string result = (manager.HandleReq(user, parameters[1]) ? "ACK :" : "NAK :");
+                       result.append(parameters[1]);
+                       user->WriteCommand("CAP", result);
+               }
+               else if (subcommand == "END")
+               {
+                       holdext.unset(user);
+               }
+               else if ((subcommand == "LS") || (subcommand == "LIST"))
+               {
+                       const bool is_ls = (subcommand.length() == 2);
+                       if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
+                               manager.Set302Protocol(user);
 
-               Implementation eventlist[] = { I_OnCheckReady };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+                       std::string result = subcommand + " :";
+                       // Show values only if supports v3.2 and doing LS
+                       manager.HandleList(result, user, is_ls, ((is_ls) && (manager.GetProtocol(user) != Cap::CAP_LEGACY)));
+                       DisplayResult(user, result);
+               }
+               else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
+               {
+                       std::string result = "ACK :";
+                       manager.HandleClear(user, result);
+                       DisplayResult(user, result);
+               }
+               else
+               {
+                       user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, subcommand.empty() ? "*" : subcommand, "Invalid CAP subcommand");
+                       return CMD_FAILURE;
+               }
+
+               return CMD_SUCCESS;
        }
+};
 
-       ModResult OnCheckReady(LocalUser* user)
-       {
-               /* Users in CAP state get held until CAP END */
-               if (cmd.reghold.get(user))
-                       return MOD_RES_DENY;
+class ModuleCap : public Module
+{
+       CommandCap cmd;
 
-               return MOD_RES_PASSTHRU;
+ public:
+       ModuleCap()
+               : cmd(this)
+       {
        }
 
-       ~ModuleCAP()
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
+               return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Client CAP extension support", VF_VENDOR);
+               return Version("Provides support for CAP capability negotiation", VF_VENDOR);
        }
 };
 
-MODULE_INIT(ModuleCAP)
-
+MODULE_INIT(ModuleCap)
diff --git a/src/modules/m_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..42cff2850c0be3b2cba8ecfb5b993591ba9b4f95 100644 (file)
 #include "inspircd.h"
 #include "xline.h"
 
-/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */
-
 /** Holds a CBAN item
  */
 class CBan : public XLine
 {
-public:
-       irc::string matchtext;
+private:
+       std::string matchtext;
 
+public:
        CBan(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& ch)
                : XLine(s_time, d, src, re, "CBAN")
-       {
-               this->matchtext = ch.c_str();
-       }
-
-       ~CBan()
+               , matchtext(ch)
        {
        }
 
@@ -50,20 +45,12 @@ public:
 
        bool Matches(const std::string &s)
        {
-               if (matchtext == s)
-                       return true;
-               return false;
-       }
-
-       void DisplayExpiry()
-       {
-               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));
+               return irc::equals(matchtext, s);
        }
 
-       const char* Displayable()
+       const std::string& Displayable()
        {
-               return matchtext.c_str();
+               return matchtext;
        }
 };
 
@@ -95,7 +82,6 @@ class CommandCBan : public Command
        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);
        }
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *user)
@@ -111,14 +97,14 @@ class CommandCBan : public Command
                        }
                        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 in list, try /stats C.");
                                return CMD_FAILURE;
                        }
                }
                else
                {
                        // Adding - XXX todo make this respect <insane> tag perhaps..
-                       long duration = ServerInstance->Duration(parameters[1]);
+                       unsigned long duration = InspIRCd::Duration(parameters[1]);
                        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());
 
@@ -131,14 +117,14 @@ class CommandCBan : public Command
                                else
                                {
                                        time_t c_requires_crap = duration + ServerInstance->Time();
-                                       std::string timestr = ServerInstance->TimeString(c_requires_crap);
+                                       std::string timestr = InspIRCd::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);
                                }
                        }
                        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;
                        }
                }
@@ -164,51 +150,46 @@ class ModuleCBan : public Module
        {
        }
 
-       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(384, cname, InspIRCd::Format("Cannot join channel, CBANed (%s)", rl->reason.c_str()));
                        ServerInstance->SNO->WriteGlobalSno('a', "%s tried to join %s which is CBANed (%s)",
-                                user->nick.c_str(), cname, 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);
        }
 };
 
 MODULE_INIT(ModuleCBan)
-
index 65b965df288f8acac65d152f05cded53d3a9eb86..a2f67177291764590e7904e22b008949344d3f91 100644 (file)
  */
 
 
-/* $ModDesc: Provides user and channel +G mode */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
 #include "inspircd.h"
-#include <iostream>
+#include "modules/exemption.h"
 
-typedef std::map<irc::string,irc::string> censor_t;
+typedef insp::flat_map<irc::string, irc::string> censor_t;
 
 /** Handles usermode +G
  */
@@ -48,31 +43,21 @@ class CensorChannel : public SimpleChannelModeHandler
 
 class ModuleCensor : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        censor_t censors;
        CensorUser cu;
        CensorChannel 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)
+               , cc(this)
        {
        }
 
        // 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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
@@ -80,12 +65,12 @@ class ModuleCensor : public Module
                bool active = false;
 
                if (target_type == TYPE_USER)
-                       active = ((User*)dest)->IsModeSet('G');
+                       active = ((User*)dest)->IsModeSet(cu);
                else if (target_type == TYPE_CHANNEL)
                {
-                       active = ((Channel*)dest)->IsModeSet('G');
                        Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"censor");
+                       active = c->IsModeSet(cc);
+                       ModResult res = CheckExemption::Call(exemptionprov, user, c, "censor");
 
                        if (res == MOD_RES_ALLOW)
                                return MOD_RES_PASSTHRU;
@@ -101,23 +86,18 @@ class ModuleCensor : public Module
                        {
                                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(ERR_WORDFILTERED, ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name : ((User*)dest)->nick), index->first.c_str(), "Your message contained a censored word, and was blocked");
                                        return MOD_RES_DENY;
                                }
 
-                               SearchAndReplace(text2, index->first, index->second);
+                               stdalgo::string::replace_all(text2, index->first, index->second);
                        }
                }
                text = text2.c_str();
                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
@@ -136,7 +116,7 @@ class ModuleCensor : public Module
                }
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides user and channel +G mode",VF_VENDOR);
        }
index 9e1a546d6ae41ffe89b6a766b1f3051282ac00a2..4c57c6fdff4260b3ee4e7913a54125b82a12b39f 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
+#include "modules/dns.h"
+#include "modules/ssl.h"
 
-/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
-
-enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
+enum
+{
+       RPL_WHOISGATEWAY = 350
+};
 
+// We need this method up here so that it can be accessed from anywhere
+static void ChangeIP(User* user, const std::string& newip)
+{
+       ServerInstance->Users->RemoveCloneCounts(user);
+       user->SetClientIP(newip);
+       ServerInstance->Users->AddClone(user);
+}
 
-/** Holds a CGI site's details
- */
-class CGIhost
+// Encapsulates information about a WebIRC host.
+class WebIRCHost
 {
-public:
+ private:
        std::string hostmask;
-       CGItype type;
+       std::string fingerprint;
        std::string password;
+       std::string passhash;
 
-       CGIhost(const std::string &mask, CGItype t, const std::string &spassword)
-       : hostmask(mask), type(t), password(spassword)
+ 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)
        {
        }
+
+       bool Matches(LocalUser* user, const std::string& pass) 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 = SSLClientCert::GetFingerprint(&user->eh);
+               if (!fingerprint.empty() && 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)
-               {
-                       if(user->registered == REG_ALL)
-                               return CMD_FAILURE;
 
-                       irc::sockets::sockaddrs ipaddr;
-                       if (!irc::sockets::aptosa(parameters[3], 0, ipaddr))
-                       {
-                               IS_LOCAL(user)->CommandFloodPenalty += 5000;
-                               ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC but gave an invalid IP address.", user->GetFullRealHost().c_str());
-                               return CMD_FAILURE;
-                       }
+       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)
+       {
+               allow_empty_last_param = false;
+               works_before_reg = true;
+               this->syntax = "password gateway hostname ip";
+       }
 
-                       for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
-                       {
-                               if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
-                               {
-                                       if(iter->type == WEBIRC && parameters[0] == iter->password)
-                                       {
-                                               realhost.set(user, user->host);
-                                               realip.set(user, user->GetIPString());
-
-                                               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());
-
-                                               // Check if we're happy with the provided hostname. If it's problematic then make sure we won't set a host later, just the IP
-                                               if (host_ok)
-                                                       webirc_hostname.set(user, parameters[2]);
-                                               else
-                                                       webirc_hostname.unset(user);
-
-                                               webirc_ip.set(user, parameters[3]);
-                                               return CMD_SUCCESS;
-                                       }
-                               }
-                       }
+       CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
+       {
+               if (user->registered == REG_ALL)
+                       return CMD_FAILURE;
 
-                       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());
+               irc::sockets::sockaddrs ipaddr;
+               if (!irc::sockets::aptosa(parameters[3], 0, ipaddr))
+               {
+                       user->CommandFloodPenalty += 5000;
+                       ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC but gave an invalid IP address.", user->GetFullRealHost().c_str());
                        return CMD_FAILURE;
                }
-};
 
+               for (std::vector<WebIRCHost>::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, parameters[0]))
+                               continue;
 
-/** 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)
-       {
-       }
+                       // The user matched a WebIRC block!
+                       gateway.set(user, parameters[1]);
+                       realhost.set(user, user->GetRealHost());
+                       realip.set(user, user->GetIPString());
 
-       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);
-               }
-       }
+                               ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s is using a WebIRC gateway; changing their IP from %s to %s.",
+                                       user->nick.c_str(), user->GetIPString().c_str(), parameters[3].c_str());
 
-       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());
+                       // Set the IP address sent via WEBIRC. We ignore the hostname and lookup
+                       // instead do our own DNS lookups because of unreliable gateways.
+                       ChangeIP(user, parameters[3]);
+                       return CMD_SUCCESS;
                }
-       }
 
-       virtual ~CGIResolver()
-       {
-               User* them = ServerInstance->FindUUID(theiruid);
-               if (!them)
-                       return;
-               int count = waiting.get(them);
-               if (count)
-                       waiting.set(them, count - 1);
+               user->CommandFloodPenalty += 5000;
+               ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s tried to use WEBIRC but didn't match any configured WebIRC hosts.", user->GetFullRealHost().c_str());
+               return CMD_FAILURE;
        }
 };
 
-class ModuleCgiIRC : public Module
+class ModuleCgiIRC : public Module, public Whois::EventListener
 {
-       CommandWebirc cmd;
-       LocalIntExt waiting;
+       CommandWebIRC cmd;
+       std::vector<std::string> hosts;
 
        static void RecheckClass(LocalUser* user)
        {
@@ -193,119 +161,84 @@ class ModuleCgiIRC : public Module
                user->CheckClass();
        }
 
-       static void ChangeIP(LocalUser* user, const std::string& newip)
+       void HandleIdent(LocalUser* user, const std::string& newip)
        {
-               ServerInstance->Users->RemoveCloneCounts(user);
-               user->SetClientIP(newip.c_str());
-               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.realhost.set(user, user->GetRealHost());
                cmd.realip.set(user, user->GetIPString());
+
+               if (cmd.notify)
+                       ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s is using an ident gateway; changing their IP from %s to %s.",
+                               user->nick.c_str(), user->GetIPString().c_str(), newip.c_str());
+
                ChangeIP(user, newip);
-               user->host = user->dhost = user->GetIPString();
-               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
-               {
-                       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);
-               }
-               catch (...)
-               {
-                       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());
-               }
        }
 
 public:
-       ModuleCgiIRC() : cmd(this), waiting("cgiirc-delay", this)
+       ModuleCgiIRC()
+               : 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<std::string> 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());
-                                       }
-
-                                       cmd.Hosts.push_back(CGIhost(hostmask, cgitype, password));
-                               }
+                               // The IP address should be looked up from the hex IP address.
+                               identhosts.push_back(mask);
+                       }
+                       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");
+
+                               // 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 OnCheckReady(LocalUser *user) CXX11_OVERRIDE
        {
-               if (waiting.get(user))
-                       return MOD_RES_DENY;
-
-               std::string *webirc_ip = cmd.webirc_ip.get(user);
-               if (!webirc_ip)
+               if (!cmd.realip.get(user))
                        return MOD_RES_PASSTHRU;
 
-               ChangeIP(user, *webirc_ip);
-
-               std::string* webirc_hostname = cmd.webirc_hostname.get(user);
-               user->host = user->dhost = (webirc_hostname ? *webirc_hostname : user->GetIPString());
-               user->InvalidateCache();
-
                RecheckClass(user);
                if (user->quitting)
                        return MOD_RES_DENY;
@@ -314,61 +247,57 @@ public:
                if (user->quitting)
                        return MOD_RES_DENY;
 
-               cmd.webirc_hostname.unset(user);
-               cmd.webirc_ip.unset(user);
-
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
-               for(CGIHostlist::iterator iter = cmd.Hosts.begin(); iter != cmd.Hosts.end(); iter++)
+               // 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;
+
+               // 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;
+       }
+
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
+       {
+               for (std::vector<std::string>::const_iterator iter = hosts.begin(); iter != hosts.end(); ++iter)
                {
-                       if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
-                       {
-                               // 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)
-                               {
-                                       // We don't need to do anything here
-                               }
-                               return MOD_RES_PASSTHRU;
-                       }
+                       if (!InspIRCd::Match(user->GetRealHost(), *iter, ascii_case_insensitive_map) && !InspIRCd::MatchCIDR(user->GetIPString(), *iter, ascii_case_insensitive_map))
+                               continue;
+
+                       CheckIdent(user); // Nothing on failure.
+                       user->CheckLines(true);
+                       break;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       bool CheckPass(LocalUser* user)
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               if(IsValidHost(user->password))
-               {
-                       HandleIdentOrPass(user, user->password, true);
-                       user->password.clear();
-                       return true;
-               }
+               if (!whois.IsSelfWhois() && !whois.GetSource()->HasPrivPermission("users/auspex"))
+                       return;
 
-               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;
+
+               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");
        }
 
        bool CheckIdent(LocalUser* user)
@@ -391,37 +320,15 @@ public:
                std::string newipstr(inet_ntoa(newip));
 
                user->ident = "~cgiirc";
-               HandleIdentOrPass(user, newipstr, false);
+               HandleIdent(user, newipstr);
 
                return true;
        }
 
-       bool IsValidHost(const std::string &host)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               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())) )
-
-                               continue;
-                       else
-                               return false;
-               }
-
-               return true;
-       }
-
-       virtual Version GetVersion()
-       {
-               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..6cf4af2352b3a91fb84a1eec15eb18a7eb2f21bf 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);
        }
 
-
-       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..90e266fc3d02fe876a5c99e40f37d27b361b6483 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
  */
@@ -38,82 +34,76 @@ class ChanFilter : public ListModeBase
  public:
        ChanFilter(Module* Creator) : ListModeBase(Creator, "filter", 'g', "End of channel spamfilter list", 941, 940, false, "chanfilter") { }
 
-       virtual bool ValidateParam(User* user, Channel* chan, std::string &word)
+       bool ValidateParam(User* user, Channel* chan, std::string &word)
        {
-               if ((word.length() > 35) || (word.empty()))
+               if (word.length() > 35)
                {
-                       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(935, chan->name, word, "%word is too long for censor list");
                        return false;
                }
 
                return true;
        }
 
-       virtual bool TellListTooLong(User* user, Channel* chan, std::string &word)
+       void 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;
+               user->WriteNumeric(939, chan->name, word, "Channel spamfilter list is full");
        }
 
-       virtual void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
+       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());
+               user->WriteNumeric(937, chan->name, InspIRCd::Format("The word %s is already on the spamfilter list", word.c_str()));
        }
 
-       virtual void TellNotSet(User* user, Channel* chan, std::string &word)
+       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());
+               user->WriteNumeric(938, chan->name, "No such spamfilter word is set");
        }
 };
 
 class ModuleChanFilter : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        ChanFilter cf;
        bool hidemask;
 
  public:
 
        ModuleChanFilter()
-               : cf(this)
+               : exemptionprov(this)
+               , cf(this)
        {
        }
 
-       void init()
-       {
-               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)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                hidemask = ServerInstance->Config->ConfValue("chanfilter")->getBool("hidemask");
                cf.DoRehash();
        }
 
-       virtual ModResult ProcessMessages(User* user,Channel* chan,std::string &text)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
-               ModResult res = ServerInstance->OnCheckExemption(user,chan,"filter");
+               if (target_type != TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
+
+               Channel* chan = static_cast<Channel*>(dest);
+               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 (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, i->mask, "Cannot send to channel (your message contained a censored word)");
                                        return MOD_RES_DENY;
                                }
                        }
@@ -122,33 +112,10 @@ 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)
-       {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               cf.DoSyncChannel(chan, proto, opaque);
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel-specific censor lists (like mode +G but varies from channel to channel)", VF_VENDOR);
        }
-
-       virtual ~ModuleChanFilter()
-       {
-       }
 };
 
 MODULE_INIT(ModuleChanFilter)
index e48e67fe5557ffb601425de6dde5ceba787e3b7b..a0929a0d01c4c6f84e7f50a8b7653bf101bef643 100644 (file)
@@ -19,8 +19,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel history for a given number of lines */
-
 struct HistoryItem
 {
        time_t ts;
@@ -32,10 +30,13 @@ 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)
        {
@@ -52,110 +53,98 @@ class HistoryMode : public ModeHandler
        }
 
  public:
-       SimpleExtItem<HistoryList> ext;
        unsigned int maxlines;
-       HistoryMode(Module* Creator) : ModeHandler(Creator, "history", 'H', PARAM_SETONLY, MODETYPE_CHANNEL),
-               ext("history", Creator) { }
+       HistoryMode(Module* Creator)
+               : ParamMode<HistoryMode, SimpleExtItem<HistoryList> >(Creator, "history", 'H')
+       {
+       }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
        {
-               if (adding)
+               std::string::size_type colon = parameter.find(':');
+               if (colon == std::string::npos)
+                       return MODEACTION_DENY;
+
+               std::string duration(parameter, colon+1);
+               if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
+                       return MODEACTION_DENY;
+
+               unsigned int len = ConvToInt(parameter.substr(0, colon));
+               int time = InspIRCd::Duration(duration);
+               if (len == 0 || time < 0)
+                       return MODEACTION_DENY;
+               if (len > maxlines && IS_LOCAL(source))
+                       return MODEACTION_DENY;
+               if (len > maxlines)
+                       len = maxlines;
+
+               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
 {
        HistoryMode m;
        bool sendnotice;
+       UserModeReference botmode;
+       bool dobots;
  public:
-       ModuleChanHistory() : m(this)
-       {
-       }
-
-       void init()
+       ModuleChanHistory() : m(this), botmode(this, "bot")
        {
-               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);
                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&)
+       void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList&, MessageType msgtype) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL && status == 0)
+               if ((target_type == TYPE_CHANNEL) && (status == 0) && (msgtype == MSG_PRIVMSG))
                {
                        Channel* c = (Channel*)dest;
                        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));
+                               const std::string line = ":" + user->GetFullHost() + " PRIVMSG " + c->name + " :" + text;
+                               list->lines.push_back(HistoryItem(line));
                                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))
                        return;
 
+               if (memb->user->IsModeSet(botmode) && !dobots)
+                       return;
+
                HistoryList* list = m.ext.get(memb->chan);
                if (!list)
                        return;
@@ -165,8 +154,7 @@ class ModuleChanHistory : public Module
 
                if (sendnotice)
                {
-                       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);
+                       memb->user->WriteNotice("Replaying up to " + ConvToStr(list->maxlen) + " lines of pre-join history spanning up to " + ConvToStr(list->maxtime) + " seconds");
                }
 
                for(std::deque<HistoryItem>::iterator i = list->lines.begin(); i != list->lines.end(); ++i)
@@ -176,7 +164,7 @@ class ModuleChanHistory : public Module
                }
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel history replayed on join", VF_VENDOR);
        }
index 6dbc0e7a860418d1154d36b00646d5bfa38040f4..f618a539cc098e920acd51042b1d1fca02ad4595 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;
@@ -59,100 +44,44 @@ class ModuleChanLog : public Module
 
                        if (channel.empty() || snomasks.empty())
                        {
-                               ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Malformed chanlog tag, ignoring");
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Malformed chanlog tag, ignoring");
                                continue;
                        }
 
                        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());
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Logging %c to %s", *it, channel.c_str());
                        }
                }
 
        }
 
-       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 = "\2" + desc + "\2: " + 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);
+                               c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), snotice.c_str());
+                               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);
        }
 };
 
-
 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..7513cb33ab591b30fe77cb7670c91edb9cd568e5 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 HandlerBase1<bool, const std::string&>
 {
  public:
-       NewIsChannelHandler() { }
-       virtual ~NewIsChannelHandler() { }
-       virtual bool Call(const char*, size_t);
+       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;
+       caller1<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);
        }
 
        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 +99,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 +129,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..ffb43eef1a514cedf57ce38dbf6eac25ad5c151f 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b j: - matching channel bans */
-
 class ModuleBadChannelExtban : public Module
 {
- private:
  public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ~ModuleBadChannelExtban()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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..b442dea9cf6c6b6f55e4f93bb87f8f08e8c30b01 100644 (file)
  */
 
 
-/* $ModDesc: Provides the /CHECK command to retrieve information on a user, channel, hostname or IP address */
-
 #include "inspircd.h"
+#include "listmode.h"
+
+enum
+{
+       RPL_CHECK = 802
+};
+
+class CheckContext
+{
+       User* const user;
+       const std::string& target;
+
+ public:
+       CheckContext(User* u, const std::string& targetstr)
+               : user(u)
+               , target(targetstr)
+       {
+               Write("START", target);
+       }
+
+       ~CheckContext()
+       {
+               Write("END", target);
+       }
+
+       void Write(const std::string& type, const std::string& text)
+       {
+               user->WriteRemoteNumeric(RPL_CHECK, type, text);
+       }
+
+       User* GetUser() const { return user; }
+
+       void DumpListMode(const ListModeBase::ModeList* list)
+       {
+               if (!list)
+                       return;
+
+               CheckContext::List modelist(*this, "modelist");
+               for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+                       modelist.Add(i->mask);
+
+               modelist.Flush();
+       }
+
+       void DumpExt(Extensible* ext)
+       {
+               CheckContext::List extlist(*this, "metadata");
+               for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); ++i)
+               {
+                       ExtensionItem* item = i->first;
+                       std::string value = item->serialize(FORMAT_USER, ext, i->second);
+                       if (!value.empty())
+                               Write("meta:" + item->name, value);
+                       else if (!item->name.empty())
+                               extlist.Add(item->name);
+               }
+
+               extlist.Flush();
+       }
+
+       class List : public Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>
+       {
+        public:
+               List(CheckContext& context, const char* checktype)
+                       : Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>(Numeric::WriteRemoteNumericSink(context.GetUser()), RPL_CHECK, false, (IS_LOCAL(context.GetUser()) ? context.GetUser()->nick.length() : ServerInstance->Config->Limits.NickMax) + strlen(checktype) + 1)
+               {
+                       GetNumeric().push(checktype).push(std::string());
+               }
+       };
+};
 
 /** Handle /CHECK
  */
 class CommandCheck : public Command
 {
+       UserModeReference snomaskmode;
+
+       std::string GetSnomasks(User* user)
+       {
+               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)
+       CommandCheck(Module* parent)
+               : Command(parent,"CHECK", 1)
+               , snomaskmode(parent, "snomask")
        {
                flags_needed = 'o'; syntax = "<nickname>|<ip>|<hostmask>|<channel> <server>";
        }
@@ -44,176 +140,139 @@ class CommandCheck : public Command
                return ret;
        }
 
-       void dumpExt(User* user, const std::string& checkstr, Extensible* ext)
-       {
-               std::stringstream dumpkeys;
-               for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); i++)
-               {
-                       ExtensionItem* item = i->first;
-                       std::string value = item->serialize(FORMAT_USER, ext, i->second);
-                       if (!value.empty())
-                               user->SendText(checkstr + " meta:" + item->name + " " + value);
-                       else if (!item->name.empty())
-                               dumpkeys << " " << item->name;
-               }
-               if (!dumpkeys.str().empty())
-                       user->SendText(checkstr + " metadata", dumpkeys);
-       }
-
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
-               if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName.c_str())
+               if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
                        return CMD_SUCCESS;
 
                User *targuser;
                Channel *targchan;
-               std::string checkstr;
                std::string chliststr;
 
-               checkstr = ":" + ServerInstance->Config->ServerName + " 304 " + user->nick + " :CHECK";
-
                targuser = ServerInstance->FindNick(parameters[0]);
                targchan = ServerInstance->FindChan(parameters[0]);
 
                /*
                 * Syntax of a /check reply:
-                *  :server.name 304 target :CHECK START <target>
-                *  :server.name 304 target :CHECK <field> <value>
-                *  :server.name 304 target :CHECK END
+                *  :server.name 802 target START <target>
+                *  :server.name 802 target <field> :<value>
+                *  :server.name 802 target END <target>
                 */
 
-               user->SendText(checkstr + " START " + parameters[0]);
+               // Constructor sends START, destructor sends END
+               CheckContext context(user, parameters[0]);
 
                if (targuser)
                {
                        LocalUser* loctarg = IS_LOCAL(targuser);
                        /* /check on a user */
-                       user->SendText(checkstr + " nuh " + targuser->GetFullHost());
-                       user->SendText(checkstr + " realnuh " + targuser->GetFullRealHost());
-                       user->SendText(checkstr + " realname " + targuser->fullname);
-                       user->SendText(checkstr + " modes +" + targuser->FormatModes());
-                       user->SendText(checkstr + " snomasks +" + 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->fullname);
+                       context.Write("modes", targuser->GetModeLetters());
+                       context.Write("snomasks", GetSnomasks(targuser));
+                       context.Write("server", targuser->server->GetName());
+                       context.Write("uid", targuser->uuid);
+                       context.Write("signon", timestring(targuser->signon));
+                       context.Write("nickts", timestring(targuser->age));
                        if (loctarg)
-                               user->SendText(checkstr + " lastmsg " + timestring(targuser->idle_lastmsg));
+                               context.Write("lastmsg", timestring(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", timestring(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);
+                                       std::string umodes = GetAllowedOperOnlyModes(loctarg, MODETYPE_USER);
+                                       std::string cmodes = GetAllowedOperOnlyModes(loctarg, MODETYPE_CHANNEL);
+                                       context.Write("modeperms", "user=" + umodes + " channel=" + cmodes);
+
+                                       CheckContext::List opcmdlist(context, "commandperms");
+                                       for (OperInfo::PrivSet::const_iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); ++i)
+                                               opcmdlist.Add(*i);
+                                       opcmdlist.Flush();
+                                       CheckContext::List privlist(context, "permissions");
+                                       for (OperInfo::PrivSet::const_iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); ++i)
+                                               privlist.Add(*i);
+                                       privlist.Flush();
                                }
                        }
 
                        if (loctarg)
                        {
-                               user->SendText(checkstr + " clientaddr " + 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("timestamp", timestring(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", timestring(targchan->topicset));
                        }
 
-                       user->SendText(checkstr + " modes " + targchan->ChanModes(true));
-                       user->SendText(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter()));
+                       context.Write("modes", targchan->ChanModes(true));
+                       context.Write("membercount", ConvToStr(targchan->GetUserCounter()));
 
                        /* now the ugly bit, spool current members of a channel. :| */
 
-                       const UserMembList *ulist= targchan->GetUsers();
+                       const Channel::MemberMap& ulist = targchan->GetUsers();
 
                        /* note that unlike /names, we do NOT check +i vs in the channel */
-                       for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+                       for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
                        {
-                               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("%-3u %s%s (%s@%s) %s ", clonecount.global,
+                                       i->second->GetAllPrefixChars().c_str(), i->first->nick.c_str(),
+                                       i->first->ident.c_str(), i->first->GetDisplayedHost().c_str(), i->first->fullname.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)->GetList(targchan));
+
+                       context.DumpExt(targchan);
                }
                else
                {
@@ -221,73 +280,46 @@ 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->fullname);
                                }
                                /* IP address */
                                else if (InspIRCd::MatchCIDR(a->second->GetIPString(), parameters[0]))
                                {
                                        /* same IP. */
-                                       user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
+                                       context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
                                }
                        }
 
-                       user->SendText(checkstr + " matches " + ConvToStr(x));
+                       context.Write("matches", ConvToStr(x));
                }
 
-               user->SendText(checkstr + " END " + parameters[0]);
-
+               // END is sent by the CheckContext destructor
                return CMD_SUCCESS;
        }
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               if (parameters.size() > 1)
+               if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
                        return ROUTE_OPT_UCAST(parameters[1]);
                return ROUTE_LOCALONLY;
        }
 };
 
-
 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);
        }
index 6aaed7831dd63e849fb0be1716fba2a97caa7879..8b588b63dd5d38073c4faf0cf48ee8132dd0bc29 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)
@@ -35,24 +32,22 @@ class CommandChghost : public Command
                allow_empty_last_param = false;
                flags_needed = 'o';
                syntax = "<nick> <newhost>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *user)
        {
-               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])
                        {
-                               user->WriteServ("NOTICE "+user->nick+" :*** CHGHOST: Invalid characters in hostname");
+                               user->WriteNotice("*** CHGHOST: Invalid characters in hostname");
                                return CMD_FAILURE;
                        }
                }
@@ -60,18 +55,18 @@ 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());
                        }
                }
 
@@ -80,10 +75,7 @@ class CommandChghost : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -92,20 +84,13 @@ class ModuleChgHost : public Module
 {
        CommandChghost cmd;
        char hostmap[256];
+
  public:
        ModuleChgHost() : cmd(this, hostmap)
        {
        }
 
-       void init()
-       {
-               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");
 
@@ -114,15 +99,10 @@ class ModuleChgHost : public Module
                        hostmap[(unsigned char)*n] = 1;
        }
 
-       ~ModuleChgHost()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the CHGHOST command", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleChgHost)
index 2112e45a3f8d298c6f0e47a4bde6e066d25d920e..8ba5b4a5b1562a11c797e4b4d28831bc19700fdd 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the CHGIDENT command */
-
 /** Handle /CHGIDENT
  */
 class CommandChgident : public Command
@@ -34,7 +32,7 @@ class CommandChgident : public Command
                allow_empty_last_param = false;
                flags_needed = 'o';
                syntax = "<nick> <newident>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *user)
@@ -43,27 +41,27 @@ class CommandChgident : public Command
 
                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());
                }
 
@@ -72,14 +70,10 @@ class CommandChgident : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
-
 class ModuleChgIdent : public Module
 {
        CommandChgident cmd;
@@ -89,21 +83,10 @@ public:
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleChgIdent()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the CHGIDENT command", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleChgIdent)
-
index 73ae3d4870bc93d5d65d67604f7ffc3175f4e8c3..2582ef6520f67f27722438f96ed670156b370117 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the CHGNAME command */
-
 /** Handle /CHGNAME
  */
 class CommandChgname : public Command
@@ -32,7 +30,7 @@ class CommandChgname : public Command
                allow_empty_last_param = false;
                flags_needed = 'o';
                syntax = "<nick> <newname>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *user)
@@ -41,25 +39,25 @@ class CommandChgname : public Command
 
                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: GECOS must be specified");
                        return CMD_FAILURE;
                }
 
                if (parameters[1].length() > ServerInstance->Config->Limits.MaxGecos)
                {
-                       user->WriteServ("NOTICE %s :*** CHGNAME: GECOS too long", user->nick.c_str());
+                       user->WriteNotice("*** CHGNAME: GECOS too long");
                        return CMD_FAILURE;
                }
 
                if (IS_LOCAL(dest))
                {
-                       dest->ChangeName(parameters[1].c_str());
+                       dest->ChangeName(parameters[1]);
                        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());
                }
 
@@ -68,14 +66,10 @@ class CommandChgname : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
-
 class ModuleChgName : public Module
 {
        CommandChgname cmd;
@@ -85,20 +79,10 @@ public:
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleChgName()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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..0668340
--- /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("Class 'n' - Connection class ban", VF_VENDOR | VF_OPTCOMMON);
+       }
+};
+
+MODULE_INIT(ModuleClassBan)
diff --git a/src/modules/m_clearchan.cpp b/src/modules/m_clearchan.cpp
new file mode 100644 (file)
index 0000000..08a7a88
--- /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(const std::vector<std::string>& parameters, User* user)
+       {
+               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.
+                       std::vector<std::string> 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("Adds /CLEARCHAN that allows opers to masskick, masskill or mass-G/ZLine users on a channel", VF_VENDOR|VF_OPTCOMMON);
+       }
+};
+
+MODULE_INIT(ModuleClearChan)
index f4cfdb54f8b07c7186e11f1e5b27b1196a6c5ee5..f9a7fa38027cc28b2b1b9e81e0d77dba00ae04a3 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 */
@@ -49,14 +43,13 @@ class CloakUser : public ModeHandler
 {
  public:
        LocalStringExt 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)
+               ext("cloaked_host", ExtensionItem::EXT_USER, source), debounce_ts(0), debounce_count(0)
        {
        }
 
@@ -70,7 +63,7 @@ class CloakUser : public ModeHandler
                 */
                if (!user)
                {
-                       dest->SetMode('x',adding);
+                       dest->SetMode(this, adding);
                        return MODEACTION_ALLOW;
                }
 
@@ -87,7 +80,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,7 +90,7 @@ 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);
@@ -110,8 +103,8 @@ class CloakUser : public ModeHandler
                        }
                        if (cloak)
                        {
-                               user->ChangeDisplayedHost(cloak->c_str());
-                               user->SetMode('x',true);
+                               user->ChangeDisplayedHost(*cloak);
+                               user->SetMode(this, true);
                                return MODEACTION_ALLOW;
                        }
                        else
@@ -122,12 +115,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
@@ -151,26 +143,13 @@ class ModuleCloaking : public Module
        std::string prefix;
        std::string suffix;
        std::string key;
-       unsigned int compatkey[4];
-       const char* xtab[4];
+       unsigned int domainparts;
        dynamic_reference<HashProvider> Hash;
 
        ModuleCloaking() : cu(this), mode(MODE_OPAQUE), 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.
         *
@@ -182,7 +161,7 @@ class ModuleCloaking : public Module
         */
        std::string LastTwoDomainParts(const std::string &host)
        {
-               int dots = 0;
+               unsigned int dots = 0;
                std::string::size_type splitdot = host.length();
 
                for (std::string::size_type x = host.length() - 1; x; --x)
@@ -192,7 +171,7 @@ class ModuleCloaking : public Module
                                splitdot = x;
                                dots++;
                        }
-                       if (dots >= 3)
+                       if (dots >= domainparts)
                                break;
                }
 
@@ -217,7 +196,7 @@ class ModuleCloaking : public Module
                input.append(1, '\0'); // null does not terminate a C++ string
                input.append(item);
 
-               std::string rv = Hash->sum(input).substr(0,len);
+               std::string rv = Hash->GenerateRaw(input).substr(0,len);
                for(int i=0; i < len; i++)
                {
                        // this discards 3 bits per byte. We have an
@@ -228,63 +207,6 @@ 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 bindata;
@@ -335,24 +257,22 @@ class ModuleCloaking : public Module
                }
                else
                {
-                       char buf[50];
                        if (ip.sa.sa_family == AF_INET6)
                        {
-                               snprintf(buf, 50, ".%02x%02x.%02x%02x%s",
+                               rv.append(InspIRCd::Format(".%02x%02x.%02x%02x%s",
                                        ip.in6.sin6_addr.s6_addr[2], ip.in6.sin6_addr.s6_addr[3],
-                                       ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str());
+                                       ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str()));
                        }
                        else
                        {
                                const unsigned char* ip4 = (const unsigned char*)&ip.in4.sin_addr;
-                               snprintf(buf, 50, ".%d.%d%s", ip4[1], ip4[0], suffix.c_str());
+                               rv.append(InspIRCd::Format(".%d.%d%s", ip4[1], ip4[0], suffix.c_str()));
                        }
-                       rv.append(buf);
                }
                return rv;
        }
 
-       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)
@@ -361,17 +281,16 @@ class ModuleCloaking : public Module
                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 (cloak && *cloak != user->GetDisplayedHost())
                {
-                       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))
+                       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,34 +298,32 @@ 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))
                {
-                       u->SetMode('x', false);
-                       u->WriteServ("MODE %s -x", u->nick.c_str());
+                       u->SetMode(cu, false);
+                       u->WriteCommand("MODE", "-" + ConvToStr(cu.GetModeChar()));
                }
        }
 
-       ~ModuleCloaking()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                std::string testcloak = "broken";
                if (Hash)
                {
                        switch (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 (domainparts == 3)
+                                               testcloak = prefix + SegmentCloak("*", 3, 8) + suffix;
+                                       else
+                                       {
+                                               irc::sockets::sockaddrs sa;
+                                               testcloak = GenCloak(sa, "", testcloak + ConvToStr(domainparts));
+                                       }
                                        break;
                                case MODE_OPAQUE:
                                        testcloak = prefix + SegmentCloak("*", 4, 8) + suffix;
@@ -415,82 +332,26 @@ class ModuleCloaking : public Module
                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")
+               if (modestr == "half")
+               {
                        mode = MODE_HALF_CLOAK;
+                       domainparts = tag->getInt("domainparts", 3, 1, 10);
+               }
                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");
+                       throw ModuleException("Bad value for <cloak:mode>; must be half or full");
 
-               if (mode == MODE_COMPAT_HOST || mode == MODE_COMPAT_IPONLY)
-               {
-                       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";
-                       }
-
-                       if (prefix.empty())
-                               prefix = ServerInstance->Config->Network;
-
-                       if (!compatkey[0] || !compatkey[1] || !compatkey[2] || !compatkey[3] ||
-                               compatkey[0] >= limit || compatkey[1] >= limit || compatkey[2] >= limit || compatkey[3] >= limit)
-                       {
-                               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);
-                       }
-               }
-               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.");
-               }
+               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.");
        }
 
        std::string GenCloak(const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host)
@@ -502,29 +363,6 @@ class ModuleCloaking : public Module
 
                switch (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)
@@ -540,13 +378,13 @@ class ModuleCloaking : public Module
                return chost;
        }
 
-       void OnUserConnect(LocalUser* dest)
+       void OnUserConnect(LocalUser* dest) CXX11_OVERRIDE
        {
                std::string* cloak = cu.ext.get(dest);
                if (cloak)
                        return;
 
-               cu.ext.set(dest, GenCloak(dest->client_sa, dest->GetIPString(), dest->host));
+               cu.ext.set(dest, GenCloak(dest->client_sa, dest->GetIPString(), dest->GetRealHost()));
        }
 };
 
@@ -561,7 +399,7 @@ CmdResult CommandCloak::Handle(const std::vector<std::string> &parameters, User
        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());
+       user->WriteNotice("*** Cloak for " + parameters[0] + " is " + cloak);
 
        return CMD_SUCCESS;
 }
index 92b1bda78fb8a8d4d186a072405c1a56340294ed..b3e695bfde0c3c224a4d17ddb63cc825da6e127b 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the /CLONES command to retrieve information on clones. */
-
 /** Handle /CLONES
  */
 class CommandClones : public Command
@@ -36,7 +34,7 @@ class CommandClones : public Command
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
 
-               std::string clonesstr = "304 " + user->nick + " :CLONES";
+               std::string clonesstr = "CLONES ";
 
                unsigned long limit = atoi(parameters[0].c_str());
 
@@ -47,47 +45,35 @@ class CommandClones : public Command
                 *  :server.name 304 target :CLONES END
                 */
 
-               user->WriteServ(clonesstr + " START");
+               user->WriteNumeric(304, clonesstr + "START");
 
                /* 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)
+                               user->WriteNumeric(304, clonesstr + ConvToStr(counts.global) + " " + i->first.str());
                }
 
-               user->WriteServ(clonesstr + " END");
+               user->WriteNumeric(304, clonesstr + "END");
 
                return CMD_SUCCESS;
        }
 };
 
-
 class ModuleClones : public Module
 {
- private:
        CommandClones cmd;
  public:
        ModuleClones() : cmd(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleClones()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides the /CLONES command to retrieve information on clones.", VF_VENDOR);
        }
-
-
 };
 
 MODULE_INIT(ModuleClones)
index 8b0ea3417a081a0c5af4eaaad2d74ce354402d48..3f0eedaafcfb9e961ddf779422b37ee75fc306e5 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides /CLOSE functionality */
-
 /** Handle /CLOSE
  */
 class CommandClose : public Command
@@ -30,15 +28,19 @@ class CommandClose : public Command
        /* Command 'close', needs operator */
        CommandClose(Module* Creator) : Command(Creator,"CLOSE", 0)
        {
-       flags_needed = 'o'; }
+               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)
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator u = list.begin(); u != list.end(); )
                {
+                       // Quitting the user removes it from the list
                        LocalUser* user = *u;
+                       ++u;
                        if (user->registered != REG_ALL)
                        {
                                ServerInstance->Users->QuitUser(user, "Closing all unknown connections per request");
@@ -50,13 +52,14 @@ class CommandClose : public Command
                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;
+                       src->WriteNotice("*** Closed " + ConvToStr(ci->second) + " unknown " + (ci->second == 1 ? "connection" : "connections") +
+                               " from [" + ci->first + "]");
+                       total += ci->second;
                }
                if (total)
-                       src->WriteServ("NOTICE %s :*** %i unknown connection%s closed",src->nick.c_str(),total,(total>1)?"s":"");
+                       src->WriteNotice("*** " + ConvToStr(total) + " unknown " + (total == 1 ? "connection" : "connections") + " closed");
                else
-                       src->WriteServ("NOTICE %s :*** No unknown connections found",src->nick.c_str());
+                       src->WriteNotice("*** No unknown connections found");
 
                return CMD_SUCCESS;
        }
@@ -71,16 +74,7 @@ class ModuleClose : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleClose()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides /CLOSE functionality", VF_VENDOR);
        }
index afa17add4512d437f41d58ecdad182d40c9b52ad..e04217e71f16d42ff45bb74bee97f78b4d0713d1 100644 (file)
@@ -19,8 +19,6 @@
 
 #include "inspircd.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
@@ -37,41 +35,24 @@ class ModulePrivacyMode : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(pm);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModulePrivacyMode()
-       {
-       }
-
-       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);
        }
 
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) 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))
+                       if (!user->IsOper() && (t->IsModeSet(pm)) && (!user->server->IsULine()) && !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());
+                               user->WriteNumeric(ERR_CANTSENDTOUSER, t->nick, "You are not permitted to send private messages to this user (+c set)");
                                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)
-       {
-               return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
-       }
 };
 
-
 MODULE_INIT(ModulePrivacyMode)
index 6b13ab1aab8da7556d193cd8249b4e394c9ef391..7a06aedd3911c5cb7eeb967e8485aada4e306a0c 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);
+       }
 
-               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);
+
+               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..c439f3bfeccf0fb6a18787b41f720a2b736bcd62 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);
+               const std::bitset<64> save = ServerInstance->Config->DisabledUModes;
+               ServerInstance->Config->DisabledUModes.reset();
 
                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;
                        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);
+               ServerInstance->Config->DisabledUModes = save;
        }
 };
 
index 1d48220a6b096330cbafecd9e077216149042f35..87b6b51f21e1bf064c5cf11e6959cfbe83b1c603 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,31 @@ 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());
 
                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, std::vector<std::string> &parameters, LocalUser* user, bool validated, const std::string &original_line) CXX11_OVERRIDE
        {
                if (command == "PONG")
                {
@@ -90,20 +80,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..e0f9717c4cb99181e39e6d4d9261922ff9471447 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);
        }
 
-       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->getInt("ipv4cidr", 32, 1, 32);
+               ipv6_cidr = tag->getInt("ipv6cidr", 128, 1, 128);
+               threshold = tag->getInt("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;
 
                switch (u->client_sa.sa.sa_family)
                {
@@ -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())
                {
@@ -96,7 +74,7 @@ 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());
+                               ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, banmessage, mask.str());
                                if (!ServerInstance->XLines->AddLine(zl, NULL))
                                {
                                        delete zl;
@@ -104,7 +82,7 @@ class ModuleConnectBan : public Module
                                }
                                ServerInstance->XLines->ApplyLines();
                                std::string maskstr = mask.str();
-                               std::string timestr = ServerInstance->TimeString(zl->expiry);
+                               std::string timestr = InspIRCd::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('a', "Connect flooding from IP range %s (%d)", maskstr.c_str(), threshold);
@@ -117,9 +95,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..1d27e3990d1581f967486cecef6ef068967c164e 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Connection throttle */
-
 class ModuleConnFlood : public Module
 {
-private:
        int seconds, timeout, boot_wait;
        unsigned int conns;
        unsigned int maxconns;
@@ -39,26 +36,19 @@ 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");
+               seconds = tag->getDuration("period", tag->getInt("seconds"));
                maxconns = tag->getInt("maxconns");
-               timeout = tag->getInt("timeout");
+               timeout = tag->getDuration("timeout");
                quitmsg = tag->getString("quitmsg");
 
                /* seconds to wait when the server just booted */
@@ -67,7 +57,7 @@ public:
                first = ServerInstance->Time();
        }
 
-       virtual ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                if (user->exempt)
                        return MOD_RES_PASSTHRU;
@@ -114,12 +104,6 @@ public:
                }
                return MOD_RES_PASSTHRU;
        }
-
-       virtual void OnRehash(User* user)
-       {
-               InitConf();
-       }
-
 };
 
 MODULE_INIT(ModuleConnFlood)
index dfc60e082560190d62736bc02c3bcd006d41da24..61c50ec0cf11b4bfe6e9c6c78842ab8246c8ca68 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;
+               long rank = tag->getInt("rank", 0, 0, UINT_MAX);
+               long setrank = tag->getInt("ranktoset", prefixrank, rank, UINT_MAX);
+               long unsetrank = tag->getInt("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,64 @@ 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 non-existent mode at " + tag->getTagLocation());
+
+                               PrefixMode* pm = mh->IsPrefixMode();
+                               if (!pm)
+                                       throw ModuleException("<customprefix:change> specified for a non-prefix mode at " + tag->getTagLocation());
+
+                               long rank = tag->getInt("rank", pm->GetPrefixRank(), 0, UINT_MAX);
+                               long setrank = tag->getInt("ranktoset", pm->GetLevelRequired(true), rank, UINT_MAX);
+                               long unsetrank = tag->getInt("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);
                                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..2a08592ea16bef9b3aa5fd7caed51c3338f6793e 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the TITLE command which allows setting of CUSTOM WHOIS TITLE line */
-
 /** Handle /TITLE
  */
 class CommandTitle : public Command
@@ -30,32 +28,15 @@ class CommandTitle : public Command
  public:
        StringExtItem ctitle;
        CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2),
-               ctitle("ctitle", Creator)
+               ctitle("ctitle", ExtensionItem::EXT_USER, Creator)
        {
                syntax = "<user> <password>";
        }
 
-       bool OneOfMatches(const char* host, const char* ip, const char* 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 Handle(const std::vector<std::string> &parameters, User* user)
        {
-               char TheHost[MAXBUF];
-               char TheIP[MAXBUF];
-
-               snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(), user->host.c_str());
-               snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(), user->GetIPString());
+               const std::string userHost = user->ident + "@" + user->GetRealHost();
+               const std::string userIP = user->ident + "@" + user->GetIPString();
 
                ConfigTagList tags = ServerInstance->Config->ConfTags("title");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
@@ -67,65 +48,57 @@ class CommandTitle : public Command
                        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())
+                       if (Name == parameters[0] && ServerInstance->PassCompare(user, pass, parameters[1], hash) &&
+                               InspIRCd::MatchMask(host, userHost, userIP) && !title.empty())
                        {
                                ctitle.set(user, title);
 
                                ServerInstance->PI->SendMetaData(user, "ctitle", title);
 
                                if (!vhost.empty())
-                                       user->ChangeDisplayedHost(vhost.c_str());
+                                       user->ChangeDisplayedHost(vhost);
 
-                               user->WriteServ("NOTICE %s :Custom title set to '%s'",user->nick.c_str(), title.c_str());
+                               user->WriteNotice("Custom title set to '" + 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)
-       {
-       }
-
-       void init()
+       ModuleCustomTitle()
+               : Whois::LineEventListener(this)
+               , cmd(this)
        {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(cmd.ctitle);
-               ServerInstance->Modules->Attach(I_OnWhoisLine, this);
        }
 
        // :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(320, 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);
        }
index 383e7b5a2d538bf2ea0a48298788543652522109..202cb123ffbff6aa1de368e5c0ecf5d8a6baab68 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);
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult HandleLocal(const std::vector<std::string> &parameters, LocalUser* user)
        {
                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(ERR_NOSUCHCHANNEL, parameters[0], "No such channel");
                        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);
        }
-
 };
 
 MODULE_INIT(ModuleCycle)
index 05fff89377f5c09f6d2faf1a097eaf70c0c2040c..e687e1341386b881888b7acd0c09fd4c8af60601 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the /DCCALLOW command */
+static const char* const helptext[] =
+{
+       "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]",
+       "You may allow DCCs from specific users by specifying a",
+       "DCC allow for the user you want to receive DCCs from.",
+       "For example, to allow the user Brain to send you inspircd.exe",
+       "you would type:",
+       "/DCCALLOW +Brain",
+       "Brain would then be able to send you files. They would have to",
+       "resend the file again if the server gave them an error message",
+       "before you added them to your DCCALLOW list.",
+       "DCCALLOW entries will be temporary by default, if you want to add",
+       "them to your DCCALLOW list until you leave IRC, type:",
+       "/DCCALLOW +Brain 0",
+       "To remove the user from your DCCALLOW list, type:",
+       "/DCCALLOW -Brain",
+       "To see the users in your DCCALLOW list, type:",
+       "/DCCALLOW LIST",
+       "NOTE: If the user leaves IRC or changes their nickname",
+       "  they will be removed from your DCCALLOW list.",
+       "  your DCCALLOW list will be deleted when you leave IRC."
+};
 
 class BannedFileList
 {
@@ -53,13 +74,17 @@ 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)
+       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 */
@@ -95,21 +120,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(998, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
                                        return CMD_FAILURE;
                                }
                        }
 
-                       std::string nick = parameters[0].substr(1);
+                       std::string nick(parameters[0], 1);
                        User *target = ServerInstance->FindNickOnly(nick);
 
-                       if ((target) && (!IS_SERVER(target)) && (!target->quitting) && (target->registered == REG_ALL))
+                       if ((target) && (!target->quitting) && (target->registered == REG_ALL))
                        {
 
                                if (action == '-')
                                {
                                        // 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 +143,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(995, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
                                                                break;
                                                        }
                                                }
@@ -128,22 +153,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(996, 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(996, user->nick, "Too many nicks on DCCALLOW list");
                                                return CMD_FAILURE;
                                        }
 
@@ -151,18 +176,18 @@ 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(996, 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 mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
                                        std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
 
-                                       long length;
+                                       unsigned long length;
                                        if (parameters.size() < 2)
                                        {
-                                               length = ServerInstance->Duration(default_length);
+                                               length = InspIRCd::Duration(default_length);
                                        }
                                        else if (!atoi(parameters[1].c_str()))
                                        {
@@ -170,10 +195,10 @@ class CommandDccallow : public Command
                                        }
                                        else
                                        {
-                                               length = ServerInstance->Duration(parameters[1]);
+                                               length = InspIRCd::Duration(parameters[1]);
                                        }
 
-                                       if (!ServerInstance->IsValidMask(mask))
+                                       if (!InspIRCd::IsValidMask(mask))
                                        {
                                                return CMD_FAILURE;
                                        }
@@ -182,11 +207,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(993, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %ld seconds", target->nick.c_str(), length));
                                        }
                                        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(994, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
                                        }
 
                                        /* route it. */
@@ -196,7 +221,7 @@ 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;
                        }
                }
@@ -210,26 +235,9 @@ class CommandDccallow : public Command
 
        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());
+               for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
+                       user->WriteNumeric(998, helptext[i]);
+               user->WriteNumeric(999, "End of DCCALLOW HELP");
 
                LocalUser* localuser = IS_LOCAL(user);
                if (localuser)
@@ -239,78 +247,53 @@ 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(990, "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(991, 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(992, "End of DCCALLOW list");
        }
 
 };
 
 class ModuleDCCAllow : public Module
 {
+       DCCAllowExt ext;
        CommandDccallow cmd;
- public:
 
+ public:
        ModuleDCCAllow()
-               : cmd(this)
-       {
-               ext = NULL;
-       }
-
-       void init()
-       {
-               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)
+               : ext("dccallow", ExtensionItem::EXT_USER, this)
+               , cmd(this, ext)
        {
-               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)
+       void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
        {
-               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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
@@ -332,7 +315,7 @@ class ModuleDCCAllow : public Module
 
                                if (strncmp(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)
@@ -345,12 +328,12 @@ class ModuleDCCAllow : public Module
                                        if (s == std::string::npos)
                                                return MOD_RES_PASSTHRU;
 
-                                       irc::string type = assign(buf.substr(0, s));
+                                       const std::string type = buf.substr(0, s);
 
                                        ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
                                        bool blockchat = conftag->getBool("blockchat");
 
-                                       if (type == "SEND")
+                                       if (stdalgo::string::equalsci(type, "SEND"))
                                        {
                                                size_t first;
 
@@ -398,16 +381,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 +404,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())
@@ -431,7 +414,7 @@ class ModuleDCCAllow : public Module
                                        {
                                                if (iter2->length != 0 && (iter2->set_on + iter2->length) <= 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(997, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
                                                        iter2 = dl->erase(iter2);
                                                }
                                                else
@@ -455,7 +438,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 +448,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(995, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
                                                        dl->erase(i);
                                                        break;
                                                }
@@ -495,8 +478,11 @@ class ModuleDCCAllow : public Module
                }
        }
 
-       void ReadFileConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
+               cmd.maxentries = tag->getInt("maxentries", 20);
+
                bfl.clear();
                ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
@@ -508,12 +494,7 @@ class ModuleDCCAllow : public Module
                }
        }
 
-       virtual ~ModuleDCCAllow()
-       {
-               delete ext;
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
        }
index 43b24cfae9c6dba39b0f6ce6b0a64c40164c792b..88919e91b072540184fe3637360109fd95537bf1 100644 (file)
@@ -21,8 +21,6 @@
 
 #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 User_d : public ModeHandler
@@ -32,31 +30,20 @@ class User_d : public ModeHandler
 
        ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
        {
+               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 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 " + dest->nick + " -d.");
+
+               dest->SetMode(this, adding);
+               return MODEACTION_ALLOW;
        }
 };
 
 class ModuleDeaf : public Module
 {
        User_d m1;
-
        std::string deaf_bypasschars;
        std::string deaf_bypasschars_uline;
 
@@ -66,84 +53,40 @@ class ModuleDeaf : public Module
        {
        }
 
-       void init()
-       {
-               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");
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL)
-               {
-                       Channel* chan = (Channel*)dest;
-                       if (chan)
-                               this->BuildDeafList(MSG_NOTICE, chan, user, status, text, exempt_list);
-               }
+               if (target_type != TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
 
-               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;
-
-               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;
-               }
-               if (!deaf_bypasschars_uline.empty())
-               {
-                       is_bypasschar_uline_avail = 1;
-                       if (deaf_bypasschars_uline.find(text[0], 0) != std::string::npos)
-                               is_bypasschar_uline = 1;
-               }
+               Channel* chan = static_cast<Channel*>(dest);
+               bool is_bypasschar = (deaf_bypasschars.find(text[0]) != std::string::npos);
+               bool is_bypasschar_uline = (deaf_bypasschars_uline.find(text[0]) != std::string::npos);
 
                /*
                 * 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;
+               if (deaf_bypasschars_uline.empty() && is_bypasschar)
+                       return MOD_RES_PASSTHRU;
 
-               for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+               const Channel::MemberMap& ulist = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
                {
                        /* not +d ? */
-                       if (!i->first->IsModeSet('d'))
+                       if (!i->first->IsModeSet(m1))
                                continue; /* deliver message */
                        /* matched both U-line only and regular bypasses */
                        if (is_bypasschar && is_bypasschar_uline)
                                continue; /* deliver message */
 
-                       is_a_uline = ServerInstance->ULine(i->first->server);
+                       bool is_a_uline = i->first->server->IsULine();
                        /* matched a U-line only bypass */
                        if (is_bypasschar_uline && is_a_uline)
                                continue; /* deliver message */
@@ -151,23 +94,17 @@ class ModuleDeaf : public Module
                        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);
                }
-       }
 
-       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);
        }
-
 };
 
 MODULE_INIT(ModuleDeaf)
index 20d4c8e8faeeed905350aac88dfc2eb79c90b511..6d125134578fc63a39eaedbbb0663ddba482fc51 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>
 
 class DelayJoinMode : public ModeHandler
 {
- private:
        CUList empty;
  public:
        DelayJoinMode(Module* Parent) : ModeHandler(Parent, "delayjoin", 'D', PARAM_NONE, MODETYPE_CHANNEL)
        {
-               levelrequired = OP_VALUE;
+               ranktoset = ranktounset = OP_VALUE;
        }
 
        ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
@@ -43,33 +39,27 @@ class ModuleDelayJoin : public Module
        DelayJoinMode djm;
  public:
        LocalIntExt unjoined;
-       ModuleDelayJoin() : djm(this), unjoined("delayjoin", this)
+       ModuleDelayJoin()
+               : djm(this)
+               , unjoined("delayjoin", ExtensionItem::EXT_MEMBERSHIP, this)
        {
        }
 
-       void init()
-       {
-               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));
-       }
-       ~ModuleDelayJoin();
-       Version GetVersion();
-       void OnNamesListItem(User* issuer, Membership*, std::string &prefixes, std::string &nick);
-       void OnUserJoin(Membership*, bool, bool, CUList&);
+       Version GetVersion() CXX11_OVERRIDE;
+       ModResult OnNamesListItem(User* 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 OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list) 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 +68,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)
+               const Channel::MemberMap& users = channel->GetUsers();
+               for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
                        creator->OnText(n->first, channel, TYPE_CHANNEL, "", 0, empty);
        }
-       channel->SetMode('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);
 }
 
-void ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ModResult ModuleDelayJoin::OnNamesListItem(User* 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,7 +107,7 @@ 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);
@@ -138,24 +126,20 @@ 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)
 {
-       /* Server origin */
-       if (!user)
-               return;
-
        if (target_type != TYPE_CHANNEL)
                return;
 
@@ -177,14 +161,13 @@ void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std:
 }
 
 /* make the user visible if he receives any mode change */
-ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, 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;
index 978ab55d2191c94b31bb74862bf454f4f01ce72d..3471c7fd2f2f9c86085483b1a0c93dfda7c828f9 100644 (file)
 
 #include "inspircd.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*)
@@ -36,65 +35,51 @@ class DelayMsgMode : public ModeHandler
                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);
+       void OnUnset(User* source, Channel* chan);
+
+       void SerializeParam(Channel* chan, int n, std::string& out)
+       {
+               out += ConvToStr(n);
+       }
 };
 
 class ModuleDelayMsg : public Module
 {
- private:
        DelayMsgMode djm;
+       bool allownotice;
  public:
        ModuleDelayMsg() : 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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
 };
 
-ModeAction DelayMsgMode::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());
-
-               /* Wrap low values at 32768 */
-               if (limit < 0)
-                       limit = 0x7FFF;
+       // Setting a new limit, sanity check
+       unsigned int limit = ConvToInt(parameter);
+       if (limit == 0)
+               limit = 1;
 
-               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);
@@ -102,19 +87,18 @@ Version ModuleDelayMsg::GetVersion()
 
 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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype)
 {
-       /* Server origin */
-       if ((!user) || (!IS_LOCAL(user)))
+       if (!IS_LOCAL(user))
                return MOD_RES_PASSTHRU;
 
-       if (target_type != TYPE_CHANNEL)
+       if ((target_type != TYPE_CHANNEL) || ((!allownotice) && (msgtype == MSG_NOTICE)))
                return MOD_RES_PASSTHRU;
 
        Channel* channel = (Channel*) dest;
@@ -128,14 +112,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 channel (+d)", len));
                        return MOD_RES_DENY;
                }
        }
@@ -147,19 +130,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..467c8a03b3dfe6f0c02a2ea18506cdc00949c941 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements config tags which allow blocking of joins to channels */
-
 class ModuleDenyChannels : public Module
 {
+       ChanModeReference redirectmode;
+
  public:
-       void init()
+       ModuleDenyChannels()
+               : 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");
@@ -45,10 +44,10 @@ class ModuleDenyChannels : public Module
                        if (!redirect.empty())
                        {
 
-                               if (!ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
+                               if (!ServerInstance->IsChannel(redirect))
                                {
-                                       if (user)
-                                               user->WriteServ("NOTICE %s :Invalid badchan redirect '%s'", user->nick.c_str(), redirect.c_str());
+                                       if (status.srcuser)
+                                               status.srcuser->WriteNotice("Invalid badchan redirect '" + redirect + "'");
                                        throw ModuleException("Invalid badchan redirect, not a channel");
                                }
 
@@ -67,8 +66,8 @@ class ModuleDenyChannels : public Module
                                                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());
+                                                       if (status.srcuser)
+                                                               status.srcuser->WriteNotice("Badchan " + name + " redirects to badchan " + redirect);
                                                        throw ModuleException("Badchan redirect loop");
                                                }
                                        }
@@ -77,24 +76,20 @@ class ModuleDenyChannels : public Module
                }
        }
 
-       virtual ~ModuleDenyChannels()
-       {
-       }
-
-       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)
                {
                        if (InspIRCd::Match(cname, j->second->getString("name")))
                        {
-                               if (IS_OPER(user) && j->second->getBool("allowopers"))
+                               if (user->IsOper() && j->second->getBool("allowopers"))
                                {
                                        return MOD_RES_PASSTHRU;
                                }
@@ -112,19 +107,19 @@ class ModuleDenyChannels : public Module
                                                }
                                        }
 
-                                       if (ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
+                                       if (ServerInstance->IsChannel(redirect))
                                        {
                                                /* simple way to avoid potential loops: don't redirect to +L channels */
                                                Channel *newchan = ServerInstance->FindChan(redirect);
-                                               if ((!newchan) || (!(newchan->IsModeSet('L'))))
+                                               if ((!newchan) || (!newchan->IsModeSet(redirectmode)))
                                                {
-                                                       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());
+                                                       user->WriteNumeric(926, cname, InspIRCd::Format("Channel %s is forbidden, redirecting to %s: %s", cname.c_str(), redirect.c_str(), reason.c_str()));
+                                                       Channel::JoinUser(user, redirect);
                                                        return MOD_RES_DENY;
                                                }
                                        }
 
-                                       user->WriteNumeric(926, "%s %s :Channel %s is forbidden: %s",user->nick.c_str(),cname,cname,reason.c_str());
+                                       user->WriteNumeric(926, cname, InspIRCd::Format("Channel %s is forbidden: %s", cname.c_str(), reason.c_str()));
                                        return MOD_RES_DENY;
                                }
                        }
diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp
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)
index 3dea080cee59dd99474510ae5fe5567f4eda3b44..b2b9c30ffd165d6fb19a9672a40c60d142915a1f 100644 (file)
@@ -23,8 +23,7 @@
 
 #include "inspircd.h"
 #include "xline.h"
-
-/* $ModDesc: Provides handling of DNS blacklists */
+#include "modules/dns.h"
 
 /* Class holding data for a single entry */
 class DNSBLConfEntry : public refcountbase
@@ -40,13 +39,12 @@ class DNSBLConfEntry : public refcountbase
                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
  */
-class DNSBLResolver : public Resolver
+class DNSBLResolver : public DNS::Request
 {
        std::string theiruid;
        LocalStringExt& nameExt;
@@ -55,175 +53,177 @@ 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)
+               {
+                       ServerInstance->SNO->WriteGlobalSno('a', "DNSBL: %s returned address outside of acceptable subnet 127.0.0.0/8: %s", ConfEntry->domain.c_str(), ans_record->rdata.c_str());
+                       ConfEntry->stats_misses++;
+                       return;
+               }
+
+               int i = countExt.get(them);
+               if (i)
+                       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)
                {
-                       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)
+                       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->WriteNumeric(304, "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->WriteNumeric(304, "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))
+                                       {
+                                               std::string timestr = InspIRCd::TimeString(kl->expiry);
+                                               ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
+                                                       them->GetIPString().c_str(), 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 = InspIRCd::TimeString(gl->expiry);
+                                               ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
+                                                       them->GetIPString().c_str(), 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 = InspIRCd::TimeString(zl->expiry);
+                                               ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on %s to expire on %s: %s",
+                                                       them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+                                               ServerInstance->XLines->ApplyLines();
+                                       }
+                                       else
+                                       {
+                                               delete zl;
+                                               return;
+                                       }
+                                       break;
+                               }
+                               case DNSBLConfEntry::I_UNKNOWN:
+                               default:
+                                       break;
                        }
+
+                       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);
                }
+               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)
-               {
-                       int i = countExt.get(them);
-                       if (i)
-                               countExt.set(them, i - 1);
-               }
-       }
+               if (!them)
+                       return;
 
-       virtual ~DNSBLResolver()
-       {
+               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)
+                       ConfEntry->stats_misses++;
        }
 };
 
 class ModuleDNSBL : public Module
 {
        std::vector<reference<DNSBLConfEntry> > DNSBLConfEntries;
+       dynamic_reference<DNS::Manager> DNS;
        LocalStringExt nameExt;
        LocalIntExt countExt;
 
@@ -246,25 +246,21 @@ class ModuleDNSBL : public Module
                return DNSBLConfEntry::I_UNKNOWN;
        }
  public:
-       ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
-
-       void init()
+       ModuleDNSBL()
+               : DNS(this, "DNS")
+               , nameExt("dnsbl_match", ExtensionItem::EXT_USER, this)
+               , countExt("dnsbl_pending", ExtensionItem::EXT_USER, this)
        {
-               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));
        }
 
-       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();
 
@@ -296,7 +292,7 @@ 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 */
 
@@ -321,11 +317,6 @@ class ModuleDNSBL : public Module
                                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());
-                       }
                        else
                        {
                                if (e->reason.empty())
@@ -341,14 +332,9 @@ class ModuleDNSBL : public Module
                }
        }
 
-       void OnRehash(User* user)
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
        {
-               ReadConf();
-       }
-
-       void OnSetUserIP(LocalUser* user)
-       {
-               if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET))
+               if ((user->exempt) || !DNS)
                        return;
 
                if (user->MyClass)
@@ -357,40 +343,61 @@ 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.sa.sa_family == AF_INET)
+               {
+                       unsigned int a, b, c, d;
+                       d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
+                       c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
+                       b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
+                       a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
 
-               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;
+                       reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
+               }
+               else if (user->client_sa.sa.sa_family == AF_INET6)
+               {
+                       const unsigned char* ip = user->client_sa.in6.sin6_addr.s6_addr;
 
-               snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
-               reversedip = std::string(reversedipbuf);
+                       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;
+
+               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))
@@ -401,17 +408,17 @@ class ModuleDNSBL : public Module
                        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 +428,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..0c67037f0ee01228f2b36e14ad9f1179c53d2d9f 100644 (file)
@@ -18,9 +18,8 @@
 
 
 #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
  */
@@ -31,121 +30,111 @@ class ExemptChanOps : public ListModeBase
 
        bool ValidateParam(User* user, Channel* chan, std::string &word)
        {
-               // 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, chan->name, word, "Invalid exemptchanops entry, format is <restriction>:<prefix>");
+                       return false;
+               }
+
+               std::string restriction(word, 0, p);
+               // If there is a '-' in the restriction string ignore it and everything after it
+               // to support "auditorium-vis" and "auditorium-see" in m_auditorium
+               p = restriction.find('-');
+               if (p != std::string::npos)
+                       restriction.erase(p);
+
+               if (!ServerInstance->Modes->FindMode(restriction, MODETYPE_CHANNEL))
                {
-                       user->WriteNumeric(955, "%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(955, chan->name, restriction, "Unknown restriction");
                        return false;
                }
 
                return true;
        }
 
-       bool TellListTooLong(User* user, Channel* chan, std::string &word)
+       void 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;
+               user->WriteNumeric(959, chan->name, word, "Channel exemptchanops list is full");
        }
 
        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());
+               user->WriteNumeric(957, chan->name, InspIRCd::Format("The word %s is already on the exemptchanops list", word.c_str()));
        }
 
        void TellNotSet(User* user, Channel* chan, std::string &word)
        {
-               user->WriteNumeric(958, "%s %s :No such exemptchanops word is set",user->nick.c_str(), chan->name.c_str());
+               user->WriteNumeric(958, chan->name, "No such exemptchanops word is set");
        }
 };
 
-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);
        }
 
-       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..0badd137757152a8b57d45588301757280222c1b 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
-#include "m_regex.h"
-
-/* $ModDesc: Text (spam) filtering */
-
-class ModuleFilter;
+#include "modules/regex.h"
 
 enum FilterFlags
 {
@@ -48,6 +44,7 @@ enum FilterAction
 class FilterResult
 {
  public:
+       Regex* regex;
        std::string freeform;
        std::string reason;
        FilterAction action;
@@ -60,9 +57,12 @@ 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, long gt, const std::string& fla)
+               : freeform(free), reason(rea), action(act), gline_time(gt)
        {
+               if (!RegexEngine)
+                       throw ModuleException("Regex module implementing '"+RegexEngine.GetProvider()+"' is not loaded!");
+               regex = RegexEngine->Create(free);
                this->FillFlags(fla);
        }
 
@@ -154,17 +154,10 @@ class CommandFilter : public Command
        }
 };
 
-class ImplFilter : public FilterResult
-{
- public:
-       Regex* regex;
-
-       ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs);
-};
-
-
 class ModuleFilter : public Module
 {
+       typedef insp::flat_set<std::string, irc::insensitive_swo> ExemptTargetSet;
+
        bool initing;
        RegexFactory* factory;
        void FreeFilters();
@@ -173,28 +166,30 @@ 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);
+       CullResult cull() CXX11_OVERRIDE;
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) 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();
+       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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
        bool AppliesToMe(User* user, FilterResult* filter, int flags);
        void ReadFilters();
        static bool StringToFilterAction(const std::string& str, FilterAction& fa);
@@ -209,13 +204,13 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
                Module *me = creator;
                if (static_cast<ModuleFilter *>(me)->DeleteFilter(parameters[0]))
                {
-                       user->WriteServ("NOTICE %s :*** Removed filter '%s'", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNotice("*** Removed filter '" + parameters[0] + "'");
                        ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" removed filter '"+parameters[0]+"'");
                        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 in list, try /stats s.");
                        return CMD_FAILURE;
                }
        }
@@ -232,7 +227,7 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
 
                        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());
+                               user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.");
                                return CMD_FAILURE;
                        }
 
@@ -240,12 +235,12 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
                        {
                                if (parameters.size() >= 5)
                                {
-                                       duration = ServerInstance->Duration(parameters[3]);
+                                       duration = InspIRCd::Duration(parameters[3]);
                                        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 gline type filter, a gline duration must be specified as the third parameter.");
                                        return CMD_FAILURE;
                                }
                        }
@@ -258,9 +253,9 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
                        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());
+                               user->WriteNotice("*** Added filter '" + freeform + "', type '" + parameters[1] + "'" +
+                                       (duration ? ", duration " +  parameters[3] : "") + ", flags '" + flags + "', reason: '" +
+                                       parameters[reasonindex] + "'");
 
                                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]);
 
@@ -268,13 +263,13 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
                        }
                        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 +278,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;
@@ -301,14 +296,6 @@ ModuleFilter::ModuleFilter()
 {
 }
 
-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);
-}
-
 CullResult ModuleFilter::cull()
 {
        FreeFilters();
@@ -317,29 +304,19 @@ 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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype)
 {
+       // 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 = (msgtype == MSG_PRIVMSG) ? FLAG_PRIVMSG : FLAG_NOTICE;
 
        FilterResult* f = this->FilterMatch(user, text, flags);
        if (f)
@@ -348,12 +325,16 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s
                if (target_type == TYPE_USER)
                {
                        User* t = (User*)dest;
+                       // 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)
                {
                        Channel* t = (Channel*)dest;
-                       if (exemptfromfilter.find(t->name) != exemptfromfilter.end())
+                       if (exemptedchans.count(t->name))
                                return MOD_RES_PASSTHRU;
 
                        target = t->name;
@@ -362,16 +343,16 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s
                {
                        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());
+                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked and opers notified (%s)", f->reason.c_str()));
                        else
-                               user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked and opers notified: "+f->reason);
+                               user->WriteNotice("Your message to "+target+" was blocked and opers notified: "+f->reason);
                }
                else if (f->action == FA_SILENT)
                {
                        if (target_type == TYPE_CHANNEL)
-                               user->WriteNumeric(404, "%s %s :Message to channel blocked (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked (%s)", f->reason.c_str()));
                        else
-                               user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked: "+f->reason);
+                               user->WriteNotice("Your message to "+target+" was blocked: "+f->reason);
                }
                else if (f->action == FA_KILL)
                {
@@ -388,7 +369,7 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s
                                delete gl;
                }
 
-               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;
@@ -396,7 +377,7 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s
 
 ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
 {
-       if (validated && IS_LOCAL(user))
+       if (validated)
        {
                flags = 0;
                bool parting;
@@ -416,7 +397,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;
@@ -446,7 +427,7 @@ 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)
@@ -466,15 +447,25 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
        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");
@@ -554,11 +545,11 @@ 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)));
+               server.SendMetaData("filter", EncodeFilter(&(*i)));
        }
 }
 
@@ -573,27 +564,19 @@ void ModuleFilter::OnDecodeMetaData(Extensible* target, const std::string &extna
                }
                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,20 +588,15 @@ 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)
 {
-       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)
                {
@@ -632,7 +610,7 @@ bool ModuleFilter::DeleteFilter(const std::string &freeform)
 
 std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flgs)
 {
-       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 +620,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));
        }
        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 +632,15 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform
 
 bool ModuleFilter::StringToFilterAction(const std::string& str, FilterAction& fa)
 {
-       irc::string s(str.c_str());
-
-       if (s == "gline")
+       if (stdalgo::string::equalsci(str, "gline"))
                fa = FA_GLINE;
-       else if (s == "block")
+       else if (stdalgo::string::equalsci(str, "block"))
                fa = FA_BLOCK;
-       else if (s == "silent")
+       else if (stdalgo::string::equalsci(str, "silent"))
                fa = FA_SILENT;
-       else if (s == "kill")
+       else if (stdalgo::string::equalsci(str, "kill"))
                fa = FA_KILL;
-       else if (s == "none")
+       else if (stdalgo::string::equalsci(str, "none"))
                fa = FA_NONE;
        else
                return false;
@@ -695,7 +671,7 @@ void ModuleFilter::ReadFilters()
                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 gline_time = i->second->getDuration("duration", 10*60, 1);
                if (flgs.empty())
                        flgs = "*";
 
@@ -705,27 +681,31 @@ void ModuleFilter::ReadFilters()
 
                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());
+                       filters.push_back(FilterResult(RegexEngine, pattern, reason, fa, gline_time, flgs));
+                       ServerInstance->Logs->Log(MODNAME, LOG_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());
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason().c_str());
                }
        }
 }
 
-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->gline_time)+" :"+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..4e7af4b
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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 (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");
+               timeout = tag->getDuration("timeout", 5, 1);
+               std::string file = tag->getString("file");
+
+               if (!file.empty())
+               {
+                       try
+                       {
+                               FileReader reader(file);
+                               policy_reply = reader.GetString();
+                       }
+                       catch (CoreException&)
+                       {
+                               const std::string error_message = "A file was specified for FlashPD, but it could not be loaded.";
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, error_message);
+                               ServerInstance->SNO->WriteGlobalSno('a', error_message);
+                               policy_reply.clear();
+                       }
+                       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 (ls->bind_tag->getString("type", "clients") != "clients" || ls->bind_tag->getString("ssl", "plaintext") != "plaintext")
+                                       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>";
+       }
+
+       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..a15f1941844ae04e79a349d7ba54beed1eca7981 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b r: - realname (gecos) bans */
-
 class ModuleGecosBan : public Module
 {
  public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ~ModuleGecosBan()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Extban 'r' - realname (gecos) 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] == 'r') && (mask[1] == ':'))
                {
@@ -49,9 +37,9 @@ class ModuleGecosBan : public Module
                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('r');
        }
 };
 
index aed65045f58d78c9d5577e3a66e6a6834844de64..36a90bf55589cc3d4ddb668f80be35277e31c2d9 100644 (file)
@@ -22,8 +22,6 @@
  */
 
 
-/* $ModDesc: Allows global loading of a module. */
-
 #include "inspircd.h"
 
 /** Handle /GLOADMODULE
@@ -35,7 +33,6 @@ class CommandGloadmodule : public Command
        {
                flags_needed = 'o';
                syntax = "<modulename> [servermask]";
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
        }
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
@@ -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
@@ -79,6 +76,13 @@ class CommandGunloadmodule : public Command
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
+               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());
@@ -112,25 +115,6 @@ class CommandGunloadmodule : public Command
        }
 };
 
-class GReloadModuleWorker : public HandlerBase1<void, bool>
-{
- public:
-       const std::string nick;
-       const std::string name;
-       const std::string uid;
-       GReloadModuleWorker(const std::string& usernick, const std::string& uuid, const std::string& modn)
-               : nick(usernick), name(modn), uid(uuid) {}
-       void Call(bool result)
-       {
-               ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
-               User* user = ServerInstance->FindNick(uid);
-               if (user)
-                       user->WriteNumeric(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
@@ -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;
                        }
                }
@@ -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()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows global loading of a module.", VF_COMMON | VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleGlobalLoad)
-
index 85d84252b2b11875100f7b25148f40bf221980f5..1cb87324b5417cbd9803a9eed9d3011905f30874 100644 (file)
@@ -23,8 +23,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for GLOBOPS and snomask +g */
-
 /** Handle /GLOBOPS
  */
 class CommandGlobops : public Command
@@ -33,7 +31,6 @@ class CommandGlobops : public Command
        CommandGlobops(Module* Creator) : Command(Creator,"GLOBOPS", 1,1)
        {
                flags_needed = 'o'; syntax = "<any-text>";
-               TRANSLATE2(TR_TEXT, TR_END);
        }
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
@@ -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);
        }
-
 };
 
 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)
index 4bbe8785e4ef4f7b2f22d059b56f81682eb8d6fe..95f69774bb0cad6cc543f7aa964b1688ac361342 100644 (file)
  */
 
 
-/* $ModDesc: Provides the /HELPOP command for useful information */
-
 #include "inspircd.h"
 
-static std::map<irc::string, std::string> helpop_map;
+typedef std::map<std::string, std::string, irc::insensitive_swo> HelpopMap;
+static HelpopMap helpop_map;
 
 /** Handles user mode +h
  */
@@ -42,41 +41,40 @@ class Helpop : public SimpleUserModeHandler
  */
 class CommandHelpop : public Command
 {
+       const std::string startkey;
  public:
-       CommandHelpop(Module* Creator) : Command(Creator, "HELPOP", 0)
+       CommandHelpop(Module* Creator)
+               : Command(Creator, "HELPOP", 0)
+               , startkey("start")
        {
                syntax = "<any-text>";
        }
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
-               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(290, "HELPOP topic index");
+                       for (HelpopMap::const_iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
+                               user->WriteNumeric(292, InspIRCd::Format("  %s", iter->first.c_str()));
+                       user->WriteNumeric(292, "*** 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());
+                       user->WriteNumeric(290, InspIRCd::Format("*** HELPOP for %s", parameter.c_str()));
+                       user->WriteNumeric(292, " -");
 
-                       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");
                        }
 
-                       std::string value = iter->second;
+                       const std::string& value = iter->second;
                        irc::sepstream stream(value, '\n');
                        std::string token = "*";
 
@@ -84,48 +82,40 @@ class CommandHelpop : public Command
                        {
                                // Writing a blank line will not work with some clients
                                if (token.empty())
-                                       user->WriteServ("292 %s : ", user->nick.c_str());
+                                       user->WriteNumeric(292, ' ');
                                else
-                                       user->WriteServ("292 %s :%s", user->nick.c_str(), token.c_str());
+                                       user->WriteNumeric(292, token);
                        }
 
-                       user->WriteServ("292 %s : -", user->nick.c_str());
-                       user->WriteServ("292 %s :*** End of HELPOP", user->nick.c_str());
+                       user->WriteNumeric(292, " -");
+                       user->WriteNumeric(292, "*** 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 */
 
@@ -151,20 +141,15 @@ class ModuleHelpop : public Module
                        helpop_map.swap(help);
                }
 
-               void OnRehash(User* user)
-               {
-                       ReadConfig();
-               }
-
-               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(310, "is available for help.");
                        }
                }
 
-               Version GetVersion()
+               Version GetVersion() CXX11_OVERRIDE
                {
                        return Version("Provides the /HELPOP command for useful information", VF_VENDOR);
                }
index 008c622086efc8caf14147480de27d4ef194b5df..08caae6b2c75e1fbcd229f0949eaf4e46800c827 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for hiding channels with user mode +I */
-
 /** Handles user mode +I
  */
 class HideChans : public SimpleUserModeHandler
@@ -30,49 +28,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 +68,7 @@ class ModuleHideChans : public Module
                        return MOD_RES_DENY;
 
                /* doesn't affect opers, sender is opered */
-               if (user->HasPrivPermission("users/auspex"))
+               if (whois.GetSource()->HasPrivPermission("users/auspex"))
                        return MOD_RES_PASSTHRU;
 
                /* user must be opered, boned. */
@@ -88,5 +76,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..97173c1
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ListWatcher : public ModeWatcher
+{
+       // Minimum rank required to view the list
+       const unsigned int minrank;
+
+ public:
+       ListWatcher(Module* mod, const std::string& modename, unsigned int rank)
+               : ModeWatcher(mod, modename, MODETYPE_CHANNEL)
+               , minrank(rank)
+       {
+       }
+
+       bool BeforeMode(User* user, User* destuser, Channel* chan, std::string& param, bool adding)
+       {
+               // Only handle listmode list requests
+               if (!param.empty())
+                       return true;
+
+               // If the user requesting the list is a member of the channel see if they have the
+               // rank required to view the list
+               Membership* memb = chan->GetUser(user);
+               if ((memb) && (memb->getRank() >= minrank))
+                       return true;
+
+               if (user->HasPrivPermission("channels/auspex"))
+                       return true;
+
+               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You do not have access to view the %s list", GetModeName().c_str()));
+               return false;
+       }
+};
+
+class ModuleHideList : public Module
+{
+       std::vector<ListWatcher*> watchers;
+
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               stdalgo::delete_all(watchers);
+               watchers.clear();
+
+               ConfigTagList tags = ServerInstance->Config->ConfTags("hidelist");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       std::string modename = tag->getString("mode");
+                       // If rank is set to 0 everyone inside the channel can view the list,
+                       // but non-members may not
+                       unsigned int rank = tag->getInt("rank", HALFOP_VALUE, 0);
+                       watchers.push_back(new ListWatcher(this, modename, rank));
+               }
+       }
+
+       ~ModuleHideList()
+       {
+               stdalgo::delete_all(watchers);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for hiding the list of listmodes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleHideList)
index 32999d9f0b36025e6e25acf95b1f53caf09eccad..6650b7f16d26df98a0eca2d988f51ff848a89808 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for hiding oper status with user mode +H */
-
 /** Handles user mode +H
  */
 class HideOper : public SimpleUserModeHandler
@@ -50,43 +48,32 @@ class HideOper : public SimpleUserModeHandler
        }
 };
 
-class ModuleHideOper : public Module
+class ModuleHideOper : public Module, public Whois::LineEventListener
 {
        HideOper hm;
        bool active;
  public:
        ModuleHideOper()
-               : hm(this)
+               : Whois::LineEventListener(this)
+               , hm(this)
                , active(false)
        {
        }
 
-       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 +81,72 @@ 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 OnSendWhoLine(User* source, const std::vector<std::string>& params, 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 ((params.size() > 1) && (params[1].find('o') != std::string::npos))
+                               return MOD_RES_DENY;
+
                        // hide the "*" that marks the user as an oper from the /WHO line
-                       std::string::size_type 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() < 6)
+                               return MOD_RES_PASSTHRU;
+
+                       std::string& param = numeric.GetParams()[5];
+                       const std::string::size_type pos = param.find('*');
                        if (pos != std::string::npos)
-                               line.erase(pos, 1);
-                       // hide the line completely if doing a "/who * o" query
-                       if (params.size() > 1 && params[1].find('o') != std::string::npos)
-                               line.clear();
+                               param.erase(pos, 1);
                }
+               return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnStats(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..6d5896ef51bd73ada8bedd4d875224030cc6a613 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.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
@@ -47,21 +45,13 @@ typedef std::vector<std::pair<std::string, Host> > hostchanges_t;
 
 class ModuleHostChange : public Module
 {
- private:
        hostchanges_t hostchanges;
        std::string MySuffix;
        std::string MyPrefix;
        std::string MySeparator;
 
  public:
-       void init()
-       {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnRehash, I_OnUserConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* host = ServerInstance->Config->ConfValue("host");
                MySuffix = host->getString("suffix");
@@ -97,14 +87,14 @@ class ModuleHostChange : public Module
                }
        }
 
-       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);
        }
 
-       virtual void OnUserConnect(LocalUser* user)
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
                for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
                {
@@ -160,9 +150,9 @@ class ModuleHostChange : public Module
                                }
                                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);
+                                       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..0f7405d
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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;
+
+       /** Send fake quit/join/mode messages for host or ident cycle.
+        */
+       void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const char* quitmsg)
+       {
+               // GetFullHost() returns the original data at the time this function is called
+               const std::string quitline = ":" + user->GetFullHost() + " QUIT :" + quitmsg;
+
+               already_sent_t silent_id = 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->Write(quitline);
+                               }
+                               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;
+                       const std::string joinline = ":" + newfullhost + " JOIN " + c->name;
+                       std::string modeline;
+
+                       if (!memb->modes.empty())
+                       {
+                               modeline = ":" + (ServerInstance->Config->CycleHostsFromUser ? newfullhost : ServerInstance->Config->ServerName)
+                                       + " MODE " + c->name + " +" + memb->modes;
+
+                               for (size_t j = 0; j < memb->modes.length(); j++)
+                                       modeline.append(" ").append(user->nick);
+                       }
+
+                       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->Write(quitline);
+                                       u->already_sent = seen_id;
+                               }
+
+                               u->Write(joinline);
+                               if (!memb->modes.empty())
+                                       u->Write(modeline);
+                       }
+               }
+       }
+
+ public:
+       ModuleHostCycle()
+               : chghostcap(this, "chghost")
+       {
+       }
+
+       void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+       {
+               DoHostCycle(user, newident, user->GetDisplayedHost(), "Changing ident");
+       }
+
+       void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+       {
+               DoHostCycle(user, user->ident, newhost, "Changing host");
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Cycles users in all their channels when their host or ident changes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleHostCycle)
index 2b079c6ff9fe19d017d8347332f38d4b0a66fe95..05112eb9cc61ddedf2cdfa10a33e3fb2fe58a5ef 100644 (file)
 
 
 #include "inspircd.h"
-#include "httpd.h"
-
-/* $ModDesc: Provides HTTP serving facilities to modules */
-/* $ModDep: httpd.h */
+#include "iohook.h"
+#include "modules/httpd.h"
 
 class ModuleHttpServer;
 
 static ModuleHttpServer* HttpModule;
-static bool claimed;
-static std::set<HttpServerSocket*> sockets;
+static insp::intrusive_list<HttpServerSocket> sockets;
+static Events::ModuleEventProvider* aclevprov;
+static Events::ModuleEventProvider* reqevprov;
 
 /** HTTP socket states
  */
@@ -45,7 +44,7 @@ enum HttpState
 
 /** A socket used for HTTP transport
  */
-class HttpServerSocket : public BufferedSocket
+class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
 {
        HttpState InternalState;
        std::string ip;
@@ -58,18 +57,37 @@ class HttpServerSocket : public BufferedSocket
        std::string uri;
        std::string http_version;
 
- public:
-       const time_t createtime;
+       /** True if this object is in the cull list
+        */
+       bool waitingcull;
 
-       HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
-               : BufferedSocket(newfd), ip(IP), postsize(0)
-               , createtime(ServerInstance->Time())
+       bool Tick(time_t currtime) CXX11_OVERRIDE
        {
-               InternalState = HTTP_SERVE_WAIT_REQUEST;
+               AddToCull();
+               return false;
+       }
 
-               FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
-               if (GetIOHook())
-                       GetIOHook()->OnStreamSocketAccept(this, client, server);
+ public:
+       HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
+               : BufferedSocket(newfd)
+               , Timer(timeoutsec)
+               , InternalState(HTTP_SERVE_WAIT_REQUEST)
+               , ip(IP)
+               , postsize(0)
+               , waitingcull(false)
+       {
+               if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
+               {
+                       via->iohookprovs.back()->OnAccept(this, client, server);
+                       // IOHook may have errored
+                       if (!getError().empty())
+                       {
+                               AddToCull();
+                               return;
+                       }
+               }
+
+               ServerInstance->Timers.AddTimer(this);
        }
 
        ~HttpServerSocket()
@@ -77,9 +95,9 @@ class HttpServerSocket : public BufferedSocket
                sockets.erase(this);
        }
 
-       virtual void OnError(BufferedSocketError)
+       void OnError(BufferedSocketError) CXX11_OVERRIDE
        {
-               ServerInstance->GlobalCulls.AddItem(this);
+               AddToCull();
        }
 
        std::string Response(int response)
@@ -188,13 +206,8 @@ class HttpServerSocket : public BufferedSocket
 
                WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
 
-               time_t local = ServerInstance->Time();
-               struct tm *timeinfo = gmtime(&local);
-               char *date = asctime(timeinfo);
-               date[strlen(date) - 1] = '\0';
-               rheaders.CreateHeader("Date", date);
-
-               rheaders.CreateHeader("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,7 +224,7 @@ class HttpServerSocket : public BufferedSocket
                WriteData("\r\n");
        }
 
-       void OnDataReady()
+       void OnDataReady() CXX11_OVERRIDE
        {
                if (InternalState == HTTP_SERVE_RECV_POSTDATA)
                {
@@ -225,7 +238,7 @@ class HttpServerSocket : public BufferedSocket
 
                        if (reqbuffer.length() >= 8192)
                        {
-                               ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
                                reqbuffer.clear();
                                SetError("Buffer");
                        }
@@ -265,7 +278,7 @@ class HttpServerSocket : public BufferedSocket
                                continue;
                        }
 
-                       std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
+                       std::string cheader(reqbuffer, hbegin, hend - hbegin);
 
                        std::string::size_type fieldsep = cheader.find(':');
                        if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
@@ -296,7 +309,7 @@ class HttpServerSocket : public BufferedSocket
 
                        if (reqbuffer.length() >= postsize)
                        {
-                               postdata = reqbuffer.substr(0, postsize);
+                               postdata.assign(reqbuffer, 0, postsize);
                                reqbuffer.erase(0, postsize);
                        }
                        else if (!reqbuffer.empty())
@@ -318,14 +331,14 @@ class HttpServerSocket : public BufferedSocket
        {
                InternalState = HTTP_SERVE_SEND_DATA;
 
-               claimed = false;
-               HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
-               acl.Send();
-               if (!claimed)
+               ModResult MOD_RESULT;
+               HTTPRequest acl(request_type, uri, &headers, this, ip, postdata);
+               FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
+               if (MOD_RESULT != MOD_RES_DENY)
                {
-                       HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
-                       url.Send();
-                       if (!claimed)
+                       HTTPRequest url(request_type, uri, &headers, this, ip, postdata);
+                       FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
+                       if (MOD_RESULT == MOD_RES_PASSTHRU)
                        {
                                SendHTTPError(404);
                        }
@@ -337,73 +350,76 @@ class HttpServerSocket : public BufferedSocket
                SendHeaders(n->str().length(), response, *hheaders);
                WriteData(n->str());
        }
+
+       void AddToCull()
+       {
+               if (waitingcull)
+                       return;
+
+               waitingcull = true;
+               Close();
+               ServerInstance->GlobalCulls.AddItem(this);
+       }
+};
+
+class HTTPdAPIImpl : public HTTPdAPIBase
+{
+ public:
+       HTTPdAPIImpl(Module* parent)
+               : HTTPdAPIBase(parent)
+       {
+       }
+
+       void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
+       {
+               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;
        }
 
-       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")
                        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;
@@ -411,20 +427,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..00a50d7a1b6946fe9f27166806add95b2b33fb47 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,19 +34,22 @@ 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();
                ConfigTagList acls = ServerInstance->Config->ConfTags("httpdacl");
@@ -86,38 +86,29 @@ 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));
                }
        }
 
-       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="")
        {
-               ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "BlockAccess (%d)", returnval);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BlockAccess (%d)", 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)
                        {
@@ -133,10 +124,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)",
+                                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
                                                                                http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
                                                                BlockAccess(http, 403);
-                                                               return;
+                                                               return false;
                                                        }
                                                }
                                        }
@@ -155,16 +146,16 @@ 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)",
+                                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
                                                                        http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
                                                        BlockAccess(http, 403);
-                                                       return;
+                                                       return false;
                                                }
                                        }
                                        if (!this_acl->password.empty() && !this_acl->username.empty())
                                        {
                                                /* 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",
+                                               ServerInstance->Logs->Log(MODNAME, LOG_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());
 
                                                if (http->headers->IsSet("Authorization"))
@@ -183,7 +174,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 +184,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 +204,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..c47ddb5b2fb8ae70b5d36d832f246e76b3bb4f9e 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)
@@ -67,14 +66,12 @@ class ModuleHttpConfig : public Module
                return ret;
        }
 
-       void OnEvent(Event& event)
+       ModResult HandleRequest(HTTPRequest* http)
        {
                std::stringstream data("");
 
-               if (event.id == "httpd_url")
                {
-                       ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
-                       HTTPRequest* http = (HTTPRequest*)&event;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
 
                        if ((http->GetURI() == "/config") || (http->GetURI() == "/config/"))
                        {
@@ -84,8 +81,8 @@ class ModuleHttpConfig : public Module
                                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++)
+                                       const ConfigItems& items = x->second->getItems();
+                                       for (ConfigItems::const_iterator j = items.begin(); j != items.end(); j++)
                                        {
                                                data << Sanitize(j->first) << "=&quot;" << Sanitize(j->second) << "&quot; ";
                                        }
@@ -95,20 +92,23 @@ class ModuleHttpConfig : public Module
                                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("X-Powered-By", MODNAME);
                                response.headers.SetHeader("Content-Type", "text/html");
-                               response.Send();
+                               API->SendResponse(response);
+                               return MOD_RES_DENY; // Handled
                        }
                }
+               return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleHttpConfig()
+       ModResult OnHTTPRequest(HTTPRequest& req) CXX11_OVERRIDE
        {
+               return HandleRequest(&req);
        }
 
-       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..00cab319790fb769a56911d2dd14b1509a3164d9 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
+class ModuleHttpStats : public Module, public HTTPRequestEventListener
 {
-       static std::map<char, char const*> const &entities;
+       static const insp::flat_map<char, char const*>& entities;
+       HTTPdAPI API;
 
  public:
-
-       void init()
+       ModuleHttpStats()
+               : HTTPRequestEventListener(this)
+               , API(this)
        {
-               Implementation eventlist[] = { I_OnEvent };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
        std::string Sanitize(const std::string &str)
@@ -47,7 +44,7 @@ class ModuleHttpStats : public Module
 
                for (std::string::const_iterator x = str.begin(); x != str.end(); ++x)
                {
-                       std::map<char, char const*>::const_iterator it = entities.find(*x);
+                       insp::flat_map<char, char const*>::const_iterator it = entities.find(*x);
 
                        if (it != entities.end())
                        {
@@ -91,14 +88,12 @@ class ModuleHttpStats : public Module
                data << "</metadata>";
        }
 
-       void OnEvent(Event& event)
+       ModResult HandleRequest(HTTPRequest* http)
        {
                std::stringstream data("");
 
-               if (event.id == "httpd_url")
                {
-                       ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
-                       HTTPRequest* http = (HTTPRequest*)&event;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
 
                        if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
                        {
@@ -107,19 +102,21 @@ class ModuleHttpStats : public Module
                                        << Sanitize(ServerInstance->GetVersionString()) << "</version></server>";
 
                                data << "<general>";
-                               data << "<usercount>" << ServerInstance->Users->clientlist->size() << "</usercount>";
-                               data << "<channelcount>" << ServerInstance->chanlist->size() << "</channelcount>";
+                               data << "<usercount>" << ServerInstance->Users->GetUsers().size() << "</usercount>";
+                               data << "<channelcount>" << ServerInstance->GetChans().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 << "<socketcount>" << (SocketEngine::GetUsedFds()) << "</socketcount><socketmax>" << SocketEngine::GetMaxFds() << "</socketmax>";
+                               data << "<uptime><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
 
-                               data << "<isupport>" << Sanitize(ServerInstance->Config->data005) << "</isupport></general><xlines>";
+                               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)
+                               {
+                                       const Numeric::Numeric& num = *i;
+                                       for (std::vector<std::string>::const_iterator j = num.GetParams().begin(); j != num.GetParams().end()-1; ++j)
+                                               data << "<token>" << Sanitize(*j) << "</token>" << std::endl;
+                               }
+                               data << "</isupport></general><xlines>";
                                std::vector<std::string> xltypes = ServerInstance->XLines->GetAllTypes();
                                for (std::vector<std::string>::iterator it = xltypes.begin(); it != xltypes.end(); ++it)
                                {
@@ -138,35 +135,35 @@ class ModuleHttpStats : public Module
                                }
 
                                data << "</xlines><modulelist>";
-                               std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
+                               const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
 
-                               for (std::vector<std::string>::iterator i = module_names.begin(); i != module_names.end(); ++i)
+                               for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.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>";
+                                       Version v = i->second->GetVersion();
+                                       data << "<module><name>" << i->first << "</name><description>" << Sanitize(v.description) << "</description></module>";
                                }
                                data << "</modulelist><channellist>";
 
-                               for (chan_hash::const_iterator a = ServerInstance->chanlist->begin(); a != ServerInstance->chanlist->end(); ++a)
+                               const chan_hash& chans = ServerInstance->GetChans();
+                               for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
                                {
-                                       Channel* c = a->second;
+                                       Channel* c = i->second;
 
                                        data << "<channel>";
-                                       data << "<usercount>" << c->GetUsers()->size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
+                                       data << "<usercount>" << c->GetUsers().size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
                                        data << "<channeltopic>";
                                        data << "<topictext>" << Sanitize(c->topic) << "</topictext>";
                                        data << "<setby>" << Sanitize(c->setby) << "</setby>";
                                        data << "<settime>" << c->topicset << "</settime>";
                                        data << "</channeltopic>";
                                        data << "<channelmodes>" << Sanitize(c->ChanModes(true)) << "</channelmodes>";
-                                       const UserMembList* ulist = c->GetUsers();
 
-                                       for (UserMembCIter x = ulist->begin(); x != ulist->end(); ++x)
+                                       const Channel::MemberMap& ulist = c->GetUsers();
+                                       for (Channel::MemberMap::const_iterator x = ulist.begin(); x != ulist.end(); ++x)
                                        {
                                                Membership* memb = x->second;
                                                data << "<channelmember><uid>" << memb->user->uuid << "</uid><privs>"
-                                                       << Sanitize(c->GetAllPrefixChars(x->first)) << "</privs><modes>"
+                                                       << Sanitize(memb->GetAllPrefixChars()) << "</privs><modes>"
                                                        << memb->modes << "</modes>";
                                                DumpMeta(data, memb);
                                                data << "</channelmember>";
@@ -179,23 +176,24 @@ class ModuleHttpStats : public Module
 
                                data << "</channellist><userlist>";
 
-                               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 i = users.begin(); i != users.end(); ++i)
                                {
-                                       User* u = a->second;
+                                       User* u = i->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))
+                                               << u->GetRealHost() << "</realhost><displayhost>" << u->GetDisplayedHost() << "</displayhost><gecos>"
+                                               << Sanitize(u->fullname) << "</gecos><server>" << u->server->GetName() << "</server>";
+                                       if (u->IsAway())
                                                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>";
+                                       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 << "<port>" << lu->GetServerPort() << "</port><servaddr>"
-                                                       << irc::sockets::satouser(lu->server_sa) << "</servaddr>";
+                                                       << lu->server_sa.str() << "</servaddr>";
                                        data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
 
                                        DumpMeta(data, u);
@@ -205,10 +203,10 @@ class ModuleHttpStats : public Module
 
                                data << "</userlist><serverlist>";
 
-                               ProtoServerList sl;
+                               ProtocolInterface::ServerList sl;
                                ServerInstance->PI->GetServerList(sl);
 
-                               for (ProtoServerList::iterator b = sl.begin(); b != sl.end(); ++b)
+                               for (ProtocolInterface::ServerList::const_iterator b = sl.begin(); b != sl.end(); ++b)
                                {
                                        data << "<server>";
                                        data << "<servername>" << b->servername << "</servername>";
@@ -221,30 +219,41 @@ class ModuleHttpStats : public Module
                                        data << "</server>";
                                }
 
-                               data << "</serverlist></inspircdstats>";
+                               data << "</serverlist><commandlist>";
+
+                               const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+                               for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+                               {
+                                       data << "<command><name>" << i->second->name << "</name><usecount>" << i->second->use_count << "</usecount></command>";
+                               }
+
+                               data << "</commandlist></inspircdstats>";
 
                                /* Send the document back to m_httpd */
                                HTTPDocumentResponse response(this, *http, &data, 200);
-                               response.headers.SetHeader("X-Powered-By", "m_httpd_stats.so");
+                               response.headers.SetHeader("X-Powered-By", MODNAME);
                                response.headers.SetHeader("Content-Type", "text/xml");
-                               response.Send();
+                               API->SendResponse(response);
+                               return MOD_RES_DENY; // Handled
                        }
                }
+               return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleHttpStats()
+       ModResult OnHTTPRequest(HTTPRequest& req) CXX11_OVERRIDE
        {
+               return HandleRequest(&req);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides statistics over HTTP via m_httpd.so", VF_VENDOR);
+               return Version("Provides statistics over HTTP via m_httpd", VF_VENDOR);
        }
 };
 
-static std::map<char, char const*> const &init_entities()
+static const insp::flat_map<char, char const*>& init_entities()
 {
-       static std::map<char, char const*> entities;
+       static insp::flat_map<char, char const*> entities;
        entities['<'] = "lt";
        entities['>'] = "gt";
        entities['&'] = "amp";
@@ -252,6 +261,6 @@ static std::map<char, char const*> const &init_entities()
        return entities;
 }
 
-std::map<char, char const*> const &ModuleHttpStats::entities = init_entities ();
+const insp::flat_map<char, char const*>& ModuleHttpStats::entities = init_entities();
 
 MODULE_INIT(ModuleHttpStats)
index f0ced1db79789ba313711dc97defae384f1dc8b1..806c70ad2410b86c4ece7c09850f50abab959b3d 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for RFC1413 ident lookups */
-
 /* --------------------------------------------------------------
  * Note that this is the third incarnation of m_ident. The first
  * two attempts were pretty crashy, mainly due to the fact we tried
@@ -119,33 +117,32 @@ 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.sa, connaddr.sa_size()) == -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];
 
@@ -161,34 +158,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 +169,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 +179,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 +199,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 +231,66 @@ 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;
                        }
                }
        }
-};
 
-class ModuleIdent : public Module
-{
-       int RequestTimeout;
-       SimpleExtItem<IdentRequestSocket> ext;
- public:
-       ModuleIdent() : ext("ident_socket", this)
+       void OnEventHandlerError(int errornum) CXX11_OVERRIDE
        {
+               Close();
+               done = true;
        }
 
-       void init()
+       CullResult cull() CXX11_OVERRIDE
        {
-               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));
+               Close();
+               return EventHandler::cull();
        }
+};
 
-       ~ModuleIdent()
+class ModuleIdent : public Module
+{
+       int RequestTimeout;
+       bool NoLookupPrefix;
+       SimpleExtItem<IdentRequestSocket, stdalgo::culldeleter> ext;
+ public:
+       ModuleIdent()
+               : ext("ident_socket", 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");
+               RequestTimeout = tag->getDuration("timeout", 5, 1);
+               NoLookupPrefix = tag->getBool("nolookupprefix", false);
        }
 
-       void OnUserInit(LocalUser *user)
+       void OnUserInit(LocalUser *user) CXX11_OVERRIDE
        {
                ConfigTag* tag = user->MyClass->config;
                if (!tag->getBool("useident", true))
                        return;
 
-               user->WriteServ("NOTICE Auth :*** Looking up your ident...");
+               user->WriteNotice("*** Looking up your ident...");
 
                try
                {
-                       IdentRequestSocket *isock = new IdentRequestSocket(IS_LOCAL(user));
+                       IdentRequestSocket *isock = new IdentRequestSocket(user);
                        ext.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,18 +298,17 @@ 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);
                if (!isock)
                {
-                       ServerInstance->Logs->Log("m_ident",DEBUG, "No ident socket :(");
+                       if ((NoLookupPrefix) && (user->ident[0] != '~'))
+                               user->ident.insert(user->ident.begin(), 1, '~');
                        return MOD_RES_PASSTHRU;
                }
 
-               ServerInstance->Logs->Log("m_ident",DEBUG, "Has ident_socket");
-
                time_t compare = isock->age;
                compare += RequestTimeout;
 
@@ -347,28 +316,24 @@ class ModuleIdent : public Module
                if (ServerInstance->Time() >= compare)
                {
                        /* Ident timeout */
-                       user->WriteServ("NOTICE Auth :*** Ident request timed out.");
-                       ServerInstance->Logs->Log("m_ident",DEBUG, "Timeout");
+                       user->WriteNotice("*** Ident request timed out.");
                }
                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())
                {
                        user->ident.insert(user->ident.begin(), 1, '~');
-                       user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident.c_str());
+                       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());
+                       user->WriteNotice("*** Found your ident, '" + user->ident + "'");
                }
 
                user->InvalidateCache();
@@ -377,35 +342,12 @@ class ModuleIdent : public Module
                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] == '~')
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
-
-       virtual void OnCleanup(int target_type, void *item)
-       {
-               /* 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);
-               }
-       }
 };
 
 MODULE_INIT(ModuleIdent)
-
index 747a3b30a53be2512bf01a80e4520ec0dea8cb8e..6229e1fa2fac06495210048034962468f47b7f62 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.
@@ -54,27 +51,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"] = "I";
        }
 
-       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,25 +73,20 @@ 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();
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the +I channel mode", VF_VENDOR);
        }
index b7dd0e81bbfcd1e50a00f9f5aa198ec27804d0e2..543fb49a40d9c3a275434abe47fec2b3fa67e77e 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/cap.h"
+#include "modules/ircv3.h"
 
-class ModuleIRCv3 : public Module
+class ModuleIRCv3 : public Module, public AccountEventListener
 {
-       GenericCap cap_accountnotify;
-       GenericCap cap_awaynotify;
-       GenericCap cap_extendedjoin;
-       bool accountnotify;
-       bool awaynotify;
-       bool extendedjoin;
+       Cap::Capability cap_accountnotify;
+       Cap::Capability cap_awaynotify;
+       Cap::Capability cap_extendedjoin;
 
        CUList last_excepts;
 
-       void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
-       {
-               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);
-                       }
-               }
-       }
-
  public:
-       ModuleIRCv3() : cap_accountnotify(this, "account-notify"),
+       ModuleIRCv3()
+               : AccountEventListener(this)
+               , cap_accountnotify(this, "account-notify"),
                                        cap_awaynotify(this, "away-notify"),
                                        cap_extendedjoin(this, "extended-join")
        {
        }
 
-       void init()
-       {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnUserJoin, I_OnPostJoin, I_OnSetAway, I_OnEvent, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
-               accountnotify = conf->getBool("accountnotify", conf->getBool("accoutnotify", true));
-               awaynotify = conf->getBool("awaynotify", true);
-               extendedjoin = conf->getBool("extendedjoin", true);
+               cap_accountnotify.SetActive(conf->getBool("accountnotify", true));
+               cap_awaynotify.SetActive(conf->getBool("awaynotify", true));
+               cap_extendedjoin.SetActive(conf->getBool("extendedjoin", true));
        }
 
-       void OnEvent(Event& ev)
+       void OnAccountChange(User* user, const std::string& newaccount) CXX11_OVERRIDE
        {
-               if (awaynotify)
-                       cap_awaynotify.HandleEvent(ev);
-               if (extendedjoin)
-                       cap_extendedjoin.HandleEvent(ev);
-
-               if (accountnotify)
-               {
-                       cap_accountnotify.HandleEvent(ev);
-
-                       if (ev.id == "account_login")
-                       {
-                               AccountEvent* ae = static_cast<AccountEvent*>(&ev);
-
-                               // :nick!user@host ACCOUNT account
-                               // or
-                               // :nick!user@host ACCOUNT *
-                               std::string line =  ":" + ae->user->GetFullHost() + " ACCOUNT ";
-                               if (ae->account.empty())
-                                       line += "*";
-                               else
-                                       line += std::string(ae->account);
-
-                               WriteNeighboursWithExt(ae->user, line, cap_accountnotify.ext);
-                       }
-               }
+               // :nick!user@host ACCOUNT account
+               // or
+               // :nick!user@host ACCOUNT *
+               std::string line = ":" + user->GetFullHost() + " ACCOUNT ";
+               if (newaccount.empty())
+                       line += "*";
+               else
+                       line += newaccount;
+
+               IRCv3::WriteNeighborsWithCap(user, line, cap_accountnotify);
        }
 
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
        {
                // Remember who is not going to see the JOIN because of other modules
-               if ((awaynotify) && (IS_AWAY(memb->user)))
+               if ((cap_awaynotify.IsActive()) && (memb->user->IsAway()))
                        last_excepts = excepts;
 
-               if (!extendedjoin)
+               if (!cap_extendedjoin.IsActive())
                        return;
 
                /*
@@ -143,12 +81,12 @@ class ModuleIRCv3 : public Module
                std::string line;
                std::string mode;
 
-               const UserMembList* userlist = memb->chan->GetUsers();
-               for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
+               const Channel::MemberMap& userlist = memb->chan->GetUsers();
+               for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
                {
                        // Send the extended join line if the current member is local, has the extended-join cap and isn't excepted
                        User* member = IS_LOCAL(it->first);
-                       if ((member) && (cap_extendedjoin.ext.get(member)) && (excepts.find(member) == excepts.end()))
+                       if ((member) && (cap_extendedjoin.get(member)) && (excepts.find(member) == excepts.end()))
                        {
                                // Construct the lines we're going to send if we haven't constructed them already
                                if (line.empty())
@@ -195,9 +133,9 @@ class ModuleIRCv3 : public Module
                }
        }
 
-       ModResult OnSetAway(User* user, const std::string &awaymsg)
+       ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE
        {
-               if (awaynotify)
+               if (cap_awaynotify.IsActive())
                {
                        // Going away: n!u@h AWAY :reason
                        // Back from away: n!u@h AWAY
@@ -205,24 +143,24 @@ class ModuleIRCv3 : public Module
                        if (!awaymsg.empty())
                                line += " :" + awaymsg;
 
-                       WriteNeighboursWithExt(user, line, cap_awaynotify.ext);
+                       IRCv3::WriteNeighborsWithCap(user, line, cap_awaynotify);
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void OnPostJoin(Membership *memb)
+       void OnPostJoin(Membership *memb) CXX11_OVERRIDE
        {
-               if ((!awaynotify) || (!IS_AWAY(memb->user)))
+               if ((!cap_awaynotify.IsActive()) || (!memb->user->IsAway()))
                        return;
 
                std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
 
-               const UserMembList* userlist = memb->chan->GetUsers();
-               for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
+               const Channel::MemberMap& userlist = memb->chan->GetUsers();
+               for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
                {
                        // Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
                        User* member = IS_LOCAL(it->first);
-                       if ((member) && (cap_awaynotify.ext.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
+                       if ((member) && (cap_awaynotify.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
                        {
                                member->Write(line);
                        }
@@ -231,12 +169,12 @@ class ModuleIRCv3 : public Module
                last_excepts.clear();
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
        }
 
-       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_capnotify.cpp b/src/modules/m_ircv3_capnotify.cpp
new file mode 100644 (file)
index 0000000..93c30df
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/reload.h"
+
+class CapNotify : public Cap::Capability
+{
+       bool OnRequest(LocalUser* user, bool add) CXX11_OVERRIDE
+       {
+               // Users using the negotiation protocol v3.2 or newer may not turn off cap-notify
+               if ((!add) && (GetProtocol(user) != Cap::CAP_LEGACY))
+                       return false;
+               return true;
+       }
+
+       bool OnList(LocalUser* user) CXX11_OVERRIDE
+       {
+               // If the client supports 3.2 enable cap-notify for them
+               if (GetProtocol(user) != Cap::CAP_LEGACY)
+                       set(user, true);
+               return true;
+       }
+
+ public:
+       CapNotify(Module* mod)
+               : Cap::Capability(mod, "cap-notify")
+       {
+       }
+};
+
+class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public ReloadModule::EventListener
+{
+       CapNotify capnotify;
+       std::string reloadedmod;
+       std::vector<std::string> reloadedcaps;
+
+       void Send(const std::string& capname, Cap::Capability* cap, bool add)
+       {
+               std::string msg = (add ? "NEW :" : "DEL :");
+               msg.append(capname);
+               std::string msgwithval = msg;
+               msgwithval.push_back('=');
+               std::string::size_type msgpos = msgwithval.size();
+
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+               {
+                       LocalUser* user = *i;
+                       if (!capnotify.get(user))
+                               continue;
+
+                       // If the cap is being added and the client supports cap values then show the value, if any
+                       if ((add) && (capnotify.GetProtocol(user) != Cap::CAP_LEGACY))
+                       {
+                               const std::string* capvalue = cap->GetValue(user);
+                               if ((capvalue) && (!capvalue->empty()))
+                               {
+                                       msgwithval.append(*capvalue);
+                                       user->WriteCommand("CAP", msgwithval);
+                                       msgwithval.erase(msgpos);
+                                       continue;
+                               }
+                       }
+                       user->WriteCommand("CAP", msg);
+               }
+       }
+
+ public:
+       ModuleIRCv3CapNotify()
+               : Cap::EventListener(this)
+               , ReloadModule::EventListener(this)
+               , capnotify(this)
+       {
+       }
+
+       void OnCapAddDel(Cap::Capability* cap, bool add) CXX11_OVERRIDE
+       {
+               if (cap->creator == this)
+                       return;
+
+               if (cap->creator->ModuleSourceFile == reloadedmod)
+               {
+                       if (!add)
+                               reloadedcaps.push_back(cap->GetName());
+                       return;
+               }
+               Send(cap->GetName(), cap, add);
+       }
+
+       void OnCapValueChange(Cap::Capability* cap) CXX11_OVERRIDE
+       {
+               // The value of a cap has changed, send CAP DEL and CAP NEW with the new value
+               Send(cap->GetName(), cap, false);
+               Send(cap->GetName(), cap, true);
+       }
+
+       void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+       {
+               if (mod == this)
+                       return;
+               reloadedmod = mod->ModuleSourceFile;
+               // Request callback when reload is complete
+               cd.add(this, NULL);
+       }
+
+       void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
+       {
+               // Reloading can change the set of caps provided by a module so assuming that if the reload succeded all
+               // caps that the module previously provided are available or all were lost if the reload failed is wrong.
+               // Instead, we verify the availability of each cap individually.
+               dynamic_reference_nocheck<Cap::Manager> capmanager(this, "capmanager");
+               if (capmanager)
+               {
+                       for (std::vector<std::string>::const_iterator i = reloadedcaps.begin(); i != reloadedcaps.end(); ++i)
+                       {
+                               const std::string& capname = *i;
+                               if (!capmanager->Find(capname))
+                                       Send(capname, NULL, false);
+                       }
+               }
+               reloadedmod.clear();
+               reloadedcaps.clear();
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the cap-notify IRCv3.2 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3CapNotify)
diff --git a/src/modules/m_ircv3_chghost.cpp b/src/modules/m_ircv3_chghost.cpp
new file mode 100644 (file)
index 0000000..0a9e055
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/ircv3.h"
+
+class ModuleIRCv3ChgHost : public Module
+{
+       Cap::Capability cap;
+
+       void DoChgHost(User* user, const std::string& ident, const std::string& host)
+       {
+               std::string line(1, ':');
+               line.append(user->GetFullHost()).append(" CHGHOST ").append(ident).append(1, ' ').append(host);
+               IRCv3::WriteNeighborsWithCap(user, line, cap);
+       }
+
+ public:
+       ModuleIRCv3ChgHost()
+               : cap(this, "chghost")
+       {
+       }
+
+       void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+       {
+               DoChgHost(user, newident, user->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.2 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3ChgHost)
diff --git a/src/modules/m_ircv3_echomessage.cpp b/src/modules/m_ircv3_echomessage.cpp
new file mode 100644 (file)
index 0000000..8773d71
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2013-2015 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+static const char* MessageTypeStringSp[] = { "PRIVMSG ", "NOTICE " };
+
+class ModuleIRCv3EchoMessage : public Module
+{
+       Cap::Capability cap;
+
+ public:
+       ModuleIRCv3EchoMessage()
+               : cap(this, "echo-message")
+       {
+       }
+
+       void OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
+       {
+               if (!cap.get(user))
+                       return;
+
+               std::string msg = MessageTypeStringSp[msgtype];
+               if (target_type == TYPE_USER)
+               {
+                       User* destuser = static_cast<User*>(dest);
+                       msg.append(destuser->nick);
+               }
+               else if (target_type == TYPE_CHANNEL)
+               {
+                       if (status)
+                               msg.push_back(status);
+
+                       Channel* chan = static_cast<Channel*>(dest);
+                       msg.append(chan->name);
+               }
+               else
+               {
+                       const char* servername = static_cast<const char*>(dest);
+                       msg.append(servername);
+               }
+               msg.append(" :").append(text);
+               user->WriteFrom(user, msg);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the echo-message IRCv3.2 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3EchoMessage)
diff --git a/src/modules/m_ircv3_invitenotify.cpp b/src/modules/m_ircv3_invitenotify.cpp
new file mode 100644 (file)
index 0000000..3783ff3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+class ModuleIRCv3InviteNotify : public Module
+{
+       Cap::Capability cap;
+
+ public:
+       ModuleIRCv3InviteNotify()
+               : cap(this, "invite-notify")
+       {
+       }
+
+       void OnUserInvite(User* source, User* dest, Channel* chan, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE
+       {
+               std::string msg = "INVITE ";
+               msg.append(dest->nick).append(1, ' ').append(chan->name);
+               const Channel::MemberMap& users = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+               {
+                       User* user = i->first;
+                       // Skip members who don't use this extension or were excluded by other modules
+                       if ((!cap.get(user)) || (notifyexcepts.count(user)))
+                               continue;
+
+                       Membership* memb = i->second;
+                       // Check whether the member has a high enough rank to see the notification
+                       if (memb->getRank() < notifyrank)
+                               continue;
+
+                       // Send and add the user to the exceptions so they won't get the NOTICE invite announcement message
+                       user->WriteFrom(source, msg);
+                       notifyexcepts.insert(user);
+               }
+       }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               // Prioritize after all modules to see all excepted users
+               ServerInstance->Modules.SetPriority(this, I_OnUserInvite, PRIORITY_LAST);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the invite-notify IRCv3.2 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3InviteNotify)
index 63bcc38a4180c30526814f66ee6396d433b94ae8..077ceff52c14ff7ea507f54ff2279bf06a089331 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +j (join flood protection) */
+// 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 +66,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)
        {
-               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(608, channel->name, "Invalid flood parameter");
+                       return MODEACTION_DENY;
                }
-               else
+
+               /* Set up the flood parameters for this channel */
+               unsigned int njoins = ConvToInt(parameter.substr(0, colon));
+               unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
+               if ((njoins<1) || (nsecs<1))
                {
-                       if (channel->IsModeSet('j'))
-                       {
-                               ext.unset(channel);
-                               channel->SetModeParam('j', "");
-                               return MODEACTION_ALLOW;
-                       }
+                       source->WriteNumeric(608, channel->name, "Invalid flood 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(609, chan->name, "This channel is temporarily unavailable (+j). 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,16 +168,12 @@ 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);
        }
index 44c6fc0d9fb20cfbd11c5a23015faac793c73f36..89391c8a4bf6268e3ae8c6e438787049e1491bf0 100644 (file)
@@ -20,8 +20,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support for the RPL_REDIR numeric and the /JUMPSERVER command. */
+#include "modules/ssl.h"
 
 /** Handle /JUMPSERVER
  */
@@ -32,11 +31,14 @@ class CommandJumpserver : public Command
        std::string redirect_to;
        std::string reason;
        int port;
+       int sslport;
 
        CommandJumpserver(Module* Creator) : Command(Creator, "JUMPSERVER", 0, 4)
        {
-               flags_needed = 'o'; syntax = "[<server> <port> <+/-an> <reason>]";
+               flags_needed = 'o';
+               syntax = "[<server> <port>[:<sslport>] <+/-an> <reason>]";
                port = 0;
+               sslport = 0;
                redirect_new_users = false;
        }
 
@@ -53,11 +55,12 @@ class CommandJumpserver : public Command
                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);
+                               user->WriteNotice("*** Disabled jumpserver (previously set to '" + redirect_to + ":" + ConvToStr(port) + "')");
                        else
-                               user->WriteServ("NOTICE %s :*** Jumpserver was not enabled.", user->nick.c_str());
+                               user->WriteNotice("*** Jumpserver was not enabled.");
 
                        port = 0;
+                       sslport = 0;
                        redirect_to.clear();
                        return CMD_SUCCESS;
                }
@@ -84,27 +87,36 @@ class CommandJumpserver : public Command
                                                redirect_new_users = direction;
                                        break;
                                        default:
-                                               user->WriteServ("NOTICE %s :*** Invalid JUMPSERVER flag: %c", user->nick.c_str(), *n);
+                                               user->WriteNotice("*** Invalid JUMPSERVER flag: " + ConvToStr(*n));
                                                return CMD_FAILURE;
                                        break;
                                }
                        }
 
-                       if (!atoi(parameters[1].c_str()))
+                       size_t delimpos = parameters[1].find(':');
+                       port = ConvToInt(parameters[1].substr(0, delimpos ? delimpos : std::string::npos));
+                       sslport = (delimpos == std::string::npos ? 0 : ConvToInt(parameters[1].substr(delimpos + 1)));
+
+                       if (parameters[1].find_first_not_of("0123456789:") != std::string::npos
+                               || parameters[1].rfind(':') != delimpos
+                               || port > 65535 || sslport > 65535)
                        {
-                               user->WriteServ("NOTICE %s :*** Invalid port number", user->nick.c_str());
+                               user->WriteNotice("*** Invalid port number");
                                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)
+                               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+                               for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); )
                                {
-                                       User* t = *i;
-                                       if (!IS_OPER(t))
+                                       // Quitting the user removes it from the list
+                                       LocalUser* t = *i;
+                                       ++i;
+                                       if (!t->IsOper())
                                        {
-                                               t->WriteNumeric(10, "%s %s %s :Please use this Server/Port instead", t->nick.c_str(), parameters[0].c_str(), parameters[1].c_str());
+                                               t->WriteNumeric(RPL_REDIR, parameters[0], GetPort(t), "Please use this Server/Port instead");
                                                ServerInstance->Users->QuitUser(t, reason);
                                                n_done++;
                                        }
@@ -116,24 +128,24 @@ class CommandJumpserver : public Command
                        }
 
                        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());
+                       user->WriteNotice("*** Set jumpserver to server '" + parameters[0] + "' port '" + (port ? ConvToStr(port) : "Auto") + ", SSL " + (sslport ? ConvToStr(sslport) : "Auto") + "', flags '+" +
+                               (redirect_all_immediately ? "a" : "") + (redirect_new_users ? "n'" : "'") +
+                               (n_done ? " (" + n_done_s + "user(s) redirected): " : ": ") + reason);
                }
 
                return CMD_SUCCESS;
        }
-};
 
+       int GetPort(LocalUser* user)
+       {
+               int p = (SSLIOHook::IsSSL(&user->eh) ? sslport : port);
+               if (p == 0)
+                       p = user->GetServerPort();
+               return p;
+       }
+};
 
 class ModuleJumpServer : public Module
 {
@@ -143,40 +155,29 @@ class ModuleJumpServer : public Module
        {
        }
 
-       void init()
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
-               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)
+               if (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);
+                       int port = js.GetPort(user);
+                       user->WriteNumeric(RPL_REDIR, js.redirect_to, port, "Please use this Server/Port instead");
                        ServerInstance->Users->QuitUser(user, js.reason);
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                // Emergency way to unlock
-               if (!user) js.redirect_new_users = false;
+               if (!status.srcuser)
+                       js.redirect_new_users = false;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the RPL_REDIR numeric and the /JUMPSERVER command.", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleJumpServer)
index a914f38693108b69d37a357d7131a92d72c6d43c..ad8bfdcb6d1c60b283f8f604d8c341cb0f288907 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */
+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;
+
+       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;
+       }
 
-typedef std::map<std::string, time_t> delaylist;
+       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)
        {
-               if (adding)
-               {
-                       int v = ConvToInt(parameter);
-                       if (v <= 0)
-                               return MODEACTION_DENY;
-                       if (parameter == channel->GetModeParameter(this))
-                               return MODEACTION_DENY;
+               int v = ConvToInt(parameter);
+               if (v <= 0)
+                       return MODEACTION_DENY;
 
-                       if ((IS_LOCAL(source) && ((unsigned int)v > max)))
-                               v = max;
+               if ((IS_LOCAL(source) && ((unsigned int)v > max)))
+                       v = max;
 
-                       parameter = ConvToStr(v);
-                       channel->SetModeParam(this, parameter);
-               }
-               else
-               {
-                       if (!channel->IsModeSet(this))
-                               return MODEACTION_DENY;
-
-                       ext.unset(channel);
-                       channel->SetModeParam(this, "");
-               }
+               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
@@ -75,85 +120,41 @@ class ModuleKickNoRejoin : public Module
        KickRejoin kr;
 
 public:
-
        ModuleKickNoRejoin()
                : kr(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) && (!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_DELAYREJOIN, chan, InspIRCd::Format("You must wait %u seconds after being kicked to rejoin (+J)", data->delay));
+                               return MOD_RES_DENY;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       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() CXX11_OVERRIDE
        {
-       }
-
-       Version GetVersion()
-       {
-               return Version("Channel mode to delay rejoin after kick", VF_VENDOR);
+               return Version("Channel mode to delay rejoin after kick", VF_VENDOR | VF_COMMON, kr.GetModuleSettings());
        }
 };
 
-
 MODULE_INIT(ModuleKickNoRejoin)
index 8d2aa4543065be26e7b410ef41007c6d664cd37d..cf623c4abdd2e1c12c1d0553f9b65e8af4afb356 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for /KNOCK and channel mode +K */
-
 /** 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>";
                Penalty = 5;
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
        }
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
@@ -42,35 +45,35 @@ class CommandKnock : public Command
                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::NoSuchNick(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(480, 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());
 
-               user->WriteServ("NOTICE %s :KNOCKing on %s", user->nick.c_str(), c->name.c_str());
+               user->WriteNotice("KNOCKing on " + c->name);
                return CMD_SUCCESS;
        }
 
@@ -80,43 +83,27 @@ class CommandKnock : public Command
        }
 };
 
-/** 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,7 +115,7 @@ 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);
        }
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp
new file mode 100644 (file)
index 0000000..fedf02b
--- /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 answer from 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..45e8333
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 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;
+
+               std::vector<std::string> params;
+               params.push_back(opername);
+               params.push_back(password);
+               oper_command->Handle(params, user);
+       }
+
+       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, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line) 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..068573e0dccc2cedbca3f3728199cbbaeb1674d8 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)
        {
-               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)
        {
-               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,52 +84,38 @@ 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()
-       {
-       }
-
-
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                // Emergency way to unlock
-               if (!user) locked = false;
+               if (!status.srcuser)
+                       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);
        }
index 546e342ae36180d4644921501689e8b6c24b6f3f..41de2997d3db56a361b4050cd9b2c82ba7a9b9d2 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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Hide /MAP and /LINKS in the same form as ircu (mostly useless)", VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleMapHide)
-
index c902ee3cb753c8f9c527a0bfd3e381656261c766..26ff4cffc7bb9aca8f09895a85aeb3df7e1537ce 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)
        {
                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..fa929294c9a8427add9809e12d4ea4527613ea23 100644 (file)
@@ -24,8 +24,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +f (message flood protection) */
+#include "modules/exemption.h"
 
 /** Holds flood settings and state for mode +f
  */
@@ -36,7 +35,7 @@ class floodsettings
        unsigned int secs;
        unsigned int lines;
        time_t reset;
-       std::map<User*, unsigned int> counters;
+       insp::flat_map<User*, unsigned int> counters;
 
        floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
        {
@@ -56,92 +55,77 @@ 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)
        {
-               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(608, channel->name, "Invalid flood 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 = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
+               unsigned int nsecs = ConvToInt(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(608, channel->name, "Invalid flood 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
 {
+       CheckExemption::EventProvider exemptionprov;
        MsgFlood mf;
 
  public:
 
        ModuleMsgFlood()
-               : mf(this)
+               : exemptionprov(this)
+               , mf(this)
        {
        }
 
-       void init()
+       ModResult OnUserPreMessage(User* user, void* voiddest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
-               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 != 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 = static_cast<Channel*>(voiddest);
+               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 +137,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,30 +154,13 @@ 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)
-       {
-               if (target_type == TYPE_CHANNEL)
-                       return ProcessMessages(user,(Channel*)dest,text);
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       ModResult OnUserPreNotice(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;
-       }
-
-       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);
        }
index d1df81354b65e7ce9c64b5b9cbbbe1b4026488e4..85787ae9644b2e6c2c21983141c9a8fb3ceea46c 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);
        }
 
-       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 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..056eb4a
--- /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 = "<modes> <message>";
+               flags_needed = 'o';
+       }
+
+       CmdResult Handle(const std::vector<std::string>& parameters, User *src)
+       {
+               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 std::vector<std::string>& parameters)
+       {
+               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..c69732a
--- /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(unset_raw(container));
+               }
+
+               std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+               {
+                       std::string ret;
+                       if (format == FORMAT_NETWORK)
+                               return ret;
+
+                       const ExtData* extdata = static_cast<ExtData*>(item);
+                       for (WatchedList::const_iterator i = extdata->list.begin(); i != extdata->list.end(); ++i)
+                       {
+                               const Entry* entry = *i;
+                               ret.append(entry->GetNick()).push_back(' ');
+                       }
+                       if (!ret.empty())
+                               ret.erase(ret.size()-1);
+                       return ret;
+               }
+
+               void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
+
+               void free(void* item)
+               {
+                       delete static_cast<ExtData*>(item);
+               }
+       };
+
+ public:
+       Manager(Module* mod, const std::string& extname)
+               : ext(mod, extname, *this)
+       {
+       }
+
+       enum WatchResult
+       {
+               WR_OK,
+               WR_TOOMANY,
+               WR_ALREADYWATCHING,
+               WR_INVALIDNICK
+       };
+
+       WatchResult Watch(LocalUser* user, const std::string& nick, unsigned int maxwatch)
+       {
+               if (!ServerInstance->IsNick(nick))
+                       return WR_INVALIDNICK;
+
+               WatchedList* watched = GetWatchedPriv(user, true);
+               if (watched->size() >= maxwatch)
+                       return WR_TOOMANY;
+
+               Entry* entry = AddWatcher(nick, user);
+               if (stdalgo::isin(*watched, entry))
+                       return WR_ALREADYWATCHING;
+
+               entry->watchers.push_back(user);
+               watched->push_back(entry);
+               return WR_OK;
+       }
+
+       bool Unwatch(LocalUser* user, const std::string& nick)
+       {
+               WatchedList* list = GetWatchedPriv(user);
+               if (!list)
+                       return false;
+
+               bool ret = RemoveWatcher(nick, user, *list);
+               // If no longer watching any nick unset ext
+               if (list->empty())
+                       ext.unset(user);
+               return ret;
+       }
+
+       const WatchedList& GetWatched(LocalUser* user)
+       {
+               WatchedList* list = GetWatchedPriv(user);
+               if (list)
+                       return *list;
+               return emptywatchedlist;
+       }
+
+       void UnwatchAll(LocalUser* user)
+       {
+               WatchedList* list = GetWatchedPriv(user);
+               if (!list)
+                       return;
+
+               while (!list->empty())
+               {
+                       Entry* entry = list->front();
+                       RemoveWatcher(entry->GetNick(), user, *list);
+               }
+               ext.unset(user);
+       }
+
+       WatcherList* GetWatcherList(const std::string& nick)
+       {
+               Entry* entry = Find(nick);
+               if (entry)
+                       return &entry->watchers;
+               return NULL;
+       }
+
+       static User* FindNick(const std::string& nick)
+       {
+               User* user = ServerInstance->FindNickOnly(nick);
+               if ((user) && (user->registered == REG_ALL))
+                       return user;
+               return NULL;
+       }
+
+ private:
+       typedef TR1NS::unordered_map<std::string, Entry, irc::insensitive, irc::StrHashComp> NickHash;
+
+       Entry* Find(const std::string& nick)
+       {
+               NickHash::iterator it = nicks.find(nick);
+               if (it != nicks.end())
+                       return &it->second;
+               return NULL;
+       }
+
+       Entry* AddWatcher(const std::string& nick, LocalUser* user)
+       {
+               std::pair<NickHash::iterator, bool> ret = nicks.insert(std::make_pair(nick, Entry()));
+               Entry& entry = ret.first->second;
+               if (ret.second)
+                       entry.SetNick(nick);
+               return &entry;
+       }
+
+       bool RemoveWatcher(const std::string& nick, LocalUser* user, WatchedList& watchedlist)
+       {
+               NickHash::iterator it = nicks.find(nick);
+               // If nobody is watching this nick the user trying to remove it isn't watching it for sure
+               if (it == nicks.end())
+                       return false;
+
+               Entry& entry = it->second;
+               // Erase from the user's list of watched nicks
+               if (!stdalgo::vector::swaperase(watchedlist, &entry))
+                       return false; // User is not watching this nick
+
+               // Erase from the nick's list of watching users
+               stdalgo::vector::swaperase(entry.watchers, user);
+
+               // If nobody else is watching the nick remove map entry
+               if (entry.watchers.empty())
+                       nicks.erase(it);
+
+               return true;
+       }
+
+       WatchedList* GetWatchedPriv(LocalUser* user, bool create = false)
+       {
+               ExtData* extdata = ext.get(user, create);
+               if (!extdata)
+                       return NULL;
+               return &extdata->list;
+       }
+
+       NickHash nicks;
+       ExtItem ext;
+       WatchedList emptywatchedlist;
+};
+
+// inline is needed in static builds to support m_watch including the Manager code from this file
+inline void IRCv3::Monitor::Manager::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+       if (format == FORMAT_NETWORK)
+               return;
+
+       irc::spacesepstream ss(value);
+       for (std::string nick; ss.GetToken(nick); )
+               manager.Watch(static_cast<LocalUser*>(container), nick, UINT_MAX);
+}
+
+#ifndef INSPIRCD_MONITOR_MANAGER_ONLY
+
+enum
+{
+       RPL_MONONLINE = 730,
+       RPL_MONOFFLINE = 731,
+       RPL_MONLIST = 732,
+       RPL_ENDOFMONLIST = 733,
+       ERR_MONLISTFULL = 734
+};
+
+class CommandMonitor : public SplitCommand
+{
+       typedef Numeric::Builder<> ReplyBuilder;
+       // Additional penalty for the /MONITOR L and /MONITOR S commands that request a list from the server
+       static const unsigned int ListPenalty = 3000;
+
+       IRCv3::Monitor::Manager& manager;
+
+       void HandlePlus(LocalUser* user, const std::string& input)
+       {
+               ReplyBuilder online(user, RPL_MONONLINE);
+               ReplyBuilder offline(user, RPL_MONOFFLINE);
+               irc::commasepstream ss(input);
+               for (std::string nick; ss.GetToken(nick); )
+               {
+                       IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxmonitor);
+                       if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
+                       {
+                               // List is full, send error which includes the remaining nicks that were not processed
+                               user->WriteNumeric(ERR_MONLISTFULL, maxmonitor, InspIRCd::Format("%s%s%s", nick.c_str(), (ss.StreamEnd() ? "" : ","), ss.GetRemaining().c_str()), "Monitor list is full");
+                               break;
+                       }
+                       else if (result != IRCv3::Monitor::Manager::WR_OK)
+                               continue; // Already added or invalid nick
+
+                       ReplyBuilder& out = (IRCv3::Monitor::Manager::FindNick(nick) ? online : offline);
+                       out.Add(nick);
+               }
+
+               online.Flush();
+               offline.Flush();
+       }
+
+       void HandleMinus(LocalUser* user, const std::string& input)
+       {
+               irc::commasepstream ss(input);
+               for (std::string nick; ss.GetToken(nick); )
+                       manager.Unwatch(user, nick);
+       }
+
+ public:
+       unsigned int maxmonitor;
+
+       CommandMonitor(Module* mod, IRCv3::Monitor::Manager& managerref)
+               : SplitCommand(mod, "MONITOR", 1)
+               , manager(managerref)
+       {
+               Penalty = 2;
+               allow_empty_last_param = false;
+               syntax = "[C|L|S|+ <nick1>[,<nick2>]|- <nick1>[,<nick2>]";
+       }
+
+       CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+       {
+               char subcmd = toupper(parameters[0][0]);
+               if (subcmd == '+')
+               {
+                       if (parameters.size() > 1)
+                               HandlePlus(user, parameters[1]);
+               }
+               else if (subcmd == '-')
+               {
+                       if (parameters.size() > 1)
+                               HandleMinus(user, parameters[1]);
+               }
+               else if (subcmd == 'C')
+               {
+                       manager.UnwatchAll(user);
+               }
+               else if (subcmd == 'L')
+               {
+                       user->CommandFloodPenalty += ListPenalty;
+                       const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+                       ReplyBuilder out(user, RPL_MONLIST);
+                       for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+                       {
+                               IRCv3::Monitor::Entry* entry = *i;
+                               out.Add(entry->GetNick());
+                       }
+                       out.Flush();
+                       user->WriteNumeric(RPL_ENDOFMONLIST, "End of MONITOR list");
+               }
+               else if (subcmd == 'S')
+               {
+                       user->CommandFloodPenalty += ListPenalty;
+
+                       ReplyBuilder online(user, RPL_MONONLINE);
+                       ReplyBuilder offline(user, RPL_MONOFFLINE);
+
+                       const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+                       for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+                       {
+                               IRCv3::Monitor::Entry* entry = *i;
+                               ReplyBuilder& out = (IRCv3::Monitor::Manager::FindNick(entry->GetNick()) ? online : offline);
+                               out.Add(entry->GetNick());
+                       }
+
+                       online.Flush();
+                       offline.Flush();
+               }
+               else
+                       return CMD_FAILURE;
+
+               return CMD_SUCCESS;
+       }
+};
+
+class ModuleMonitor : public Module
+{
+       IRCv3::Monitor::Manager manager;
+       CommandMonitor cmd;
+
+       void SendAlert(unsigned int numeric, const std::string& nick)
+       {
+               const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
+               if (!list)
+                       return;
+
+               for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
+               {
+                       LocalUser* curr = *i;
+                       curr->WriteNumeric(numeric, nick);
+               }
+       }
+
+ public:
+       ModuleMonitor()
+               : manager(this, "monitor")
+               , cmd(this, manager)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("monitor");
+               cmd.maxmonitor = tag->getInt("maxentries", 30, 1);
+       }
+
+       void OnPostConnect(User* user) CXX11_OVERRIDE
+       {
+               SendAlert(RPL_MONONLINE, user->nick);
+       }
+
+       void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
+       {
+               // Detect and ignore nickname case change
+               if (ServerInstance->FindNickOnly(oldnick) == user)
+                       return;
+
+               SendAlert(RPL_MONOFFLINE, oldnick);
+               SendAlert(RPL_MONONLINE, user->nick);
+       }
+
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+       {
+               LocalUser* localuser = IS_LOCAL(user);
+               if (localuser)
+                       manager.UnwatchAll(localuser);
+               SendAlert(RPL_MONOFFLINE, user->nick);
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["MONITOR"] = ConvToStr(cmd.maxmonitor);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides MONITOR support", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleMonitor)
+
+#endif
index 767af29017b7dde4751342f3918a479e3fac4564..c9caf6a6ad8e7345b7e7949886091483505dc214 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b m: - mute bans */
-
 class ModuleQuietBan : public Module
 {
- private:
  public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleQuietBan()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements extban +b 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 OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user) || target_type != TYPE_CHANNEL)
                        return MOD_RES_PASSTHRU;
@@ -49,24 +36,17 @@ class ModuleQuietBan : public Module
                Channel* chan = static_cast<Channel*>(dest);
                if (chan->GetExtBanStatus(user, 'm') == MOD_RES_DENY && chan->GetPrefixValue(user) < VOICE_VALUE)
                {
-                       user->WriteNumeric(404, "%s %s :Cannot send to channel (you're muted)", user->nick.c_str(), chan->name.c_str());
+                       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)
-       {
-               return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
-       }
-
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('m');
+               tokens["EXTBAN"].push_back('m');
        }
 };
 
-
 MODULE_INIT(ModuleQuietBan)
-
index 46710946b67a7adde9193e01aa4a14733a8d93d3..7f2687d66280b74ed5aa74e407cc2742d5b53312 100644 (file)
  */
 
 
-/* $ModDesc: Provides the ability to manipulate modes via long names. */
-
 #include "inspircd.h"
 
-static void DisplayList(User* user, Channel* channel)
+static void DisplayList(LocalUser* user, Channel* channel)
 {
-       std::stringstream items;
-       for(char letter = 'A'; letter <= 'z'; letter++)
+       Numeric::ParamBuilder<1> numeric(user, 961);
+       numeric.AddStatic(channel->name);
+
+       const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+       for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
        {
-               ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
-               if (!mh || mh->IsListMode())
-                       continue;
-               if (!channel->IsModeSet(letter))
+               ModeHandler* mh = i->second;
+               if (!channel->IsModeSet(mh))
                        continue;
-               items << " +" << mh->name;
-               if (mh->GetNumParams(true))
+               numeric.Add("+" + mh->name);
+               if (mh->NeedsParam(true))
                {
-                       if ((letter == 'k') && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
-                               items << " <key>";
+                       if ((mh->name == "key") && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
+                               numeric.Add("<key>");
                        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(960, channel->name, "End of mode list");
 }
 
-class CommandProp : public Command
+class CommandProp : public SplitCommand
 {
  public:
-       CommandProp(Module* parent) : Command(parent, "PROP", 1)
+       CommandProp(Module* parent)
+               : SplitCommand(parent, "PROP", 1)
        {
                syntax = "<user|channel> {[+-]<mode> [<value>]}*";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *src)
+       CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* src)
        {
+               Channel* const chan = ServerInstance->FindChan(parameters[0]);
+               if (!chan)
+               {
+                       src->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+                       return CMD_FAILURE;
+               }
+
                if (parameters.size() == 1)
                {
-                       Channel* chan = ServerInstance->FindChan(parameters[0]);
-                       if (chan)
-                               DisplayList(src, chan);
+                       DisplayList(src, chan);
                        return CMD_SUCCESS;
                }
                unsigned int i = 1;
-               std::vector<std::string> modes;
-               modes.push_back(parameters[0]);
-               modes.push_back("");
+               Modes::ChangeList modes;
                while (i < parameters.size())
                {
                        std::string prop = parameters[i++];
@@ -76,21 +77,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 +101,13 @@ class DummyZ : public ModeHandler
        {
                list = true;
        }
+
+       // Handle /MODE #chan Z
+       void DisplayList(User* user, Channel* chan)
+       {
+               if (IS_LOCAL(user))
+                       ::DisplayList(static_cast<LocalUser*>(user), chan);
+       }
 };
 
 class ModuleNamedModes : public Module
@@ -113,99 +119,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);
        }
 
-       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);
                                }
-                               for(char letter = 'A'; modechar == 0 && letter <= 'z'; letter++)
+
+                               ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+                               if (!mh)
                                {
-                                       mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
-                                       if (mh && mh->name == name)
+                                       // Mode handler not found
+                                       i = list.erase(i);
+                                       continue;
+                               }
+
+                               curr.param.clear();
+                               if (mh->NeedsParam(curr.adding))
+                               {
+                                       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..beac968ef0c19e0a4693cddb053f1fe0e291dd31 100644 (file)
 
 
 #include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the NAMESX (CAP multi-prefix) capability. */
+#include "modules/cap.h"
 
 class ModuleNamesX : public Module
 {
+       Cap::Capability cap;
  public:
-       GenericCap cap;
        ModuleNamesX() : cap(this, "multi-prefix")
        {
        }
 
-       void init()
-       {
-               Implementation eventlist[] = { I_OnPreCommand, I_OnNamesListItem, I_On005Numeric, I_OnEvent, I_OnSendWhoLine };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-
-       ~ModuleNamesX()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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 +52,37 @@ 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(User* 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 OnSendWhoLine(User* source, const std::vector<std::string>& params, 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);
-       }
+               // #chan ident localhost insp22.test nick H@ :0 Attila
+               if (numeric.GetParams().size() < 6)
+                       return MOD_RES_PASSTHRU;
 
-       void OnEvent(Event& ev)
-       {
-               cap.HandleEvent(ev);
+               numeric.GetParams()[5].append(prefixes, 1, std::string::npos);
+               return MOD_RES_PASSTHRU;
        }
 };
 
index bc90c9fad5a620ce6050cbd83430f59a8b5a3c5c..d03468de7c8e89d3cf8aa5e5431fe136ba205c97 100644 (file)
    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 HandlerBase1<bool, const std::string&>
 {
  public:
-       lwbNickHandler() { }
-       virtual ~lwbNickHandler() { }
-       virtual bool Call(const char*, size_t);
+       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,29 @@ 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;
+       caller1<bool, const std::string&> rememberer;
        bool forcequit;
        const unsigned char * lowermap_rememberer;
        unsigned char prev_map[256];
 
+       template <typename T>
+       void RehashHashmap(T& hashmap)
+       {
+               T newhash(hashmap.bucket_count());
+               for (typename T::const_iterator i = hashmap.begin(); i != hashmap.end(); ++i)
+                       newhash.insert(std::make_pair(i->first, i->second));
+               hashmap.swap(newhash);
+       }
+
        void CheckRehash()
        {
                // See if anything changed
@@ -238,20 +242,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 +254,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);
        }
 
-       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 +290,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 +310,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 +326,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 +344,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..bf36fb43097276abfdccd695d218b377c6323fc0 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)
        {
-               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(608, channel->name, "Invalid flood 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 = ConvToInt(parameter.substr(0, colon));
+               unsigned int nsecs = ConvToInt(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(608, channel->name, "Invalid flood 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,11 +200,7 @@ class ModuleNickFlood : public Module
                }
        }
 
-       ~ModuleNickFlood()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Channel mode F - nick flood protection", VF_VENDOR);
        }
index 4f5e5941cea5d653bb2453e19e154b4fcb68d82b..fae9d36d81d4b5990e95907897c0ef1cc019a35e 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
  */
@@ -35,7 +41,7 @@ class CommandNicklock : public Command
        {
                flags_needed = 'o';
                syntax = "<oldnick> <newnick>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle(const std::vector<std::string>& parameters, User *user)
@@ -44,20 +50,20 @@ class CommandNicklock : public Command
 
                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
                        {
@@ -80,10 +86,7 @@ class CommandNicklock : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -98,7 +101,7 @@ class CommandNickunlock : public Command
        {
                flags_needed = 'o';
                syntax = "<locked-nick>";
-               TRANSLATE2(TR_NICK, TR_END);
+               TRANSLATE1(TR_NICK);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
@@ -107,7 +110,7 @@ class CommandNickunlock : public Command
 
                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;
                        }
                }
@@ -132,14 +133,10 @@ class CommandNickunlock : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
-
 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..4fc844b74851f9c29edb21c2a9ee0fd04cd9b102 100644 (file)
@@ -20,8 +20,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +C to block CTCPs */
+#include "modules/exemption.h"
 
 class NoCTCP : public SimpleChannelModeHandler
 {
@@ -31,38 +30,22 @@ class NoCTCP : public SimpleChannelModeHandler
 
 class ModuleNoCTCP : public Module
 {
-
+       CheckExemption::EventProvider exemptionprov;
        NoCTCP nc;
 
  public:
-
        ModuleNoCTCP()
-               : nc(this)
-       {
-       }
-
-       void init()
+               : exemptionprov(this)
+               , nc(this)
        {
-               ServerInstance->Modules->AddService(nc);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual ~ModuleNoCTCP()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel mode +C to block CTCPs", VF_VENDOR);
        }
 
-       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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
                {
@@ -70,22 +53,22 @@ class ModuleNoCTCP : public Module
                        if ((text.empty()) || (text[0] != '\001') || (!strncmp(text.c_str(),"\1ACTION ", 8)) || (text == "\1ACTION\1") || (text == "\1ACTION"))
                                return MOD_RES_PASSTHRU;
 
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"noctcp");
+                       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_NOCTCPALLOWED, "%s %s :Can't send CTCP to channel (+C set)",user->nick.c_str(), c->name.c_str());
+                               user->WriteNumeric(ERR_NOCTCPALLOWED, c->name, "Can't send CTCP to channel (+C 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..fb3455567b53621ca12b1e7809e384601c980775 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +Q to prevent kicks on the channel. */
-
 class NoKicks : public SimpleChannelModeHandler
 {
  public:
@@ -40,38 +38,26 @@ class ModuleNoKicks : public Module
        {
        }
 
-       void init()
-       {
-               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 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);
        }
 };
 
-
 MODULE_INIT(ModuleNoKicks)
index 672a48f8d9bca45091d980ebb5290efb4f2a64ca..91a1303c6c53670ff79a50a046a437ea90113769 100644 (file)
@@ -20,8 +20,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support for channel mode +N & extban +b N: which prevents nick changes on channel */
+#include "modules/exemption.h"
 
 class NoNicks : public SimpleChannelModeHandler
 {
@@ -31,61 +30,44 @@ class NoNicks : public SimpleChannelModeHandler
 
 class ModuleNoNickChange : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        NoNicks nn;
        bool override;
  public:
-       ModuleNoNickChange() : nn(this)
-       {
-       }
-
-       void init()
-       {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(nn);
-               Implementation eventlist[] = { I_OnUserPreNick, I_On005Numeric, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleNoNickChange()
+       ModuleNoNickChange()
+               : exemptionprov(this)
+               , nn(this)
        {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('N');
+               tokens["EXTBAN"].push_back('N');
        }
 
-       virtual 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;
-
-               // 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 (override && user->IsOper())
                                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("Can't change nickname while on %s (+N is set)",
+                                       curr->name.c_str()));
                                return MOD_RES_DENY;
                        }
                }
@@ -93,7 +75,7 @@ class ModuleNoNickChange : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                override = ServerInstance->Config->ConfValue("nonicks")->getBool("operoverride", false);
        }
index c5b9f3a1c784f6dd894a4ec6b6d5237705826f73..f6496120b3a4aabdcb95c37f5ef67b3e7fbfdf1e 100644 (file)
@@ -20,8 +20,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +T to block notices to the channel */
+#include "modules/exemption.h"
 
 class NoNotice : public SimpleChannelModeHandler
 {
@@ -31,40 +30,35 @@ class NoNotice : public SimpleChannelModeHandler
 
 class ModuleNoNotice : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        NoNotice nt;
  public:
 
        ModuleNoNotice()
-               : nt(this)
-       {
-       }
-
-       void init()
+               : exemptionprov(this)
+               , nt(this)
        {
-               ServerInstance->Modules->AddService(nt);
-               Implementation eventlist[] = { I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('T');
+               tokens["EXTBAN"].push_back('T');
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                ModResult res;
-               if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+               if ((msgtype == MSG_NOTICE) && (target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
                {
                        Channel* c = (Channel*)dest;
-                       if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet('T')))
+                       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 set)");
                                        return MOD_RES_DENY;
                                }
                        }
@@ -72,11 +66,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..7aeb669200c38f5626fb60612a6fa2db605b4816 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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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..a96e47bc6977743ed250272531459c6305e149c8 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(const std::vector<std::string>& parameters, LocalUser* user)
        {
                // 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,54 +83,13 @@ 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()
-       {
-               return NETWORK_VALUE;
-       }
-
-       void RemoveMode(User* user, irc::modestacker* stack)
+       NetworkPrefix(Module* parent, char NPrefix)
+               : PrefixMode(parent, "official-join", 'Y', NETWORK_VALUE, NPrefix)
        {
+               ranktoset = ranktounset = UINT_MAX;
        }
 
        ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
@@ -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);
        }
 };
 
 MODULE_INIT(ModuleOjoin)
-
index ca948d95b3c195e955ffa24bfd84d7a5aad7bc74..0b074ebab4247a183ea2360d7def1f986015a15b 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for oper-only chans via the +O channel mode */
-
 class OperChans : public SimpleChannelModeHandler
 {
  public:
@@ -42,44 +40,32 @@ 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 IRC 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);
        }
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..bf758b1f76812d894c2693a290110c83d8d46eaa 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()
-               {
-                       ServerInstance->Modules->Attach(I_OnKill, this);
-               }
-
-               virtual Version GetVersion()
+               Version GetVersion() CXX11_OVERRIDE
                {
                        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());
@@ -50,8 +43,8 @@ class ModuleOperLevels : public Module
                                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());
+                                       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 +53,3 @@ class ModuleOperLevels : public Module
 };
 
 MODULE_INIT(ModuleOperLevels)
-
index edb9109e89f246d0cde5089655c1ae7279d0c7c6..68f50bf6d046e47ebbc77067321b54b225afdd75 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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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->HasPermission(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 + " " + irc::stringjoiner(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..6a18e9c9d198f241c395b078ddd74f3d111cda2f 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);
+               irc::spacesepstream ss(smodes);
                std::vector<std::string> 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..2431853c25e922928bcf283ead0aa93e5eba672b 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */
-
 /** Handle /OPERMOTD
  */
 class CommandOpermotd : public Command
@@ -45,28 +43,27 @@ class CommandOpermotd : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               if (!parameters.empty())
+               if ((!parameters.empty()) && (parameters[0].find('.') != std::string::npos))
                        return ROUTE_OPT_UCAST(parameters[0]);
                return ROUTE_LOCALONLY;
        }
 
        void ShowOperMOTD(User* user)
        {
-               const std::string& servername = ServerInstance->Config->ServerName;
                if (opermotd.empty())
                {
-                       user->SendText(":%s 455 %s :OPERMOTD file is missing", servername.c_str(), user->nick.c_str());
+                       user->WriteRemoteNumeric(455, "OPERMOTD file is missing");
                        return;
                }
 
-               user->SendText(":%s 375 %s :- IRC Operators Message of the Day", servername.c_str(), user->nick.c_str());
+               user->WriteRemoteNumeric(RPL_MOTDSTART, "- IRC Operators Message of the Day");
 
                for (file_cache::const_iterator i = opermotd.begin(); i != opermotd.end(); ++i)
                {
-                       user->SendText(":%s 372 %s :- %s", servername.c_str(), user->nick.c_str(), i->c_str());
+                       user->WriteRemoteNumeric(RPL_MOTD, InspIRCd::Format("- %s", i->c_str()));
                }
 
-               user->SendText(":%s 376 %s :- End of OPERMOTD", servername.c_str(), user->nick.c_str());
+               user->WriteRemoteNumeric(RPL_ENDOFMOTD, "- End of OPERMOTD");
        }
 };
 
@@ -82,34 +79,32 @@ 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);
        }
 
-       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);
        }
 
-       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));
+               try
+               {
+                       FileReader reader(conf->getString("file", "opermotd"));
+                       cmd.opermotd = reader.GetVector();
+               }
+               catch (CoreException&)
+               {
+                       // Nothing happens here as we do the error handling in ShowOperMOTD.
+               }
 
                if (conf->getBool("processcolors"))
                        InspIRCd::ProcessColors(cmd.opermotd);
index 7d5e6d1181151f0cfae5c7b243205fed2d910248..8b68dbe607a0462fb6ed060fe994a6cd2bf001e7 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);
 };
 
 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)
-       {
-               if ((mw_added) && (mod->ModuleSourceFile == "m_hideoper.so") && (ServerInstance->Modes->DelModeWatcher(&hideoperwatcher)))
-                       mw_added = false;
-       }
-
-       ~ModuleOperPrefixMode()
-       {
-               if (mw_added)
-                       ServerInstance->Modes->DelModeWatcher(&hideoperwatcher);
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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..2094d3c9681312717dac185b8e59e910bceb5ab7 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support for allowing opers to override certain things. */
+#include "modules/invite.h"
 
 class ModuleOverride : public Module
 {
        bool RequireKey;
        bool NoisyOverride;
+       ChanModeReference topiclock;
+       ChanModeReference inviteonly;
+       ChanModeReference key;
+       ChanModeReference limit;
+       Invite::API invapi;
 
-       static bool IsOverride(unsigned int userlevel, const std::string& modeline)
+       static bool IsOverride(unsigned int userlevel, const Modes::ChangeList::List& list)
        {
-               for (std::string::const_iterator i = modeline.begin(); i != modeline.end(); ++i)
+               for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
                {
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
-                       if (!mh)
-                               continue;
-
-                       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()
+               : 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));
        }
 
-       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)
@@ -80,11 +100,11 @@ class ModuleOverride : public Module
        }
 
 
-       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 +116,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 +131,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() >= ConvToInt(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..3050dba0b5343444dee22d5803601539960783f6 100644 (file)
  */
 
 
-/* $ModDesc: Forwards a password users can send on connect (for example for NickServ identification). */
-
 #include "inspircd.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");
@@ -54,22 +44,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,17 +72,21 @@ 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;
+
                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;
                }
 
@@ -102,7 +96,7 @@ class ModulePassForward : public Module
 
                tmp.clear();
                FormatStr(tmp,forwardcmd, user);
-               ServerInstance->Parser->ProcessBuffer(tmp,user);
+               ServerInstance->Parser.ProcessBuffer(tmp,user);
        }
 };
 
index 98462780b09a7fda288759b029d0a9bd327ca845..09cdbb402c180dd15e95cf15aabb75458f14af1d 100644 (file)
  */
 
 
-/* $ModDesc: Allows for hashed oper passwords */
-
 #include "inspircd.h"
-#include "hash.h"
+#include "modules/hash.h"
 
 /* Handle /MKPASSWD
  */
@@ -36,34 +34,39 @@ class CommandMkpasswd : public Command
 
        void MakeHash(User* user, const std::string& algo, const std::string& stuff)
        {
-               if (algo.substr(0,5) == "hmac-")
+               if (!algo.compare(0, 5, "hmac-", 5))
                {
-                       std::string type = algo.substr(5);
+                       std::string type(algo, 5);
                        HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
                        if (!hp)
                        {
-                               user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
+                               user->WriteNotice("Unknown hash type");
+                               return;
+                       }
+
+                       if (hp->IsKDF())
+                       {
+                               user->WriteNotice(type + " does not support HMAC");
                                return;
                        }
-                       std::string salt = ServerInstance->GenRandomStr(6, false);
+
+                       std::string salt = ServerInstance->GenRandomStr(hp->out_size, false);
                        std::string target = hp->hmac(salt, stuff);
                        std::string str = BinToBase64(salt) + "$" + BinToBase64(target, NULL, 0);
 
-                       user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
-                               user->nick.c_str(), algo.c_str(), stuff.c_str(), str.c_str());
+                       user->WriteNotice(algo + " hashed password for " + stuff + " is " + 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());
+                       std::string hexsum = hp->Generate(stuff);
+                       user->WriteNotice(algo + " hashed password for " + stuff + " is " + hexsum);
                }
                else
                {
-                       user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
+                       user->WriteNotice("Unknown hash type");
                }
        }
 
@@ -84,24 +87,21 @@ class ModuleOperHash : public Module
        {
        }
 
-       void init()
+       ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) CXX11_OVERRIDE
        {
-               /* 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)
-       {
-               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,19 +120,18 @@ 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);
        }
diff --git a/src/modules/m_pbkdf2.cpp b/src/modules/m_pbkdf2.cpp
new file mode 100644 (file)
index 0000000..314f6b8
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+// Format:
+// Iterations:B64(Hash):B64(Salt)
+// E.g.
+// 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+class PBKDF2Hash
+{
+ public:
+       unsigned int iterations;
+       unsigned int length;
+       std::string salt;
+       std::string hash;
+
+       PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
+               : iterations(itr), length(dkl), salt(slt), hash(hsh)
+       {
+       }
+
+       PBKDF2Hash(const std::string& data)
+       {
+               irc::sepstream ss(data, ':');
+               std::string tok;
+
+               ss.GetToken(tok);
+               this->iterations = ConvToInt(tok);
+
+               ss.GetToken(tok);
+               this->hash = Base64ToBin(tok);
+
+               ss.GetToken(tok);
+               this->salt = Base64ToBin(tok);
+
+               this->length = this->hash.length();
+       }
+
+       std::string ToString()
+       {
+               if (!IsValid())
+                       return "";
+               return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
+       }
+
+       bool IsValid()
+       {
+               if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
+                       return false;
+               return true;
+       }
+};
+
+class PBKDF2Provider : public HashProvider
+{
+ public:
+       HashProvider* provider;
+       unsigned int iterations;
+       unsigned int dkey_length;
+
+       std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
+       {
+               size_t blocks = std::ceil((double)dkl / provider->out_size);
+
+               std::string output;
+               std::string tmphash;
+               std::string salt_block = salt;
+               for (size_t block = 1; block <= blocks; block++)
+               {
+                       char salt_data[4];
+                       for (size_t i = 0; i < sizeof(salt_data); i++)
+                               salt_data[i] = block >> (24 - i * 8) & 0x0F;
+
+                       salt_block.erase(salt.length());
+                       salt_block.append(salt_data, sizeof(salt_data));
+
+                       std::string blockdata = provider->hmac(pass, salt_block);
+                       std::string lasthash = blockdata;
+                       for (size_t iter = 1; iter < itr; iter++)
+                       {
+                               tmphash = provider->hmac(pass, lasthash);
+                               for (size_t i = 0; i < provider->out_size; i++)
+                                       blockdata[i] ^= tmphash[i];
+
+                               lasthash.swap(tmphash);
+                       }
+                       output += blockdata;
+               }
+
+               output.erase(dkl);
+               return output;
+       }
+
+       std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+       {
+               PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
+               hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
+               return hs.ToString();
+       }
+
+       bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+       {
+               PBKDF2Hash hs(hash);
+               if (!hs.IsValid())
+                       return false;
+
+               std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
+               return (cmp == hs.hash);
+       }
+
+       std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+       {
+               return raw;
+       }
+
+       PBKDF2Provider(Module* mod, HashProvider* hp)
+               : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
+               , provider(hp)
+       {
+               DisableAutoRegister();
+       }
+};
+
+class ModulePBKDF2 : public Module
+{
+       std::vector<PBKDF2Provider*> providers;
+
+       void GetConfig()
+       {
+               // First set the common values
+               ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
+               unsigned int global_iterations = tag->getInt("iterations", 12288, 1);
+               unsigned int global_dkey_length = tag->getInt("length", 32, 1, 1024);
+               for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+               {
+                       PBKDF2Provider* pi = *i;
+                       pi->iterations = global_iterations;
+                       pi->dkey_length = global_dkey_length;
+               }
+
+               // Then the specific values
+               ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       tag = i->second;
+                       std::string hash_name = "hash/" + tag->getString("hash");
+                       for (std::vector<PBKDF2Provider*>::iterator j = providers.begin(); j != providers.end(); ++j)
+                       {
+                               PBKDF2Provider* pi = *j;
+                               if (pi->provider->name != hash_name)
+                                       continue;
+
+                               pi->iterations = tag->getInt("iterations", global_iterations, 1);
+                               pi->dkey_length = tag->getInt("length", global_dkey_length, 1, 1024);
+                       }
+               }
+       }
+
+ public:
+       ~ModulePBKDF2()
+       {
+               stdalgo::delete_all(providers);
+       }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               OnLoadModule(NULL);
+       }
+
+       void OnLoadModule(Module* mod) CXX11_OVERRIDE
+       {
+               bool newProv = false;
+               // As the module doesn't tell us what ServiceProviders it has, let's iterate all (yay ...) the ServiceProviders
+               // Good thing people don't run loading and unloading those all the time
+               for (std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.begin(); i != ServerInstance->Modules->DataProviders.end(); ++i)
+               {
+                       ServiceProvider* provider = i->second;
+
+                       // Does the service belong to the new mod?
+                       // In the case this is our first run (mod == NULL, continue anyway)
+                       if (mod && provider->creator != mod)
+                               continue;
+
+                       // Check if it's a hash provider
+                       if (provider->name.compare(0, 5, "hash/"))
+                               continue;
+
+                       HashProvider* hp = static_cast<HashProvider*>(provider);
+
+                       if (hp->IsKDF())
+                               continue;
+
+                       bool has_prov = false;
+                       for (std::vector<PBKDF2Provider*>::const_iterator j = providers.begin(); j != providers.end(); ++j)
+                       {
+                               if ((*j)->provider == hp)
+                               {
+                                       has_prov = true;
+                                       break;
+                               }
+                       }
+                       if (has_prov)
+                               continue;
+
+                       newProv = true;
+
+                       PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
+                       providers.push_back(prov);
+                       ServerInstance->Modules->AddService(*prov);
+               }
+
+               if (newProv)
+                       GetConfig();
+       }
+
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+       {
+               for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); )
+               {
+                       PBKDF2Provider* item = *i;
+                       if (item->provider->creator != mod)
+                       {
+                               ++i;
+                               continue;
+                       }
+
+                       ServerInstance->Modules->DelService(*item);
+                       delete item;
+                       i = providers.erase(i);
+               }
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               GetConfig();
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Implements PBKDF2 hashing", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModulePBKDF2)
index 74a798356c1a67b8cf05aac577932d9d0afbb11e..0f2a2ef6f3506c069fa6d0175bd462474e2ed2c0 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)
+       {
+               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))
                        {
-                               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 empty or too long 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;
+                               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);
        }
 
-       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..1bb28583e7b27690fe8dd48515a5322213314736 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..b14de9ff91eeecda7d8eda473c4a858f2354db7f 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)
        {
-               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(ERR_NOSUCHCHANNEL, parameter, "Invalid channel name");
+                               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;
+       }
 
+       void SerializeParam(Channel* chan, const std::string* str, std::string& out)
+       {
+               out += *str;
        }
 };
 
@@ -92,75 +77,62 @@ class Redirect : public ModeHandler
 class AntiRedirect : public SimpleUserModeHandler
 {
        public:
-               AntiRedirect(Module* Creator) : SimpleUserModeHandler(Creator, "antiredirect", 'L') {}
+               AntiRedirect(Module* Creator) : SimpleUserModeHandler(Creator, "antiredirect", 'L')
+               {
+                       if (!ServerInstance->Config->ConfValue("redirect")->getBool("antiredirect"))
+                               DisableAutoRegister();
+               }
 };
 
 class ModuleRedirect : public Module
 {
-
        Redirect re;
        AntiRedirect re_u;
+       ChanModeReference limitmode;
        bool UseUsermode;
 
  public:
-
        ModuleRedirect()
-               : re(this), re_u(this)
+               : re(this)
+               , re_u(this)
+               , limitmode(this, "limit")
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                /* 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() >= ConvToInt(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 (UseUsermode && user->IsModeSet(re_u))
                                        {
-                                               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 redirect channel.");
+                                               Channel::JoinUser(user, channel);
                                                return MOD_RES_DENY;
                                        }
                                }
@@ -169,11 +141,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..9c316288523528477f299baa7ac46555e8206a0e 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,11 +49,12 @@ 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);
        }
index 61f94c0bd1b81f26bc4cf9ea2bcdb10d8d84c0c2..78b20ef6b9f96206c1711f65176431d59039218c 100644 (file)
 
 
 #include "inspircd.h"
-#include "account.h"
-
-/* $ModDesc: Prevents users whose nicks are not registered from creating new channels */
+#include "modules/account.h"
 
 class ModuleRegOnlyCreate : public Module
 {
+       UserModeReference regusermode;
+
  public:
-       void init()
+       ModuleRegOnlyCreate()
+               : regusermode(this, "u_registered")
        {
-               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)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (chan)
                        return MOD_RES_PASSTHRU;
 
-               if (IS_OPER(user))
+               if (user->IsOper())
                        return MOD_RES_PASSTHRU;
 
-               if (user->IsModeSet('r'))
+               if (user->IsModeSet(regusermode))
                        return MOD_RES_PASSTHRU;
 
                const AccountExtItem* ext = GetAccountExtItem();
@@ -50,15 +49,11 @@ class ModuleRegOnlyCreate : public Module
                        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);
+               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, cname, "You must have a registered nickname to create a new channel");
                return MOD_RES_DENY;
        }
 
-       ~ModuleRegOnlyCreate()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Prevents users whose nicks are not registered from creating new channels", VF_VENDOR);
        }
index cf139f4a3d5dbe9032039dd18009e5ad872e3fff..5872b59788fbbe421980b8e7307012cbfa771e87 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(const std::vector<std::string>& parameters, User *user, 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.
@@ -74,40 +76,48 @@ class RemoveBase : public Command
                /* Fix by brain - someone needs to learn to validate their input! */
                if ((!target) || (target->registered != REG_ALL) || (!channel))
                {
-                       user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), !channel ? channame.c_str() : username.c_str());
+                       user->WriteNumeric(Numerics::NoSuchNick(channel ? username.c_str() : channame.c_str()));
                        return CMD_FAILURE;
                }
 
                if (!channel->HasUser(target))
                {
-                       user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
+                       user->WriteNotice(InspIRCd::Format("*** The user %s is not on channel %s", target->nick.c_str(), channel->name.c_str()));
                        return CMD_FAILURE;
                }
 
-               int ulevel = channel->GetPrefixValue(user);
-               int tlevel = channel->GetPrefixValue(target);
-
-               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(482, 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.
+                        * Nobody may remove people with >= protectedrank rank.
                         */
-                       if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && (tlevel != 50000)))
+                       unsigned int ulevel = channel->GetPrefixValue(user);
+                       unsigned int tlevel = channel->GetPrefixValue(target);
+                       if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && ((protectedrank == 0) || (tlevel < protectedrank))))
                        {
-                               // REMOVE/FPART will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
+                               // REMOVE will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
                                if (!IS_LOCAL(target))
+                               {
+                                       // Send an ENCAP REMOVE with parameters being in the old <user> <chan> order which is
+                                       // compatible with both 2.0 and 3.0. This also turns FPART into REMOVE.
+                                       std::vector<std::string> p;
+                                       p.push_back(target->uuid);
+                                       p.push_back(channel->name);
+                                       if (parameters.size() > 2)
+                                               p.push_back(":" + parameters[2]);
+                                       ServerInstance->PI->SendEncapsulatedData(target->server->GetName(), "REMOVE", p, user);
+
                                        return CMD_SUCCESS;
+                               }
 
                                std::string reasonparam;
 
@@ -120,27 +130,26 @@ class RemoveBase : public Command
                                /* Build up the part reason string. */
                                reason = "Removed by " + user->nick + ": " + reasonparam;
 
-                               channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name.c_str(), user->nick.c_str(), target->nick.c_str());
-                               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 (nokicks mode 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,25 +157,17 @@ class RemoveBase : public Command
 class CommandRemove : public RemoveBase
 {
  public:
-       CommandRemove(Module* Creator, bool& snk)
-               : RemoveBase(Creator, snk, "REMOVE")
+       CommandRemove(Module* Creator, bool& snk, ChanModeReference& nkm)
+               : RemoveBase(Creator, snk, nkm, "REMOVE")
        {
-               syntax = "<nick> <channel> [<reason>]";
-               TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
+               syntax = "<channel> <nick> [<reason>]";
+               TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
        {
                return HandleRMB(parameters, user, false);
        }
-
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
-       }
 };
 
 /** Handle /FPART
@@ -174,67 +175,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);
+               TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
        {
                return HandleRMB(parameters, user, true);
        }
-
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               User* dest = ServerInstance->FindNick(parameters[1]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
-       }
 };
 
 class ModuleRemove : public Module
 {
+       ChanModeReference nokicksmode;
        CommandRemove cmd1;
        CommandFpart cmd2;
        bool supportnokicks;
 
-
  public:
-       ModuleRemove() : cmd1(this, supportnokicks), cmd2(this, supportnokicks)
-       {
-       }
-
-       void init()
+       ModuleRemove()
+               : nokicksmode(this, "nokick")
+               , cmd1(this, supportnokicks, nokicksmode)
+               , cmd2(this, supportnokicks, nokicksmode)
        {
-               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));
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" REMOVE");
+               tokens["REMOVE"];
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               supportnokicks = ServerInstance->Config->ConfValue("remove")->getBool("supportnokicks");
+               ConfigTag* tag = ServerInstance->Config->ConfValue("remove");
+               supportnokicks = tag->getBool("supportnokicks");
+               cmd1.protectedrank = cmd2.protectedrank = tag->getInt("protectedrank", 50000);
        }
 
-       virtual ~ModuleRemove()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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..f1ebe18
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * 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 int 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)
+       {
+               // 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)
+       {
+               ChannelSettings settings;
+               if (!ParseSettings(source, parameter, settings))
+               {
+                       source->WriteNotice("*** Invalid syntax. Syntax is {[~*]}[lines]:[time]{:[difference]}{:[backlog]}");
+                       return MODEACTION_DENY;
+               }
+
+               if ((settings.Backlog > 0) && (settings.Lines > settings.Backlog))
+               {
+                       source->WriteNotice("*** You can't set needed lines higher than backlog");
+                       return MODEACTION_DENY;
+               }
+
+               LocalUser* localsource = IS_LOCAL(source);
+               if ((localsource) && (!ValidateSettings(localsource, 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->getInt("maxlines", 20);
+               ms.MaxBacklog = conf->getInt("maxbacklog", 20);
+               ms.MaxSecs = conf->getDuration("maxtime", conf->getInt("maxsecs", 0));
+
+               ms.MaxDiff = conf->getInt("maxdistance", 50);
+               if (ms.MaxDiff > 100)
+                       ms.MaxDiff = 100;
+
+               unsigned int newsize = conf->getInt("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 = ConvToInt(item)) == 0)
+                       return false;
+
+               if ((!stream.GetToken(item)) || ((settings.Seconds = InspIRCd::Duration(item)) == 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 = ConvToInt(item)) == 0)
+                               return false;
+
+                       if (stream.GetToken(item))
+                       {
+                               // There is a backlog parameter, see if it's valid
+                               if ((settings.Backlog = ConvToInt(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, const ChannelSettings& settings)
+       {
+               if (settings.Backlog && !ms.MaxBacklog)
+               {
+                       source->WriteNotice("*** The server administrator has disabled backlog matching");
+                       return false;
+               }
+
+               if (settings.Diff)
+               {
+                       if (settings.Diff > ms.MaxDiff)
+                       {
+                               if (ms.MaxDiff == 0)
+                                       source->WriteNotice("*** The server administrator has disabled matching on edit distance");
+                               else
+                                       source->WriteNotice("*** The distance you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxDiff));
+                               return false;
+                       }
+
+                       if (ms.MaxLines && settings.Lines > ms.MaxLines)
+                       {
+                               source->WriteNotice("*** The line number you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxLines));
+                               return false;
+                       }
+
+                       if (ms.MaxSecs && settings.Seconds > ms.MaxSecs)
+                       {
+                               source->WriteNotice("*** The seconds you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxSecs));
+                               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, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
+       {
+               if (target_type != TYPE_CHANNEL || !IS_LOCAL(user))
+                       return MOD_RES_PASSTHRU;
+
+               Channel* chan = reinterpret_cast<Channel*>(dest);
+               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, 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 the +E channel mode - for blocking of similar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+       }
+};
+
+MODULE_INIT(RepeatModule)
index c76b0e79fc2317ed49bfc377dd1650c8379d502b..9c7ed1213ee2cfdfa230ae0bc1d5af0c6ef831d7 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Only opers may create new channels if this module is loaded */
-
 class ModuleRestrictChans : public Module
 {
-       std::set<irc::string> allowchans;
+       insp::flat_set<std::string, irc::insensitive_swo> allowchans;
 
-       void ReadConfig()
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                allowchans.clear();
                ConfigTagList tags = ServerInstance->Config->ConfTags("allowchannel");
@@ -36,48 +35,26 @@ class ModuleRestrictChans : public Module
                {
                        ConfigTag* tag = i->second;
                        std::string txt = tag->getString("name");
-                       allowchans.insert(txt.c_str());
+                       allowchans.insert(txt);
                }
        }
 
- public:
-       void init()
-       {
-               ReadConfig();
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual void OnRehash(User* user)
-       {
-               ReadConfig();
-       }
-
-
-       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
        {
-               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)
                {
                        // user is not an oper and its not in the allow list
-                       if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end()))
+                       if ((!user->IsOper()) && (allowchans.find(cname) == allowchans.end()))
                        {
-                               user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Only IRC operators may create new channels",user->nick.c_str(),cname);
+                               user->WriteNumeric(ERR_BANNEDFROMCHAN, cname, "Only IRC operators may create new channels");
                                return MOD_RES_DENY;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleRestrictChans()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Only opers may create new channels if this module is loaded",VF_VENDOR);
        }
index 2a9f1dc9328646a228ac846c0be8d76d3e6cc9c3..8ca531ed5ba5ee45942ab773ab3a97a3f1538db3 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */
-
-
 class ModuleRestrictMsg : public Module
 {
- 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*)
-       {
-               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)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
                {
@@ -54,11 +35,11 @@ class ModuleRestrictMsg : public Module
                        // (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,16 +47,7 @@ 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)
-       {
-               return this->OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       virtual ~ModuleRestrictMsg()
-       {
-       }
-
-       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);
        }
index 04c27e83d181bfe38003000da054cbef7eefbbbe..8d3131bc02a9639083538284a88d43766ec28243 100644 (file)
  */
 
 
-/* $ModDesc: Allows for RIPEMD-160 encrypted oper passwords */
-
 /* macro definitions */
 
 #include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
+#include "modules/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
+typedef uint8_t byte;
+typedef uint32_t dword;
 
 /* collect four bytes into one word: */
 #define BYTES_TO_DWORD(strptr)                    \
@@ -167,7 +157,6 @@ class RIProv : public HashProvider
        {
                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];
@@ -176,7 +165,6 @@ class RIProv : public HashProvider
                }
                else
                {
-                       ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with default mdbuf");
                        MDbuf[0] = 0x67452301UL;
                        MDbuf[1] = 0xefcdab89UL;
                        MDbuf[2] = 0x98badcfeUL;
@@ -417,7 +405,6 @@ class RIProv : public HashProvider
 
        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                      */
@@ -447,18 +434,13 @@ class RIProv : public HashProvider
                return (byte *)hashcode;
        }
 public:
-       std::string sum(const std::string& data)
+       std::string GenerateRaw(const std::string& data)
        {
                char* rv = (char*)RMD((byte*)data.data(), data.length(), NULL);
                return std::string(rv, RMDsize / 8);
        }
 
-       std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
-       {
-               return "";
-       }
-
-       RIProv(Module* m) : HashProvider(m, "hash/ripemd160", 20, 64) {}
+       RIProv(Module* m) : HashProvider(m, "ripemd160", 20, 64) {}
 };
 
 class ModuleRIPEMD160 : public Module
@@ -467,15 +449,12 @@ class ModuleRIPEMD160 : public Module
        RIProv mr;
        ModuleRIPEMD160() : mr(this)
        {
-               ServerInstance->Modules->AddService(mr);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides RIPEMD-160 hashing", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleRIPEMD160)
-
index d1ab5d9ba42cc35645ca77b54170a315f4319046..6fd7b832c568b190633aa1d80551a688b5dc423f 100644 (file)
  */
 
 
-/* $ModDesc: RLINE: Regexp user banning. */
-
 #include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
 #include "xline.h"
 
 static bool ZlineOnMatch = false;
@@ -60,11 +58,13 @@ class RLine : public XLine
 
        bool Matches(User *u)
        {
-               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->fullname;
+               const std::string ip = u->nick + "!" + u->ident + "@" + u->GetIPString() + " " + u->fullname;
+               return (regex->Matches(host) || regex->Matches(ip));
        }
 
        bool Matches(const std::string &compare)
@@ -79,7 +79,7 @@ class RLine : public XLine
                        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);
+                               std::string timestr = InspIRCd::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());
                                added_zline = true;
@@ -90,15 +90,9 @@ class RLine : public XLine
                DefaultApply(u, "R", false);
        }
 
-       void DisplayExpiry()
-       {
-               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()
+       const std::string& Displayable()
        {
-               return matchtext.c_str();
+               return matchtext;
        }
 
        std::string matchtext;
@@ -116,7 +110,7 @@ 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)
@@ -129,10 +123,6 @@ class RLineFactory : public XLineFactory
 
                return new RLine(set_time, duration, source, reason, xline_specific_mask, rxfactory);
        }
-
-       ~RLineFactory()
-       {
-       }
 };
 
 /** Handle /RLINE
@@ -156,7 +146,7 @@ class CommandRLine : public Command
                {
                        // Adding - XXX todo make this respect <insane> tag perhaps..
 
-                       long duration = ServerInstance->Duration(parameters[1]);
+                       unsigned long duration = InspIRCd::Duration(parameters[1]);
                        XLine *r = NULL;
 
                        try
@@ -165,7 +155,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 RLINE: " + e.GetReason());
                        }
 
                        if (r)
@@ -179,7 +169,7 @@ class CommandRLine : public Command
                                        else
                                        {
                                                time_t c_requires_crap = duration + ServerInstance->Time();
-                                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
+                                               std::string timestr = InspIRCd::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());
                                        }
 
@@ -188,7 +178,7 @@ 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");
                                }
                        }
                }
@@ -200,7 +190,7 @@ class CommandRLine : public Command
                        }
                        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 in list, try /stats R.");
                        }
                }
 
@@ -218,7 +208,6 @@ class CommandRLine : public Command
 
 class ModuleRLine : public Module
 {
- private:
        dynamic_reference<RegexFactory> rxfactory;
        RLineFactory f;
        CommandRLine r;
@@ -233,29 +222,23 @@ class ModuleRLine : public Module
        {
        }
 
-       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 : "");
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                // Apply lines on user connect
                XLine *rl = ServerInstance->XLines->MatchesLine("R", user);
@@ -269,7 +252,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");
 
@@ -302,16 +285,16 @@ class ModuleRLine : public Module
                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 +311,7 @@ class ModuleRLine : public Module
                }
        }
 
-       virtual void OnBackgroundTimer(time_t curtime)
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                if (added_zline)
                {
@@ -337,7 +320,7 @@ 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 (!rxfactory)
@@ -351,7 +334,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..7c15247
--- /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(const std::vector<std::string> &parameters, User *user)
+       {
+               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..8bf86531916a1e54f885a5ad315da506a49d67b7 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)
        {
-               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", false))
+                       {
+                               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)
        {
-               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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides command SAJOIN to allow opers to force-join users to channels", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSajoin)
index afca49e251ba17b3294670277915a5f17b8b0bd5..81a74502bc2e7a5e2290ce4b8386a6c3b8d234c3 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)
        {
                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,20 +58,16 @@ 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;
@@ -83,10 +75,7 @@ class CommandSakick : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[1]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[1]);
        }
 };
 
@@ -99,21 +88,10 @@ class ModuleSakick : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSakick()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides a SAKICK command", VF_OPTCOMMON|VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSakick)
-
index ea2ae24ab3d525cc5fc56a156e703a8056f9f0c3..394cea722011205736235c6dcef676970a5674d3 100644 (file)
@@ -20,8 +20,6 @@
  */
 
 
-/* $ModDesc: Provides command SAMODE to allow opers to change modes on channels and users */
-
 #include "inspircd.h"
 
 /** Handle /SAMODE
@@ -33,7 +31,7 @@ class CommandSamode : public Command
        CommandSamode(Module* Creator) : Command(Creator,"SAMODE", 2)
        {
                allow_empty_last_param = false;
-               flags_needed = 'o'; Penalty = 0; syntax = "<target> <modes> {<mode-parameters>}";
+               flags_needed = 'o'; syntax = "<target> <modes> {<mode-parameters>}";
                active = false;
        }
 
@@ -44,16 +42,35 @@ class CommandSamode : public Command
                        User* target = ServerInstance->FindNickOnly(parameters[0]);
                        if ((!target) || (target->registered != REG_ALL))
                        {
-                               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
                                return CMD_FAILURE;
                        }
+
+                       // Changing the modes of another user requires a special permission
+                       if ((target != user) && (!user->HasPrivPermission("users/samode-usermodes", true)))
+                               return CMD_FAILURE;
                }
 
+               // XXX: Make ModeParser clear LastParse
+               Modes::ChangeList emptychangelist;
+               ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
+
                this->active = true;
-               ServerInstance->Parser->CallHandler("MODE", parameters, user);
-               if (ServerInstance->Modes->GetLastParse().length())
-                       ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
+               CmdResult result = ServerInstance->Parser.CallHandler("MODE", parameters, user);
                this->active = false;
+
+               if (result == CMD_SUCCESS)
+               {
+                       // If lastparse is empty and the MODE command handler returned CMD_SUCCESS then
+                       // the client queried the list of a listmode (e.g. /SAMODE #chan b), which was
+                       // handled internally by the MODE command handler.
+                       //
+                       // Viewing the modes of a user or a channel can also result in CMD_SUCCESS, but
+                       // that is not possible with /SAMODE because we require at least 2 parameters.
+                       const std::string& lastparse = ServerInstance->Modes.GetLastParse();
+                       ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + (lastparse.empty() ? irc::stringjoiner(parameters) : lastparse));
+               }
+
                return CMD_SUCCESS;
        }
 };
@@ -67,32 +84,22 @@ class ModuleSaMode : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->Attach(I_OnPreMode, this);
-       }
-
-       ~ModuleSaMode()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides command SAMODE to allow opers to change modes on channels and users", VF_VENDOR);
        }
 
-       ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
+       ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
        {
                if (cmd.active)
                        return MOD_RES_ALLOW;
                return MOD_RES_PASSTHRU;
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                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..c9ceba78ee3d2fbf7058304ed508696c533840ce 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for SANICK command */
-
 /** Handle /SANICK
  */
 class CommandSanick : public Command
@@ -31,8 +29,8 @@ 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> <new-nick>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
@@ -42,21 +40,21 @@ class CommandSanick : public Command
                /* 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]);
                        }
@@ -81,10 +79,7 @@ class CommandSanick : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -98,20 +93,10 @@ class ModuleSanick : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSanick()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for SANICK command", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSanick)
index 89256e0e480bfc5cc9811e69a27f4a12d66b545e..b51316dc5ccf7a7156803630da8559585ddfaee3 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)
        {
+               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,33 +65,14 @@ 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;
@@ -92,10 +80,7 @@ class CommandSapart : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -109,21 +94,10 @@ class ModuleSapart : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSapart()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides command SAPART to force-part users from a channel.", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSapart)
-
index 909a026abe306cacd77663f041b25233990d7d6a..9f700ec5fdc14e8414de77cd25b6f9ff78900bdd 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)
        {
                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)
        {
-               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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for an SAQUIT command, exits user with a reason", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSaquit)
index 0ef93ec5ae66476923dfe7eaded192dba54711df..e8a0e12a908b2bcbf79f60bb044b1c8d7d038e9f 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/spanningtree.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 SpanningTreeEventListener
+{
+       bool online;
+
+       void Update(const Server* server, bool linked)
+       {
+               if (sasl_target == "*")
+                       return;
+
+               if (InspIRCd::Match(server->GetName(), sasl_target))
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_VERBOSE, "SASL target server \"%s\" %s", sasl_target.c_str(), (linked ? "came online" : "went offline"));
+                       online = linked;
+               }
+       }
+
+       void OnServerLink(const Server* server) CXX11_OVERRIDE
+       {
+               Update(server, true);
+       }
+
+       void OnServerSplit(const Server* server) CXX11_OVERRIDE
+       {
+               Update(server, false);
+       }
+
+ public:
+       ServerTracker(Module* mod)
+               : SpanningTreeEventListener(mod)
+       {
+               Reset();
+       }
+
+       void Reset()
+       {
+               if (sasl_target == "*")
+               {
+                       online = true;
+                       return;
+               }
+
+               online = false;
+
+               ProtocolInterface::ServerList servers;
+               ServerInstance->PI->GetServerList(servers);
+               for (ProtocolInterface::ServerList::const_iterator i = servers.begin(); i != servers.end(); ++i)
+               {
+                       const ProtocolInterface::ServerInfo& server = *i;
+                       if (InspIRCd::Match(server.servername, sasl_target))
+                       {
+                               online = true;
+                               break;
+                       }
+               }
+       }
+
+       bool IsOnline() const { return online; }
+};
+
+class SASLCap : public Cap::Capability
+{
+       std::string mechlist;
+       const ServerTracker& servertracker;
+
+       bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
+       {
+               // Requesting this cap is allowed anytime
+               if (adding)
+                       return true;
+
+               // But removing it can only be done when unregistered
+               return (user->registered != REG_ALL);
+       }
+
+       bool OnList(LocalUser* user) CXX11_OVERRIDE
+       {
+               return servertracker.IsOnline();
+       }
+
+       const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
+       {
+               return &mechlist;
+       }
+
+ public:
+       SASLCap(Module* mod, const ServerTracker& tracker)
+               : Cap::Capability(mod, "sasl")
+               , servertracker(tracker)
+       {
+       }
+
+       void SetMechlist(const std::string& newmechlist)
+       {
+               if (mechlist == newmechlist)
+                       return;
+
+               mechlist = newmechlist;
+               NotifyValueChange();
+       }
+};
 
 enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
 enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
 
-static std::string sasl_target = "*";
+static Events::ModuleEventProvider* saslevprov;
 
-static void SendSASL(const parameterlist& params)
+static void SendSASL(LocalUser* user, const std::string& agent, char mode, const parameterlist& parameters)
 {
-       if (!ServerInstance->PI->SendEncapsulatedData(params))
+       parameterlist params(parameters.size() + 3);
+       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));
        }
 }
 
@@ -46,95 +164,35 @@ 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)
-       {
-               ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
-               if (!wiext)
-                       return false;
-
-               if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
-                       return false;
-
-               StringExtItem* stringext = static_cast<StringExtItem*>(wiext);
-               std::string* addr = stringext->get(user);
-               if (!addr)
-                       return false;
-
-               out = *addr;
-               return true;
-       }
-
-
        void SendHostIP()
        {
-               std::string host, ip;
-
-               if (!ReadCGIIRCExt("cgiirc_webirc_hostname", user, host))
-               {
-                       host = user->host;
-               }
-               if (!ReadCGIIRCExt("cgiirc_webirc_ip", user, ip))
-               {
-                       ip = user->GetIPString();
-               }
-               else
-               {
-                       /* IP addresses starting with a : on irc are a Bad Thing (tm) */
-                       if (ip.c_str()[0] == ':')
-                               ip.insert(ip.begin(),1,'0');
-               }
-
                parameterlist params;
-               params.push_back(sasl_target);
-               params.push_back("SASL");
-               params.push_back(user->uuid);
-               params.push_back("*");
-               params.push_back("H");
-               params.push_back(host);
-               params.push_back(ip);
-
-               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");
-               }
+               params.push_back(user->GetRealHost());
+               params.push_back(user->GetIPString());
+               params.push_back(SSLIOHook::IsSSL(&user->eh) ? "S" : "P");
 
-               SendSASL(params);
+               SendSASL(user, "*", 'H', params);
        }
 
  public:
-       SaslAuthenticator(User* user_, const std::string& method)
+       SaslAuthenticator(LocalUser* user_, const std::string& method)
                : 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");
                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 = SSLClientCert::GetFingerprint(&user->eh);
+               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_)
@@ -172,46 +230,32 @@ class SaslAuthenticator
                                this->result = this->GetSaslResult(msg[3]);
                        }
                        else if (msg[2] == "M")
-                               this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
+                               this->user->WriteNumeric(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 +270,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,29 +286,39 @@ 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;
+       CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, Cap::Capability& Cap)
+               : SplitCommand(Creator, "AUTHENTICATE", 1)
+               , authExt(ext)
+               , cap(Cap)
        {
                works_before_reg = true;
                allow_empty_last_param = false;
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
        {
-               /* Only allow AUTHENTICATE on unregistered clients */
-               if (user->registered != REG_ALL)
                {
-                       if (!cap.ext.get(user))
+                       if (!cap.get(user))
                                return CMD_FAILURE;
 
                        if (parameters[0].find(' ') != std::string::npos || parameters[0][0] == ':')
                                return CMD_FAILURE;
 
+                       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]));
@@ -289,10 +343,10 @@ class CommandSASL : public Command
 
        CmdResult Handle(const std::vector<std::string>& parameters, User *user)
        {
-               User* target = ServerInstance->FindNick(parameters[1]);
-               if ((!target) || (IS_SERVER(target)))
+               User* target = ServerInstance->FindUUID(parameters[1]);
+               if (!target)
                {
-                       ServerInstance->Logs->Log("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;
                }
 
@@ -318,52 +372,46 @@ class CommandSASL : public Command
 class ModuleSASL : public Module
 {
        SimpleExtItem<SaslAuthenticator> authExt;
-       GenericCap cap;
+       ServerTracker servertracker;
+       SASLCap cap;
        CommandAuthenticate auth;
        CommandSASL sasl;
+       Events::ModuleEventProvider sasleventprov;
+
  public:
        ModuleSASL()
-               : authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
+               : authExt("sasl_auth", ExtensionItem::EXT_USER, this)
+               , servertracker(this)
+               , cap(this, servertracker)
+               , auth(this, authExt, cap)
+               , sasl(this, authExt)
+               , sasleventprov(this, "event/sasl")
        {
+               saslevprov = &sasleventprov;
        }
 
-       void init()
+       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", "*");
+               servertracker.Reset();
        }
 
-       void OnUserConnect(LocalUser *user)
+       void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string& extdata) CXX11_OVERRIDE
        {
-               SaslAuthenticator *sasl_ = authExt.get(user);
-               if (sasl_)
-               {
-                       sasl_->Abort();
-                       authExt.unset(user);
-               }
+               if ((target == NULL) && (extname == "saslmechlist"))
+                       cap.SetMechlist(extdata);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
        }
-
-       void OnEvent(Event &ev)
-       {
-               cap.HandleEvent(ev);
-       }
 };
 
 MODULE_INIT(ModuleSASL)
index ae1c19d9148022e2324d27ba96ec19182b051f99..f45d9c8cd4aa235b2bf80afb1091a7d7ca12c6c4 100644 (file)
@@ -17,8 +17,6 @@
  */
 
 
-/* $ModDesc: Provides a SATOPIC command */
-
 #include "inspircd.h"
 
 /** Handle /SATOPIC
@@ -28,7 +26,7 @@ class CommandSATopic : public Command
  public:
        CommandSATopic(Module* Creator) : Command(Creator,"SATOPIC", 2, 2)
        {
-               flags_needed = 'o'; Penalty = 0; syntax = "<target> <topic>";
+               flags_needed = 'o'; syntax = "<target> <topic>";
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
@@ -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::NoSuchNick(parameters[0]));
                        return CMD_FAILURE;
                }
        }
@@ -65,16 +67,7 @@ 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);
        }
index 5d11d27f7b0fa16b72a2e568d232745e279e0f42..e36b9e403b8f487f47ebac69aff60a0cd901b027 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Disallows /LIST for recently connected clients to hinder spam bots */
-
 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()
-       {
-       }
-
-       virtual Version GetVersion()
+ public:
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Disallows /LIST for recently connected clients to hinder spam bots", VF_VENDOR);
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                allowlist.clear();
 
@@ -53,7 +40,7 @@ class ModuleSecureList : public Module
                for (ConfigIter i = tags.first; i != tags.second; ++i)
                        allowlist.push_back(i->second->getString("exception"));
 
-               WaitTime = ServerInstance->Config->ConfValue("securelist")->getInt("waittime", 60);
+               WaitTime = ServerInstance->Config->ConfValue("securelist")->getDuration("waittime", 60);
        }
 
 
@@ -61,13 +48,13 @@ 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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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++)
@@ -75,20 +62,20 @@ class ModuleSecureList : public Module
                                        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..bff3516f1c7c0a90e5cfedf0a88aa2d3b96b6ad8 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);
        }
 
-       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..f51d1d373f3cb45147042072247dccf6ce94b0b1 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b s: - server name bans */
-
 class ModuleServerBan : public Module
 {
- private:
  public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ~ModuleServerBan()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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..249ba35ce5857d08aabb52b8f6135e7110bcf5c6 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/exemption.h"
+
+enum
+{
+       // 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
  */
@@ -40,15 +49,15 @@ class Channel_r : public ModeHandler
                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(500, "Only a server may modify the +r channel mode");
                }
                return MODEACTION_DENY;
        }
@@ -66,15 +75,15 @@ class User_r : public ModeHandler
        {
                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(500, "Only a server may modify the +r user mode");
                }
                return MODEACTION_DENY;
        }
@@ -104,86 +113,96 @@ class AChannel_M : public SimpleChannelModeHandler
        AChannel_M(Module* Creator) : SimpleChannelModeHandler(Creator, "regmoderated", 'M') { }
 };
 
-class ModuleServicesAccount : public Module
+class AccountExtItemImpl : public AccountExtItem
 {
-       AChannel_R m1;
-       AChannel_M m2;
-       AUser_R m3;
-       Channel_r m4;
-       User_r m5;
-       AccountExtItem accountname;
-       bool checking_ban;
+       Events::ModuleEventProvider eventprov;
 
-       static bool ReadCGIIRCExt(const char* extname, User* user, const std::string*& out)
+ public:
+       AccountExtItemImpl(Module* mod)
+               : AccountExtItem("accountname", ExtensionItem::EXT_USER, mod)
+               , eventprov(mod, "event/account")
        {
-               ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
-               if (!wiext)
-                       return false;
+       }
 
-               if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
-                       return false;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+       {
+               User* user = static_cast<User*>(container);
 
-               StringExtItem* stringext = static_cast<StringExtItem*>(wiext);
-               std::string* addr = stringext->get(user);
-               if (!addr)
-                       return false;
+               StringExtItem::unserialize(format, container, value);
 
-               out = addr;
-               return true;
-       }
+               // If we are being reloaded then don't send the numeric or run the event
+               if (format == FORMAT_INTERNAL)
+                       return;
 
- public:
-       ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this),
-               accountname("accountname", this), checking_ban(false)
-       {
+               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));
        }
+};
 
-       void init()
+class ModuleServicesAccount : public Module, public Whois::EventListener
+{
+       CheckExemption::EventProvider exemptionprov;
+       AChannel_R m1;
+       AChannel_M m2;
+       AUser_R m3;
+       Channel_r m4;
+       User_r m5;
+       AccountExtItemImpl accountname;
+       bool checking_ban;
+ public:
+       ModuleServicesAccount()
+               : Whois::EventListener(this)
+               , exemptionprov(this)
+               , m1(this), m2(this), m3(this), 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(330, *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(307, "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 OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
@@ -194,12 +213,12 @@ class ModuleServicesAccount : public Module
                if (target_type == TYPE_CHANNEL)
                {
                        Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"regmoderated");
+                       ModResult res = CheckExemption::Call(exemptionprov, user, c, "regmoderated");
 
-                       if (c->IsModeSet('M') && !is_registered && res != MOD_RES_ALLOW)
+                       if (c->IsModeSet(m2) && !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");
+                               user->WriteNumeric(ERR_NEEDREGGEDNICK, c->name, "You need to be identified to a registered account to message this channel");
                                return MOD_RES_DENY;
                        }
                }
@@ -207,17 +226,17 @@ class ModuleServicesAccount : public Module
                {
                        User* u = (User*)dest;
 
-                       if (u->IsModeSet('R') && !is_registered)
+                       if (u->IsModeSet(m3) && !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");
+                               user->WriteNumeric(ERR_NEEDREGGEDNICK, u->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 OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
        {
                if (checking_ban)
                        return MOD_RES_PASSTHRU;
@@ -253,27 +272,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)
-       {
-               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)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               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,56 +292,14 @@ 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);
        }
index b4f2b5bbd5ae5ba5a42ac78a9db2587e8a7c4ec9..97670237b4fee551079f29398bf8e42e147ecca4 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides usermode +k to protect services from kicks, kills and mode changes. */
-
 /** Handles user mode +k
  */
 class ServProtectMode : public ModeHandler
@@ -44,47 +42,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()
-       {
-               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()
+               : Whois::EventListener(this)
+               , Whois::LineEventListener(this)
+               , bm(this)
        {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides usermode +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(310, "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 +87,10 @@ class ModuleServProtectMode : public Module
                                 * This includes any prefix permission mode, even those registered in other modules, e.g. +qaohv. Using ::ModeString()
                                 * here means that the number of modes is restricted to only modes the user has, limiting it to as short a loop as possible.
                                 */
-                               if (u->IsModeSet('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 +99,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(485, InspIRCd::Format("You are not permitted to kill %s services!", ServerInstance->Config->Network.c_str()));
                        ServerInstance->SNO->WriteGlobalSno('a', src->nick+" tried to kill service "+dst->nick+" ("+reason+")");
                        return MOD_RES_DENY;
                }
                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..b37207b4fa7e93020aa3bb13ac0d795c9225bc52 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)
        {
                allow_empty_last_param = false;
                flags_needed = 'o'; syntax = "<new-hostname>";
-               TRANSLATE2(TR_TEXT, TR_END);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
        {
-               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)
+               {
+                       user->WriteNotice("*** SETHOST: Host too long");
+                       return CMD_FAILURE;
+               }
+
+               for (std::string::const_iterator x = parameters[0].begin(); x != parameters[0].end(); x++)
                {
                        if (!hostmap[(const unsigned char)*x])
                        {
-                               user->WriteServ("NOTICE "+user->nick+" :*** SETHOST: Invalid characters in hostname");
+                               user->WriteNotice("*** SETHOST: Invalid characters in hostname");
                                return CMD_FAILURE;
                        }
                }
 
-               if (len > 64)
-               {
-                       user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick.c_str());
-                       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;
                }
 
@@ -70,21 +66,14 @@ class ModuleSetHost : public Module
 {
        CommandSethost cmd;
        char hostmap[256];
+
  public:
        ModuleSetHost()
                : cmd(this, hostmap)
        {
        }
 
-       void init()
-       {
-               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");
 
@@ -93,15 +82,10 @@ class ModuleSetHost : public Module
                        hostmap[(unsigned char)*n] = 1;
        }
 
-       virtual ~ModuleSetHost()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the SETHOST command", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSetHost)
index f63be1381bb2ecee0ebc61142ac4916922280b55..93dd4c332bb2f97a4c1cd158feb5c3091562ea8d 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the SETIDENT command */
-
 /** Handle /SETIDENT
  */
 class CommandSetident : public Command
@@ -33,31 +31,29 @@ class CommandSetident : public Command
        {
                allow_empty_last_param = false;
                flags_needed = 'o'; syntax = "<new-ident>";
-               TRANSLATE2(TR_TEXT, TR_END);
        }
 
        CmdResult Handle(const std::vector<std::string>& parameters, User *user)
        {
                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()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the SETIDENT command", VF_VENDOR);
        }
-
 };
 
-
 MODULE_INIT(ModuleSetIdent)
index fdb29d14fe7904233bb573950207283ebdb79d13..6302b36544fd1d1a10bd3d2f61557f0fec436d6a 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(const std::vector<std::string>& parameters, LocalUser* user)
        {
-               time_t idle = ServerInstance->Duration(parameters[0]);
+               int idle = InspIRCd::Duration(parameters[0]);
                if (idle < 1)
                {
-                       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,16 +67,7 @@ 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);
        }
index d0610853bc4ce7e1308d01616473136b25c8eb98..0e71840f733a820ed3bd9dfeef3c6b66974df657 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the SETNAME command */
-
 
 
 class CommandSetname : public Command
@@ -32,18 +30,17 @@ class CommandSetname : public Command
        {
                allow_empty_last_param = false;
                syntax = "<new-gecos>";
-               TRANSLATE2(TR_TEXT, TR_END);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
        {
                if (parameters[0].size() > ServerInstance->Config->Limits.MaxGecos)
                {
-                       user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick.c_str());
+                       user->WriteNotice("*** SETNAME: GECOS too long");
                        return CMD_FAILURE;
                }
 
-               if (user->ChangeName(parameters[0].c_str()))
+               if (user->ChangeName(parameters[0]))
                {
                        ServerInstance->SNO->WriteGlobalSno('a', "%s used SETNAME to change their GECOS to '%s'", user->nick.c_str(), parameters[0].c_str());
                }
@@ -62,16 +59,7 @@ class ModuleSetName : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSetName()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for 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..798539d
--- /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)
+       {
+               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..48bfc00410a6d62204c28376f579d3b093969c8b 100644 (file)
  * 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;
-#endif
+#include "modules/hash.h"
 
 #define SHA256_DIGEST_SIZE (256 / 8)
 #define SHA256_BLOCK_SIZE  (512 / 8)
@@ -256,19 +247,14 @@ class HashSHA256 : public HashProvider
        }
 
  public:
-       std::string sum(const std::string& data)
+       std::string GenerateRaw(const std::string& data)
        {
                unsigned char bytes[SHA256_DIGEST_SIZE];
                SHA256(data.data(), bytes, data.length());
                return std::string((char*)bytes, SHA256_DIGEST_SIZE);
        }
 
-       std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
-       {
-               return "";
-       }
-
-       HashSHA256(Module* parent) : HashProvider(parent, "hash/sha256", 32, 64) {}
+       HashSHA256(Module* parent) : HashProvider(parent, "sha256", 32, 64) {}
 };
 
 class ModuleSHA256 : public Module
@@ -277,10 +263,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..57c501e
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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"
+
+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(const std::vector<std::string>& parameters, User* user) CXX11_OVERRIDE
+       {
+               if (method == SF_NUMERIC)
+               {
+                       if (!introtext.empty())
+                               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()));
+
+                       user->WriteRemoteNumeric(endnumeric, endtext.c_str());
+               }
+               else
+               {
+                       const char* msgcmd = (method == SF_MSG ? "PRIVMSG" : "NOTICE");
+                       for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+                       {
+                               const std::string& line = *i;
+                               user->WriteCommand(msgcmd, ":" + line);
+                       }
+               }
+               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->getInt("intronumeric", RPL_RULESTART, 0, 999);
+               textnumeric = tag->getInt("numeric", RPL_RULES, 0, 999);
+               endnumeric = tag->getInt("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;
+               if (tag->getBool("colors"))
+                       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..99774563d3d827a7b7612210afb8eb8962c7e87d 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */
-
 /** 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,9 +49,9 @@ 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)
@@ -66,49 +69,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()
-       {
-               delete sw;
-       }
-
-       Version GetVersion()
+       Version GetVersion() 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)
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               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))
@@ -118,14 +114,11 @@ class ModuleShowwhois : public Module
                else
                {
                        std::vector<std::string> params;
-                       params.push_back(dest->server);
-                       params.push_back("WHOISNOTICE");
                        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 3147d5476f40eda1f19775a88654b27b777a1831..66cc7fd58dc488e2a4585d57d76ffa9675bc1ac0 100644 (file)
@@ -23,8 +23,6 @@
 #include "inspircd.h"
 #include "xline.h"
 
-/* $ModDesc: Provides the /SHUN command, which stops a user from executing all except configured commands. */
-
 class Shun : public XLine
 {
 public:
@@ -36,19 +34,19 @@ public:
        {
        }
 
-       ~Shun()
-       {
-       }
-
        bool Matches(User *u)
        {
                // E: overrides shun
-               if (u->exempt)
+               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;
        }
 
@@ -59,15 +57,9 @@ public:
                return false;
        }
 
-       void DisplayExpiry()
+       const std::string& Displayable()
        {
-               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();
+               return matchtext;
        }
 };
 
@@ -107,7 +99,7 @@ class CommandShun : public Command
                /* '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();
@@ -120,22 +112,22 @@ class CommandShun : public Command
                        }
                        else if (ServerInstance->XLines->DelLine(target.c_str(), "SHUN", 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", user->nick.c_str(), target.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 in list, try /stats H.");
                                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]);
+                               duration = InspIRCd::Duration(parameters[1]);
                                expr = parameters[2];
                        }
                        else
@@ -155,7 +147,7 @@ class CommandShun : public Command
                                else
                                {
                                        time_t c_requires_crap = duration + ServerInstance->Time();
-                                       std::string timestr = ServerInstance->TimeString(c_requires_crap);
+                                       std::string timestr = InspIRCd::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());
                                }
@@ -163,7 +155,7 @@ class CommandShun : public Command
                        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;
                        }
                }
@@ -183,7 +175,7 @@ class ModuleShun : public Module
 {
        CommandShun cmd;
        ShunFactory f;
-       std::set<std::string> ShunEnabledCommands;
+       insp::flat_set<std::string> ShunEnabledCommands;
        bool NotifyOfShun;
        bool affectopers;
 
@@ -192,38 +184,33 @@ class ModuleShun : public Module
        {
        }
 
-       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 +221,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 +233,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, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line) CXX11_OVERRIDE
        {
                if (validated)
                        return MOD_RES_PASSTHRU;
@@ -257,18 +244,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 +272,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);
        }
 };
 
 MODULE_INIT(ModuleShun)
-
index c82ab3f9d6052ecf7c21f571bdfee029291b119a..cb065d2fc3cf3e29be2ee700f0e7aa252837c119 100644 (file)
@@ -23,8 +23,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the /SILENCE command */
-
 /* 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>
  *
@@ -47,8 +45,8 @@
 // pair of hostmask and flags
 typedef std::pair<std::string, int> silenceset;
 
-// deque list of pairs
-typedef std::deque<silenceset> silencelist;
+// list of pairs
+typedef std::vector<silenceset> silencelist;
 
 // intmasks for flags
 static int SILENCE_PRIVATE     = 0x0001; /* p  private messages      */
@@ -66,7 +64,7 @@ class CommandSVSSilence : public Command
        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. */
+               TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
@@ -78,7 +76,7 @@ class CommandSVSSilence : public Command
                 * 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))
+               if (!user->server->IsULine())
                        return CMD_FAILURE;
 
                User *u = ServerInstance->FindNick(parameters[0]);
@@ -87,7 +85,7 @@ class CommandSVSSilence : public Command
 
                if (IS_LOCAL(u))
                {
-                       ServerInstance->Parser->CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
+                       ServerInstance->Parser.CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
                }
 
                return CMD_SUCCESS;
@@ -95,10 +93,7 @@ class CommandSVSSilence : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* target = ServerInstance->FindNick(parameters[0]);
-               if (target)
-                       return ROUTE_OPT_UCAST(target->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -108,11 +103,11 @@ class CommandSilence : public Command
  public:
        SimpleExtItem<silencelist> ext;
        CommandSilence(Module* Creator, unsigned int &max) : Command(Creator, "SILENCE", 0),
-               maxsilence(max), ext("silence_list", Creator)
+               maxsilence(max)
+               , ext("silence_list", ExtensionItem::EXT_USER, Creator)
        {
                allow_empty_last_param = false;
                syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
        }
 
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
@@ -127,17 +122,17 @@ class CommandSilence : public Command
                                for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
                                {
                                        std::string decomppattern = DecompPattern(c->second);
-                                       user->WriteNumeric(271, "%s %s %s %s",user->nick.c_str(), user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
+                                       user->WriteNumeric(271, user->nick, c->first, decomppattern);
                                }
                        }
-                       user->WriteNumeric(272, "%s :End of Silence List",user->nick.c_str());
+                       user->WriteNumeric(272, "End of Silence List");
 
                        return CMD_SUCCESS;
                }
                else if (parameters.size() > 0)
                {
                        // one or more parameters, add or delete entry from the list (only the first parameter is used)
-                       std::string mask = parameters[0].substr(1);
+                       std::string mask(parameters[0], 1);
                        char action = parameters[0][0];
                        // Default is private and notice so clients do not break
                        int pattern = CompilePattern("pn");
@@ -149,7 +144,7 @@ class CommandSilence : public Command
 
                        if (pattern == 0)
                        {
-                               user->WriteServ("NOTICE %s :Bad SILENCE pattern",user->nick.c_str());
+                               user->WriteNotice("Bad SILENCE pattern");
                                return CMD_INVALID;
                        }
 
@@ -172,11 +167,11 @@ class CommandSilence : public Command
                                        for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
                                        {
                                                // search through for the item
-                                               irc::string listitem = i->first.c_str();
-                                               if (listitem == mask && i->second == pattern)
+                                               const std::string& listitem = i->first;
+                                               if ((irc::equals(listitem, mask)) && (i->second == pattern))
                                                {
                                                        sl->erase(i);
-                                                       user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+                                                       user->WriteNumeric(950, user->nick, InspIRCd::Format("Removed %s %s from silence list", mask.c_str(), decomppattern.c_str()));
                                                        if (!sl->size())
                                                        {
                                                                ext.unset(user);
@@ -185,7 +180,7 @@ class CommandSilence : public Command
                                                }
                                        }
                                }
-                               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());
+                               user->WriteNumeric(952, user->nick, InspIRCd::Format("%s %s does not exist on your silence list", mask.c_str(), decomppattern.c_str()));
                        }
                        else if (action == '+')
                        {
@@ -198,29 +193,29 @@ class CommandSilence : public Command
                                }
                                if (sl->size() > maxsilence)
                                {
-                                       user->WriteNumeric(952, "%s %s :Your silence list is full",user->nick.c_str(), user->nick.c_str());
+                                       user->WriteNumeric(952, user->nick, "Your silence list is full");
                                        return CMD_FAILURE;
                                }
 
                                std::string decomppattern = DecompPattern(pattern);
                                for (silencelist::iterator n = sl->begin(); n != sl->end();  n++)
                                {
-                                       irc::string listitem = n->first.c_str();
-                                       if (listitem == mask && n->second == pattern)
+                                       const std::string& listitem = n->first;
+                                       if ((irc::equals(listitem, mask)) && (n->second == pattern))
                                        {
-                                               user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+                                               user->WriteNumeric(952, user->nick, InspIRCd::Format("%s %s is already on your silence list", mask.c_str(), decomppattern.c_str()));
                                                return CMD_FAILURE;
                                        }
                                }
                                if (((pattern & SILENCE_EXCLUDE) > 0))
                                {
-                                       sl->push_front(silenceset(mask,pattern));
+                                       sl->insert(sl->begin(), silenceset(mask, pattern));
                                }
                                else
                                {
                                        sl->push_back(silenceset(mask,pattern));
                                }
-                               user->WriteNumeric(951, "%s %s :Added %s %s to silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+                               user->WriteNumeric(951, user->nick, InspIRCd::Format("Added %s %s to silence list", mask.c_str(), decomppattern.c_str()));
                                return CMD_SUCCESS;
                        }
                }
@@ -293,6 +288,7 @@ class CommandSilence : public Command
 class ModuleSilence : public Module
 {
        unsigned int maxsilence;
+       bool ExemptULine;
        CommandSilence cmdsilence;
        CommandSVSSilence cmdsvssilence;
  public:
@@ -302,36 +298,29 @@ class ModuleSilence : public Module
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(cmdsilence);
-               ServerInstance->Modules->AddService(cmdsvssilence);
-               ServerInstance->Modules->AddService(cmdsilence.ext);
-
-               Implementation eventlist[] = { I_OnRehash, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage, I_OnUserPreInvite };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
+               ConfigTag* tag = ServerInstance->Config->ConfValue("silence");
 
-       void OnRehash(User* user)
-       {
-               maxsilence = ServerInstance->Config->ConfValue("silence")->getInt("maxentries", 32);
+               maxsilence = tag->getInt("maxentries", 32);
                if (!maxsilence)
                        maxsilence = 32;
+
+               ExemptULine = tag->getBool("exemptuline", true);
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               // we don't really have a limit...
-               output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
+               tokens["ESILENCE"];
+               tokens["SILENCE"] = ConvToStr(maxsilence);
        }
 
-       void OnBuildExemptList(MessageType message_type, Channel* chan, User* sender, char status, CUList &exempt_list, const std::string &text)
+       void BuildExemptList(MessageType message_type, Channel* chan, User* sender, CUList& exempt_list)
        {
                int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
-               const UserMembList *ulist = chan->GetUsers();
 
-               for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+               const Channel::MemberMap& ulist = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
                {
                        if (IS_LOCAL(i->first))
                        {
@@ -343,43 +332,29 @@ class ModuleSilence : public Module
                }
        }
 
-       ModResult PreText(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (target_type == TYPE_USER && IS_LOCAL(((User*)dest)))
                {
-                       return MatchPattern((User*)dest, user, silence_type);
+                       return MatchPattern((User*)dest, user, ((msgtype == MSG_PRIVMSG) ? SILENCE_PRIVATE : SILENCE_NOTICE));
                }
                else if (target_type == TYPE_CHANNEL)
                {
                        Channel* chan = (Channel*)dest;
-                       if (chan)
-                       {
-                               this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list, "");
-                       }
+                       BuildExemptList(msgtype, chan, user, exempt_list);
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
-       }
-
-       ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
-       }
-
-       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);
        }
 
        ModResult MatchPattern(User* dest, User* source, int pattern)
        {
-               /* Server source */
-               if (!source || !dest)
-                       return MOD_RES_ALLOW;
+               if (ExemptULine && source->server->IsULine())
+                       return MOD_RES_PASSTHRU;
 
                silencelist* sl = cmdsilence.ext.get(dest);
                if (sl)
@@ -393,11 +368,7 @@ class ModuleSilence : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ~ModuleSilence()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the /SILENCE command", VF_OPTCOMMON | VF_VENDOR);
        }
index 16043b2aadba47bb1813084ebc2609edadb5f6de..1bf84760456b6a39e2ed77d5cb8bc454935dda2b 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, std::vector<std::string>& 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;
+               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(), ConvToInt(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('d',"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(ConvToInt(params[3]));
        if (ServerInstance->XLines->AddLine(xl, NULL))
        {
                if (xl->duration)
                {
-                       std::string timestr = ServerInstance->TimeString(xl->expiry);
+                       std::string timestr = InspIRCd::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());
                }
@@ -82,20 +59,29 @@ bool TreeSocket::AddLine(const std::string &prefix, parameterlist &params)
                        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..7c514c49e69597251d753857331f232a082c59a6 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, std::vector<std::string>& params)
 {
-       User* u = ServerInstance->FindNick(prefix);
-       if ((!u) || (IS_SERVER(u)))
-               return true;
        if (params.size())
        {
-               FOREACH_MOD(I_OnSetAway, OnSetAway(u, params[params.size() - 1]));
+               FOREACH_MOD(OnSetAway, (u, params.back()));
 
                if (params.size() > 1)
-                       u->awaytime = atoi(params[0].c_str());
+                       u->awaytime = ConvToInt(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();
        }
        else
        {
-               FOREACH_MOD(I_OnSetAway, OnSetAway(u, ""));
+               FOREACH_MOD(OnSetAway, (u, ""));
                u->awaymsg.clear();
        }
-       Utils->DoOneToAllButSender(prefix,"AWAY",params,u->server);
-       return true;
+       return CMD_SUCCESS;
+}
+
+CommandAway::Builder::Builder(User* user)
+       : CmdBuilder(user, "AWAY")
+{
+       push_int(user->awaytime).push_last(user->awaymsg);
+}
+
+CommandAway::Builder::Builder(User* user, const std::string& msg)
+       : CmdBuilder(user, "AWAY")
+{
+       if (!msg.empty())
+               push_int(ServerInstance->Time()).push_last(msg);
 }
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..cffbe3578d9afa07ff7916be1263ac4c9993ddc1 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);
 };
-
-#endif
index 0ab815fef71e8eaef8c38516f8d6d236552071b0..7f9f9edb7b4575b753c24f7f20c3b5b7df3b9bc5 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);
+               }
        }
        return capabilities;
 }
@@ -60,23 +82,23 @@ std::string TreeSocket::MyModules(int filter)
 static std::string BuildModeList(ModeType type)
 {
        std::vector<std::string> modes;
-       for(char c='A'; c <= 'z'; c++)
+       const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes.GetModes(type);
+       for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
        {
-               ModeHandler* mh = ServerInstance->Modes->FindMode(c, type);
-               if (mh)
+               const ModeHandler* const mh = i->second;
+               std::string mdesc = mh->name;
+               mdesc.push_back('=');
+               const PrefixMode* const pm = mh->IsPrefixMode();
+               if (pm)
                {
-                       std::string mdesc = mh->name;
-                       mdesc.push_back('=');
-                       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 irc::stringjoiner(modes);
 }
 
 void TreeSocket::SendCapabilities(int phase)
@@ -91,7 +113,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 +157,15 @@ 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 this key
+       if (proto_version == 1202)
+               extra.append(" PROTOCOL="+ConvToStr(ProtocolVersion));
 
        this->WriteLine("CAPAB CAPABILITIES " /* Preprocessor does this one. */
                        ":NICKMAX="+ConvToStr(ServerInstance->Config->Limits.NickMax)+
@@ -153,18 +177,19 @@ void TreeSocket::SendCapabilities(int phase)
                        " MAXKICK="+ConvToStr(ServerInstance->Config->Limits.MaxKick)+
                        " MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxGecos)+
                        " MAXAWAY="+ConvToStr(ServerInstance->Config->Limits.MaxAway)+
-                       " IP6SUPPORT=1"+
-                       " PROTOCOL="+ConvToStr(ProtocolVersion)+extra+
+                       " MAXHOST="+ConvToStr(ServerInstance->Config->Limits.MaxHost)+
+                       extra+
                        " PREFIX="+ServerInstance->Modes->BuildPrefixes()+
-                       " CHANMODES="+ServerInstance->Modes->GiveModeList(MASK_CHANNEL)+
-                       " USERMODES="+ServerInstance->Modes->GiveModeList(MASK_USER)+
+                       " CHANMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL)+
+                       " USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER)+
+                       " 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");
 }
@@ -209,7 +234,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 = ConvToInt(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 +260,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.";
@@ -257,21 +298,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.";
 
@@ -293,7 +319,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                }
                else if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end())
                {
-                       if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MASK_CHANNEL))
+                       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.";
                }
 
@@ -315,13 +341,31 @@ bool TreeSocket::Capab(const parameterlist &params)
                }
                else if (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.";
                }
+               else
+               {
+                       // We default to rfc1459 here because if this key is not sent then
+                       // the remote server is running the 2.0 protocol which uses rfc1459
+                       // by default.
+                       std::string casemapping = "rfc1459";
+                       std::map<std::string, std::string>::iterator citer = this->capab->CapKeys.find("CASEMAPPING");
+                       if (citer != this->capab->CapKeys.end())
+                               casemapping = citer->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;
+                       }
+
+               }
 
                /* 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,7 +377,7 @@ 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);
@@ -355,7 +399,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                }
                else
                {
-                       capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ',');
+                       capab->ModuleList.push_back(');
                        capab->ModuleList.append(params[1]);
                }
        }
@@ -389,12 +433,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..59de840
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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;
+       }
+
+       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..8eea0291551d9acc005ede266c96b8ed08c3b4cb 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"
+
+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(const std::vector<std::string>& parameters, User* user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 };
 
 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(const std::vector<std::string>& parameters, User* user);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 };
 
-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);
+       CommandMap(Module* Creator);
+       CmdResult Handle(const std::vector<std::string>& parameters, User* user);
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 };
-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);
+       CommandSVSJoin(Module* Creator) : ServerCommand(Creator, "SVSJOIN", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& params);
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 };
-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);
+       CommandSVSPart(Module* Creator) : ServerCommand(Creator, "SVSPART", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& params);
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 };
-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, std::vector<std::string>& params);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
 };
-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, std::vector<std::string>& params);
+
+       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, std::vector<std::string>& 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, std::vector<std::string>& 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, std::vector<std::string>& params);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
+
+       class Builder : public CmdBuilder
+       {
+               /** Maximum possible Membership::Id length in decimal digits, used for determining whether a user will fit into
+                * a message or not
+                */
+               static const size_t membid_max_digits = 20;
+               static const size_t maxline = 510;
+               std::string::size_type pos;
+
+       protected:
+               void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+               bool has_room(std::string::size_type nummodes) const;
+
+        public:
+               Builder(Channel* chan, TreeServer* source = Utils->TreeRoot);
+               void add(Membership* memb)
+               {
+                       add(memb, memb->modes.begin(), memb->modes.end());
+               }
+
+               bool has_room(Membership* memb) const
+               {
+                       return has_room(memb->modes.size());
+               }
+
+               void clear();
+               const std::string& finalize();
+       };
+};
+
+class CommandFMode : public ServerCommand
+{
+ public:
+       CommandFMode(Module* Creator) : ServerCommand(Creator, "FMODE", 3) { }
+       CmdResult Handle(User* user, std::vector<std::string>& params);
+};
+
+class CommandFTopic : public ServerCommand
+{
+ public:
+       CommandFTopic(Module* Creator) : ServerCommand(Creator, "FTOPIC", 4, 5) { }
+       CmdResult Handle(User* user, std::vector<std::string>& params);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(Channel* chan);
+               Builder(User* user, Channel* chan);
+       };
+};
+
+class CommandFHost : public UserOnlyServerCommand<CommandFHost>
+{
+ public:
+       CommandFHost(Module* Creator) : UserOnlyServerCommand<CommandFHost>(Creator, "FHOST", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
+};
+
+class CommandFIdent : public UserOnlyServerCommand<CommandFIdent>
+{
+ public:
+       CommandFIdent(Module* Creator) : UserOnlyServerCommand<CommandFIdent>(Creator, "FIDENT", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
 };
-class CommandFMode : public Command
+
+class CommandFName : public UserOnlyServerCommand<CommandFName>
 {
  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; }
+       CommandFName(Module* Creator) : UserOnlyServerCommand<CommandFName>(Creator, "FNAME", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
 };
-class CommandFTopic : public Command
+
+class CommandIJoin : public UserOnlyServerCommand<CommandIJoin>
+{
+ public:
+       CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 2) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
+};
+
+class CommandResync : public ServerOnlyServerCommand<CommandResync>
 {
  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; }
+       CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
 };
-class CommandFHost : public Command
+
+class SpanningTree::CommandAway : public UserOnlyServerCommand<SpanningTree::CommandAway>
 {
  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; }
+       CommandAway(Module* Creator) : UserOnlyServerCommand<SpanningTree::CommandAway>(Creator, "AWAY", 0, 2) { }
+       CmdResult HandleRemote(::RemoteUser* user, std::vector<std::string>& parameters);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(User* user);
+               Builder(User* user, const std::string& msg);
+       };
 };
-class CommandFIdent : public Command
+
+class XLine;
+class CommandAddLine : public ServerCommand
 {
  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; }
+       CommandAddLine(Module* Creator) : ServerCommand(Creator, "ADDLINE", 6, 6) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(XLine* xline, User* user = ServerInstance->FakeClient);
+       };
 };
-class CommandFName : public Command
+
+class CommandDelLine : public ServerCommand
 {
  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; }
+       CommandDelLine(Module* Creator) : ServerCommand(Creator, "DELLINE", 2, 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+};
+
+class CommandEncap : public ServerCommand
+{
+ public:
+       CommandEncap(Module* Creator) : ServerCommand(Creator, "ENCAP", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+class CommandIdle : public UserOnlyServerCommand<CommandIdle>
+{
+ public:
+       CommandIdle(Module* Creator) : UserOnlyServerCommand<CommandIdle>(Creator, "IDLE", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { 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, std::vector<std::string>& parameters);
+};
+
+class SpanningTree::CommandPing : public ServerCommand
+{
+ public:
+       CommandPing(Module* Creator) : ServerCommand(Creator, "PING", 1) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class SpanningTree::CommandPong : public ServerOnlyServerCommand<SpanningTree::CommandPong>
+{
+ public:
+       CommandPong(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandPong>(Creator, "PONG", 1) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandSave : public ServerCommand
+{
+ public:
+       /** Timestamp of the uuid nick of all users who collided and got their nick changed to uuid
+        */
+       static const time_t SavedTimestamp = 100;
+
+       CommandSave(Module* Creator) : ServerCommand(Creator, "SAVE", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+};
+
+class SpanningTree::CommandServer : public ServerOnlyServerCommand<SpanningTree::CommandServer>
+{
+       static void HandleExtra(TreeServer* newserver, const std::vector<std::string>& params);
+
+ public:
+       CommandServer(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandServer>(Creator, "SERVER", 3) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+
+       class Builder : public CmdBuilder
+       {
+               void push_property(const char* key, const std::string& val)
+               {
+                       push(key).push_raw('=').push_raw(val);
+               }
+        public:
+               Builder(TreeServer* server);
+       };
+};
+
+class CommandSQuit : public ServerOnlyServerCommand<CommandSQuit>
+{
+ public:
+       CommandSQuit(Module* Creator) : ServerOnlyServerCommand<CommandSQuit>(Creator, "SQUIT", 2) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+};
+
+class CommandSNONotice : public ServerCommand
+{
+ public:
+       CommandSNONotice(Module* Creator) : ServerCommand(Creator, "SNONOTICE", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+};
+
+class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
+{
+ public:
+       CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+};
+
+class CommandSInfo : public ServerOnlyServerCommand<CommandSInfo>
+{
+ public:
+       CommandSInfo(Module* Creator) : ServerOnlyServerCommand<CommandSInfo>(Creator, "SINFO", 2) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(TreeServer* server, const char* type, const std::string& value);
+       };
+};
+
+class CommandNum : public ServerOnlyServerCommand<CommandNum>
+{
+ public:
+       CommandNum(Module* Creator) : ServerOnlyServerCommand<CommandNum>(Creator, "NUM", 3) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric);
+       };
 };
 
 class SpanningTreeCommands
 {
  public:
-       CommandRConnect rconnect;
-       CommandRSQuit rsquit;
        CommandSVSJoin svsjoin;
        CommandSVSPart svspart;
        CommandSVSNick svsnick;
@@ -144,12 +401,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..2436e74f8fc425daa1e2cfe790bb32ff6c4d63a4 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] != ':')
+               if (original_line.c_str()[0] != ':')
                {
-                       ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Sending line without server prefix!");
-                       line = ":" + ServerInstance->Config->GetSID() + " " + line;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sending line without server prefix!");
+                       WriteLine(":" + ServerInstance->Config->GetSID() + " " + original_line);
+                       return;
                }
                if (proto_version != ProtocolVersion)
                {
+                       std::string line = original_line;
                        std::string::size_type a = line.find(' ');
                        std::string::size_type b = line.find(' ', a + 1);
-                       std::string command = line.substr(a + 1, b-a-1);
+                       std::string command(line, a + 1, b-a-1);
                        // now try to find a translation entry
                        // TODO a more efficient lookup method will be needed later
-                       if (proto_version < 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 (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;
 
-                               if (subcmd == "CHGIDENT" && d != std::string::npos)
+                                       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 (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;
+
+                                       // 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 (e == std::string::npos)
-                                               return; // not valid
-                                       std::string target = line.substr(d + 1, e - 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 :
 
-                                       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Forging acceptance of CHGIDENT from 1201-protocol server");
-                                       recvq.insert(0, ":" + target + " FIDENT " + line.substr(e) + "\n");
+                                       // 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;
 
-                               Command* thiscmd = ServerInstance->Parser->GetHandler(subcmd);
-                               if (thiscmd && subcmd != "WHOISNOTICE")
+                                       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")
                                {
-                                       Version ver = thiscmd->creator->GetVersion();
-                                       if (ver.Flags & VF_OPTCOMMON)
+                                       // :22D SINFO version :InspIRCd-3.0
+                                       //     A     B       C
+                                       std::string::size_type c = line.find(' ', b + 1);
+                                       if (c == std::string::npos)
+                                               return;
+
+                                       // Only translating SINFO version, discard everything else
+                                       if (line.compare(b, 9, " version ", 9))
+                                               return;
+
+                                       line = line.substr(0, 5) + "VERSION" + line.substr(c);
+                               }
+                               else if (command == "SERVER")
+                               {
+                                       // :001 SERVER inspircd.test 002 [<anything> ...] :gecos
+                                       //     A      B             C
+                                       std::string::size_type c = line.find(' ', b + 1);
+                                       if (c == std::string::npos)
+                                               return;
+
+                                       std::string::size_type d = c + 4;
+                                       std::string::size_type spcolon = line.find(" :", d);
+                                       if (spcolon == std::string::npos)
+                                               return;
+
+                                       line.erase(d, spcolon-d);
+                                       line.insert(c, " * 0");
+
+                                       if (burstsent)
                                        {
-                                               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);
+                               }
                        }
+                       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(std::vector<std::string>& 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, std::vector<std::string>& 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;
+
+               parameterlist 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 is not bursting, then new servers it introduces are bursting
+               TreeServer* server = TreeServer::Get(who);
+               if (!server->IsBursting())
+                       params.insert(params.begin()+2, "burst=" + ConvToStr(((uint64_t)ServerInstance->Time())*1000));
+       }
+       else if (cmd == "BURST")
+       {
+               // A server is introducing another one, drop unnecessary BURST
+               return false;
+       }
+       else if (cmd == "SVSWATCH")
+       {
+               // SVSWATCH was removed because nothing was using it, but better be sure
+               return false;
+       }
+       else if (cmd == "PUSH")
+       {
+               if ((params.size() != 2) || (!this->MyRoot))
+                       return false; // Huh?
+
+               irc::tokenstream ts(params.back());
+
+               std::string srcstr;
+               ts.GetToken(srcstr);
+               srcstr.erase(0, 1);
+
+               std::string token;
+               ts.GetToken(token);
+
+               // See if it's a numeric being sent to the target via PUSH
+               unsigned int numeric_number = 0;
+               if (token.length() == 3)
+                       numeric_number = ConvToInt(token);
+
+               if ((numeric_number > 0) && (numeric_number < 1000))
+               {
+                       // It's a numeric, translate to NUM
+
+                       // srcstr must be a valid server name
+                       TreeServer* const numericsource = Utils->FindServer(srcstr);
+                       if (!numericsource)
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unable to translate PUSH numeric %s to user %s from 1202 protocol server %s: source \"%s\" doesn't exist", token.c_str(), params[0].c_str(), this->MyRoot->GetName().c_str(), srcstr.c_str());
+                               return false;
+                       }
+
+                       cmd = "NUM";
+
+                       // Second parameter becomes the target uuid
+                       params[0].swap(params[1]);
+                       // Replace first param (now the PUSH payload, not needed) with the source sid
+                       params[0] = numericsource->GetID();
+
+                       params.push_back(InspIRCd::Format("%03u", numeric_number));
+
+                       // Ignore the nickname in the numeric in PUSH
+                       ts.GetToken(token);
+
+                       // Rest of the tokens are the numeric parameters, add them to NUM
+                       while (ts.GetToken(token))
+                               params.push_back(token);
+               }
+               else if ((token == "PRIVMSG") || (token == "NOTICE"))
+               {
+                       // Command is a PRIVMSG/NOTICE
+                       cmd.swap(token);
+
+                       // Check if the PRIVMSG/NOTICE target is a nickname
+                       ts.GetToken(token);
+                       if (token.c_str()[0] == '#')
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unable to translate PUSH %s to user %s from 1202 protocol server %s, target \"%s\"", cmd.c_str(), params[0].c_str(), this->MyRoot->GetName().c_str(), token.c_str());
+                               return false;
+                       }
+
+                       // Replace second parameter with the message
+                       ts.GetToken(params[1]);
+               }
+               else
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unable to translate PUSH to user %s from 1202 protocol server %s", params[0].c_str(), this->MyRoot->GetName().c_str());
+                       return false;
+               }
+
+               return true;
+       }
+
+       return true; // Passthru
 }
index 540ca50792dcf25a4e822fd4dd6182731f0771ed..f790dc885d8a7e50257b4b4d09e72115a790403e 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, std::vector<std::string>& 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;
 
-
-       /* NOTE: No check needed on 'user', this function safely handles NULL */
+       // XLineManager::DelLine() returns true if the xline existed, false if it didn't
        if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], user))
        {
                ServerInstance->SNO->WriteToSnoMask('X',"%s removed %s%s on %s", setter.c_str(),
                                params[0].c_str(), params[0].length() == 1 ? "-line" : "", params[1].c_str());
-               Utils->DoOneToAllButSender(prefix,"DELLINE", params, prefix);
+               return CMD_SUCCESS;
        }
-       return true;
+       return CMD_FAILURE;
 }
-
index dabfc086bce5542b138d4638b074b8edbfd0acfe..8059d2a39ce5b4ee5c605fd888b6395e98d090b5 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, std::vector<std::string>& 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];
+               parameterlist 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 std::vector<std::string>& params)
+{
+       if (params[0].find_first_of("*?") != std::string::npos)
+               return ROUTE_BROADCAST;
+       return ROUTE_UNICAST(params[0]);
+}
index 4ec6e1dbb0f3d502209775cbde367ce325455ba3..c292373b3de455be754ab025e8f6c79dfe94a66a 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, std::vector<std::string>& 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,276 @@ 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)
+                       /* 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::tokenstream 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("TS for %s changed from %lu to %lu", newname.c_str(), (unsigned long) chan->age, (unsigned long) TS));
+
+       // While the name is equal in case-insensitive compare, it might differ in case; use the remote version
+       chan->name = newname;
+       chan->age = TS;
+
+       // 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..e6f49c5b964f65b786a8776cdf0579be9eaf1f7a 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, std::vector<std::string>& 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..de72d162a4bbeade99d9f98d02009bd027459526 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, std::vector<std::string>& 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..ad58e52f08f9eb871a9b062923d4c4e844a7d603 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, std::vector<std::string>& 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..c2dbcf7
--- /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, std::vector<std::string>& 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 non-existant 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, std::vector<std::string>& 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..632982623780e8dd439dfa3cdb2f5228c9404a58 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;
        std::string SendPass;
        std::string RecvPass;
        std::string Fingerprint;
-       std::string AllowMask;
+       std::vector<std::string> AllowMasks;
        bool HiddenFromStats;
        std::string Hook;
        int Timeout;
@@ -51,5 +50,3 @@ class Autoconnect : public refcountbase
        int position;
        Autoconnect(ConfigTag* Tag) : tag(Tag) {}
 };
-
-#endif
index 967b577b15d03f466df3f3df81f7d29857d8f7ae..2d76102bce6f020a60a09d8007e88dbb94433c4e 100644 (file)
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
 #include "socket.h"
 #include "xline.h"
+#include "iohook.h"
+#include "modules/spanningtree.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)
+       : rconnect(this), rsquit(this), map(this)
+       , commands(this)
+       , currmembid(0)
+       , eventprov(this, "event/spanningtree")
+       , DNS(this, "DNS")
+       , 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 +104,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());
-}
-
-int ModuleSpanningTree::CountServs()
-{
-       return Utils->serverlist.size();
+       user->WriteNumeric(RPL_LINKS, Current->GetName(),
+                       (((Utils->FlatLinks) && (!user->IsOper())) ? ServerInstance->Config->ServerName : Parent),
+                       InspIRCd::Format("%d %s", (((Utils->FlatLinks) && (!user->IsOper())) ? 0 : hops), Current->GetDesc().c_str()));
 }
 
 void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
 {
        ShowLinks(Utils->TreeRoot,user,0);
-       user->WriteNumeric(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 +153,6 @@ std::string ModuleSpanningTree::TimeToStr(time_t secs)
                        + ConvToStr(secs) + "s");
 }
 
-void ModuleSpanningTree::DoPingChecks(time_t curtime)
-{
-       /*
-        * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
-        * This prevents lost REMOTECONNECT notices
-        */
-       long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-
-restart:
-       for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
-       {
-               TreeServer *s = i->second;
-
-               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)
@@ -266,13 +194,12 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
 {
        bool ipvalid = true;
 
-       if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name), rfc_case_insensitive_map))
+       if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map))
        {
                ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Not connecting to myself.");
                return;
        }
 
-       QueryType start_type = DNS_QUERY_AAAA;
        if (strchr(x->IPAddr.c_str(),':'))
        {
                in6_addr n;
@@ -282,15 +209,15 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
        else
        {
                in_addr n;
-               if (inet_aton(x->IPAddr.c_str(),&n) < 1)
+               if (inet_pton(AF_INET, x->IPAddr.c_str(),&n) < 1)
                        ipvalid = false;
        }
 
        /* Do we already have an IP? If so, no need to resolve it. */
        if (ipvalid)
        {
-               /* 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, x->IPAddr);
                if (newsocket->GetFd() > -1)
                {
                        /* Handled automatically on success */
@@ -302,17 +229,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.sa.sa_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);
                }
        }
@@ -356,224 +296,130 @@ void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
 
 ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
 {
-       // we've already checked if pcnt > 0, so this is safe
+       // We've already confirmed that !parameters.empty(), so this is safe
        TreeServer* found = Utils->FindServerMask(parameters[0]);
        if (found)
        {
-               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()));
+               const std::string& Version = (showfull ? found->GetFullVersion() : found->GetVersion());
+               user->WriteNumeric(RPL_VERSION, Version);
        }
        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)
 {
        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::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype)
 {
-       /* Server origin */
-       if (user == NULL)
+       if (!IS_LOCAL(user))
                return;
 
+       const char* message_type = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
        if (target_type == 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 = (User*) dest;
+               if (!IS_LOCAL(d))
                {
-                       parameterlist params;
+                       CmdBuilder params(user, message_type);
                        params.push_back(d->uuid);
-                       params.push_back(":"+text);
-                       Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
+                       params.push_last(text);
+                       params.Unicast(d);
                }
        }
        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)+" PRIVMSG "+cname+" :"+text);
-                               }
-                       }
-               }
+               Utils->SendChannelMessage(user->uuid, (Channel*)dest, text, status, exempt_list, message_type);
        }
        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,"PRIVMSG",par);
-               }
+               char* target = (char*) dest;
+               CmdBuilder par(user, message_type);
+               par.push_back(target);
+               par.push_last(text);
+               par.Broadcast();
        }
 }
 
 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
 {
        AutoConnectServers(curtime);
-       DoPingChecks(curtime);
        DoConnectTimeout(curtime);
 }
 
@@ -582,25 +428,10 @@ 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);
+       CommandUID::Builder(user).Broadcast();
 
-       if (IS_OPER(user))
-       {
-               params.clear();
-               params.push_back(user->oper->name);
-               Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
-       }
+       if (user->IsOper())
+               CommandOpertype::Builder(user).Broadcast();
 
        for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
        {
@@ -610,23 +441,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,9 +479,7 @@ 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)
@@ -645,9 +487,7 @@ void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
        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(gecos).Broadcast();
 }
 
 void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
@@ -655,101 +495,77 @@ 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)
@@ -757,19 +573,29 @@ 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 +609,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 +625,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(), SpanningTreeEventListener, OnServerSplit, (server));
+               }
+               return;
+       }
+
+       // Some other module is being unloaded. If it provides an IOHook we use, we must close that server connection now.
 
 restart:
-       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 +667,96 @@ 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(ServerInstance->Config->ServerName);
-       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)
 {
        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);
-       }
+               CommandAway::Builder(user, awaymsg).Broadcast();
 
        return MOD_RES_PASSTHRU;
 }
 
-void ModuleSpanningTree::OnRequest(Request& request)
-{
-       if (!strcmp(request.id, "rehash"))
-               Utils->Rehash();
-}
-
-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, const std::string& output_mode)
 {
-       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(output_mode);
+               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(output_mode);
+               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 +768,13 @@ Version ModuleSpanningTree::GetVersion()
  * so that any activity it sees is FINAL, e.g. we arent going to send out
  * a NICK message before m_cloaking has finished putting the +x on the user,
  * etc etc.
- * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
+ * Therefore, we set our priority to PRIORITY_LAST to make sure we end up at the END of
  * the module call queue.
  */
 void ModuleSpanningTree::Prioritize()
 {
        ServerInstance->Modules->SetPriority(this, PRIORITY_LAST);
+       ServerInstance->Modules.SetPriority(this, I_OnPreTopicChange, PRIORITY_FIRST);
 }
 
 MODULE_INIT(ModuleSpanningTree)
index 3e0a831111ed205efc98c9242b0b9c0708a34907..13bab4cff365e44857e6982d8bc04489f83428d0 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 "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 long ProtocolVersion = 1205;
+const long MinCompatProtocol = 1202;
 
 /** Forward declarations
  */
-class SpanningTreeCommands;
 class SpanningTreeUtilities;
 class CacheRefreshTimer;
 class TreeServer;
@@ -52,47 +54,51 @@ class Autoconnect;
  */
 class ModuleSpanningTree : public Module
 {
-       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;
 
  public:
-       SpanningTreeUtilities* Utils;
+       dynamic_reference<DNS::Manager> DNS;
+
+       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);
-
        /** Handle SQUIT
         */
        ModResult HandleSquit(const std::vector<std::string>& parameters, User* user);
@@ -101,10 +107,6 @@ class ModuleSpanningTree : public Module
         */
        ModResult HandleRemoteWhois(const std::vector<std::string>& parameters, User* user);
 
-       /** Ping all local servers
-        */
-       void DoPingChecks(time_t curtime);
-
        /** Connect a server locally
         */
        void ConnectServer(Link* x, Autoconnect* y = NULL);
@@ -129,60 +131,45 @@ class ModuleSpanningTree : public Module
         */
        ModResult HandleConnect(const std::vector<std::string>& parameters, User* user);
 
-       /** Attempt to send a message to a user
-        */
-       void RemoteMessage(User* user, const char* format, ...) CUSTOM_PRINTF(3, 4);
-
-       /** Returns oper-specific MAP information
-        */
-       const std::string MapOperInfo(TreeServer* Current);
-
        /** 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, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
+       void OnPostCommand(Command*, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line) CXX11_OVERRIDE;
+       void OnUserConnect(LocalUser* source) CXX11_OVERRIDE;
+       void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE;
+       ModResult OnPreTopicChange(User* user, Channel* chan, const std::string& topic) CXX11_OVERRIDE;
+       void OnPostTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE;
+       void OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE;
+       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE;
+       void OnChangeHost(User* user, const std::string &newhost) CXX11_OVERRIDE;
+       void OnChangeName(User* user, const std::string &gecos) 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;
+       ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE;
+       void OnLoadModule(Module* mod) CXX11_OVERRIDE;
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
+       ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+       void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode) CXX11_OVERRIDE;
+       CullResult cull() CXX11_OVERRIDE;
        ~ModuleSpanningTree();
-       Version GetVersion();
-       void Prioritize();
+       Version GetVersion() CXX11_OVERRIDE;
+       void Prioritize() CXX11_OVERRIDE;
 };
-
-#endif
index a584f8fa8b6fcd8494d3c44537e8a324617e5184..47c2f8bc541ec19c6c3a9dde3da4a658a36d6950 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, std::vector<std::string>& 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..00f31d6
--- /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, std::vector<std::string>& params)
+{
+       ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + user->nick + ": " + params[1]);
+       return CMD_SUCCESS;
+}
+
+CmdResult CommandEndBurst::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+       server->FinishBurst();
+       return CMD_SUCCESS;
+}
index 3bce90eda30895bcb1be9cf67d013ffcfe607e41..cdafa9dedfbb86361e8005815f32a1ca11db5ae1 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"
+
+/**
+ * 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 " : "",
+               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");
+       FOREACH_MOD(OnSyncNetwork, (bs.server));
+       this->WriteLine(CmdBuilder("ENDBURST"));
        ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2.");
+
+       this->burstsent = true;
 }
 
-/** Recursively send the server tree with distances as hops.
+void TreeSocket::SendServerInfo(TreeServer* from)
+{
+       // Send public version string
+       this->WriteLine(CommandSInfo::Builder(from, "version", from->GetVersion()));
+
+       // Send full version string that contains more information and is shown to opers
+       this->WriteLine(CommandSInfo::Builder(from, "fullversion", from->GetFullVersion()));
+}
+
+/** Recursively send the server tree.
  * 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 +204,101 @@ 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());
+}
 
-               FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,Utils->Creator,this));
+/** Send channel users, topic, modes and global metadata */
+void TreeSocket::SyncChannel(Channel* chan, BurstState& bs)
+{
+       SendFJoins(chan);
+
+       // 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(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)
+{
+       ProtocolInterface::Server& piserver = bs.server;
+
+       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(OnSyncUser, (user, piserver));
        }
 }
-
diff --git a/src/modules/m_spanningtree/nick.cpp b/src/modules/m_spanningtree/nick.cpp
new file mode 100644 (file)
index 0000000..9e290e0
--- /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, std::vector<std::string>& params)
+{
+       if ((isdigit(params[0][0])) && (params[0] != user->uuid))
+               throw ProtocolException("Attempted to change nick to an invalid or non-matching UUID");
+
+       // 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..2c8697c
--- /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, std::vector<std::string>& params)
+{
+       User* const target = ServerInstance->FindUUID(params[1]);
+       if (!target)
+               return CMD_FAILURE;
+
+       LocalUser* const localtarget = IS_LOCAL(target);
+       if (!localtarget)
+               return CMD_SUCCESS;
+
+       Numeric::Numeric numeric(ConvToInt(params[2]));
+       // Passing NULL is ok, in that case the numeric source becomes this server
+       numeric.SetServer(Utils->FindServerID(params[0]));
+       numeric.GetParams().insert(numeric.GetParams().end(), params.begin()+3, params.end());
+
+       localtarget->WriteNumeric(numeric);
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandNum::GetRouting(User* user, const std::vector<std::string>& params)
+{
+       return ROUTE_UNICAST(params[1]);
+}
+
+CommandNum::Builder::Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric)
+       : CmdBuilder("NUM")
+{
+       TreeServer* const server = (numeric.GetServer() ? (static_cast<TreeServer*>(numeric.GetServer())) : Utils->TreeRoot);
+       push(server->GetID()).push(target->uuid).push(InspIRCd::Format("%03u", numeric.GetNumeric()));
+       const std::vector<std::string>& params = numeric.GetParams();
+       if (!params.empty())
+       {
+               for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
+                       push(*i);
+               push_last(params.back());
+       }
+}
diff --git a/src/modules/m_spanningtree/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..4b1dce23c9794d76241f3a63de170c72749a37dd 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, std::vector<std::string>& 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);
+       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 +47,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 an IRC 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..a22fa48acb9f7e621e005b21b8862180e59779d0 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() + ")";
        }
-       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(), ' ');
+
+       buffer += InspIRCd::Format("%5d [%5.2f%%]", current->UserCount, percent);
 
-       if (IS_OPER(user) || !Utils->FlatLinks)
-               depth = depth + 2;
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       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(const std::vector<std::string>& parameters, User* user)
+{
+       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)
+       {
+               // 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;
+       }
+       else
        {
-               // 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);
+               // This user can't see any depth
+               max = 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());
 
-       delete[] names;
-       delete[] stats;
+       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);
+
+       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 std::vector<std::string>& parameters)
+{
+       if (!parameters.empty())
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
index 7d01c8149bf62fbf5b2e0d0d5e633a6df40b68c4..9cec527d376d1a12130a768de28de05c951a66ae 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)
 {
        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..7f718985436c5ff5af1b3e06da01f811cf5948c2 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)
 {
-       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..878f8af3a3bdc2b8b44fc40cd58256a8665e472a 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, std::vector<std::string>& 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..5d97f2af2e8508bb8609c2d1ffdb16288ff256b1 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, std::vector<std::string>& 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..64ca729779249cd2a44e8bb8fc80db9119719d71 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 std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line)
 {
        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 parameterlist& 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());
+       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 +74,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 +91,40 @@ 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;
+                       SendChannelMessage(user->uuid, c, parameters[1], pfx, 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..4733d00714491ada271834fc9a098b659928d734 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)
 {
@@ -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..be95845a7e2302d46d63876252cc11aef09b28e1 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.usercount = i->second->UserCount;
+               ps.opercount = i->second->OperCount;
                ps.gecos = 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 parameterlist& 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 parameterlist& 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;
+       Utils->SendChannelMessage(ServerInstance->Config->GetSID(), target, text, status, 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..b0609005c3ddc915f3b8b1b1fe748c3e6206e69b 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 parameterlist& params, User* source) CXX11_OVERRIDE;
+       void BroadcastEncap(const std::string& cmd, const parameterlist& 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..8b8757a07f9f3786b2ee13cf70bb8c6b77637dba 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>";
@@ -39,14 +33,11 @@ CommandRConnect::CommandRConnect (Module* Creator, SpanningTreeUtilities* Util)
 
 CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, User *user)
 {
-       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? */
@@ -58,6 +49,21 @@ CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, U
                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;
 }
 
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..ded0573afeec2c50cd2b069f4bea63e674bfd6f9 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;
+       }
+
        /* 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, ans_record->rdata);
                if (newsocket->GetFd() > -1)
                {
                        /* We're all OK */
@@ -66,47 +71,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..487db28261186d17cf958f795765202d3bd97fc8 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]";
@@ -38,34 +35,26 @@ CommandRSQuit::CommandRSQuit (Module* Creator, SpanningTreeUtilities* Util)
 CmdResult CommandRSQuit::Handle (const std::vector<std::string>& parameters, User *user)
 {
        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;
@@ -75,20 +64,3 @@ RouteDescriptor CommandRSQuit::GetRouting(User* user, const std::vector<std::str
 {
        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..7131b49feeb295592cfaf7fd7e77dd325dae973c 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, std::vector<std::string>& params)
 {
-       if (params.size() < 2)
-               return true;
+       User* u = ServerInstance->FindUUID(params[0]);
+       if (!u)
+               return CMD_FAILURE;
 
-       User* u = ServerInstance->FindNick(params[0]);
        time_t ts = atol(params[1].c_str());
 
-       if ((u) && (!IS_SERVER(u)) && (u->age == ts))
-       {
-               Utils->DoOneToAllButSender(prefix,"SAVE",params,prefix);
-
-               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..50f63e117713ba60cf0604530e6ea4368cca101c 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, std::vector<std::string>& 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!");
+               socket->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;
+               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.");
+               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 \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;
+               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, const std::vector<std::string>& params)
+{
+       for (std::vector<std::string>::const_iterator i = params.begin() + 2; i != params.end() - 1; ++i)
+       {
+               const std::string& prop = *i;
+               std::string::size_type p = prop.find('=');
 
-/*
- * This is used after the other side of a connection has accepted our credentials.
- * They are then introducing themselves to us, BEFORE either of us burst. -- w
- */
-bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
+               std::string key = prop;
+               std::string val;
+               if (p != std::string::npos)
+               {
+                       key.erase(p);
+                       val.assign(prop, p+1, std::string::npos);
+               }
+
+               if (key == "burst")
+                       newserver->BeginBurst(ConvToUInt64(val));
+       }
+}
+
+Link* TreeSocket::AuthRemote(const parameterlist& params)
 {
        if (params.size() < 5)
        {
                SendError("Protocol error - Not enough parameters for SERVER command");
-               return false;
+               return NULL;
        }
 
-       irc::string servername = params[0].c_str();
-       std::string sname = params[0];
-       std::string password = params[1];
-       std::string sid = params[3];
-       std::string description = params[4];
-       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 ((!stdalgo::string::equalsci(x->Name, sname)) && (x->Name != "*")) // open link allowance
                        continue;
 
                if (!ComparePass(*x, password))
@@ -134,22 +123,36 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
                        continue;
                }
 
-               TreeServer* CheckDupe = Utils->FindServer(sname);
-               if (CheckDupe)
-               {
-                       std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>";
-                       SendError("Server "+sname+" already exists on server "+pname+"!");
-                       ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname);
-                       return false;
-               }
-               CheckDupe = Utils->FindServer(sid);
-               if (CheckDupe)
+               if (!CheckDuplicate(sname, sid))
+                       return NULL;
+
+               ServerInstance->SNO->WriteToSnoMask('l',"Verified server connection " + linkID + " ("+description+")");
+
+               const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(this);
+               if (ssliohook)
                {
-                       this->SendError("Server ID "+sid+" already exists on the network! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
-                       ServerInstance->SNO->WriteToSnoMask('l',"Server \2"+assign(servername)+"\2 being introduced denied, server ID already exists on the network. Closing link.");
-                       return false;
+                       std::string ciphersuite;
+                       ssliohook->GetCiphersuite(ciphersuite);
+                       ServerInstance->SNO->WriteToSnoMask('l', "Negotiated ciphersuite %s on link %s", ciphersuite.c_str(), x->Name.c_str());
                }
 
+               return x;
+       }
+
+       this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
+       ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
+       return NULL;
+}
+
+/*
+ * This is used after the other side of a connection has accepted our credentials.
+ * They are then introducing themselves to us, BEFORE either of us burst. -- w
+ */
+bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
+{
+       const Link* x = AuthRemote(params);
+       if (x)
+       {
                /*
                 * They're in WAIT_AUTH_2 (having accepted our credentials).
                 * Set our state to CONNECTED (since everything's peachy so far) and send our
@@ -158,32 +161,17 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
                 * While we're at it, create a treeserver object so we know about them.
                 *   -- w
                 */
-               this->LinkState = CONNECTED;
-
-               Utils->timeoutlist.erase(this);
-               linkID = sname;
-
-               MyRoot = new TreeServer(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)
        {
@@ -193,8 +181,8 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
                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)
@@ -214,58 +202,14 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
  */
 bool TreeSocket::Inbound_Server(parameterlist &params)
 {
-       if (params.size() < 5)
-       {
-               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++)
+       const Link* x = AuthRemote(params);
+       if (x)
        {
-               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..ef55cd0
--- /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 std::vector<std::string>& parameters)
+{
+       // Broadcast server-to-server commands unless overridden
+       return ROUTE_BROADCAST;
+}
+
+time_t ServerCommand::ExtractTS(const std::string& tsstr)
+{
+       time_t TS = ConvToInt(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..ee77667
--- /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, std::vector<std::string>& parameters) = 0;
+       virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& 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, std::vector<std::string>& parameters)
+       {
+               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, std::vector<std::string>& parameters)
+       {
+               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..0989ea9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+
+#include "treeserver.h"
+#include "commands.h"
+
+CmdResult CommandSInfo::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+       const std::string& key = params.front();
+       const std::string& value = params.back();
+
+       if (key == "fullversion")
+       {
+               server->SetFullVersion(value);
+       }
+       else if (key == "version")
+       {
+               server->SetVersion(value);
+       }
+       else if (key == "desc")
+       {
+               // Only sent when the description of a server changes because of a rehash; not sent on burst
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Server description of " + server->GetName() + " changed: " + value);
+               server->SetDesc(value);
+       }
+
+       return CMD_SUCCESS;
+}
+
+CommandSInfo::Builder::Builder(TreeServer* server, const char* key, const std::string& val)
+       : CmdBuilder(server->GetID(), "SINFO")
+{
+       push(key).push_last(val);
+}
index 416502369c7856312afe3d34fc43283a15b21c8f..c85e4f412a4cbedb949f9531e660009f37e37d56 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, std::vector<std::string>& 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)
 {
-       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..84cf8558cef2a3ef31fb51239106cec3515f0633 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, std::vector<std::string>& 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 = ConvToInt(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]);
                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;
@@ -63,8 +72,5 @@ CmdResult CommandSVSNick::Handle(const std::vector<std::string>& parameters, Use
 
 RouteDescriptor CommandSVSNick::GetRouting(User* user, const std::vector<std::string>& parameters)
 {
-       User* u = ServerInstance->FindNick(parameters[0]);
-       if (u)
-               return ROUTE_OPT_UCAST(u->server);
-       return ROUTE_LOCALONLY;
+       return ROUTE_OPT_UCAST(parameters[0]);
 }
index 3bdf13b25ca1e03ee8cc0859e5f8047404f08314..c4163ef3dd83e1b15fd9eae52a26ec4c551c8ca0 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, std::vector<std::string>& parameters)
 {
        User* u = ServerInstance->FindUUID(parameters[0]);
        if (!u)
@@ -48,8 +42,5 @@ CmdResult CommandSVSPart::Handle(const std::vector<std::string>& parameters, Use
 
 RouteDescriptor CommandSVSPart::GetRouting(User* user, const std::vector<std::string>& parameters)
 {
-       User* u = ServerInstance->FindUUID(parameters[0]);
-       if (u)
-               return ROUTE_OPT_UCAST(u->server);
-       return ROUTE_LOCALONLY;
+       return ROUTE_OPT_UCAST(parameters[0]);
 }
diff --git a/src/modules/m_spanningtree/translate.cpp b/src/modules/m_spanningtree/translate.cpp
new file mode 100644 (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..b29bea1346f0d9e0c594135c83e3e46f37962d69 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
 #include "xline.h"
 #include "main.h"
-#include "../spanningtree.h"
+#include "modules/spanningtree.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))
+       , 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 +114,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(), SpanningTreeEventListener, OnServerLink, (this));
 }
 
-const std::string& TreeServer::GetID()
+void TreeServer::BeginBurst(uint64_t startms)
 {
-       return sid;
+       behind_bursting++;
+
+       uint64_t now = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+       // If the start time is in the future (clocks are not synced) then use current time
+       if ((!startms) || (startms > now))
+               startms = now;
+       this->StartBurst = startms;
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %s behind_bursting %u", sid.c_str(), ConvToStr(startms).c_str(), behind_bursting);
 }
 
 void TreeServer::FinishBurstInternal()
 {
-       this->bursting = false;
-       SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
-       SetPingFlag();
-       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());
-}
+               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(), SpanningTreeEventListener, OnServerSplit, (this));
 }
 
-void TreeServer::SetVersion(const std::string &Version)
+unsigned int TreeServer::QuitUsers(const std::string& reason)
 {
-       VersionString = Version;
-}
+       std::string publicreason = ServerInstance->Config->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..f75adf54c9a65510f77441c41ee0e569554fcdd0 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;
+
+       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 SetID(const std::string &id);
+       void SQuitInternal(unsigned int& num_lost_servers);
+
+       /** Remove the reference to this server from the hash maps
+        */
+       void RemoveHash();
 
  public:
+       typedef std::vector<TreeServer*> ChildServers;
        FakeUser* const ServerUser;             /* User representing this server */
-       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);
-
-       int QuitUsers(const std::string &reason);
+       TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide);
 
-       /** 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();
-
-       /** Get server name
-        */
-       std::string GetName();
+       TreeServer* GetRoute() const { return Route; }
 
-       /** Get server description (GECOS)
+       /** Returns true if this server is the tree root (i.e.: us)
         */
-       const std::string& GetDesc();
+       bool IsRoot() const { return (this->Parent == NULL); }
 
-       /** Get server version string
+       /** Returns true if this server is locally connected
         */
-       const std::string& GetVersion();
+       bool IsLocal() const { return (this->Route == this); }
 
-       /** Set time we are next due to ping this server
+       /** Returns true if the server is awaiting destruction
+        * @return True if the server is waiting to be culled and deleted, false otherwise
         */
-       void SetNextPingTime(time_t t);
+       bool IsDead() const { return isdead; }
 
-       /** Get the time we are next due to ping this server
+       /** Get server version string
         */
-       time_t NextPingTime();
+       const std::string& GetVersion() const { return VersionString; }
 
-       /** Last ping time in milliseconds, used to calculate round trip time
+       /** Get the full version string of this server
+        * @return The full version string of this server, including patch version and other info
         */
-       unsigned long LastPingMsec;
+       const std::string& GetFullVersion() const { return fullversion; }
 
        /** Round trip time of last ping
         */
@@ -129,86 +150,87 @@ class TreeServer : public classbase
 
        /** When we recieved BURST from this server, used to calculate total burst time at ENDBURST.
         */
-       unsigned long StartBurst;
+       uint64_t StartBurst;
 
        /** True if this server is hidden
         */
        bool Hidden;
 
-       /** True if the server answered their last ping
-        */
-       bool AnsweredLastPing();
-
-       /** Set the server as responding to its last ping
+       /** Get the TreeSocket pointer for local servers.
+        * For remote servers, this returns NULL.
         */
-       void SetPingFlag();
+       TreeSocket* GetSocket() const { return Socket; }
 
-       /** Get the number of users on this server.
+       /** Get the parent server.
+        * For the root node, this returns NULL.
         */
-       unsigned int GetUserCount();
+       TreeServer* GetParent() const { return Parent; }
 
-       /** Increment or decrement the user count by diff.
+       /** Set the server version string
         */
-       void SetUserCount(int diff);
+       void SetVersion(const std::string& verstr) { VersionString = verstr; }
 
-       /** Gets the numbers of opers on this server.
+       /** Set the full version string
+        * @param verstr The version string to set
         */
-       unsigned int GetOperCount();
+       void SetFullVersion(const std::string& verstr) { fullversion = 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..3571f28169c107a5f1cdf32eb82a1375add35824 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 */
 
-       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 parameterlist& params);
+
+       /** Write a line on this socket with a new line character appended, skipping all translation for old protocols
+        * @param line Line to write without a new line character at the end
+        */
+       void WriteLineNoCompat(const std::string& line);
+
  public:
-       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 std::string& ipaddr);
 
        /** 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,13 +230,8 @@ 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
@@ -197,32 +242,12 @@ class TreeSocket : public BufferedSocket
         */
        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);
-
        /** 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.
@@ -232,11 +257,8 @@ class TreeSocket : public BufferedSocket
        /** 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,61 +270,15 @@ 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);
-
        /** (local) -> SERVER
         */
        bool Outbound_Reply_Server(parameterlist &params);
@@ -323,15 +299,12 @@ class TreeSocket : public BufferedSocket
 
        /** 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, std::vector<std::string>& params);
 };
-
-#endif
-
index c9729cc0f3a6837c96d3d290b1e44c0ad8e9887b..370c38d2c41a222f5a8728cf49dd70e2ecc61b87 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/spanningtree.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 std::string& ipaddr)
+       : 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())
-       {
-               ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook);
-               if (!prov)
-               {
-                       SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID);
-                       return;
-               }
-               AddIOHook(prov->creator);
-       }
+
        DoConnect(ipaddr, link->Port, link->Timeout, link->Bind);
        Utils->timeoutlist[this] = std::pair<std::string, 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);
+       for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
+       {
+               ListenSocket::IOHookProvRef& iohookprovref = *i;
+               if (!iohookprovref)
+                       continue;
+
+               iohookprovref->OnAccept(this, client, server);
+               // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version
+               if (!getError().empty())
+               {
+                       TreeSocket::OnError(I_ERR_OTHER);
+                       return;
+               }
+       }
+
        SendCapabilities(1);
 
        Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
 }
 
-ServerState TreeSocket::GetLinkState()
-{
-       return this->LinkState;
-}
-
 void TreeSocket::CleanNegotiationInfo()
 {
        // connect is good, reset the autoconnect block (if used)
@@ -114,20 +99,30 @@ 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)
        {
+               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 \2%s\2[%s] started.", linkID.c_str(),
                        (capab->link->HiddenFromStats ? "<hidden>" : capab->link->IPAddr.c_str()));
                this->SendCapabilities(1);
@@ -139,6 +134,7 @@ void TreeSocket::OnError(BufferedSocketError e)
        ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\002%s\002' failed with error: %s",
                linkID.c_str(), getError().c_str());
        LinkState = DYING;
+       Close();
 }
 
 void TreeSocket::SendError(const std::string &errormessage)
@@ -149,79 +145,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, std::vector<std::string>& 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 +183,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 +208,3 @@ void TreeSocket::OnDataReady()
                SendError("RecvQ overrun (line too long)");
        Utils->Creator->loopCall = false;
 }
-
-bool TreeSocket::Introduced()
-{
-       return (capab == NULL);
-}
index acb822fbfa169419b2021fca91b9c4d2c984b49e..04b850755020eebfdc0f87d820c62db5ac1e27b5 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)
@@ -47,10 +44,10 @@ void TreeSocket::Split(const std::string& line, std::string& prefix, std::string
 
        if (!tokens.GetToken(prefix))
                return;
-       
+
        if (prefix[0] == ':')
        {
-               prefix = prefix.substr(1);
+               prefix.erase(prefix.begin());
 
                if (prefix.empty())
                {
@@ -84,7 +81,7 @@ void TreeSocket::ProcessLine(std::string &line)
        std::string command;
        parameterlist 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);
 
@@ -151,7 +148,7 @@ void TreeSocket::ProcessLine(std::string &line)
                        {
                                if (params.size())
                                {
-                                       time_t them = atoi(params[0].c_str());
+                                       time_t them = ConvToInt(params[0]);
                                        time_t delta = them - ServerInstance->Time();
                                        if ((delta < -600) || (delta > 600))
                                        {
@@ -171,25 +168,7 @@ void TreeSocket::ProcessLine(std::string &line)
                                if (!CheckDuplicate(capab->name, capab->sid))
                                        return;
 
-                               this->LinkState = CONNECTED;
-                               Utils->timeoutlist.erase(this);
-
-                               linkID = capab->name;
-
-                               MyRoot = new TreeServer(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")
                        {
@@ -235,52 +214,63 @@ void TreeSocket::ProcessLine(std::string &line)
        }
 }
 
-void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+User* TreeSocket::FindSource(const std::string& prefix, const std::string& command)
 {
-       User* who = ServerInstance->FindUUID(prefix);
-       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::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+{
+       User* who = FindSource(prefix, command);
+       if (!who)
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.", command.c_str(), prefix.c_str());
+               return;
+       }
 
        /*
         * Check for fake direction here, and drop any instances that are found.
@@ -298,214 +288,68 @@ 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")
-       {
-               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")
+       // Translate commands coming from servers using an older protocol
+       if (proto_version < ProtocolVersion)
        {
-               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")
                        {
-                               // 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->Error(params);
+                               return;
+                       }
+                       else if (command == "BURST")
+                       {
+                               // This is sent even when there is no need for it, drop it here for now
+                               return;
                        }
-               }
-               if (callfnc)
-                       who->ForceNickChange(params[0].c_str());
-               Utils->RouteCommand(route_back_again, command, params, who);
-       }
-       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 + "'");
-                       return;
+                       throw ProtocolException("Unknown command");
                }
+               cmdbase = cmd;
+       }
 
-               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();
-               }
+       if (params.size() < cmdbase->min_params)
+               throw ProtocolException("Insufficient parameters");
 
-               CmdResult res = cmd->Handle(params, who);
+       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();
+       }
 
+       CmdResult res;
+       if (scmd)
+               res = scmd->Handle(who, params);
+       else
+       {
+               res = cmd->Handle(params, who);
                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, params, who);
 }
 
 void TreeSocket::OnTimeout()
@@ -515,8 +359,10 @@ void TreeSocket::OnTimeout()
 
 void TreeSocket::Close()
 {
-       if (fd != -1)
-               ServerInstance->GlobalCulls.AddItem(this);
+       if (fd < 0)
+               return;
+
+       ServerInstance->GlobalCulls.AddItem(this);
        this->BufferedSocket::Close();
        SetError("Remote host closed connection");
 
@@ -524,18 +370,30 @@ void TreeSocket::Close()
        // If the connection is fully up (state CONNECTED)
        // then propogate a netsplit to all peers.
        if (MyRoot)
-               Squit(MyRoot,getError());
+               MyRoot->SQuit(getError());
 
-       if (!ConnectionFailureShown)
-       {
-               ConnectionFailureShown = true;
-               ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
+       ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
 
-               time_t server_uptime = ServerInstance->Time() - this->age;
-               if (server_uptime)
-               {
-                       std::string timestr = 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 '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
        }
 }
+
+void TreeSocket::FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden)
+{
+       this->LinkState = CONNECTED;
+       Utils->timeoutlist.erase(this);
+
+       linkID = remotename;
+
+       MyRoot = new TreeServer(remotename, remotedesc, remotesid, Utils->TreeRoot, this, hidden);
+
+       // Mark the server as bursting
+       MyRoot->BeginBurst();
+       this->DoBurst(MyRoot);
+
+       CommandServer::Builder(MyRoot).Forward(MyRoot);
+}
index 6620dd13ac8e2aeee81b06c344da5d73dc1e8143..905061cc73e6877d7a59db0af98027e69b766b4b 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, std::vector<std::string>& 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
         */
-       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->fullname = 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->fullname.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, std::vector<std::string>& 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, std::vector<std::string>& 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, std::vector<std::string>& params)
 {
-       if (IS_SERVER(src))
-               return CMD_FAILURE;
-       src->ChangeName(params[0].c_str());
+       src->ChangeName(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->fullname);
+}
index 367a3b921a88da46f5d7477c2c8ea17a004a60ac..79dc24e6a0ccb7bab55347285f948219cbbaa5e0 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"
+
+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")
@@ -45,7 +43,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 +51,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 +67,8 @@ TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
        }
 }
 
-/** Returns the locally connected server we must route a
- * message through to reach server 'ServerName'. This
- * only applies to one-to-one and not one-to-many routing.
- * See the comments for the constructor of TreeServer
- * for more details.
- */
-TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
-{
-       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,24 +89,33 @@ 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);
 
-       this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
-       this->ReadConfiguration();
+       return NULL;
+}
+
+SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C)
+       : Creator(C), TreeRoot(NULL)
+       , PingFreq(60) // XXX: TreeServer constructor reads this and TreeRoot is created before the config is read, so init it to something (value doesn't matter) to avoid a valgrind warning in TimerManager on unload
+{
+       ServerInstance->Timers.AddTimer(&RefreshTimer);
 }
 
 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)
@@ -165,26 +133,19 @@ 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++)
+       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 +155,35 @@ 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());
                }
        }
        return;
 }
 
-bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& omit)
+void SpanningTreeUtilities::DoOneToAllButSender(const CmdBuilder& params, TreeServer* omitroute)
 {
-       TreeServer* omitroute = this->BestRouteTo(omit);
-       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++)
-       {
-               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;
-}
+       const std::string& FullLine = params.str();
 
-bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params)
-{
-       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 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 +194,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;
                        }
                }
        }
@@ -319,10 +228,9 @@ void SpanningTreeUtilities::ReadConfiguration()
        HideULines = security->getBool("hideulines");
        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");
+       PingWarnTime = options->getDuration("pingwarning");
+       PingFreq = options->getDuration("serverpingfreq");
 
        if (PingFreq == 0)
                PingFreq = 60;
@@ -339,14 +247,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->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 +267,31 @@ void SpanningTreeUtilities::ReadConfiguration()
                        throw ModuleException("Invalid configuration, found a link tag without a name!" + (!L->IPAddr.empty() ? " IP address: "+L->IPAddr : ""));
 
                if (L->Name.find('.') == std::string::npos)
-                       throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it must contain at least one '.' character");
+                       throw ModuleException("The link name '"+L->Name+"' is invalid as it must contain at least one '.' character");
 
-               if (L->Name.length() > 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.");
+                       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 +302,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 +312,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 +320,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 +331,7 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name)
        for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
        {
                Link* x = *i;
-               if (InspIRCd::Match(x->Name.c_str(), name.c_str(), rfc_case_insensitive_map))
+               if (InspIRCd::Match(x->Name, name, ascii_case_insensitive_map))
                {
                        return x;
                }
@@ -429,15 +339,20 @@ 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 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_raw(' ');
+       if (status != 0)
+               msg.push_raw(status);
+       msg.push_raw(target->name).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..f262f9a483a90d3b84833e889daef218a65b5b6f 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::set<TreeSocket*> TreeSocketSet;
        typedef std::map<TreeSocket*, std::pair<std::string, int> > TimeoutList;
 
        /** Creator module
@@ -100,14 +98,6 @@ 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;
@@ -118,39 +108,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 parameterlist& 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 +148,10 @@ class SpanningTreeUtilities : public classbase
         */
        TreeServer* FindServerID(const std::string &id);
 
-       /** Find a route to a server by name
+       /** Find a server based on a target string.
+        * @param target Target string where a command should be routed to. May be a server name, a sid, a nickname or a uuid.
         */
-       TreeServer* BestRouteTo(const std::string &ServerName);
+       TreeServer* FindRouteTarget(const std::string& target);
 
        /** Find a server by glob mask
         */
@@ -174,10 +165,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 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..1a5b68dd9206ee0f0d1e6a967bca418d4ecfaa6d 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,
@@ -39,8 +38,8 @@ class AuthQuery : public SQLQuery
                : SQLQuery(me), uid(u), pendingExt(e), verbose(v)
        {
        }
-       
-       void OnResult(SQLResult& res)
+
+       void OnResult(SQLResult& res) CXX11_OVERRIDE
        {
                User* user = ServerInstance->FindNick(uid);
                if (!user)
@@ -57,7 +56,7 @@ class AuthQuery : public SQLQuery
                }
        }
 
-       void OnError(SQLerror& error)
+       void OnError(SQLerror& error) CXX11_OVERRIDE
        {
                User* user = ServerInstance->FindNick(uid);
                if (!user)
@@ -79,19 +78,13 @@ class ModuleSQLAuth : public Module
        bool verbose;
 
  public:
-       ModuleSQLAuth() : pendingExt("sqlauth-wait", this), SQL(this, "SQL")
-       {
-       }
-
-       void init()
+       ModuleSQLAuth()
+               : pendingExt("sqlauth-wait", ExtensionItem::EXT_USER, this)
+               , SQL(this, "SQL")
        {
-               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");
@@ -105,7 +98,7 @@ class ModuleSQLAuth : public Module
                verbose = conf->getBool("verbose");
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                // Note this is their initial (unresolved) connect block
                ConfigTag* tag = user->MyClass->config;
@@ -133,18 +126,21 @@ class ModuleSQLAuth : public Module
 
                HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
                if (md5)
-                       userinfo["md5pass"] = md5->hexsum(user->password);
+                       userinfo["md5pass"] = md5->Generate(user->password);
 
                HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
                if (sha256)
-                       userinfo["sha256pass"] = sha256->hexsum(user->password);
+                       userinfo["sha256pass"] = sha256->Generate(user->password);
+
+               const std::string certfp = SSLClientCert::GetFingerprint(&user->eh);
+               userinfo["certfp"] = certfp;
 
                SQL->submit(new AuthQuery(this, user->uuid, pendingExt, verbose), freeformquery, userinfo);
 
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
                switch (pendingExt.get(user))
                {
@@ -159,7 +155,7 @@ 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);
        }
index ae581cc4b61e3b36a98962291c41243545ff77f9..b6aa90f490f080819611a27795a463c95c45bfca 100644 (file)
 
 
 #include "inspircd.h"
-#include "sql.h"
-#include "hash.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;
-}
+#include "modules/sql.h"
+#include "modules/hash.h"
 
 class OpMeQuery : public SQLQuery
 {
@@ -46,9 +30,9 @@ class OpMeQuery : public SQLQuery
        {
        }
 
-       void OnResult(SQLResult& res)
+       void OnResult(SQLResult& res) CXX11_OVERRIDE
        {
-               ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: result for %s", uid.c_str());
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "result for %s", uid.c_str());
                User* user = ServerInstance->FindNick(uid);
                if (!user)
                        return;
@@ -57,30 +41,17 @@ class OpMeQuery : public SQLQuery
                SQLEntries row;
                while (res.GetRow(row))
                {
-#if 0
-                       parameterlist 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++)
-                       {
-                               if (!row[i].nul)
-                                       items->insert(std::make_pair(cols[i], row[i]));
-                       }
-#else
                        if (OperUser(user, row[0], row[1]))
                                return;
-#endif
                }
-               ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: no matches for %s (checked %d rows)", uid.c_str(), res.Rows());
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "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(SQLerror& error) CXX11_OVERRIDE
        {
-               ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: query failed (%s)", error.Str());
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "query failed (%s)", error.Str());
                fallback();
        }
 
@@ -90,7 +61,7 @@ class OpMeQuery : public SQLQuery
                if (!user)
                        return;
 
-               Command* oper_command = ServerInstance->Parser->GetHandler("OPER");
+               Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
 
                if (oper_command)
                {
@@ -101,25 +72,25 @@ class OpMeQuery : public SQLQuery
                }
                else
                {
-                       ServerInstance->Logs->Log("m_sqloper",SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
+                       ServerInstance->Logs->Log(MODNAME, LOG_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())
+               ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(type);
+               if (iter == ServerInstance->Config->OperTypes.end())
                {
-                       ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: bad type '%s' in returned row for oper %s", type.c_str(), username.c_str());
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "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);
 
-               hostname.append("@").append(user->host);
+               hostname.append("@").append(user->GetRealHost());
 
-               if (OneOfMatches(hostname.c_str(), user->GetIPString(), pattern))
+               if (InspIRCd::MatchMask(pattern, hostname, user->GetIPString()))
                {
                        /* Opertype and host match, looks like this is it. */
 
@@ -140,15 +111,7 @@ class ModuleSQLOper : public Module
 public:
        ModuleSQLOper() : SQL(this, "SQL") {}
 
-       void init()
-       {
-               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
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("sqloper");
 
@@ -159,10 +122,10 @@ public:
                        SQL.SetProvider("SQL/" + dbid);
 
                hashtype = tag->getString("hash");
-               query = tag->getString("query", "SELECT hostname as host, type FROM ircd_opers WHERE username='$username' AND password='$password'");
+               query = tag->getString("query", "SELECT hostname as host, type FROM ircd_opers WHERE username='$username' AND password='$password' AND active=1;");
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
        {
                if (validated && command == "OPER" && parameters.size() >= 2)
                {
@@ -172,7 +135,7 @@ public:
                                /* Query is in progress, it will re-invoke OPER if needed */
                                return MOD_RES_DENY;
                        }
-                       ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: database not present");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "database not present");
                }
                return MOD_RES_PASSTHRU;
        }
@@ -184,16 +147,15 @@ public:
                ParamM userinfo;
                SQL->PopulateUserInfo(user, userinfo);
                userinfo["username"] = username;
-               userinfo["password"] = hash ? hash->hexsum(password) : password;
+               userinfo["password"] = hash ? hash->Generate(password) : password;
 
                SQL->submit(new OpMeQuery(this, user->uuid, username, password), query, userinfo);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows storage of oper credentials in an SQL table", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSQLOper)
index 083ac0f0448486a212bfc53ad61adfc57a3389ed..5a5b40319b192ee3301cce9e9e89543d7a8c9346 100644 (file)
 
 
 #include "inspircd.h"
-#include "ssl.h"
-
-/* $ModDesc: Provides SSL metadata, including /WHOIS information and /SSLINFO command */
+#include "modules/ssl.h"
 
 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));
@@ -93,101 +95,93 @@ class CommandSSLInfo : public Command
 
                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);
                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 UserCertificateAPIImpl : public UserCertificateAPIBase
 {
-       CommandSSLInfo cmd;
+       SSLCertExt& ext;
 
  public:
-       ModuleSSLInfo() : cmd(this)
+       UserCertificateAPIImpl(Module* mod, SSLCertExt& certext)
+               : UserCertificateAPIBase(mod), ext(certext)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
+       ssl_cert* GetCertificate(User* user) CXX11_OVERRIDE
+       {
+               return ext.get(user);
+       }
+};
 
-               ServerInstance->Modules->AddService(cmd.CertExt);
+class ModuleSSLInfo : public Module, public Whois::EventListener
+{
+       CommandSSLInfo cmd;
+       UserCertificateAPIImpl APIImpl;
 
-               Implementation eventlist[] = { I_OnWhois, I_OnPreCommand, I_OnSetConnectClass, I_OnUserConnect, I_OnPostConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ public:
+       ModuleSSLInfo()
+               : Whois::EventListener(this)
+               , cmd(this)
+               , APIImpl(this, cmd.CertExt)
+       {
        }
 
-       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.CertExt.get(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(671, "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(276, InspIRCd::Format("has client certificate fingerprint %s", cert->fingerprint.c_str()));
                }
        }
 
-       bool OneOfMatches(const char* host, const char* ip, const char* 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;
-       }
-
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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);
 
                                if (ifo->oper_block->getBool("sslonly") && !cert)
                                {
-                                       user->WriteNumeric(491, "%s :This oper login requires an SSL connection.", user->nick.c_str());
+                                       user->WriteNumeric(491, "This oper login requires an SSL connection.");
                                        user->CommandFloodPenalty += 10000;
                                        return MOD_RES_DENY;
                                }
@@ -195,7 +189,7 @@ class ModuleSSLInfo : public Module
                                std::string fingerprint;
                                if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
                                {
-                                       user->WriteNumeric(491, "%s :This oper login requires a matching SSL fingerprint.",user->nick.c_str());
+                                       user->WriteNumeric(491, "This oper login requires a matching SSL certificate fingerprint.");
                                        user->CommandFloodPenalty += 10000;
                                        return MOD_RES_DENY;
                                }
@@ -206,59 +200,66 @@ class ModuleSSLInfo : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void OnUserConnect(LocalUser* user)
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               SocketCertificateRequest req(&user->eh, this);
-               if (!req.cert)
-                       return;
-               cmd.CertExt.set(user, req.cert);
+               ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
+               if (cert)
+                       cmd.CertExt.set(user, cert);
        }
 
-       void OnPostConnect(User* user)
+       void OnPostConnect(User* user) CXX11_OVERRIDE
        {
-               ssl_cert *cert = cmd.CertExt.get(user);
-               if (!cert || cert->fingerprint.empty())
+               LocalUser* const localuser = IS_LOCAL(user);
+               if (!localuser)
+                       return;
+
+               const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(&localuser->eh);
+               if (!ssliohook)
+                       return;
+
+               ssl_cert* const cert = ssliohook->GetCertificate();
+
+               {
+                       std::string text = "*** You are connected 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"))
                                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());
                }
                else if (myclass->config->getBool("requiressl"))
                {
-                       ok = (req.cert != NULL);
+                       ok = (cert != NULL);
                }
 
                if (!ok)
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
-
-       void OnRequest(Request& request)
-       {
-               if (strcmp("GET_USER_CERT", request.id) == 0)
-               {
-                       UserCertificateRequest& req = static_cast<UserCertificateRequest&>(request);
-                       req.cert = cmd.CertExt.get(req.user);
-               }
-       }
 };
 
 MODULE_INIT(ModuleSSLInfo)
-
index c81c7420783747b7d0d560b475224a5a5a925a5c..d3afd00e6854ca4fac1adaf74736889904929ab9 100644 (file)
 
 
 #include "inspircd.h"
-#include "ssl.h"
+#include "modules/ssl.h"
 
-/* $ModDesc: Provides channel mode +z to allow for Secure/SSL only channels */
+enum
+{
+       // From UnrealIRCd.
+       ERR_SECUREONLYCHAN = 489
+};
 
 /** Handle channel mode +z
  */
 class SSLMode : public ModeHandler
 {
  public:
-       SSLMode(Module* Creator) : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL) { }
+       UserCertificateAPI API;
+
+       SSLMode(Module* Creator)
+               : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL)
+               , API(Creator)
+       {
+       }
 
        ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
        {
                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)
+                                               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 +75,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;
                        }
 
@@ -85,20 +97,15 @@ class ModuleSSLModes : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(sslm);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnCheckBan, I_On005Numeric };
-               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('z'))
+               if(chan && chan->IsModeSet(sslm))
                {
-                       UserCertificateRequest req(user, this);
-                       req.Send();
-                       if (req.cert)
+                       if (!sslm.API)
+                               return MOD_RES_DENY;
+
+                       ssl_cert* cert = sslm.API->GetCertificate(user);
+                       if (cert)
                        {
                                // Let them in
                                return MOD_RES_PASSTHRU;
@@ -106,7 +113,7 @@ class ModuleSSLModes : public Module
                        else
                        {
                                // Deny
-                               user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick.c_str(), cname);
+                               user->WriteNumeric(ERR_SECUREONLYCHAN, cname, "Cannot join channel; SSL users only (+z)");
                                return MOD_RES_DENY;
                        }
                }
@@ -114,33 +121,29 @@ class ModuleSSLModes : public Module
                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] == 'z') && (mask[1] == ':'))
                {
-                       UserCertificateRequest req(user, this);
-                       req.Send();
-                       if (req.cert && InspIRCd::Match(req.cert->GetFingerprint(), mask.substr(2)))
+                       if (!sslm.API)
+                               return MOD_RES_DENY;
+
+                       ssl_cert* cert = sslm.API->GetCertificate(user);
+                       if (cert && InspIRCd::Match(cert->GetFingerprint(), 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)
-       {
-               ServerInstance->AddExtBanChar('z');
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel mode +z to allow for Secure/SSL only channels", 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..b3cf5a2
--- /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(const std::vector<std::string>& parameters, LocalUser* user)
+       {
+               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 support for the STARTTLS command", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleStartTLS)
index f1504edaf329a6bf270c4441742a7a4eddb7a8bd..9afe7132cb13aedcd708794dffb38dbb3a8efa7b 100644 (file)
@@ -20,8 +20,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel +S mode (strip ansi color) */
+#include "modules/exemption.h"
 
 /** Handles channel mode +S
  */
@@ -42,32 +41,24 @@ class UserStripColor : public SimpleUserModeHandler
 
 class ModuleStripColor : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        ChannelStripColor csc;
        UserStripColor usc;
 
  public:
-       ModuleStripColor() : csc(this), usc(this)
-       {
-       }
-
-       void init()
-       {
-               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));
-       }
-
-       virtual ~ModuleStripColor()
+       ModuleStripColor()
+               : exemptionprov(this)
+               , csc(this)
+               , usc(this)
        {
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('S');
+               tokens["EXTBAN"].push_back('S');
        }
 
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
@@ -76,17 +67,17 @@ class ModuleStripColor : public Module
                if (target_type == TYPE_USER)
                {
                        User* t = (User*)dest;
-                       active = t->IsModeSet('S');
+                       active = t->IsModeSet(usc);
                }
                else if (target_type == TYPE_CHANNEL)
                {
                        Channel* t = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,t,"stripcolor");
+                       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)
@@ -97,12 +88,24 @@ class ModuleStripColor : public Module
                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);
        }
index e666b0fe24b977c1de8f5845e03c9152e01e12cc..ad6a4d1aaa15255020389a116cf86f047053be51 100644 (file)
@@ -23,8 +23,6 @@
 #include "inspircd.h"
 #include "xline.h"
 
-/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */
-
 namespace
 {
        bool silent;
@@ -35,16 +33,12 @@ 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)
                : XLine(s_time, d, src, re, "SVSHOLD")
        {
-               this->nickname = nick.c_str();
-       }
-
-       ~SVSHold()
-       {
+               this->nickname = nick;
        }
 
        bool Matches(User *u)
@@ -56,23 +50,21 @@ public:
 
        bool Matches(const std::string &s)
        {
-               if (nickname == s)
-                       return true;
-               return false;
+               return InspIRCd::Match(s, nickname);
        }
 
        void DisplayExpiry()
        {
                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 %ld seconds ago)",
+                               nickname.c_str(), source.c_str(), (long)(ServerInstance->Time() - set_time));
                }
        }
 
-       const char* Displayable()
+       const std::string& Displayable()
        {
-               return nickname.c_str();
+               return nickname;
        }
 };
 
@@ -104,7 +96,6 @@ class CommandSvshold : public Command
        CommandSvshold(Module* Creator) : Command(Creator, "SVSHOLD", 1)
        {
                flags_needed = 'o'; this->syntax = "<nickname> [<duration> :<reason>]";
-               TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
        }
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *user)
@@ -112,7 +103,7 @@ class CommandSvshold : public Command
                /* 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;
@@ -127,7 +118,7 @@ class CommandSvshold : public Command
                        }
                        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 in list, try /stats S.");
                        }
                }
                else
@@ -135,8 +126,7 @@ 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 = InspIRCd::Duration(parameters[1]);
                        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))
@@ -151,7 +141,7 @@ class CommandSvshold : public Command
                                else
                                {
                                        time_t c_requires_crap = duration + ServerInstance->Time();
-                                       std::string timestr = ServerInstance->TimeString(c_requires_crap);
+                                       std::string timestr = InspIRCd::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());
                                }
                        }
@@ -182,50 +172,46 @@ class ModuleSVSHold : public Module
        {
        }
 
-       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);
        }
index 742781747f4e091de5e9848da2ac8b80095ee52c..9a433e154131c24b017c3a19251ad712cda2a734 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the SWHOIS command which allows setting of arbitrary WHOIS lines */
-
 /** Handle /SWHOIS
  */
 class CommandSwhois : public Command
 {
  public:
        StringExtItem swhois;
-       CommandSwhois(Module* Creator) : Command(Creator,"SWHOIS", 2,2), swhois("swhois", Creator)
+       CommandSwhois(Module* Creator)
+               : Command(Creator, "SWHOIS", 2, 2)
+               , swhois("swhois", ExtensionItem::EXT_USER, Creator)
        {
                flags_needed = 'o'; syntax = "<nick> :<swhois>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle(const std::vector<std::string> &parameters, User* user)
        {
                User* dest = ServerInstance->FindNick(parameters[0]);
 
-               if ((!dest) || (IS_SERVER(dest))) // 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;
                }
 
@@ -53,11 +53,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());
@@ -81,34 +81,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)
        {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(cmd.swhois);
-               Implementation eventlist[] = { I_OnWhoisLine, I_OnPostOper };
-               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(320, *swhois);
                        }
                }
 
@@ -116,7 +110,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;
@@ -130,11 +124,7 @@ class ModuleSWhois : public Module
                ServerInstance->PI->SendMetaData(user, "swhois", swhois);
        }
 
-       ~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..8c1454d7ed2a0fba80809aefa3b39e8a5357288b 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,21 +40,30 @@ 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);
+               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)
@@ -64,54 +71,47 @@ class CommandTban : public Command
                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::NoSuchNick(parameters[0]));
                        return CMD_FAILURE;
                }
                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 duration = InspIRCd::Duration(parameters[1]);
                unsigned long expire = duration + ServerInstance->Time();
                if (duration < 1)
                {
-                       user->WriteServ("NOTICE "+user->nick+" :Invalid ban time");
+                       user->WriteNotice("Invalid ban time");
                        return CMD_FAILURE;
                }
                std::string mask = parameters[2];
-               std::vector<std::string> setban;
-               setban.push_back(parameters[0]);
-               setban.push_back("+b");
                bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
-               if (!isextban && !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->GetLastParse().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;
@@ -119,7 +119,7 @@ class CommandTban : public Command
 
                const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + ConvToStr(duration) + " seconds.";
                // 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());
@@ -133,6 +133,34 @@ class CommandTban : public Command
        }
 };
 
+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)
+       {
+               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 +180,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 +205,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());
                                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);
        }
 };
 
 MODULE_INIT(ModuleTimedBans)
-
index b4e7e5a995941e829ef33ca38969504f84b46b0e..77ec0e26ceb06f6211a5583463d2da70c1665beb 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
@@ -34,14 +32,13 @@ class CommandTline : public Command
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
-               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()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleTLine()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides /tline command used to test who a mask matches", VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleTLine)
-
index 3e8a846e76ba60e62ba7a7db0f5e6fc169a51e14..8a0712c3e9a444e65e79e3aa56e235b12efe7332 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:
@@ -31,7 +35,7 @@ class CommandSVSTOPIC : public Command
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *user)
        {
-               if (!ServerInstance->ULine(user->server))
+               if (!user->server->IsULine())
                {
                        // Ulines only
                        return CMD_FAILURE;
@@ -47,36 +51,17 @@ class CommandSVSTOPIC : public Command
                        time_t topicts = ConvToInt(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;
@@ -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)
        {
        }
 
@@ -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..ce9c517f49149dbb3c81ac135561f8ce4b4e8d2e 100644 (file)
 
 
 #include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the UHNAMES facility. */
+#include "modules/cap.h"
 
 class ModuleUHNames : public Module
 {
- public:
-       GenericCap cap;
+       Cap::Capability cap;
 
+ public:
        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));
-       }
-
-       ~ModuleUHNames()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides the UHNAMES facility.",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, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) 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 +52,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(User* 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..d3a424dff003629c12e2e26a0bf47423b9d56fcc 100644 (file)
  */
 
 
-/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */
-
 #include "inspircd.h"
+#include "modules/invite.h"
 
 /** Handle /UNINVITE
  */
 class CommandUninvite : public Command
 {
+       Invite::API invapi;
  public:
-       CommandUninvite(Module* Creator) : Command(Creator,"UNINVITE", 2)
+       CommandUninvite(Module* Creator)
+               : Command(Creator, "UNINVITE", 2)
+               , invapi(Creator)
        {
                syntax = "<nick> <channel>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
@@ -49,11 +51,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::NoSuchNick(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 +65,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,19 +77,26 @@ 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(493, InspIRCd::Format("You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str()));
 
                        std::string msg = "*** " + user->nick + " uninvited " + u->nick + ".";
-                       c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE " + c->name + " :" + msg);
+                       c->WriteNotice(msg);
                        ServerInstance->PI->SendChannelNotice(c, 0, msg);
                }
 
@@ -96,8 +105,7 @@ class CommandUninvite : public Command
 
        RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
        {
-               User* u = ServerInstance->FindNick(parameters[0]);
-               return u ? ROUTE_OPT_UCAST(u->server) : ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -111,20 +119,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..6fa367bff9d4be17829a1dd0ae098699cf1fef1e 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for USERIP command */
-
 /** Handle /USERIP
  */
 class CommandUserip : public Command
@@ -35,7 +33,7 @@ class CommandUserip : public Command
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
-               std::string retbuf = "340 " + user->nick + " :";
+               std::string retbuf;
                int nicks = 0;
                bool checked_privs = false;
                bool has_privs = false;
@@ -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)
-       {
-               output = output + " USERIP";
-       }
-
-       virtual ~ModuleUserIP()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for USERIP command",VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleUserIP)
-
index 31c504af8df7084a4ec713af73d631991eeb6dbd..53910fdbe5c6f65e14f82fd76feb7ecda9fe7b5f 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */
-
 /** Handle /VHOST
  */
 class CommandVhost : public Command
@@ -45,25 +43,24 @@ class CommandVhost : public Command
                        std::string pass = tag->getString("pass");
                        std::string hash = tag->getString("hash");
 
-                       if (parameters[0] == username && !ServerInstance->PassCompare(user, pass, parameters[1], hash))
+                       if (parameters[0] == username && ServerInstance->PassCompare(user, pass, parameters[1], hash))
                        {
                                if (!mask.empty())
                                {
-                                       user->WriteServ("NOTICE "+user->nick+" :Setting your VHost: " + mask);
-                                       user->ChangeDisplayedHost(mask.c_str());
+                                       user->WriteNotice("Setting your VHost: " + mask);
+                                       user->ChangeDisplayedHost(mask);
                                        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:
@@ -71,22 +68,10 @@ class ModuleVHost : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleVHost()
-       {
-       }
-
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides masking of user hostnames via traditional /VHOST command",VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleVHost)
-
index a86483291805b8250a6320c768b18b48ecba3799..7fa8ad8f48cc1f010dfa96be2bb4f6069255423b 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"
 
-/* $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
+};
 
-/*
- * 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)
                {
-                       ServerInstance->Parser->CallHandler("WATCH", parameters, u);
+                       // List is full, send error numeric
+                       user->WriteNumeric(ERR_TOOMANYWATCH, nick, "Too many WATCH entries");
+                       return;
                }
+               else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK)
+               {
+                       user->WriteNumeric(942, nick, "Invalid nickname");
+                       return;
+               }
+               else if (result != IRCv3::Monitor::Manager::WR_OK)
+                       return;
 
-               return CMD_SUCCESS;
+               SendOnlineOffline(user, nick);
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       void HandleMinus(LocalUser* user, const std::string& nick)
        {
-               User* target = ServerInstance->FindNick(parameters[0]);
+               if (!manager.Unwatch(user, nick))
+                       return;
+
+               User* target = IRCv3::Monitor::Manager::FindNick(nick);
                if (target)
-                       return ROUTE_OPT_UCAST(target->server);
-               return ROUTE_LOCALONLY;
+                       user->WriteNumeric(RPL_WATCHOFF, target->nick, target->ident, target->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);
-
-                               wl->erase(n);
-                       }
+       void HandleStats(LocalUser* user)
+       {
+               user->CommandFloodPenalty += ListPenalty;
 
-                       if (wl->empty())
-                       {
-                               ext.unset(user);
-                       }
+               // Do not show how many clients are watching this nick, it's pointless
+               const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+               user->WriteNumeric(RPL_WATCHSTAT, InspIRCd::Format("You have %lu and are on 0 WATCH entries", (unsigned long)list.size()));
 
-                       watchentries::iterator x = whos_watching_me->find(nick);
-                       if (x != whos_watching_me->end())
-                       {
-                               /* People are watching this user, am i one of them? */
-                               std::deque<User*>::iterator n2 = std::find(x->second.begin(), x->second.end(), user);
-                               if (n2 != x->second.end())
-                                       /* I'm no longer watching you... */
-                                       x->second.erase(n2);
-
-                               if (x->second.empty())
-                                       /* nobody else is, either. */
-                                       whos_watching_me->erase(nick);
-                       }
+               Numeric::Builder<' '> out(user, RPL_WATCHLIST);
+               for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+               {
+                       const IRCv3::Monitor::Entry* entry = *i;
+                       out.Add(entry->GetNick());
                }
-
-               return CMD_SUCCESS;
+               out.Flush();
+               user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH S");
        }
 
-       CmdResult add_watch(User* user, const char* nick)
+ public:
+       unsigned int maxwatch;
+
+       CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref)
+               : SplitCommand(mod, "WATCH")
+               , manager(managerref)
        {
-               if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
-               {
-                       user->WriteNumeric(942, "%s %s :Invalid nickname",user->nick.c_str(),nick);
-                       return CMD_FAILURE;
-               }
+               allow_empty_last_param = false;
+               syntax = "[<C|L|S|l|+<nick1>|-<nick>>]";
+       }
 
-               watchlist* wl = ext.get(user);
-               if (!wl)
+       CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+       {
+               if (parameters.empty())
                {
-                       wl = new watchlist();
-                       ext.set(user, wl);
+                       HandleList(user, false);
+                       return CMD_SUCCESS;
                }
 
-               if (wl->size() >= MAX_WATCH)
-               {
-                       user->WriteNumeric(512, "%s %s :Too many WATCH entries", user->nick.c_str(), nick);
-                       return CMD_FAILURE;
-               }
+               bool watch_l_done = false;
+               bool watch_s_done = false;
 
-               watchlist::iterator n = wl->find(nick);
-               if (n == wl->end())
+               for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
                {
-                       /* Don't already have the user on my watch list, proceed */
-                       watchentries::iterator x = whos_watching_me->find(nick);
-                       if (x != whos_watching_me->end())
-                       {
-                               /* People are watching this user, add myself */
-                               x->second.push_back(user);
-                       }
-                       else
+                       const std::string& token = *i;
+                       char subcmd = toupper(token[0]);
+                       if (subcmd == '+')
                        {
-                               std::deque<User*> newlist;
-                               newlist.push_back(user);
-                               (*(whos_watching_me))[nick] = newlist;
+                               HandlePlus(user, token.substr(1));
                        }
-
-                       User* target = ServerInstance->FindNick(nick);
-                       if ((target) && (target->registered == REG_ALL))
+                       else if (subcmd == '-')
                        {
-                               (*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);
-                               }
+                               HandleMinus(user, token.substr(1));
                        }
-                       else
+                       else if (subcmd == 'C')
                        {
-                               (*wl)[nick].clear();
-                               user->WriteNumeric(605, "%s %s * * 0 :is offline",user->nick.c_str(), nick);
+                               manager.UnwatchAll(user);
                        }
-               }
-
-               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 == 'L') && (!watch_l_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());
-                               }
+                               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'));
                        }
-                       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++)
+                       else if ((subcmd == 'S') && (!watch_s_done))
                        {
-                               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
 {
-       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()
+               : 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->getInt("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();
-                               }
-                       }
-               }
-
-               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());
-                               }
-                       }
-               }
+               LocalUser* localuser = IS_LOCAL(user);
+               if (localuser)
+                       manager.UnwatchAll(localuser);
+               Offline(user, user->nick);
        }
 
-       virtual void On005Numeric(std::string &output)
+       ModResult OnSetAway(User* user, const std::string& awaymsg) CXX11_OVERRIDE
        {
-               // we don't really have a limit...
-               output = output + " WATCH=" + ConvToStr(maxwatch);
+               if (awaymsg.empty())
+                       SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time());
+               else
+                       SendAlert(user, user->nick, RPL_GONEAWAY, awaymsg.c_str(), user->awaytime);
+
+               return MOD_RES_PASSTHRU;
        }
 
-       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..a7457f7
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "iohook.h"
+#include "modules/hash.h"
+
+static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+static const char whitespace[] = " \t\r\n";
+static dynamic_reference_nocheck<HashProvider>* sha1;
+
+class WebSocketHookProvider : public IOHookProvider
+{
+ public:
+       WebSocketHookProvider(Module* mod)
+               : IOHookProvider(mod, "websocket", IOHookProvider::IOH_UNKNOWN, true)
+       {
+       }
+
+       void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+       }
+};
+
+class WebSocketHook : public IOHookMiddle
+{
+       class HTTPHeaderFinder
+       {
+               std::string::size_type bpos;
+               std::string::size_type len;
+
+        public:
+               bool Find(const std::string& req, const char* header, std::string::size_type headerlen, std::string::size_type maxpos)
+               {
+                       std::string::size_type keybegin = req.find(header);
+                       if ((keybegin == std::string::npos) || (keybegin > maxpos) || (keybegin == 0) || (req[keybegin-1] != '\n'))
+                               return false;
+
+                       keybegin += headerlen;
+
+                       bpos = req.find_first_not_of(whitespace, keybegin, sizeof(whitespace)-1);
+                       if ((bpos == std::string::npos) || (bpos > maxpos))
+                               return false;
+
+                       const std::string::size_type epos = req.find_first_of(whitespace, bpos, sizeof(whitespace)-1);
+                       len = epos - bpos;
+
+                       return true;
+               }
+
+               std::string ExtractValue(const std::string& req) const
+               {
+                       return std::string(req, bpos, len);
+               }
+       };
+
+       enum OpCode
+       {
+               OP_CONTINUATION = 0x00,
+               OP_TEXT = 0x01,
+               OP_BINARY = 0x02,
+               OP_CLOSE = 0x08,
+               OP_PING = 0x09,
+               OP_PONG = 0x0a
+       };
+
+       enum State
+       {
+               STATE_HTTPREQ,
+               STATE_ESTABLISHED
+       };
+
+       static const unsigned char WS_MASKBIT = (1 << 7);
+       static const unsigned char WS_FINBIT = (1 << 7);
+       static const unsigned char WS_PAYLOAD_LENGTH_MAGIC_LARGE = 126;
+       static const unsigned char WS_PAYLOAD_LENGTH_MAGIC_HUGE = 127;
+       static const size_t WS_MAX_PAYLOAD_LENGTH_SMALL = 125;
+       static const size_t WS_MAX_PAYLOAD_LENGTH_LARGE = 65535;
+       static const size_t MAXHEADERSIZE = sizeof(uint64_t) + 2;
+
+       // Clients sending ping or pong frames faster than this are killed
+       static const time_t MINPINGPONGDELAY = 10;
+
+       State state;
+       time_t lastpingpong;
+
+       static size_t FillHeader(unsigned char* outbuf, size_t sendlength, OpCode opcode)
+       {
+               size_t pos = 0;
+               outbuf[pos++] = WS_FINBIT | opcode;
+
+               if (sendlength <= WS_MAX_PAYLOAD_LENGTH_SMALL)
+               {
+                       outbuf[pos++] = sendlength;
+               }
+               else if (sendlength <= WS_MAX_PAYLOAD_LENGTH_LARGE)
+               {
+                       outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_LARGE;
+                       outbuf[pos++] = (sendlength >> 8) & 0xff;
+                       outbuf[pos++] = sendlength & 0xff;
+               }
+               else
+               {
+                       outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_HUGE;
+                       const uint64_t len = sendlength;
+                       for (int i = sizeof(uint64_t)-1; i >= 0; i--)
+                               outbuf[pos++] = ((len >> i*8) & 0xff);
+               }
+
+               return pos;
+       }
+
+       static StreamSocket::SendQueue::Element PrepareSendQElem(size_t size, OpCode opcode)
+       {
+               unsigned char header[MAXHEADERSIZE];
+               const size_t n = FillHeader(header, size, opcode);
+
+               return StreamSocket::SendQueue::Element(reinterpret_cast<const char*>(header), n);
+       }
+
+       int HandleAppData(StreamSocket* sock, std::string& appdataout, bool allowlarge)
+       {
+               std::string& myrecvq = GetRecvQ();
+               // Need 1 byte opcode, minimum 1 byte len, 4 bytes masking key
+               if (myrecvq.length() < 6)
+                       return 0;
+
+               const std::string& cmyrecvq = myrecvq;
+               unsigned char len1 = (unsigned char)cmyrecvq[1];
+               if (!(len1 & WS_MASKBIT))
+               {
+                       sock->SetError("WebSocket protocol violation: unmasked client frame");
+                       return -1;
+               }
+
+               len1 &= ~WS_MASKBIT;
+
+               // Assume the length is a single byte, if not, update values later
+               unsigned int len = len1;
+               unsigned int payloadstartoffset = 6;
+               const unsigned char* maskkey = reinterpret_cast<const unsigned char*>(&cmyrecvq[2]);
+
+               if (len1 == WS_PAYLOAD_LENGTH_MAGIC_LARGE)
+               {
+                       // allowlarge is false for control frames according to the RFC meaning large pings, etc. are not allowed
+                       if (!allowlarge)
+                       {
+                               sock->SetError("WebSocket protocol violation: large control frame");
+                               return -1;
+                       }
+
+                       // Large frame, has 2 bytes len after the magic byte indicating the length
+                       // Need 1 byte opcode, 3 bytes len, 4 bytes masking key
+                       if (myrecvq.length() < 8)
+                               return 0;
+
+                       unsigned char len2 = (unsigned char)cmyrecvq[2];
+                       unsigned char len3 = (unsigned char)cmyrecvq[3];
+                       len = (len2 << 8) | len3;
+
+                       if (len <= WS_MAX_PAYLOAD_LENGTH_SMALL)
+                       {
+                               sock->SetError("WebSocket protocol violation: non-minimal length encoding used");
+                               return -1;
+                       }
+
+                       maskkey += 2;
+                       payloadstartoffset += 2;
+               }
+               else if (len1 == WS_PAYLOAD_LENGTH_MAGIC_HUGE)
+               {
+                       sock->SetError("WebSocket: Huge frames are not supported");
+                       return -1;
+               }
+
+               if (myrecvq.length() < payloadstartoffset + len)
+                       return 0;
+
+               unsigned int maskkeypos = 0;
+               const std::string::iterator endit = myrecvq.begin() + payloadstartoffset + len;
+               for (std::string::const_iterator i = myrecvq.begin() + payloadstartoffset; i != endit; ++i)
+               {
+                       const unsigned char c = (unsigned char)*i;
+                       appdataout.push_back(c ^ maskkey[maskkeypos++]);
+                       maskkeypos %= 4;
+               }
+
+               myrecvq.erase(myrecvq.begin(), endit);
+               return 1;
+       }
+
+       int HandlePingPongFrame(StreamSocket* sock, bool isping)
+       {
+               if (lastpingpong + MINPINGPONGDELAY >= ServerInstance->Time())
+               {
+                       sock->SetError("WebSocket: Ping/pong flood");
+                       return -1;
+               }
+
+               lastpingpong = ServerInstance->Time();
+
+               std::string appdata;
+               const int result = HandleAppData(sock, appdata, false);
+               // If it's a pong stop here regardless of the result so we won't generate a reply
+               if ((result <= 0) || (!isping))
+                       return result;
+
+               StreamSocket::SendQueue::Element elem = PrepareSendQElem(appdata.length(), OP_PONG);
+               elem.append(appdata);
+               GetSendQ().push_back(elem);
+
+               SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_WRITE);
+               return 1;
+       }
+
+       int HandleWS(StreamSocket* sock, std::string& destrecvq)
+       {
+               if (GetRecvQ().empty())
+                       return 0;
+
+               unsigned char opcode = (unsigned char)GetRecvQ().c_str()[0];
+               opcode &= ~WS_FINBIT;
+
+               switch (opcode)
+               {
+                       case OP_CONTINUATION:
+                       case OP_TEXT:
+                       case OP_BINARY:
+                       {
+                               return HandleAppData(sock, destrecvq, true);
+                       }
+
+                       case OP_PING:
+                       {
+                               return HandlePingPongFrame(sock, true);
+                       }
+
+                       case OP_PONG:
+                       {
+                               // A pong frame may be sent unsolicited, so we have to handle it.
+                               // It may carry application data which we need to remove from the recvq as well.
+                               return HandlePingPongFrame(sock, false);
+                       }
+
+                       case OP_CLOSE:
+                       {
+                               sock->SetError("Connection closed");
+                               return -1;
+                       }
+
+                       default:
+                       {
+                               sock->SetError("WebSocket: Invalid opcode");
+                               return -1;
+                       }
+               }
+       }
+
+       void FailHandshake(StreamSocket* sock, const char* httpreply, const char* sockerror)
+       {
+               GetSendQ().push_back(StreamSocket::SendQueue::Element(httpreply));
+               sock->DoWrite();
+               sock->SetError(sockerror);
+       }
+
+       int HandleHTTPReq(StreamSocket* sock)
+       {
+               std::string& recvq = GetRecvQ();
+               const std::string::size_type reqend = recvq.find("\r\n\r\n");
+               if (reqend == std::string::npos)
+                       return 0;
+
+               HTTPHeaderFinder keyheader;
+               if (!keyheader.Find(recvq, "Sec-WebSocket-Key:", 18, reqend))
+               {
+                       FailHandshake(sock, "HTTP/1.1 501 Not Implemented\r\nConnection: close\r\n\r\n", "WebSocket: Received HTTP request which is not a websocket upgrade");
+                       return -1;
+               }
+
+               if (!*sha1)
+               {
+                       FailHandshake(sock, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n", "WebSocket: SHA-1 provider missing");
+                       return -1;
+               }
+
+               state = STATE_ESTABLISHED;
+
+               std::string key = keyheader.ExtractValue(recvq);
+               key.append(MagicGUID);
+
+               std::string reply = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
+               reply.append(BinToBase64((*sha1)->GenerateRaw(key), NULL, '=')).append("\r\n\r\n");
+               GetSendQ().push_back(StreamSocket::SendQueue::Element(reply));
+
+               SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_WRITE);
+
+               recvq.erase(0, reqend + 4);
+
+               return 1;
+       }
+
+ public:
+       WebSocketHook(IOHookProvider* Prov, StreamSocket* sock)
+               : IOHookMiddle(Prov)
+               , state(STATE_HTTPREQ)
+               , lastpingpong(0)
+       {
+               sock->AddIOHook(this);
+       }
+
+       int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
+       {
+               StreamSocket::SendQueue& mysendq = GetSendQ();
+
+               // Return 1 to allow sending back an error HTTP response
+               if (state != STATE_ESTABLISHED)
+                       return (mysendq.empty() ? 0 : 1);
+
+               if (!uppersendq.empty())
+               {
+                       StreamSocket::SendQueue::Element elem = PrepareSendQElem(uppersendq.bytes(), OP_BINARY);
+                       mysendq.push_back(elem);
+                       mysendq.moveall(uppersendq);
+               }
+
+               return 1;
+       }
+
+       int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
+       {
+               if (state == STATE_HTTPREQ)
+               {
+                       int httpret = HandleHTTPReq(sock);
+                       if (httpret <= 0)
+                               return httpret;
+               }
+
+               int wsret;
+               do
+               {
+                       wsret = HandleWS(sock, destrecvq);
+               }
+               while ((!GetRecvQ().empty()) && (wsret > 0));
+
+               return wsret;
+       }
+
+       void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+       {
+       }
+};
+
+void WebSocketHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+       new WebSocketHook(this, sock);
+}
+
+class ModuleWebSocket : public Module
+{
+       dynamic_reference_nocheck<HashProvider> hash;
+       WebSocketHookProvider hookprov;
+
+ public:
+       ModuleWebSocket()
+               : hash(this, "hash/sha1")
+               , hookprov(this)
+       {
+               sha1 = &hash;
+       }
+
+       void OnCleanup(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 2237b0d081e8036217606e7a3c22de29d7b0942e..d220027fef5fdb5fe7cbf87e27ecfac92c6e7c02 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;
        }
@@ -68,17 +59,12 @@ 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)
-       {
-               dirty = true;
-       }
-
-       void OnExpireLine(XLine *line)
+       void OnDelLine(User* source, XLine* line) CXX11_OVERRIDE
        {
                dirty = true;
        }
 
-       void OnBackgroundTimer(time_t now)
+       void OnBackgroundTimer(time_t now) CXX11_OVERRIDE
        {
                if (dirty)
                {
@@ -89,25 +75,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('a', "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 +100,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 +113,21 @@ 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(),
-                                       ServerInstance->Config->ServerName.c_str(), (unsigned long)line->set_time, (unsigned long)line->duration, line->reason.c_str());
+                               stream << "LINE " << line->type << " " << line->Displayable() << " "
+                                       << ServerInstance->Config->ServerName << " " << 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('a', "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 +135,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('a', "database: cannot replace old xline db \"%s\" with new db \"%s\": %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
                        return false;
                }
 
@@ -162,42 +145,23 @@ 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('a', "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;
@@ -208,18 +172,14 @@ class ModuleXLineDB : public Module
                                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());
+                                       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('a', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
                                        return false;
                                }
@@ -246,18 +206,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);
        }
 };
 
 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..1eebdd9e043ad93d02ff612bcb1be01e04b55aef 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,153 @@ 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["ELIST"] = "MU";
+       tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick);
+       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["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..fd6a2709a13c4d180e6432138ec2dabb80773c77 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,42 @@ 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('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 +111,44 @@ 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)
+{
+       std::string log = desc;
+       log.append(": ").append(msg);
+       ServerInstance->Logs->Log("snomask", LOG_DEFAULT, log);
+
+       std::string finalmsg = "*** ";
+       finalmsg.append(log);
+       /* 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..91c7cdeca8aa9943a5b0d98bfd7b616994d2523a 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)
-{
-       sockaddrs servaddr;
-       int ret;
-
-       if ((*addr == '*' || *addr == '\0') && port == -1)
-       {
-               /* Port -1: Means UDP IPV4 port binding - Special case
-                * used by DNS engine.
-                */
-               memset(&servaddr, 0, sizeof(servaddr));
-               servaddr.in4.sin_family = AF_INET;
-       }
-       else if (!irc::sockets::aptosa(addr, port, servaddr))
-               return false;
-
-       ret = SE->Bind(sockfd, servaddr);
-
-       if (ret < 0)
-       {
-               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;
-               }
-       }
-}
 
 int InspIRCd::BindPorts(FailedPortList &failed_ports)
 {
@@ -89,7 +36,7 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports)
                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.");
+                       this->Logs->Log("SOCKET", LOG_DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
 
                irc::portparser portrange(porttag, false);
                int portno = -1;
@@ -98,14 +45,15 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports)
                        irc::sockets::sockaddrs bindspec;
                        if (!irc::sockets::aptosa(Addr, portno, bindspec))
                                continue;
-                       std::string bind_readable = bindspec.str();
 
                        bool skip = false;
                        for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
                        {
-                               if ((**n).bind_desc == bind_readable)
+                               if ((**n).bind_sa == bindspec)
                                {
                                        (*n)->bind_tag = tag; // Replace tag, we know addr and port match, but other info (type, ssl) may not
+                                       (*n)->ResetIOHookProvider();
+
                                        skip = true;
                                        old_ports.erase(n);
                                        break;
@@ -122,7 +70,7 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports)
                                }
                                else
                                {
-                                       failed_ports.push_back(std::make_pair(bind_readable, strerror(errno)));
+                                       failed_ports.push_back(std::make_pair(bindspec, errno));
                                        delete ll;
                                }
                        }
@@ -136,12 +84,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
@@ -197,46 +145,37 @@ std::string irc::sockets::sockaddrs::addr() const
        char addrv[INET6_ADDRSTRLEN+1];
        if (sa.sa_family == AF_INET)
        {
-               if (!inet_ntop(AF_INET, &in4.sin_addr, addrv, sizeof(addrv)))
+               if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, addrv, sizeof(addrv)))
                        return "";
                return addrv;
        }
        else if (sa.sa_family == AF_INET6)
        {
-               if (!inet_ntop(AF_INET6, &in6.sin6_addr, addrv, sizeof(addrv)))
+               if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, addrv, sizeof(addrv)))
                        return "";
                return addrv;
        }
        return "";
 }
 
-bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port)
-{
-       port = sa.port();
-       addr = sa.addr();
-       return !addr.empty();
-}
-
 std::string irc::sockets::sockaddrs::str() const
 {
-       char buffer[MAXBUF];
        if (sa.sa_family == AF_INET)
        {
-               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));
+               char ipaddr[INET_ADDRSTRLEN];
+               inet_ntop(AF_INET, (void*)&in4.sin_addr, ipaddr, sizeof(ipaddr));
+               return InspIRCd::Format("%s:%u", ipaddr, ntohs(in4.sin_port));
        }
-       else if (sa.sa_family == AF_INET6)
+
+       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));
+               char ipaddr[INET6_ADDRSTRLEN];
+               inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ipaddr, sizeof(ipaddr));
+               return InspIRCd::Format("[%s]:%u", ipaddr, ntohs(in6.sin6_port));
        }
-       else
-               return "<unknown>";
-       return std::string(buffer);
+
+       // This should never happen.
+       return "<unknown>";
 }
 
 int irc::sockets::sockaddrs::sa_size() const
@@ -367,4 +306,3 @@ bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
        irc::sockets::cidr_mask tmp(addr, length);
        return tmp == *this;
 }
-
index 4a9a2ef10539e1b0c50d5c77fbc02e6aa6b49227..58e15af4444bcc936fc353ed164687c3e0ed947f 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,33 @@ void EventHandler::SetFd(int FD)
        this->fd = FD;
 }
 
-SocketEngine::SocketEngine()
+void EventHandler::OnEventHandlerWrite()
+{
+}
+
+void EventHandler::OnEventHandlerError(int errornum)
 {
-       TotalEvents = WriteEvents = ReadEvents = ErrorEvents = 0;
-       lastempty = ServerInstance->Time();
-       indata = outdata = 0;
 }
 
-SocketEngine::~SocketEngine()
+void SocketEngine::InitError()
 {
+       std::cerr << con_red << "FATAL ERROR!" << con_reset << " Socket engine initialization failed. " << strerror(errno) << '.' << std::endl;
+       ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
 }
 
-void SocketEngine::SetEventMask(EventHandler* eh, int mask)
+void SocketEngine::LookupMaxFds()
 {
-       eh->event_mask = mask;
+       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;
 }
 
 void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
@@ -60,7 +94,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 +122,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 +167,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 +178,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,35 +226,57 @@ void SocketEngine::SetReuse(int fd)
 int SocketEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen)
 {
        int nbRecvd = recvfrom(fd->GetFd(), (char*)buf, len, flags, from, fromlen);
-       if (nbRecvd > 0)
-               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 nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, to, tolen);
-       if (nbSent > 0)
-               this->UpdateStats(0, nbSent);
+       stats.UpdateWriteCounters(nbSent);
        return nbSent;
 }
 
+int SocketEngine::WriteV(EventHandler* fd, const IOVector* iovec, int count)
+{
+       int sent = writev(fd->GetFd(), iovec, count);
+       stats.UpdateWriteCounters(sent);
+       return sent;
+}
+
+#ifdef _WIN32
+int SocketEngine::WriteV(EventHandler* fd, const iovec* iovec, int count)
+{
+       // On Windows the fields in iovec are not in the order required by the Winsock API; IOVector has
+       // the fields in the correct order.
+       // Create temporary IOVectors from the iovecs and pass them to the WriteV() method that accepts the
+       // platform's native struct.
+       IOVector wiovec[128];
+       count = std::min(count, static_cast<int>(sizeof(wiovec) / sizeof(IOVector)));
+
+       for (int i = 0; i < count; i++)
+       {
+               wiovec[i].iov_len = iovec[i].iov_len;
+               wiovec[i].iov_base = reinterpret_cast<char*>(iovec[i].iov_base);
+       }
+       return WriteV(fd, wiovec, count);
+}
+#endif
+
 int SocketEngine::Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen)
 {
        int ret = connect(fd->GetFd(), serv_addr, addrlen);
@@ -231,24 +307,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;
-}
index 58b72ee3efc8c3b47d64764b85bba05962ca983b..a7a9ec99b925cc55ae44b0462308c69016e12b07 100644 (file)
  */
 
 
-/* $Core */
+#ifdef INSPIRCD_ENABLE_TESTSUITE
 
 #include "inspircd.h"
 #include "testsuite.h"
-#include "threadengine.h"
 #include <iostream>
 
 class TestSuiteThread : public Thread
@@ -76,8 +75,12 @@ TestSuite::TestSuite()
                switch (choice)
                {
                        case '1':
-                               FOREACH_MOD(I_OnRunTestSuite, OnRunTestSuite());
+                       {
+                               const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+                               for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+                                       i->second->OnRunTestSuite();
                                break;
+                       }
                        case '2':
                                std::cout << "Enter module filename to load: ";
                                std::cin >> modname;
@@ -331,36 +334,25 @@ bool TestSuite::DoThreadTests()
 
 bool TestSuite::DoGenerateUIDTests()
 {
-       bool success = RealGenerateUIDTests();
+       const unsigned int UUID_LENGTH = UIDGenerator::UUID_LENGTH;
+       UIDGenerator uidgen;
+       uidgen.init(ServerInstance->Config->GetSID());
+       std::string first_uid = uidgen.GetUID();
 
-       // 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)
+       if (first_uid.length() != UUID_LENGTH)
        {
                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')
+       if (uidgen.current_uid.c_str()[UUID_LENGTH] != '\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');
+       std::string correct_uid = ServerInstance->Config->sid + std::string(UUID_LENGTH - 3, 'A');
        if (first_uid != correct_uid)
        {
                std::cout << "GENERATEUID: Generated an invalid first UID: " << first_uid << " instead of " << correct_uid << std::endl;
@@ -368,16 +360,16 @@ bool TestSuite::RealGenerateUIDTests()
        }
 
        // 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';
+       uidgen.current_uid[3] = 'Z';
+       for (unsigned int i = 4; i < UUID_LENGTH; i++)
+               uidgen.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();
+       std::string before_increment(uidgen.current_uid);
+       std::string generated_uid = uidgen.GetUID();
 
        // Correct UID after incrementing ...Z99999 is ...0AAAAA
-       correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 5, 'A');
+       correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 4, 'A');
 
        if (generated_uid != correct_uid)
        {
@@ -386,11 +378,11 @@ bool TestSuite::RealGenerateUIDTests()
        }
 
        // 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';
+       for (unsigned int i = 3; i < UUID_LENGTH; i++)
+               uidgen.current_uid[i] = '9';
 
-       before_increment.assign(ServerInstance->current_uid);
-       generated_uid = ServerInstance->GetUID();
+       before_increment.assign(uidgen.current_uid);
+       generated_uid = uidgen.GetUID();
 
        // Correct UID after rolling over is the first UID we've generated (...AAAAAA)
        if (generated_uid != first_uid)
@@ -407,3 +399,4 @@ TestSuite::~TestSuite()
        std::cout << "\n\n*** END OF TEST SUITE ***\n";
 }
 
+#endif
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..0b0d8bac3664d93add0cffa572fc5666ba161403 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include "timer.h"
 
-TimerManager::TimerManager()
+void Timer::SetInterval(time_t 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 2cb7ad51117a4743eb8835f6cd5c79dba9456d6c..02c030a42434b1c1414deebe2705e8e05532e432 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
-#include "bancache.h"
+#include "iohook.h"
+
+namespace
+{
+       class WriteCommonQuit : public User::ForEachNeighborHandler
+       {
+               std::string line;
+               std::string operline;
+
+               void Execute(LocalUser* user) CXX11_OVERRIDE
+               {
+                       user->Write(user->IsOper() ? operline : line);
+               }
+
+        public:
+               WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
+                       : line(":" + user->GetFullHost() + " QUIT :")
+                       , operline(line)
+               {
+                       line += msg;
+                       operline += opermsg;
+                       user->ForEachNeighbor(*this, false);
+               }
+       };
+}
 
 UserManager::UserManager()
-       : unregistered_count(0), local_count(0)
+       : already_sent_id(0)
+       , unregistered_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)
+UserManager::~UserManager()
 {
-       /* NOTE: Calling this one parameter constructor for User automatically
-        * allocates a new UUID and places it in the hash_map.
-        */
-       LocalUser* New = NULL;
-       try
+       for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
        {
-               New = new LocalUser(socket, client, server);
+               delete i->second;
        }
-       catch (...)
+}
+
+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;
+
+       ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
+
+       this->unregistered_count++;
+       this->clientlist[New->nick] = New;
+       this->AddClone(New);
+       this->local_users.push_front(New);
+
+       if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
        {
-               ServerInstance->Logs->Log("USERS", DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
-               ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
+               ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
+               this->QuitUser(New, "Internal error handling connection");
                return;
        }
-       UserIOHandler* eh = &New->eh;
 
-       /* Give each of the modules an attempt to hook the user for I/O */
-       FOREACH_MOD(I_OnHookIO, OnHookIO(eh, via));
-
-       if (eh->GetIOHook())
+       // 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)
        {
-               try
-               {
-                       eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
-               }
-               catch (CoreException& modexcept)
+               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())
                {
-                       ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+                       QuitUser(New, eh->getError());
+                       return;
                }
        }
 
-       ServerInstance->Logs->Log("USERS", DEBUG,"New user fd: %d", socket);
-
-       this->unregistered_count++;
-
-       /* The users default nick is their UUID */
-       New->nick = New->uuid;
-       (*(this->clientlist))[New->nick] = New;
-
-       New->registered = REG_NONE;
-       New->signon = ServerInstance->Time();
-       New->lastping = 1;
-
-       ServerInstance->Users->AddLocalClone(New);
-       ServerInstance->Users->AddGlobalClone(New);
-
-       New->localuseriter = this->local_users.insert(local_users.end(), New);
-       local_count++;
-
-       if ((this->local_users.size() > ServerInstance->Config->SoftLimit) || (this->local_users.size() >= (unsigned int)ServerInstance->SE->GetMaxFds()))
+       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,20 +119,21 @@ 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);
                        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
@@ -139,277 +150,220 @@ 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.");
+               New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
 
-       FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
+       FOREACH_MOD(OnSetUserIP, (New));
        if (New->quitting)
                return;
 
-       FOREACH_MOD(I_OnUserInit,OnUserInit(New));
-
-       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(OnUserInit, (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());
+       user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operreason ? operreason->c_str() : quitreason.c_str());
 
        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);
+       uuidlist.erase(user->uuid);
+       user->PurgeEmptyChannels();
 }
 
-
-void UserManager::AddLocalClone(User *user)
+void UserManager::AddClone(User* user)
 {
-       local_clones[user->GetCIDRMask()]++;
-}
-
-void UserManager::AddGlobalClone(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)
-{
-       clonemap::iterator x = global_clones.find(user->GetCIDRMask());
-       if (x != global_clones.end())
-               return x->second;
-       else
-               return 0;
-}
-
-unsigned long UserManager::LocalCloneCount(User *user)
+const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
 {
-       clonemap::iterator x = local_clones.find(user->GetCIDRMask());
-       if (x != local_clones.end())
-               return x->second;
+       CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
+       if (it != clonemap.end())
+               return it->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);
+       message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
 
-       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));
+               t->WriteServ(message);
        }
 }
 
-void UserManager::ServerPrivmsgAll(const char* text, ...)
+/* 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 UserManager::AllModulesReportReady(LocalUser* user)
 {
-       if (!text)
-               return;
+       ModResult res;
+       FIRST_MOD_RESULT(OnCheckReady, res, (user));
+       return (res == MOD_RES_PASSTHRU);
+}
 
-       char textbuffer[MAXBUF];
-       char formatbuffer[MAXBUF];
-       va_list argsPtr;
-       va_start (argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
+/**
+ * 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()
+{
+       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;
 
-       snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
+               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();
+               }
 
-       for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
-       {
-               User* t = *i;
-               t->WriteServ(std::string(formatbuffer));
+               switch (curr->registered)
+               {
+                       case REG_ALL:
+                               if (ServerInstance->Time() >= curr->nping)
+                               {
+                                       // This user didn't answer the last ping, remove them
+                                       if (!curr->lastping)
+                                       {
+                                               time_t time = ServerInstance->Time() - (curr->nping - curr->MyClass->GetPingTime());
+                                               const std::string message = "Ping timeout: " + ConvToStr(time) + (time != 1 ? " seconds" : " second");
+                                               this->QuitUser(curr, message);
+                                               continue;
+                                       }
+
+                                       curr->Write("PING :" + ServerInstance->Config->ServerName);
+                                       curr->lastping = 0;
+                                       curr->nping = ServerInstance->Time() + curr->MyClass->GetPingTime();
+                               }
+                               break;
+                       case REG_NICKUSER:
+                               if (AllModulesReportReady(curr))
+                               {
+                                       /* 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 && (ServerInstance->Time() > (curr->signon + curr->MyClass->GetRegTimeout())))
+               {
+                       /*
+                        * registration timeout -- didnt send USER/NICK/HOST
+                        * in the time specified in their connection class.
+                        */
+                       this->QuitUser(curr, "Registration timeout");
+                       continue;
+               }
        }
 }
 
-
-/* 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 9e06485e5182c6a1ed511a152e7510438261790a..397a132678433e82a41f8f82826b618d53de9341 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++;
-       }
-}
 
 bool User::IsNoticeMaskSet(unsigned char sm)
 {
@@ -137,104 +33,84 @@ 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()
-{
-       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;
-}
-
-bool User::IsModeSet(unsigned char m)
-{
-       if (!isalpha(m))
-               return false;
-       return (modes[m-65]);
-}
-
-void User::SetMode(unsigned char m, bool value)
+bool User::IsModeSet(unsigned char m) const
 {
-       if (!isalpha(m))
-               return;
-       modes[m-65] = value;
+       ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
+       return (mh && modes[mh->GetId()]);
 }
 
-const char* User::FormatModes(bool showparameters)
+std::string User::GetModeLetters(bool includeparams) const
 {
-       static 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, int 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)
+       , bytes_in(0)
+       , bytes_out(0)
+       , cmds_in(0)
+       , cmds_out(0)
+       , quitting_sendq(false)
+       , lastping(true)
+       , exempt(false)
+       , nping(0)
+       , idle_lastmsg(0)
+       , CommandFloodPenalty(0)
+       , already_sent(0)
+{
+       signon = ServerInstance->Time();
+       // The user's default nick is their UUID
+       nick = uuid;
        ident = "unknown";
-       lastping = 0;
        eh.SetFd(myfd);
        memcpy(&client_sa, client, sizeof(irc::sockets::sockaddrs));
        memcpy(&server_sa, servaddr, sizeof(irc::sockets::sockaddrs));
-       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 +118,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 +128,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 +138,35 @@ 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;
 
+       const unsigned char mode = mh->GetModeChar();
        if (mode < 'A' || mode > ('A' + 64)) return false;
 
-       return ((type == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
+       return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
 
 }
 /*
@@ -404,7 +184,7 @@ bool User::HasPermission(const std::string&)
 bool LocalUser::HasPermission(const std::string &command)
 {
        // are they even an oper at all?
-       if (!IS_OPER(this))
+       if (!this->IsOper())
        {
                return false;
        }
@@ -424,10 +204,10 @@ bool User::HasPrivPermission(const std::string &privstr, bool noisy)
 
 bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
 {
-       if (!IS_OPER(this))
+       if (!this->IsOper())
        {
                if (noisy)
-                       this->WriteServ("NOTICE %s :You are not an oper", this->nick.c_str());
+                       this->WriteNotice("You are not an oper");
                return false;
        }
 
@@ -441,7 +221,8 @@ bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
        }
 
        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());
+               this->WriteNotice("Oper type " + oper->name + " does not have access to priv " + privstr);
+
        return false;
 }
 
@@ -467,7 +248,7 @@ void UserIOHandler::OnDataReady()
        while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
        {
                std::string line;
-               line.reserve(MAXBUF);
+               line.reserve(ServerInstance->Config->Limits.MaxLine);
                std::string::size_type qpos = 0;
                while (qpos < recvq.length())
                {
@@ -482,21 +263,21 @@ void UserIOHandler::OnDataReady()
                        case '\n':
                                goto eol_found;
                        }
-                       if (line.length() < MAXBUF - 2)
+                       if (line.length() < ServerInstance->Config->Limits.MaxLine - 2)
                                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, qpos);
 
                // TODO should this be moved to when it was inserted in recvq?
-               ServerInstance->stats->statsRecv += qpos;
+               ServerInstance->stats.Recv += qpos;
                user->bytes_in += qpos;
                user->cmds_in++;
 
-               ServerInstance->Parser->ProcessBuffer(line, user);
+               ServerInstance->Parser.ProcessBuffer(line, user);
                if (user->quitting)
                        return;
        }
@@ -531,7 +312,6 @@ CullResult User::cull()
 {
        if (!quitting)
                ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
-       PurgeEmptyChannels();
 
        if (client_sa.sa.sa_family != AF_UNSPEC)
                ServerInstance->Users->RemoveCloneCounts(this);
@@ -541,18 +321,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,20 +329,20 @@ 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'))
+       ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+       if (this->IsModeSet(opermh))
                this->UnOper();
 
-       this->modes[UM_OPERATOR] = 1;
+       this->SetMode(opermh, true);
        this->oper = info;
-       this->WriteServ("MODE %s :+o", this->nick.c_str());
-       FOREACH_MOD(I_OnOper, OnOper(this, info->name));
+       this->WriteCommand("MODE", "+o");
+       FOREACH_MOD(OnOper, (this, info->name));
 
        std::string opername;
        if (info->oper_block)
@@ -585,24 +353,24 @@ void User::Oper(OperInfo* info)
                LocalUser* l = IS_LOCAL(this);
                std::string vhost = oper->getConfig("vhost");
                if (!vhost.empty())
-                       l->ChangeDisplayedHost(vhost.c_str());
+                       l->ChangeDisplayedHost(vhost);
                std::string opClass = oper->getConfig("class");
                if (!opClass.empty())
                        l->SetClass(opClass);
        }
 
        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());
+               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->Logs->Log("OPER", DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->NameStr());
+       ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->name.c_str());
        ServerInstance->Users->all_opers.push_back(this);
 
        // Expand permissions from config for faster lookup
        if (IS_LOCAL(this))
                oper->init();
 
-       FOREACH_MOD(I_OnPostOper,OnPostOper(this, oper->name, opername));
+       FOREACH_MOD(OnPostOper, (this, oper->name, opername));
 }
 
 void OperInfo::init()
@@ -660,7 +428,7 @@ void OperInfo::init()
 
 void User::UnOper()
 {
-       if (!IS_OPER(this))
+       if (!this->IsOper())
                return;
 
        /*
@@ -672,44 +440,28 @@ 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;
-}
-
-/* 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);
+       this->SetMode(opermh, false);
 }
 
 /*
  * Check class restrictions
  */
-void LocalUser::CheckClass()
+void LocalUser::CheckClass(bool clone_count)
 {
        ConnectClass* a = this->MyClass;
 
@@ -723,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->nping = ServerInstance->Time() + a->GetPingTime();
 }
 
-bool User::CheckLines(bool doZline)
+bool LocalUser::CheckLines(bool doZline)
 {
        const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
 
@@ -764,7 +520,7 @@ bool User::CheckLines(bool doZline)
 
 void LocalUser::FullConnect()
 {
-       ServerInstance->stats->statsConnects++;
+       ServerInstance->stats.Connects++;
        this->idle_lastmsg = ServerInstance->Time();
 
        /*
@@ -781,19 +537,14 @@ 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__);
+       this->WriteNumeric(RPL_WELCOME, InspIRCd::Format("Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str()));
+       this->WriteNumeric(RPL_YOURHOSTIS, InspIRCd::Format("Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH));
+       this->WriteNumeric(RPL_SERVERCREATED, InspIRCd::TimeString(ServerInstance->startup_time, "This server was created %H:%M:%S %b %d %Y"));
 
-       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());
+       const TR1NS::array<std::string, 3>& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
+       this->WriteNumeric(RPL_SERVERVERSION, ServerInstance->Config->ServerName, INSPIRCD_BRANCH, modelist[0], modelist[1], modelist[2]);
 
-       ServerInstance->Config->Send005(this);
-       this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str());
+       ServerInstance->ISupport.SendTo(this);
 
        /* Now registered */
        if (ServerInstance->Users->unregistered_count)
@@ -801,17 +552,17 @@ void LocalUser::FullConnect()
 
        /* Trigger MOTD and LUSERS output, give modules a chance too */
        ModResult MOD_RESULT;
-       std::string command("MOTD");
+       std::string command("LUSERS");
        std::vector<std::string> parameters;
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
        if (!MOD_RESULT)
-               ServerInstance->Parser->CallHandler(command, parameters, this);
+               ServerInstance->Parser.CallHandler(command, parameters, this);
 
        MOD_RESULT = MOD_RES_PASSTHRU;
-       command = "LUSERS";
+       command = "MOTD";
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
        if (!MOD_RESULT)
-               ServerInstance->Parser->CallHandler(command, parameters, this);
+               ServerInstance->Parser.CallHandler(command, parameters, this);
 
        if (ServerInstance->Config->RawLog)
                WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
@@ -820,16 +571,16 @@ void LocalUser::FullConnect()
         * 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));
 
        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->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str());
+       ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
+       ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
        // reset the flood penalty (which could have been raised due to things like auto +x)
        CommandFloodPenalty = 0;
 }
@@ -844,72 +595,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)
+       User* const InUse = ServerInstance->FindNickOnly(newnick);
+       if (InUse == this)
        {
-               ServerInstance->stats->statsCollisions++;
-               return false;
-       }
-
-       if (assign(newnick) == assign(nick))
-       {
-               // case change, don't need to check Q:lines and such
+               // case change, don't need to check campers
                // and, if it's identical including case, we can leave right now
+               // We also don't update the nick TS if it's a case change, either
                if (newnick == nick)
                        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
@@ -919,29 +623,23 @@ 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)
@@ -950,15 +648,25 @@ bool User::ChangeNick(const std::string& newnick, bool force)
        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;
 }
 
+void LocalUser::OverruleNick()
+{
+       this->WriteFrom(this, "NICK %s", this->uuid.c_str());
+       this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
+
+       // Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
+       this->registered &= ~REG_NICK;
+       this->ChangeNick(this->uuid);
+}
+
 int LocalUser::GetServerPort()
 {
        switch (this->server_sa.sa.sa_family)
@@ -971,18 +679,32 @@ int LocalUser::GetServerPort()
        return 0;
 }
 
-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;
 }
 
 irc::sockets::cidr_mask User::GetCIDRMask()
@@ -1000,10 +722,10 @@ 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, bool recheck_eline)
 {
        this->InvalidateCache();
-       return irc::sockets::aptosa(sip, 0, client_sa);
+       return irc::sockets::aptosa(address, 0, client_sa);
 }
 
 void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
@@ -1012,10 +734,10 @@ void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
        memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
 }
 
-bool LocalUser::SetClientIP(const char* sip, bool recheck_eline)
+bool LocalUser::SetClientIP(const std::string& address, bool recheck_eline)
 {
        irc::sockets::sockaddrs sa;
-       if (!irc::sockets::aptosa(sip, 0, sa))
+       if (!irc::sockets::aptosa(address, 0, sa))
                // Invalid
                return false;
 
@@ -1031,7 +753,7 @@ void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_elin
                if (recheck_eline)
                        this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL);
 
-               FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this));
+               FOREACH_MOD(OnSetUserIP, (this));
        }
 }
 
@@ -1047,23 +769,23 @@ void User::Write(const char *text, ...)
 
 void LocalUser::Write(const std::string& text)
 {
-       if (!ServerInstance->SE->BoundsCheckFd(&eh))
+       if (!SocketEngine::BoundsCheckFd(&eh))
                return;
 
-       if (text.length() > MAXBUF - 2)
+       if (text.length() > ServerInstance->Config->Limits.MaxLine - 2)
        {
                // this should happen rarely or never. Crop the string at 512 and try again.
-               std::string try_again = text.substr(0, MAXBUF - 2);
+               std::string try_again(text, 0, ServerInstance->Config->Limits.MaxLine - 2);
                Write(try_again);
                return;
        }
 
-       ServerInstance->Logs->Log("USEROUTPUT", RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
+       ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
 
        eh.AddWriteBuf(text);
        eh.AddWriteBuf(wide_newline);
 
-       ServerInstance->stats->statsSent += text.length() + 2;
+       ServerInstance->stats.Sent += text.length() + 2;
        this->bytes_out += text.length() + 2;
        this->cmds_out++;
 }
@@ -1072,14 +794,9 @@ void LocalUser::Write(const std::string& text)
  */
 void LocalUser::Write(const char *text, ...)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->Write(std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->Write(textbuffer);
 }
 
 void User::WriteServ(const std::string& text)
@@ -1092,50 +809,49 @@ void User::WriteServ(const std::string& text)
  */
 void User::WriteServ(const char* text, ...)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteServ(std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteServ(textbuffer);
 }
 
-
-void User::WriteNumeric(unsigned int numeric, const char* text, ...)
+void User::WriteCommand(const char* command, const std::string& text)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
+       this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
+}
 
-       this->WriteNumeric(numeric, std::string(textbuffer));
+namespace
+{
+       std::string BuildNumeric(const std::string& source, User* targetuser, unsigned int num, const std::vector<std::string>& params)
+       {
+               const char* const target = (targetuser->registered & REG_NICK ? targetuser->nick.c_str() : "*");
+               std::string raw = InspIRCd::Format(":%s %03u %s", source.c_str(), num, target);
+               if (!params.empty())
+               {
+                       for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
+                               raw.append(1, ' ').append(*i);
+                       raw.append(" :").append(params.back());
+               }
+               return raw;
+       }
 }
 
-void User::WriteNumeric(unsigned int numeric, const std::string &text)
+void User::WriteNumeric(const Numeric::Numeric& numeric)
 {
-       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));
+       const std::string& servername = (numeric.GetServer() ? numeric.GetServer()->GetName() : ServerInstance->Config->ServerName);
+       this->Write(BuildNumeric(servername, this, numeric.GetNumeric(), numeric.GetParams()));
 }
 
 void User::WriteFrom(User *user, const std::string &text)
 {
-       char tb[MAXBUF];
-
-       snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(),text.c_str());
-
-       this->Write(std::string(tb));
+       const std::string message = ":" + user->GetFullHost() + " " + text;
+       this->Write(message);
 }
 
 
@@ -1143,196 +859,110 @@ void User::WriteFrom(User *user, const std::string &text)
 
 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));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteFrom(user, textbuffer);
 }
 
-
-/* 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
+       {
+               const std::string& msg;
 
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
-       va_end(argsPtr);
+               void Execute(LocalUser* user) CXX11_OVERRIDE
+               {
+                       user->Write(msg);
+               }
 
-       this->WriteCommonRaw(std::string(textbuffer), true);
+        public:
+               WriteCommonRawHandler(const std::string& message)
+                       : msg(message)
+               {
+               }
+       };
 }
 
-void User::WriteCommonExcept(const char* text, ...)
+void User::WriteCommon(const char* text, ...)
 {
-       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);
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
+       this->WriteCommonRaw(textbuffer, true);
 }
 
 void User::WriteCommonRaw(const std::string &line, bool include_self)
 {
-       if (this->registered != REG_ALL || quitting)
-               return;
-
-       LocalUser::already_sent_id++;
-
-       UserChanList include_c(chans);
-       std::map<User*,bool> exceptions;
-
-       exceptions[this] = include_self;
-
-       FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
-       for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
-       {
-               LocalUser* u = IS_LOCAL(i->first);
-               if (u && !u->quitting)
-               {
-                       u->already_sent = LocalUser::already_sent_id;
-                       if (i->second)
-                               u->Write(line);
-               }
-       }
-       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);
-                       }
-               }
-       }
+       WriteCommonRawHandler handler(line);
+       ForEachNeighbor(handler, include_self);
 }
 
-void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
 {
-       char tb1[MAXBUF];
-       char tb2[MAXBUF];
-
-       if (this->registered != REG_ALL)
-               return;
+       // 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.
 
-       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;
+       // 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 = uniq_id;
-                       if (i->second)
-                               u->Write(IS_OPER(u) ? out2 : out1);
+                       // 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)
+
+       // Now consider the real neighbors
+       for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
        {
-               const UserMembList* ulist = (*v)->GetUsers();
-               for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
+               Channel* chan = (*i)->chan;
+               const Channel::MemberMap& userlist = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
                {
-                       LocalUser* u = IS_LOCAL(i->first);
-                       if (u && !u->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)
-{
-       Write(line);
-}
-
-void RemoteUser::SendText(const std::string& line)
-{
-       ServerInstance->PI->PushToClient(this, line);
-}
-
-void FakeUser::SendText(const std::string& line)
+void User::WriteRemoteNumeric(const Numeric::Numeric& numeric)
 {
-}
-
-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
@@ -1349,22 +979,19 @@ 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::ChangeName(const std::string& gecos)
 {
        if (!this->fullname.compare(gecos))
                return true;
@@ -1375,87 +1002,16 @@ bool User::ChangeName(const char* gecos)
                FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
                if (MOD_RESULT == MOD_RES_DENY)
                        return false;
-               FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
+               FOREACH_MOD(OnChangeName, (this,gecos));
        }
        this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
 
        return true;
 }
 
-void User::DoHostCycle(const std::string &quitline)
-{
-       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)
+bool User::ChangeDisplayedHost(const std::string& shost)
 {
-       if (dhost == shost)
+       if (GetDisplayedHost() == shost)
                return true;
 
        if (IS_LOCAL(this))
@@ -1466,104 +1022,47 @@ bool User::ChangeDisplayedHost(const char* shost)
                        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());
+               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;
-
-       FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
+       if (displayhost == host)
+               return;
+       
+       if (displayhost.empty() && !resetdisplay)
+               displayhost = realhost;
 
-       std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
-
-       this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
+       else if (displayhost == host || resetdisplay)
+               displayhost.clear();
 
+       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;
-
-       prefix << this->nick << " " << dest->nick << " :";
-       line = prefix.str();
-       int namelen = ServerInstance->Config->ServerName.length() + 6;
+       if (this->ident == newident)
+               return true;
 
-       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();
-               }
+       FOREACH_MOD(OnChangeIdent, (this,newident));
 
-               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;
 }
 
 /*
@@ -1577,27 +1076,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));
@@ -1605,7 +1104,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;
                        }
@@ -1619,9 +1118,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;
                        }
 
@@ -1631,26 +1130,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->GetServerPort()))
+                               {
+                                       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;
                                }
                        }
@@ -1670,27 +1169,13 @@ 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);
        }
 
@@ -1701,31 +1186,30 @@ const std::string& FakeUser::GetFullHost()
 {
        if (!ServerInstance->Config->HideWhoisServer.empty())
                return ServerInstance->Config->HideWhoisServer;
-       return server;
+       return server->GetName();
 }
 
 const std::string& FakeUser::GetFullRealHost()
 {
        if (!ServerInstance->Config->HideWhoisServer.empty())
                return ServerInstance->Config->HideWhoisServer;
-       return server;
+       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;
+       config = tag;
 }
 
 void ConnectClass::Update(const ConnectClass* src)
@@ -1747,4 +1231,6 @@ void ConnectClass::Update(const ConnectClass* src)
        maxconnwarn = src->maxconnwarn;
        maxchans = src->maxchans;
        limit = src->limit;
+       resolvehostnames = src->resolvehostnames;
+       ports = src->ports;
 }
index 4172331bf3ba2877759d731b6bc4b02f69c9b9f8..3842876792f5b6a1ba104d2c249945d683076009 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/sh
-echo "InspIRCd-2.0.25"
+echo "InspIRCd-3.0.0-a2"
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 0506005add1926b256762955f579b6feea55cbbf..f21b2b4fb5d4bc623a32d5cf3b611baf5652ca95 100644 (file)
@@ -23,7 +23,6 @@
 
 #include "inspircd.h"
 #include "xline.h"
-#include "bancache.h"
 
 /** An XLineFactory specialized to generate GLine* pointers
  */
@@ -156,9 +155,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 */
@@ -278,7 +278,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,7 +286,7 @@ 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;
 }
@@ -308,15 +308,13 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
        if (simulate)
                return true;
 
-       ServerInstance->BanCache->RemoveEntries(y->second->type, true);
+       ServerInstance->BanCache.RemoveEntries(y->second->type, true);
 
-       FOREACH_MOD(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);
@@ -404,7 +402,7 @@ XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pat
 // removes lines that have expired
 void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
 {
-       FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
+       FOREACH_MOD(OnExpireLine, (item->second));
 
        item->second->DisplayExpiry();
        item->second->Unset();
@@ -413,9 +411,7 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
         * is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
         * -- Brain
         */
-       std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
-       if (pptr != pending_lines.end())
-               pending_lines.erase(pptr);
+       stdalgo::erase(pending_lines, item->second);
 
        delete item->second;
        container->second.erase(item);
@@ -425,10 +421,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 +441,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 +462,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;
                }
@@ -526,35 +522,33 @@ bool XLine::IsBurstable()
 
 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->duration);
-               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);
        }
 }
 
 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 +565,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;
@@ -593,12 +588,13 @@ void GLine::Apply(User* u)
 
 bool ELine::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;
@@ -610,7 +606,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))
@@ -636,7 +633,7 @@ bool QLine::Matches(User *u)
 void QLine::Apply(User* u)
 {
        /* Force to uuid on apply of qline, no need to disconnect any more :) */
-       u->ForceNickChange(u->uuid.c_str());
+       u->ChangeNick(u->uuid);
 }
 
 
@@ -674,67 +671,45 @@ bool GLine::Matches(const std::string &str)
 void ELine::OnAdd()
 {
        /* When adding one eline, only check the one eline */
-       for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
        {
-               User* u = (User*)(*u2);
+               LocalUser* u = *u2;
                if (this->Matches(u))
                        u->exempt = true;
        }
 }
 
-void ELine::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));
-}
-
-void QLine::DisplayExpiry()
-{
-       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));
-}
-
-void ZLine::DisplayExpiry()
-{
-       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));
-}
-
-void KLine::DisplayExpiry()
-{
-       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));
-}
-
-void GLine::DisplayExpiry()
+void XLine::DisplayExpiry()
 {
-       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));
+       bool onechar = (type.length() == 1);
+       ServerInstance->SNO->WriteToSnoMask('x', "Removing expired %s%s %s (set by %s %ld seconds ago)",
+               type.c_str(), (onechar ? "-Line" : ""), Displayable().c_str(), source.c_str(), (long)(ServerInstance->Time() - set_time));
 }
 
-const char* ELine::Displayable()
+const std::string& ELine::Displayable()
 {
-       return matchtext.c_str();
+       return matchtext;
 }
 
-const char* KLine::Displayable()
+const std::string& KLine::Displayable()
 {
-       return matchtext.c_str();
+       return matchtext;
 }
 
-const char* GLine::Displayable()
+const std::string& GLine::Displayable()
 {
-       return matchtext.c_str();
+       return matchtext;
 }
 
-const char* ZLine::Displayable()
+const std::string& ZLine::Displayable()
 {
-       return ipaddr.c_str();
+       return ipaddr;
 }
 
-const char* QLine::Displayable()
+const std::string& QLine::Displayable()
 {
-       return nick.c_str();
+       return nick;
 }
 
 bool KLine::IsBurstable()
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..c74dbb4
--- /dev/null
@@ -0,0 +1,79 @@
+#!/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} = $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 (!defined $ENV{TEST_BUILD_DYNAMIC}) {
+                       $ENV{INSPIRCD_STATIC} = 1;
+                       if (system 'make', '--jobs', get_cpu_count, 'install') {
+                               say "Failed to compile with static modules using the $compiler compiler and the $socketengine socket engine!";
+                               exit 1;
+                       }
+               }
+               if (!defined $ENV{TEST_BUILD_STATIC}) {
+                       delete $ENV{INSPIRCD_STATIC};
+                       if (system 'make', '--jobs', get_cpu_count, 'install') {
+                               say "Failed to compile with dynamic modules 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..bb32e19a1b5cb19cbd8adf725e17e4b23a265997 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/bash
-set -v
+set -ev
 if [ "$TRAVIS_OS_NAME" = "linux" ]
 then
        sudo apt-get update --assume-yes
@@ -8,8 +8,5 @@ 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_geoip.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp"
+./tools/test-build $CXX
index f53ca0701fdc963b535f866175d9cceeea720fd6..4b875ca61c70ba738b164be0eb573c4120cdd36c 100644 (file)
@@ -1,3 +1,2 @@
-inspircd_version.h\r
-inspircd_config.h\r
+config.h\r
 inspircd.rc\r
index 7be08a3fcd43f2c18d4dea2f508347af1dd863ef..e53f6c6d3c45ebf0ab999113352fe77027e87e25 100644 (file)
@@ -2,25 +2,27 @@ 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}")
 
 if(MSVC)
        # Without /SAFESEH:NO old libraries compiled with VS 2010 or older won't link correctly to VS2012 (eg, extra module libs)
@@ -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/COPYING")
 
        set(CPACK_GENERATOR "NSIS")
index 908cd39202595be1a6b7aa29b1672ef1fd9bde98..f5b2d8c83f7df9bceab39daf7bfc155f734617f3 100644 (file)
@@ -1,7 +1,7 @@
 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
+       Visual Studio 2015 or newer (https://www.visualstudio.com/)\r
        CMake 2.8 or newer (http://www.cmake.org/)\r
        If building the installer, NSIS http://nsis.sourceforge.net/\r
 \r
index ba52ad5d2dec3a719ec7d408fd704ee8705005eb..06012b3f58ec0c88845da08e71f35a6389a5e35a 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 "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..8b418928d28be458e1441502ba9920e41a0e1ca9 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,29 @@ 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;
+}
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..2c2617e2b44e194b1913a93169303cc473ce6737 100644 (file)
@@ -2,33 +2,41 @@
 # 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
 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
        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
+       set_target_properties(${SO_NAME} PROPERTIES\r
+               PREFIX ""\r
+               SUFFIX ""\r
+               COMPILE_DEFINITIONS "MODNAME=\"${BASE_NAME}\""\r
+       )\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 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 */