]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge insp20
authorAttila Molnar <attilamolnar@hush.com>
Mon, 22 Feb 2016 11:52:18 +0000 (12:52 +0100)
committerAttila Molnar <attilamolnar@hush.com>
Mon, 22 Feb 2016 11:52:18 +0000 (12:52 +0100)
553 files changed:
.gitignore
README.md
configure
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/rfc/rfc1035.txt [deleted file]
docs/rfc/rfc1413.txt [deleted file]
docs/rfc/rfc1459.txt [deleted file]
extras/m_sqloper.mssql.sql
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/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/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/whois.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/gnutlscert.pm [deleted file]
make/opensslcert.pm [deleted file]
make/run-cc.pl [deleted file]
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/eventfd.cpp [new file with mode: 0644]
make/test/kqueue.cpp [new file with mode: 0644]
make/unit-cc.pl
make/utilities.pm
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
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_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_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
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_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/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
src/modules/m_spanningtree/rconnect.cpp
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_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
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]
win/.gitignore
win/CMakeLists.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/modules/CMakeLists.txt
win/win32service.cpp
win/win32service.h

index f39aa4a55c73c92d57ef9e017ec358a30ad9b4d7..9400478be58d7473a6890b72163bcac10da46104 100644 (file)
@@ -2,19 +2,22 @@
 *.pem
 *.swp
 
-/.config.cache
-/.modulemanager
+.*
+!.git*
+
 /BSDmakefile
 /GNUmakefile
 /build
 /docs/doxygen
 /inspircd
+/inspircd.1
+/inspircd-genssl.1
+/inspircd.service
 /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
@@ -24,6 +27,7 @@
 /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
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 fd00ff89c04ace244a996c09f27d0463a2f0fe0f..ef6846e50ad6545e60225e7542d1fc1b79142657 100755 (executable)
--- a/configure
+++ b/configure
 
 
 BEGIN {
-       require 5.8.0;
+       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 Getopt::Long          qw(GetOptions);
+use POSIX                 qw(getgid getuid);
 
-# Utility functions for our buildsystem
-use make::utilities;
+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;
+
+my ($opt_binary_dir,
+    $opt_config_dir,
+    $opt_data_dir,
+    $opt_development,
+    $opt_disable_interactive,
+    $opt_distribution_label,
+    $opt_gid,
+    $opt_log_dir,
+    $opt_manual_dir,
+    $opt_module_dir,
+    $opt_prefix,
+    $opt_socketengine,
+    $opt_system,
+    $opt_uid);
 
 sub list_extras ();
 
@@ -66,38 +68,30 @@ 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,
+       '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) {
@@ -111,945 +105,245 @@ 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_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';
-}
+my %version = get_version();
+print_format "<|BOLD Configuring InspIRCd $version{MAJOR}.$version{MINOR}.$version{PATCH}+$version{LABEL} on $^O.|>\n";
 
-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.
-
-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
-       }
-       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";
+our %config;
+if ($interactive) {
+       %config = read_configure_cache();
+       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);
        }
 }
 
-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";
+$config{CXX} = find_compiler($config{CXX} // $ENV{CXX});
+unless ($config{CXX}) {
+       print "A suitable C++ compiler could not be detected on your system!\n";
+       print "Set the CXX environment variable to the compiler binary path if this is incorrect.\n";
+       exit 1; 
 }
+my %compiler = get_compiler_info($config{CXX});
 
-if (!defined $opt_disable_debug) {
-       $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
-} else {
-       $config{OPTIMISATI}      = "-O2";
-}
+$config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', '-lrt');
+$config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
 
-$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";
+if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
+       $config{SOCKETENGINE} //= 'epoll';
 }
-$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;
-}
-our $exec = $config{CC} . " -dumpversion | cut -c 1";
-chomp($config{GCCVER}          = `$exec`);                             # Major GCC Version
-$exec = $config{CC} . " -dumpversion | cut -c 3";
-chomp($config{GCCMINOR}                = `$exec`);
-$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{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
+       $config{SOCKETENGINE} //= 'kqueue';
 }
 
-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;
+if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
+       $config{SOCKETENGINE} //= 'ports';
 }
 
-# Get and Set some important vars..
-getmodules();
-
-sub clean
-{
-       unlink(".config.cache");
+if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
+       $config{SOCKETENGINE} //= 'poll';
 }
 
-our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
+# Select is available on all platforms
+$config{HAS_SELECT} = 1;
+$config{SOCKETENGINE} //= 'select';
 
-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 (defined $opt_socketengine) {
+       my $cfgkey = 'HAS_' . uc $opt_socketengine;
+       if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
+               $config{SOCKETENGINE} = $opt_socketengine;
+       } else {
+               print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
+               print "Available socket engines are:";
+               foreach (<src/socketengines/socketengine_*.cpp>) {
+                       s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
+                       print " $1" if $config{'HAS_' . uc $1};
                }
-       };
-       if ($@)
-       {
-               print "Configure update failed: $@\n";
+               print "\n";     
+               exit 1;
        }
-       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;
-}
-
-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";
-
-$exec = $config{CC} . " -dumpversion | cut -c 1";
-chomp($config{GCCVER}          = `$exec`);                             # Major GCC Version
-$exec = $config{CC} . " -dumpversion | cut -c 3";
-chomp($config{GCCMINOR}                = `$exec`);
-
-printf "Checking if stdint.h exists... ";
-$config{HAS_STDINT} = test_compile('stdint');
-print $config{HAS_STDINT} ? "yes\n" : "no\n";
-
-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 the user has specified a distribution label then we use it in
+# place of the label from src/version.sh or Git.
+if (defined $opt_distribution_label) {
+       $version{LABEL} = $opt_distribution_label;
 }
-print "yes\n" if $has_ports == 1;
-print "no\n" if $has_ports == 0;
-
-$config{HAS_EPOLL} = $has_epoll;
-$config{HAS_KQUEUE} = $has_kqueue;
 
-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';
 } 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';
+}
+
+# Parse --gid=123 or --gid=foo and extract the group id.
+my @group;
+if (defined $opt_gid) {
+       @group = $opt_gid =~ /^\d+$/ ? getgrgid($opt_gid) : getgrnam($opt_gid);
+       print_error "there is no '$opt_gid' group on this system!" unless @group;
 } else {
-       print "no\n";
-       $config{HAS_OPENSSL} = "n";
-}
-
-printf "Checking if you are running an ancient, unsupported OS... ";
-if ($config{OSNAME} =~ /FreeBSD/i)
-{
-       my $version = `uname -r`;
-       if ($version =~ /^4\./)
-       {
-               print "yes.\n";
-               print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
-               print "To build, you will need to add the following to CXXFLAGS:\n";
-               print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
-       }
-       else
-       {
-               print "no ($version)\n";
-       }
-}
-else
-{
-       print "no ($config{OSNAME})\n";
-}
-
-################################################################################
-#                        BEGIN INTERACTIVE PART                              #
-################################################################################
-
-# Clear the Screen..
-if ($interactive)
-{
-       print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
-       my $wholeos = $^O;
-
-       my $rev = getrevision();
-       # Display Introduction Message..
-       print <<"STOP" ;
-Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m)
-\e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
-
-*** If you are unsure of any of these values, leave it blank for    ***
-*** standard settings that will work, and your server will run      ***
-*** using them. Please consult your IRC network admin if in doubt.  ***
-
-Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
-a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
-dir, otherwise you won't have a config file!
-
-Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
-Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
-STOP
-       if ($rev eq "r0") {
-               print " (Non-SVN build)";
-       }
-       print ".\n\n";
-
-       $config{CHANGE_COMPILER} = "n";
-       print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
-
-       while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
-               print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
-should NOT be used. You should probably specify a newer compiler.\n\n";
-               yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
-               if ($config{CHANGE_COMPILER} =~ /y/i) {
-                       print "What command do you want to use to invoke your compiler?\n";
-                       print "[\e[1;32m$config{CC}\e[0m] -> ";
-                       chomp($config{CC} = <STDIN>);
-                       if ($config{CC} eq "") {
-                               $config{CC} = "g++";
-                       }
-                       chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
-                       if ($foo ne "") {
-                               chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
-                               chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
-                               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";
-                               }
-                       }
-               }
-       }
-       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";
-       }
+       @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];
 
-# 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);
-}
-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";
+# 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;
+
+# 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}
+
+Do you want to change these settings?
+EOQ
+if (prompt_bool $interactive, $question, 0) {
+       my $original_base_dir = $config{BASE_DIR};
+       $config{BASE_DIR} = prompt_dir $interactive, 'In what directory do you wish to install the InspIRCd base?', $config{BASE_DIR};
+       foreach my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR)) {
+               $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
+       }
+       $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};
+}
+
+# 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 "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
-       }
+} 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_openssl.cpp' unless system 'pkg-config --exists openssl >/dev/null 2>&1';
 }
 
-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";
-       }
+# 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_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
-       print "Skipping SSL certificate generation as SSL support is not available.\n\n";
-}
-
-depcheck();
-writefiles(1);
-makecache();
 
-print "\n\n";
-print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
-if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
-       print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
-       print "modules in your config. This configure script has added those modules to the\n";
-       print "build process. For more info, please refer to:\n";
-       print "\e[1;32mhttp://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";
-}
+write_configure_cache %config;
+parse_templates \%config, \%compiler, \%version;
 
-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
-}
+print_format <<"EOM";
 
-################################################################################
-#                            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;
-}
+Configuration is complete! You have chosen to build with the following settings:
 
-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);
-}
+<|GREEN Compiler:|>
+  <|GREEN Binary:|>  $config{CXX}
+  <|GREEN Name:|>    $compiler{NAME}
+  <|GREEN Version:|> $compiler{VERSION}
 
-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";
-               }
+<|GREEN Extra Modules:|>
+EOM
 
-               $var = resolve_directory($var);
-               if (! -e $var) {
-                       print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
-                       chomp(my $tmp = <STDIN>);
-                       if (($tmp eq "") || ($tmp =~ /^y/i)) {
-                               # Attempt to Create the Dir..
-                               my $chk = eval {
-                                       use File::Path ();
-                                       File::Path::mkpath($var, 0, 0777);
-                                       1;
-                               };
-                               unless (defined($chk) && -d $var) {
-                                       print "Unable to create directory. ($var)\n\n";
-                                       # Restart Loop..
-                                       next;
-                               }
-                       } else {
-                               # They said they don't want to create, and we can't install there.
-                               print "\n\n";
-                               next;
-                       }
-               } else {
-                       if (!is_dir($var)) {
-                               # Target exists, but is not a directory.
-                               print "File $var exists, but is not a directory.\n\n";
-                               next;
-                       }
-               }
-               # Either Dir Exists, or was created fine.
-               $config{$hash_key} = $var;
-               $complete = 1;
-               print "\n";
-       }
+for my $file (<src/modules/m_*>) {
+       my $module = basename $file, '.cpp';
+       say "  * $module" if -l $file;
 }
 
-our $SHARED = "";
+print_format <<"EOM";
 
-my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
+<|GREEN Paths:|>
+  <|GREEN Base:|>   $config{BASE_DIR}
+  <|GREEN Binary:|> $config{BINARY_DIR}
+  <|GREEN Config:|> $config{CONFIG_DIR}
+  <|GREEN Data:|>   $config{DATA_DIR}
+  <|GREEN Log:|>    $config{LOG_DIR}
+  <|GREEN Manual:|> $config{MANUAL_DIR}
+  <|GREEN Module:|> $config{MODULE_DIR}
 
-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 __CONFIGURATION_AUTO__
-#define __CONFIGURATION_AUTO__
-
-/* 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";
-               }
-               my $use_hiperf = 0;
-               if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
-                       print FILEHANDLE "#define USE_KQUEUE\n";
-                       $config{SOCKETENGINE} = "socketengine_kqueue";
-                       $use_hiperf = 1;
-               }
-               if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
-                       print FILEHANDLE "#define USE_EPOLL\n";
-                       $config{SOCKETENGINE} = "socketengine_epoll";
-                       $use_hiperf = 1;
-               }
-               if (($has_ports) && ($config{USE_PORTS} eq "y")) {
-                       print FILEHANDLE "#define USE_PORTS\n";
-                       $config{SOCKETENGINE} = "socketengine_ports";
-                       $use_hiperf = 1;
-               }
-               # user didn't choose either epoll or select for their OS.
-               # default them to USE_SELECT (ewwy puke puke)
-               if (!$use_hiperf) {
-                       print "no hi-perf, " . $config{USE_POLL};
-                       if ($config{USE_POLL} eq "y")
-                       {
-                               print FILEHANDLE "#define USE_POLL\n";
-                               $config{SOCKETENGINE} = "socketengine_poll";
-                       }
-                       else
-                       {
-                               print FILEHANDLE "#define USE_SELECT\n";
-                               $config{SOCKETENGINE} = "socketengine_select";
-                       }
-               }
-               print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
-               close(FILEHANDLE);
-
-               open(FILEHANDLE, ">include/inspircd_version.h.tmp");
-               print FILEHANDLE <<EOF;
-#define BRANCH "$branch"
-#define VERSION "$version"
-#define REVISION "$revision2"
-#define SYSTEM "$incos"
-EOF
-               close FILEHANDLE;
-
-               for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
-                       my $diff = 0;
-                       open my $fh1, $file or $diff = 1;
-                       open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
-                       while (!$diff) {
-                               my $line1 = <$fh1>;
-                               my $line2 = <$fh2>;
-                               if (defined($line1) != defined($line2)) {
-                                       $diff = 1;
-                               } elsif (!defined $line1) {
-                                       last;
-                               } else {
-                                       $diff = ($line1 ne $line2);
-                               }
-                       }
-                       if ($diff) {
-                               unlink $file;
-                               rename "$file.tmp", $file;
-                       } else {
-                               unlink "$file.tmp";
-                       }
-               }
-       }
+<|GREEN Execution Group:|> $config{GROUP} ($config{GID})
+<|GREEN Execution User:|>  $config{USER} ($config{UID})
+<|GREEN Socket Engine:|>   $config{SOCKETENGINE}
 
-       # Write all .in files.
-       my $tmp = "";
-       my $file = "";
-       my $exe = "inspircd";
+To build with these settings run '<|GREEN make -j${\get_cpu_count}|>' now.
 
-       # Do this once here, and cache it in the .*.inc files,
-       # rather than attempting to read src/version.sh from
-       # compiled code -- we might not have the source to hand.
-       # Fix for bug#177 by Brain.
-
-       chomp($version = `sh ./src/version.sh`);
-       chomp(my $revision = getrevision());
-       $version = "$version(r$revision)";
-
-       # We can actually parse any file starting with . and ending with .inc,
-       # but right now we only parse .inspircd.inc to form './inspircd'
-       prepare_dynamic_makefile();
-
-       my @dotfiles = qw(main.mk inspircd);
-       push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
-
-       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
-               )) {
-                       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
@@ -1114,7 +408,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_property($abs_extra, 'ModDep');
                for my $dep (@deps) {
                        if (exists($extras{$dep})) {
                                my $ref = \$extras{$dep}; # Take reference.
@@ -1161,10 +455,10 @@ sub enable_extras (@) {
                        next;
                }
                # Get dependencies, and add them to be processed.
-               my @deps = split / +/, getdependencies($extrapath);
+               my @deps = split /\s+/, get_property($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;
@@ -1197,7 +491,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_property("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 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..cc72fe9a217e5c66941ed9d9a005a09f542d7cef 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:
 #
 # 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..b9a3392e6dff04d8b1b727ff9416a3cc793d13a0 100644 (file)
@@ -34,7 +34,7 @@ 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
@@ -102,18 +102,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="rmode" value="/RMODE [channel] [modeletter] {[pattern]}
+
+Removes listmodes from a channel.
+E.g. /RMODE #Chan b m:* will remove all mute extbans.">
+
 <helpop key="fpart" value="/FPART <channel> <nick> [<reason>]
 
-This behaves identically to /REMOVE, the only difference is that the
-<channel> and <nick> parameters are switched around to match /KICK's
-syntax. Also, /REMOVE is a built-in mIRC command which caused trouble
-for some users.">
+This behaves identically to /REMOVE. /REMOVE is a built-in mIRC command
+which caused trouble for some users.">
 
 <helpop key="devoice" value="/DEVOICE <channel>
 
@@ -277,11 +280,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 +390,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
 
@@ -538,13 +536,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>]
 
@@ -663,30 +662,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 +767,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 +838,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 +903,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
@@ -1045,8 +1057,8 @@ Matching extbans:
                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
index b4d3214f6e74e85af23096ad31f9ea7200b5e128..20596e6969ca5d5b4ce724b7e389aeaa96c200ab 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.
 #
@@ -37,7 +37,7 @@ 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
@@ -62,7 +62,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 +117,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 +182,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
index 9fd0adfd34d1e17e43c3bd379d3eb3c954d5bb54..33f45535770eeddc07b5dab7a377492da65c9510 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">
 
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
 #  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!                                #
 
       # 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="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 number of seconds 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"
 >
 
 <bind address="" port="6660-6669" type="clients">
 # 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.
+# You must load the ssl_openssl module for OpenSSL or ssl_gnutls for GnuTLS.
 
 <bind address="" port="7000,7001" type="servers">
 <bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
 
 <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.
+       # Requires the module for selected hash (md5, sha256, or
+       # ripemd160) be loaded and the password hashing module
+       # (password_hash) 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:
          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".
+         # for selected hash (md5, sha256 or ripemd160) be loaded and the
+         # password hashing module (password_hash) loaded.
          # Optional, but recommended. Create hashed passwords with:
          # /mkpasswd <hash> <password>
          #hash="sha256"
          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.
          # 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.
          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
          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.
          # 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
          # 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"
-
          # 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"
 
          # 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. cmd_privmsg.so).
+          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:
         # maxident: Maximum length of a ident/username.
         maxident="11"
 
+        # 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
 # to do what they want.
 #
 # 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
 # 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   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # 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">
 
 
 #########################################################################
index a1bab2b3aaebe9241df556136f896d8abc7748ff..90849ede2897564e6ba10e16872678a3ecc7460b 100644 (file)
@@ -10,7 +10,7 @@
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
 #  If you want to link servers to InspIRCd you must load the          #
-#  m_spanningtree.so module!                                          #
+#  spanningtree module!                                               #
 #                                                                     #
 #                                                                     #
 
@@ -29,7 +29,7 @@
 
       # 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
       # making an outbound connection to the server. Options are: "openssl"
       # and "gnutls" (they are compatible with each other).
       #
-      # 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
+      # You will need to load the ssl_openssl module for OpenSSL,
+      # or ssl_gnutls for GnuTLS. The server port that you connect to
       # must be capable of accepting this type of connection.
       ssl="gnutls"
 
       # fingerprint: If defined, this option will force servers to be
-      # authenticated using SSL Fingerprints. See http://wiki.inspircd.org/SSL
-      # for more information. This will require an SSL link for both inbound
-      # and outbound connections.
+      # authenticated using SSL certificate fingerprints. See
+      # http://wiki.inspircd.org/SSL for more information. This will
+      # require an SSL link for both inbound and outbound connections.
       #fingerprint=""
 
       # bind: Local IP address to bind to.
@@ -95,7 +95,7 @@
 # Simple autoconnect block. This enables automatic connection of a server
 # Recommended setup is to have leaves connect to the hub, and have no
 # automatic connections started by the hub.
-<autoconnect period="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
index 97d69da90477b77dac16bae68e3d2419d1bc0c5a..ef2765c2836d27464d4ace0030ef9d6d22fe3580 100644 (file)
@@ -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.   #
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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.              #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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    #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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:                                 #
 # http://wiki.inspircd.org/Modules/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">
+# Adds snomask +w for monitoring CGI:IRC connections.
+#<module name="cgiirc">
 #
 #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
 #
-# Optional - If you specify to use m_cgiirc, then you must specify one
+# Optional - If you specify to use cgiirc, then you must specify one
 # or more cgihost tags which indicate authorised CGI:IRC servers which
 # will be connecting to your network, and an optional cgiirc tag.
 # For more information see: http://wiki.inspircd.org/Modules/cgiirc
 # 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.
-#<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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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 #
 #   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.              #
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #
 #<cloak mode="half"
 #       key="secret"
 # 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>.
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Custom prefixes: Allows for channel prefixes to be added.
-# This replaces m_chanprotect and m_halfop.
-#<module name="m_customprefix.so">
+#<module name="customprefix">
 #
 # name       The name of the mode, must be unique from other modes.
 # letter     The letter used for this mode. Required.
 #<customprefix name="halfop" letter="h" prefix="%" rank="20000" ranktoset="30000">
 #<customprefix name="halfvoice" letter="V" prefix="-" rank="1" ranktoset="20000">
 #
-# Do /RELOADMODULE m_customprefix.so after changing the settings of this module.
+# Do /RELOADMODULE customprefix after changing the settings of this module.
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Custom title module: Adds the /TITLE command which allows for trusted
 # users to gain a custom whois line and an optional vhost can be
 # specified.
-#<module name="m_customtitle.so">
+#<module name="customtitle">
 #
 #-#-#-#-#-#-#-#-#-#-  CUSTOM TITLE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#
 #  name     - The username used to identify.
 #  password - The password used to identify.
 #  hash     - The hash for the specific user's password (optional).
-#             m_password_hash.so and a hashing module must be loaded
+#             password_hash and a hashing module must be loaded
 #             for this to work.
 #  host     - Allowed hostmask (optional).
 #  title    - Title shown in whois.
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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.   #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Devoice module: Let users devoice themselves using /DEVOICE #chan.
-#<module name="m_devoice.so">
+#<module name="devoice">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # DNS blacklist module: Provides support for looking up IPs on one or #
 # more blacklists.                                                    #
-#<module name="m_dnsbl.so">                                           #
+#<module name="dnsbl">                                                #
 #                                                                     #
-# For configuration options please see the wiki page for m_dnsbl at   #
+# For configuration options please see the wiki page for dnsbl at     #
 # http://wiki.inspircd.org/Modules/dnsbl                              #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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.
-#<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  -#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 #<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.
 #<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.org/extensions/
 #
-#<module name="m_ircv3.so">
+#<module name="ircv3">
 # The following block can be used to control which extensions are
-# enabled. Note that extended-join can be incompatible with m_delayjoin
+# enabled. Note that extended-join can be incompatible with delayjoin
 # and host cycling.
 #<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 cap-notify module: Provides the cap-notify IRCv3.2 extension.
+# Required for IRCv3.2 conformance.
+#<module name="ircv3_capnotify">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 chghost module: Provides the chghost IRCv3.2 extension which
+# allows capable clients to learn when the host/ident of another user
+# changes without cycling the user. This module is compatible with the
+# hostcycle module. If both are loaded, clients supporting the chghost
+# extension will get the chghost message and won't see host cycling.
+#<module name="ircv3_chghost">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 echo-message module: Provides the echo-message IRCv3.2
+# extension which allows capable clients to get an acknowledgement when
+# their messages are delivered and learn what modifications, if any,
+# were applied to them.
+#<module name="ircv3_echomessage">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 invite-notify module: Provides the invite-notify IRCv3.2
+# extension which notifies supporting clients when a user invites
+# another user into a channel. This respects <options:announceinvites>.
+#<module name="ircv3_invitenotify">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Join flood module: Adds support for join flood protection +j X:Y.
 # Closes the channel for 60 seconds if X users join in Y seconds.
-#<module name="m_joinflood.so">
+#<module name="joinflood">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">                             #
 #                                                                     #
 # The attribute value indicates the attribute which is used to locate #
 # a user account by name. On POSIX systems this is usually 'uid'.     #
 #                                                                     #
-# 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.                                   #
+# 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.                                                              #
 #                                                                     #
 # 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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+#
+# Set the maximum number of entries on a user's monitor list below.
+#<monitor maxentries="30">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # MsSQL module: Allows other SQL modules to access MS SQL Server
 # 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">
+#<module name="mssql">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_mssql.so is more complex than described here, see wiki for more   #
+# mssql is more complex than described here, see the wiki for more    #
 # info http://wiki.inspircd.org/Modules/mssql                         #
 #
 #<database module="mssql" name="db" user="user" pass="pass" host="localhost" id="db1">
 # This module is in extras. Re-run configure with:
 # ./configure --enable-extras=m_mysql.cpp
 # and run make install, then uncomment this module to enable it.
-#<module name="m_mysql.so">
+#<module name="mysql">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_mysql.so is more complex than described here, see the wiki for    #
-# more: http://wiki.inspircd.org/Modules/mysql                        #
+# mysql is more complex than described here, see the wiki for more    #
+# info: http://wiki.inspircd.org/Modules/mysql                        #
 #
 #<database module="mysql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database2">
 
 # 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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:        #
+# override is too complex it describe here, see the wiki:             #
 # http://wiki.inspircd.org/Modules/override                           #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 sha256 also needs to be loaded.
+#<module name="password_hash">
 #
 #-#-#-#-#-#-#-#-#-# PASSWORD HASH CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
 #
 # 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.
 # This module is in extras. Re-run configure with:
 # ./configure --enable-extras=m_pgsql.cpp
 # and run make install, then uncomment this module to enable it.
-#<module name="m_pgsql.so">
+#<module name="pgsql">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_pgsql.so is more complex than described here, see the wiki for    #
+# pgsql is more complex than described here, see the wiki for    #
 # more: http://wiki.inspircd.org/Modules/pgsql                        #
 #
 #<database module="pgsql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database" ssl="no">
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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.
+# maxsecs - Maximum value of seconds 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" maxsecs="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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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 (see below).
-#<module name="m_sslmodes.so">
+#<module name="sslmodes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
 # if enabled. You must answer 'yes' in ./configure when asked or
 # manually symlink the source for this module from the directory
 # src/modules/extra, if you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+#<module name="ssl_gnutls">
 #
 #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
+# ssl_gnutls is too complex to describe here, see the wiki:           #
 # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SSL info module: Allows users to retrieve information about other
 # 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
 #
 #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
+# ssl_openssl is too complex to describe here, see the wiki:          #
 # http://wiki.inspircd.org/Modules/ssl_openssl                        #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Strip color module: Adds channel mode +S that strips mIRC color
-# codes from all messages sent to the channel.
-#<module name="m_stripcolor.so">
+# Strip color module: Adds channel mode +S that strips color codes and
+# all control codes except CTCP from all messages sent to the channel.
+#<module name="stripcolor">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Silence module: Adds support for the /SILENCE command, which allows
 # users to have a server-side ignore list for their client.
-#<module name="m_silence.so">
+#<module name="silence">
 #
 # Set the maximum number of entries allowed on a user's silence list.
-#<silence maxentries="32">
+#<silence maxentries="32"
+#
+# Whether messages from U-lined servers will bypass silence masks.
+#exemptuline="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SQLite3 module: Allows other SQL modules to access SQLite3          #
 # ./configure --enable-extras=m_sqlite.cpp
 # and run make install, then uncomment this module to enable it.      #
 #
-#<module name="m_sqlite3.so">
+#<module name="sqlite3">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_sqlite.so is more complex than described here, see the wiki for   #
-# more: http://wiki.inspircd.org/Modules/sqlite3                      #
+# sqlite is more complex than described here, see the wiki for more   #
+# info: http://wiki.inspircd.org/Modules/sqlite3                      #
 #
 #<database module="sqlite" hostname="/full/path/to/database.db" id="anytext">
 
 # ./configure --enable-extras=m_sqlauth.cpp
 # and run make install, then uncomment this module to enable it.
 #
-#<module name="m_sqlauth.so">
+#<module name="sqlauth">
 #
 #-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_sqlauth.so is too complex to describe here, see the wiki:         #
+# sqlauth is too complex to describe here, see the wiki:              #
 # http://wiki.inspircd.org/Modules/sqlauth                            #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # ./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   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 #                                                                     #
 #<sqloper dbid="1" hash="md5">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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.                                          #
 #
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
 # 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">
 #   |  _ <  __/ (_| | (_| |   | | | | | | \__ \ | |_) | | |_|_|       #
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
-# 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 7840b4ef54fddf821b692be3a85add46bf058315..6f8171b41111ecda3528f565e2de17a81b8a0730 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: http://wiki.inspircd.org/Modules/cgiirc
 # 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:      #
+# ssl_gnutls is too complex to describe here, see the wiki:           #
 # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SSL Info module: Allows users to retrieve information about other
 # user's peer SSL certificates and keys. This can be used by client
-# scripts to validate users. For this to work, one of m_ssl_gnutls.so
-# or m_ssl_openssl.so must be loaded. This module also adds the
+# scripts to validate users. For this to work, one of ssl_gnutls
+# or ssl_openssl must be loaded. This module also adds the
 # "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the ability
-# to force opers to use SSL connections in order to oper up.
+# opers to use SSL cert fingerprints to verify their identity and the
+# ability to force opers to use SSL connections in order to oper up.
 # It is highly recommended to load this module especially if
 # you use SSL on your network.
 # For how to use the oper features, please see the first example <oper> tag
 # in opers.conf.example.
 #
-#<module name="m_sslinfo.so">
+#<module name="sslinfo">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
 #
 #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
+# ssl_openssl is too complex to describe here, see the wiki:          #
 # http://wiki.inspircd.org/Modules/ssl_openssl                        #
 
-<module name="m_stripcolor.so">
-<module name="m_svshold.so">
-<module name="m_tline.so">
-<module name="m_uhnames.so">
-<module name="m_watch.so">
+<module name="stripcolor">
+<module name="svshold">
+<module name="tline">
+<module name="uhnames">
+<module name="watch">
 <watch maxentries="32">
-<module name="m_xline_db.so">
+<module name="xline_db">
 
-<module name="m_spanningtree.so">
+<module name="spanningtree">
index 58f36da23b1e6e96888df8391f132aa4455bc49b..10230766199f1bdf3826a6713618dd2f189b7ab5 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: http://wiki.inspircd.org/Modules/cgiirc
 # 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">
+<module name="devoice">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Filter module: Provides message filtering, similar to SPAMFILTER.
-<module name="m_filter.so">
+<module name="filter">
 #                                                                     #
-# This module depends upon a regex provider such as m_regex_pcre or   #
-# m_regex_glob to function. You must specify which of these you want  #
-# m_filter to use via the tag below.                                  #
+# This module depends upon a regex provider such as regex_pcre or     #
+# regex_glob to function. You must specify which of these you want    #
+# the filter module to use via the tag below.                         #
 #                                                                     #
 # Valid engines are:                                                  #
 #                                                                     #
-# glob   - Glob patterns, provided via m_regex_glob.                  #
-# 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:        #
+# The override module is too complex to describe here, see the wiki:  #
 # http://wiki.inspircd.org/Modules/override                           #
 
-<module name="m_operlevels.so">
-<module name="m_opermodes.so">
-<module name="m_password_hash.so">
-<module name="m_muteban.so">
+<module name="operlevels">
+<module name="opermodes">
+<module name="password_hash">
+<module name="muteban">
 
-<module name="m_redirect.so">
-<module name="m_regex_glob.so">
+<module name="redirect">
+<module name="regex_glob">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for PCRE (Perl-Compatible Regular
 # Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter
+# or the rline module.
+#<module name="regex_pcre">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for POSIX Regular Expressions.
 # You shouldn't need any additional libraries on a POSIX-compatible
 # system (ie: any Linux, BSD, but not Windows). You must have at least
-# 1 provider loaded to use m_filter or m_rline.
+# 1 provider loaded to use the filter or rline module.
 # On POSIX-compliant systems, regex syntax can be found by using the
 # command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Regular expression provider for TRE Regular Expressions.
 # 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:      #
+# ssl_gnutls is too complex to describe here, see the wiki:           #
 # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
 
-<module name="m_sslinfo.so">
+<module name="sslinfo">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
 # if enabled. You must answer 'yes' in ./configure when asked or symlink
 # the source for this module from the directory src/modules/extra, if
 # you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
 #
 #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
+# ssl_openssl is too complex to describe here, see the wiki:          #
 # http://wiki.inspircd.org/Modules/ssl_openssl                        #
 
-<module name="m_stripcolor.so">
-<module name="m_svshold.so">
-<module name="m_swhois.so">
-<module name="m_tline.so">
-<module name="m_uhnames.so">
-<module name="m_userip.so">
-<module name="m_watch.so">
+<module name="stripcolor">
+<module name="svshold">
+<module name="swhois">
+<module name="tline">
+<module name="uhnames">
+<module name="userip">
+<module name="watch">
 <watch maxentries="32">
 
-<module name="m_spanningtree.so">
+<module name="spanningtree">
index 75b54faf049066889c11c10004f63c5dcfe9a3b9..3ede475f63677f2d7680b8ad0fe7aaf44eb6e5e5 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
       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.
+      # Requires the module for selected hash (md5, sha256 or ripemd160)
+      # be loaded and the password hashing module (password_hash) loaded.
       # Options here are: "md5", "sha256" and "ripemd160", or one of
       # these prefixed with "hmac-", e.g.: "hmac-sha256".
       # Create hashed passwords with: /mkpasswd <hash> <password>
       hash="hmac-sha256"
 
       # 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/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
index 5056e12e9806ec9d52f209c88f77c60d59550550..7580a439126ecc21faa5c92bb729ae1c486111b8 100644 (file)
@@ -4,5 +4,6 @@ CREATE TABLE [dbo].[ircd_opers] (
   [password] varchar(255) NULL,\r
   [hostname] varchar(255) NULL,\r
   [type] varchar(255) NULL,\r
+  [active] bit NOT NULL DEFAULT 1,\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 0a4456f3a7fd398c2aea015b38dc88c76a5d5ded..c378afc1c9bc5fb8eedf135145bfdd3d26838968 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..62ccaf6
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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, '@')
+       {
+               levelrequired = OP_VALUE;
+       }
+};
+
+/** Channel mode +v
+ */
+class ModeChannelVoice : public PrefixMode
+{
+ public:
+       ModeChannelVoice()
+               : PrefixMode(NULL, "voice", 'v', VOICE_VALUE, '+')
+       {
+               levelrequired = 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);
+       void OnParameterMissing(User* user, User* dest, Channel* channel);
+
+       /** 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(User* user);
+};
+
+/** 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..0cf477f2207e496d1052ceb11b0af9b7e17214db 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,22 @@ 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.
         */
-       int SetTopic(User *u, std::string &t, bool forceset = false);
+       void SetTopic(User* user, const std::string& topic);
 
        /** 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 +172,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 +184,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 +299,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 +319,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
@@ -388,10 +332,40 @@ class CoreExport Channel : public Extensible, public InviteBase
        /** Get the status of an "action" type extban
         */
        ModResult GetExtBanStatus(User *u, char type);
-
-       /** Clears the cached max bans value
-        */
-       void ResetMaxBans();
 };
 
-#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..0f39d35860f3ec7415372fadb7eb5bfd2e625882 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*> 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 comparision" (see irc::string).
+        * 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..aaea31864f38bc2ec53cb46811fb14406f5b47ea 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,16 +192,11 @@ 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
@@ -89,53 +205,4 @@ class CommandWhowas : public 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..e7719bc
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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 <unordered_map>
+# include <type_traits>
+#else
+# define TR1NS std::tr1
+# 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..02619e759fe76b65f4c18a4d61471b6044b223ce 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> 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 b01a979a7c2703384bb17155e52688370af0d3df..fe1e5da9eb066edec6fc68c2487cff82d60ae864 100644 (file)
@@ -21,8 +21,7 @@
  */
 
 
-#ifndef INSPIRCD_CONFIGREADER
-#define INSPIRCD_CONFIGREADER
+#pragma once
 
 #include <sstream>
 #include <string>
@@ -45,12 +44,22 @@ class CoreExport ConfigTag : public refcountbase
        /** Get the value of an option, using def if it does not exist */
        std::string getString(const std::string& key, const std::string& def = "");
        /** 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,6 +68,16 @@ 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; }
@@ -93,14 +112,15 @@ 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);
 };
 
 struct CommandLineConf
@@ -130,11 +150,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 +157,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;
@@ -170,11 +184,6 @@ class CoreExport OperInfo : public refcountbase
        /** 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 +198,40 @@ 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()
+                       : Config(INSPIRCD_CONFIG_PATH)
+                       , Data(INSPIRCD_DATA_PATH)
+                       , Log(INSPIRCD_LOG_PATH)
+                       , Module(INSPIRCD_MODULE_PATH) { }
+
+               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 +271,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 +288,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,71 +308,6 @@ 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;
@@ -358,13 +326,6 @@ class CoreExport ServerConfig
         */
        char DisabledCModes[64];
 
-       /** The full path to the modules directory.
-        * This is either set at compile time, or
-        * overridden in the configuration file via
-        * the \<path> tag.
-        */
-       std::string ModPath;
-
        /** If set to true, then all opers on this server are
         * shown with a generic 'is an IRC operator' line rather
         * than the oper type. Oper types are still used internally.
@@ -376,12 +337,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.
         */
@@ -398,6 +353,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.
@@ -447,16 +409,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.
         */
@@ -470,52 +422,33 @@ 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.
-        */
-       bool CycleHosts;
-
        /** If set to true, the CycleHosts mode change will be sourced from the user,
         * rather than the server
         */
        bool CycleHostsFromUser;
 
-       /** If set to true, prefixed channel NOTICEs and PRIVMSGs will have the prefix
-        *  added to the outgoing text for undernet style msg prefixing.
-        */
-       bool UndernetMsgPrefix;
-
        /** 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 2.2
         */
        unsigned int MaxChans;
 
-       /** Oper max channels per user
+       /** Default value for <oper:maxchans>, deprecated in 2.2
         */
        unsigned int OperMaxChans;
 
@@ -534,15 +467,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
@@ -557,36 +482,17 @@ 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
+       /** 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 const char* CleanFilename(const char* name);
-
-       /** Check if a file exists.
-        * @param file The full path to a file
-        * @return True if the file exists and is readable.
-        */
-       static bool FileExists(const char* file);
-
-       /** If this value is true, invites will bypass more than just +i
-        */
-       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
@@ -614,4 +520,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
index f9cd08cb3ae9dc8b209770bb05d1de4d6d7c468c..bc4226ea93b5c7b73165fc748086c25195fd74ec 100644 (file)
@@ -21,8 +21,7 @@
  */
 
 
-#ifndef CTABLES_H
-#define CTABLES_H
+#pragma once
 
 /** Used to indicate command success codes
  */
@@ -44,7 +43,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 +75,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 +104,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 +125,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 +163,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)
-       {
-       }
-
-       /** Handle the command from a user.
-        * @param parameters The parameters for the command.
-        * @param user The user who issued the command.
-        * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
-        */
-       virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user) = 0;
+       CommandBase(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0);
 
-       virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               return ROUTE_LOCALONLY;
-       }
+       virtual RouteDescriptor GetRouting(User* user, const 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 +198,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 +253,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 5e66ddbb0493938a440eb42afcabf1f3c0447833..d42cf61bf965855dbbb8231e374543f1b5b04ada 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.
@@ -65,6 +64,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..9714e9654d3d2a026c3b1f13413718caaeba6bbb 100644 (file)
@@ -17,8 +17,7 @@
  */
 
 
-#ifndef EXTENSIBLE_H
-#define EXTENSIBLE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -39,7 +38,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 +69,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 +92,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 +101,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)
@@ -95,33 +116,48 @@ class CoreExport Extensible : public classbase
        virtual CullResult cull();
        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 +174,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 +233,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..87b2636fca0f1d96a07aa27ba95c3b7cac302106 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
@@ -73,25 +72,25 @@ 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;
+       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.
@@ -110,12 +109,22 @@ namespace irc
                bool operator()(const std::string& s1, const std::string& s2) const;
        };
 
+       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 +153,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 +161,82 @@ 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.
+        * @separator The character to place between the items.
         */
-       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
+        * an empty string.
+        */
+       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
-                */
-               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.
+               /** Initialize with comma separator
                 */
-               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,35 +252,13 @@ 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.
@@ -357,76 +284,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
@@ -481,12 +338,6 @@ 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
@@ -590,72 +441,3 @@ inline std::string& trim(std::string &str)
 
        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 e2eaf82927171fd7b8cec3bb9bc23cb3987caa7b..20a6508c97fc246332c887b1939f91adfabc89f0 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 <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 "dynref.h"
+#include "consolecolors.h"
 #include "caller.h"
 #include "cull_list.h"
 #include "extensible.h"
+#include "fileutils.h"
 #include "numerics.h"
 #include "uid.h"
+#include "server.h"
 #include "users.h"
 #include "channels.h"
 #include "timer.h"
@@ -98,44 +91,26 @@ 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
+#include "bancache.h"
+#include "isupportmanager.h"
 
 /** 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;
+       if (in == 0)
+               return "0";
        T quotient = in;
-       while (quotient) {
-               *out = "0123456789"[ std::abs( (long)quotient % 10 ) ];
-               ++out;
+       std::string out;
+       while (quotient)
+       {
+               out += "0123456789"[ std::abs( (long)quotient % 10 ) ];
                quotient /= 10;
        }
        if (in < 0)
-               *out++ = '-';
-       *out = 0;
-       std::reverse(res,out);
-       return res;
+               out += '-';
+       std::reverse(out.begin(), out.end());
+       return out;
 }
 
 /** Template function to convert any input type to std::string
@@ -192,6 +167,15 @@ template<typename T> inline long ConvToInt(const T &in)
        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;
+}
+
 /** This class contains various STATS counters
  * It is used by the InspIRCd class, which internally
  * has an instance of it.
@@ -201,38 +185,38 @@ class serverstats
   public:
        /** Number of accepted connections
         */
-       unsigned long statsAccept;
+       unsigned long Accept;
        /** Number of failed accepts
         */
-       unsigned long statsRefused;
+       unsigned long Refused;
        /** Number of unknown commands seen
         */
-       unsigned long statsUnknown;
+       unsigned long Unknown;
        /** Number of nickname collisions handled
         */
-       unsigned long statsCollisions;
+       unsigned long Collisions;
        /** Number of DNS queries sent out
         */
-       unsigned long statsDns;
+       unsigned long Dns;
        /** Number of good DNS replies received
         * NOTE: This may not tally to the number sent out,
         * due to timeouts and other latency issues.
         */
-       unsigned long statsDnsGood;
+       unsigned long DnsGood;
        /** Number of bad (negative) DNS replies received
         * NOTE: This may not tally to the number sent out,
         * due to timeouts and other latency issues.
         */
-       unsigned long statsDnsBad;
+       unsigned long DnsBad;
        /** Number of inbound connections seen
         */
-       unsigned long statsConnects;
+       unsigned long Connects;
        /** Total bytes of data transmitted
         */
-       unsigned long statsSent;
+       unsigned long Sent;
        /** Total bytes of data received
         */
-       unsigned long statsRecv;
+       unsigned long Recv;
 #ifdef _WIN32
        /** Cpu usage at last sample
        */
@@ -254,23 +238,18 @@ 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_HANDLER1(IsIdentHandler, bool, const std::string&);
+DEFINE_HANDLER1(IsChannelHandler, bool, const std::string&);
 DEFINE_HANDLER3(OnCheckExemptionHandler, ModResult, User*, Channel*, const std::string&);
 
-class TestSuite;
-
 /** The main class of the irc server.
  * This class contains instances of all the other classes in this software.
  * Amongst other things, it contains a ModeParser, a DNS object, a CommandParser
@@ -280,10 +259,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 +268,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 +277,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 +296,8 @@ 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 +310,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 +328,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 +344,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 +367,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 +379,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 +397,16 @@ 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;
+       StringExtItem 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 +438,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 +445,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 +455,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
-        */
-       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
+       /** Get a hash map containing all channels, keyed by their name
+        * @return A hash map mapping channel names to Channel pointers
         */
-       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 +494,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 +520,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 +530,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);
+       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
+       /** 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.
         */
-       CmdResult CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user);
-
-       /** 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
-        */
-       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 +557,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 +575,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
@@ -792,33 +599,6 @@ class CoreExport InspIRCd
         */
        InspIRCd(int argc, char** argv);
 
-       /** Send a line of WHOIS data to a user.
-        * @param user user to send the line to
-        * @param dest user being WHOISed
-        * @param numeric Numeric to send
-        * @param text Text of the numeric
-        */
-       void SendWhoisLine(User* user, User* dest, int numeric, const std::string &text);
-
-       /** Send a line of WHOIS data to a user.
-        * @param user user to send the line to
-        * @param dest user being WHOISed
-        * @param numeric Numeric to send
-        * @param format Format string for the numeric
-        * @param ... Parameters for the format string
-        */
-       void SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...) CUSTOM_PRINTF(5, 6);
-
-       /** 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
@@ -826,52 +606,39 @@ class CoreExport InspIRCd
         */
        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)
+       /** Return a time_t as a human-readable string.
+        * @param format The format to retrieve the date/time in. See `man 3 strftime`
+        * for more information. If NULL, "%a %b %d %T %Y" is assumed.
+        * @param utc True to convert the time to string as-is, false to convert it to local time first.
+        * @return A string representing the given date/time.
         */
-       void RehashUsersAndChans();
+       static std::string TimeString(time_t curtime, const char* format = NULL, bool utc = false);
 
-       /** Resets the cached max bans value on all channels.
-        * Called by rehash.
+       /** 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
         */
-       void ResetMaxBans();
-
-       /** Return a time_t as a human-readable string.
-        */
-       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;
@@ -885,15 +652,17 @@ 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"
index c62c5a25099375193622b4331963256ef2e819ea..53eca2e91d4c292c31a396161cae342ae2bc9401 100644 (file)
  */
 
 
-#ifndef INSPSOCKET_H
-#define INSPSOCKET_H
+#pragma once
 
 #include "timer.h"
 
+class IOHook;
+
 /**
  * States which a socket may be in
  */
@@ -89,11 +90,11 @@ class CoreExport SocketTimeout : public Timer
         * @param secs_from_now Seconds from now to time out
         * @param now The current time
         */
-       SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now, time_t now) : Timer(secs_from_now, now), sock(thesock), sfd(fd) { }
+       SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now) : Timer(secs_from_now), sock(thesock), sfd(fd) { }
 
        /** Handle tick event
         */
-       virtual void Tick(time_t now);
+       virtual bool Tick(time_t now);
 };
 
 /**
@@ -102,30 +103,155 @@ 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;
+               }
+
+        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();
+
  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,7 +274,9 @@ class CoreExport StreamSocket : public EventHandler
         */
        bool GetNextLine(std::string& line, char delim = '\n');
        /** Useful for implementing sendq exceeded */
-       inline size_t getSendQSize() const { return sendq_len; }
+       size_t getSendQSize() const { return sendq.size(); }
+
+       SendQueue& GetSendQ() { return sendq; }
 
        /**
         * Close the socket, remove from socket engine, etc
@@ -224,14 +352,11 @@ 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::AddIOHook(IOHook* hook) { iohook = hook; }
+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..cf27fcb
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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
+{
+ public:
+       enum Type
+       {
+               IOH_UNKNOWN,
+               IOH_SSL
+       };
+
+       const Type type;
+
+       IOHookProvider(Module* mod, const std::string& Name, Type hooktype = IOH_UNKNOWN)
+               : ServiceProvider(mod, Name, SERVICE_IOHOOK), type(hooktype) { }
+
+       /** Called immediately after a connection is accepted. This is intended for raw socket
+        * processing (e.g. modules which wrap the tcp connection within another library) and provides
+        * no information relating to a user record as the connection has not been assigned yet.
+        * @param sock The socket in question
+        * @param client The client IP address and port
+        * @param server The server IP address and port
+        */
+       virtual void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) = 0;
+
+       /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
+        * by a module.
+        * @param sock The socket in question
+        */
+       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;
+
+       IOHook(IOHookProvider* provider)
+               : prov(provider) { }
+
+       /**
+        * Called when a hooked stream has data to write, or when the socket
+        * engine returns it as writable
+        * @param sock The socket in question
+        * @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) = 0;
+
+       /** 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) = 0;
+
+       /**
+        * 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) = 0;
+};
diff --git a/include/isupportmanager.h b/include/isupportmanager.h
new file mode 100644 (file)
index 0000000..1f41de5
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** This class manages the generation and transmission of ISUPPORT. */
+class CoreExport ISupportManager
+{
+ private:
+       /** The generated lines which are sent to clients. */
+       std::vector<std::string> cachedlines;
+
+ public:
+       /** (Re)build the ISUPPORT vector.
+        * Called by the core on boot after all modules have been loaded, and every time when a module is loaded
+        * or unloaded. Calls the On005Numeric hook, letting modules manipulate the ISUPPORT tokens.
+        */
+       void Build();
+
+       /** Returns the cached std::vector of ISUPPORT lines.
+        * @return A list of strings prepared for sending to users
+        */
+       const std::vector<std::string>& 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..94af1d5
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * 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;
+
+       /** 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);
+
+       /** 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..c56859a62d57691ea20e2eb045c6d5d2798789cb 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.
  */
@@ -77,9 +87,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 +103,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 +139,6 @@ class CoreExport LogManager
        FileLogMap FileLogs;
 
  public:
-
        LogManager();
        ~LogManager();
 
@@ -199,17 +210,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..05d6b3796fb1ac72e44a035117d927077b4878f0 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;
+
+       /** Id of this Membership, set by the protocol module, other components should never read or
+        * write this field.
+        */
+       Id id;
+
+       /** Converts a string to a Membership::Id
+        * @param str The string to convert
+        * @return Raw value of type Membership::Id
+        */
+       static Id IdFromString(const std::string& str)
+       {
+               return ConvToUInt64(str);
+       }
+
+       /** Constructor, sets the user and chan fields to the parameters, does NOT update any bookkeeping
+        * information in the User or the Channel.
+        * Call Channel::JoinUser() or ForceJoin() to make a user join a channel instead of constructing
+        * Membership objects directly.
+        */
        Membership(User* u, Channel* c) : user(u), chan(c) {}
+
+       /** Returns true if this member has a given prefix mode set
+        * @param m The prefix mode letter to check
+        * @return True if the member has the prefix mode set, false otherwise
+        */
        inline bool hasMode(char m) const
        {
                return modes.find(m) != std::string::npos;
        }
-       unsigned int getRank();
-};
-
-class CoreExport InviteBase
-{
- protected:
-       InviteList invites;
 
- public:
-       void ClearInvites();
-
-       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.
+        */
+       const char* GetAllPrefixChars() const;
 };
-
-#endif
index 1dab442d40ac6d993ae8c8787a89fc93c66e83a1..e7ac756ecdaa636faf524166a272776f3fcea400 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,6 +144,10 @@ class CoreExport ModeHandler : public ServiceProvider
         */
        ModeType m_type;
 
+       /** The object type of this mode handler
+        */
+       const Class type_id;
+
        /** The prefix char needed on channel to use this mode,
         * only checked for channel modes
         */
@@ -159,36 +163,43 @@ 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);
+       ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type, Class mclass = MC_OTHER);
        virtual CullResult cull();
        virtual ~ModeHandler();
+
+       /** Register this object in the ModeParser
+        */
+       void RegisterService() CXX11_OVERRIDE;
+
        /**
         * Returns true if the mode is a list mode
         */
-       bool IsListMode();
+       bool IsListMode() const { return list; }
+
        /**
-        * Mode prefix or 0. If this is defined, you should
-        * also implement GetPrefixRank() to return an integer
-        * value for this mode prefix.
+        * Check whether this mode is a prefix mode
+        * @return non-NULL if this mode is a prefix mode, NULL otherwise
         */
-       inline char GetPrefix() const { return prefix; }
+       PrefixMode* IsPrefixMode();
+
        /**
-        * Get the 'value' of this modes prefix.
-        * determines which to display when there are multiple.
-        * The mode with the highest value is ranked first. See the
-        * PrefixModeValue enum and Channel::GetPrefixValue() for
-        * more information.
+        * Check whether this mode 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 ParamModeBase, NULL otherwise
         */
-       inline ModeType GetModeType() const { return m_type; }
+       ParamModeBase* IsParameterMode();
+
        /**
-        * Returns the mode's parameter translation type
+        * Returns the mode's type
         */
-       inline TranslateType GetTranslateType() const { return m_paramtype; }
+       inline ModeType GetModeType() const { return m_type; }
        /**
         * Returns true if the mode can only be set/unset by an oper
         */
@@ -206,6 +217,11 @@ class CoreExport ModeHandler : public ServiceProvider
         */
        inline char GetModeChar() { 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);
@@ -269,28 +285,104 @@ class CoreExport ModeHandler : public ServiceProvider
        virtual bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
 
        /**
-        * When a MODETYPE_USER mode handler is being removed, the server will call this method for every user on the server.
-        * Your mode handler should remove its user mode from the user by sending the appropriate server modes using
-        * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters,
-        * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove
-        * your mode properly from each user.
+        * When a MODETYPE_USER mode handler is being removed, the core will call this method for every user on the server.
+        * The usermode will be removed using the appropiate server mode using InspIRCd::SendMode().
         * @param user The user which the server wants to remove your mode from
-        * @param stack The mode stack to add the mode change to
         */
-       virtual void RemoveMode(User* user, irc::modestacker* stack = NULL);
+       void RemoveMode(User* user);
 
        /**
         * When a MODETYPE_CHANNEL mode handler is being removed, the server will call this method for every channel on the server.
-        * Your mode handler should remove its user mode from the channel by sending the appropriate server modes using
-        * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters,
-        * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove
-        * your mode properly from each channel. Note that in the case of listmodes, you should remove the entire list of items.
+        * The mode handler has to populate the given modestacker with mode changes that remove the mode from the channel.
+        * The default implementation of this method can remove all kinds of channel modes except listmodes.
+        * In the case of listmodes, the entire list of items must be added to the modestacker (which is handled by ListModeBase,
+        * so if you inherit from it or your mode can be removed by the default implementation then you do not have to implement
+        * this function).
         * @param channel The channel which the server wants to remove your mode from
-        * @param stack The mode stack to add the mode change to
+        * @param changelist Mode change list to populate with the removal of this mode
         */
-       virtual void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
+       virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
 
        inline unsigned int GetLevelRequired() const { return levelrequired; }
+
+       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;
+
+ 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);
+
+       /**
+        * 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);
+
+       /**
+        * Removes this prefix mode from all users on the given channel
+        * @param chan The channel which the server wants to remove your mode from
+        * @param changelist Mode change list to populate with the removal of this mode
+        */
+       void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
+
+       /**
+        * 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 +395,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,18 +408,7 @@ class CoreExport SimpleChannelModeHandler : public ModeHandler
  public:
        SimpleChannelModeHandler(Module* Creator, const std::string& Name, char modeletter)
                : ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_CHANNEL) {}
-       virtual ~SimpleChannelModeHandler() {}
-       virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-};
-
-class CoreExport ParamChannelModeHandler : public ModeHandler
-{
- public:
-       ParamChannelModeHandler(Module* Creator, const std::string& Name, char modeletter)
-               : ModeHandler(Creator, Name, modeletter, PARAM_SETONLY, MODETYPE_CHANNEL) {}
        virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       /** Validate the parameter - you may change the value to normalize it. Return true if it is valid. */
-       virtual bool ParamValidate(std::string& parameter);
 };
 
 /**
@@ -339,11 +419,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,17 +435,18 @@ 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)
@@ -380,11 +462,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 +474,145 @@ class CoreExport ModeWatcher : public classbase
         * @param parameter The parameter of the mode, if the mode is supposed to have a parameter.
         * You cannot alter the parameter here, as the mode handler has already processed it.
         * @param adding True if the mode is being added and false if it is being removed
-        * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL
         */
-       virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding, ModeType type);
+       virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding);
 };
 
-typedef std::vector<ModeWatcher*>::iterator ModeWatchIter;
-
 /** The mode parser handles routing of modes and handling of mode strings.
  * It marshalls, controls and maintains both ModeWatcher and ModeHandler classes,
  * parses client to server MODE strings for user and channel modes, and performs
  * processing for the 004 mode list numeric, amongst other things.
  */
-class CoreExport ModeParser
+class CoreExport ModeParser : public fakederef<ModeParser>
 {
+ public:
+       static const ModeHandler::Id MODEID_MAX = 64;
+
+       /** Type of the container that maps mode names to ModeHandlers
+        */
+       typedef TR1NS::unordered_map<std::string, ModeHandler*, irc::insensitive, irc::StrHashComp> ModeHandlerMap;
+
  private:
+       /** Type of the container that maps mode names to ModeWatchers
+        */
+       typedef insp::flat_multimap<std::string, ModeWatcher*> ModeWatcherMap;
+
+       /** Last item in the ModeType enum
+        */
+       static const unsigned int MODETYPE_LAST = 2;
+
        /** Mode handlers for each mode, to access a handler subtract
         * 65 from the ascii value of the mode letter.
         * The upper bit of the value indicates if its a usermode
         * or a channel mode, so we have 256 of them not 64.
         */
-       ModeHandler* modehandlers[256];
-       /** Mode watcher classes arranged in the same way as the
-        * mode handlers, except for instead of having 256 of them
-        * we have 256 lists of them.
+       ModeHandler* modehandlers[MODETYPE_LAST][128];
+
+       /** An array of mode handlers indexed by the mode id
         */
-       std::vector<ModeWatcher*> modewatchers[256];
-       /** Displays the current modes of a channel or user.
-        * Used by ModeParser::Process.
+       ModeHandler* modehandlersbyid[MODETYPE_LAST][MODEID_MAX];
+
+       /** A map of mode handlers keyed by their name
         */
-       void DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text);
-       /** Displays the value of a list mode
-        * Used by ModeParser::Process.
+       ModeHandlerMap modehandlersbyname[MODETYPE_LAST];
+
+       /** Lists of mode handlers by type
         */
-       void DisplayListModes(User* user, Channel* chan, std::string &mode_sequence);
+       struct
+       {
+               /** List of mode handlers that inherit from ListModeBase
+                */
+               std::vector<ListModeBase*> list;
+
+               /** List of mode handlers that inherit from PrefixMode
+                */
+               std::vector<PrefixMode*> prefix;
+       } mhlist;
+
+       /** Mode watcher classes
+        */
+       ModeWatcherMap modewatchermap;
+
+       /** Last processed mode change
+        */
+       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
+        */
+       std::string 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::levelrequired and ModeHandler::AccessCheck().
+                * Modules may explicitly allow a mode change regardless of this flag by returning
+                * MOD_RES_ALLOW from the OnPreMode hook. Only affects channel mode changes.
+                */
+               MODE_CHECKACCESS = 4
+       };
 
-       /** 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();
+
        /** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out.
         * E.g.
         *
@@ -474,13 +632,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 +654,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 +665,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 +723,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);
-
-       /** Returns a list of mode characters which are usermodes.
-        * This is used in the 004 numeric when users connect.
-        */
-       std::string UserModeList();
-
-       /** Returns a list of channel mode characters which are listmodes.
-        * This is used in the 004 numeric when users connect.
-        */
-       std::string ChannelModeList();
+       PrefixMode* FindPrefix(unsigned const char pfxletter);
 
-       /** Returns a list of channel mode characters which take parameters.
-        * This is used in the 004 numeric when users connect.
+       /** Returns a list of modes, space seperated by type:
+        * 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 ParaModeList();
+       const std::string& GetModeListFor004Numeric();
 
        /** Generates a list of modes, comma seperated by type:
         *  1; Listmodes EXCEPT those with a prefix
@@ -552,14 +750,53 @@ 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 std::string& ModeParser::GetModeListFor004Numeric()
+{
+       return Cached004ModeList;
+}
+
+inline PrefixMode* ModeHandler::IsPrefixMode()
+{
+       return (this->type_id == MC_PREFIX ? static_cast<PrefixMode*>(this) : NULL);
+}
+
+inline ListModeBase* ModeHandler::IsListModeBase()
+{
+       return (this->type_id == MC_LIST ? reinterpret_cast<ListModeBase*>(this) : NULL);
+}
+
+inline ParamModeBase* ModeHandler::IsParameterMode()
+{
+       return (this->type_id == MC_PARAM ? reinterpret_cast<ParamModeBase*>(this) : NULL);
+}
diff --git a/include/modechange.h b/include/modechange.h
new file mode 100644 (file)
index 0000000..e206657
--- /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 handler Mode handler
+        * @param add True if this mode is being set, false if removed
+        * @param parameter Mode parameter
+        */
+       void push(ModeHandler* mh, bool adding, const std::string& param = std::string())
+       {
+               items.push_back(Change(mh, adding, param));
+       }
+
+       /** Add a new mode to this ChangeList which will be set on the target
+        * @param handler Mode handler
+        * @param parameter Mode parameter
+        */
+       void push_add(ModeHandler* mh, const std::string& param = std::string())
+       {
+               push(mh, true, param);
+       }
+
+       /** Add a new mode to this ChangeList which will be unset from the target
+        * @param handler Mode handler
+        * @param parameter Mode parameter
+        */
+       void push_remove(ModeHandler* mh, const std::string& param = std::string())
+       {
+               push(mh, false, param);
+       }
+
+       /** Remove all mode changes from this stack
+        */
+       void clear() { items.clear(); }
+
+       /** Checks whether the ChangeList is empty, equivalent to (size() != 0).
+        * @return True if the ChangeList is empty, false otherwise.
+        */
+       bool empty() const { return items.empty(); }
+
+       /** Get number of mode changes in this ChangeList
+        * @return Number of mode changes in this ChangeList
+        */
+       List::size_type size() const { return items.size(); }
+
+       /** Get the list of mode changes in this ChangeList
+        * @return List of modes added to this ChangeList
+        */
+       const List& getlist() const { return items; }
+
+       /** Get the list of mode changes in this ChangeList
+        * @return List of modes added to this ChangeList
+        */
+       List& getlist() { return items; }
+
+ private:
+       List items;
+};
diff --git a/include/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 9857012fcfc0ac1c71793198e15f5b0a902d5e0c..526c283b2f1a96fa8426b4e754fb166a278f146b 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
@@ -109,35 +106,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 9
+#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 +144,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)
@@ -212,69 +205,6 @@ class CoreExport Version
        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
 {
  public:
@@ -282,38 +212,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 +220,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 +246,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
         */
@@ -387,6 +289,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 +386,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 +415,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 +468,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 +487,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 +506,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 +520,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 +540,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 +560,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 +610,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
@@ -870,7 +673,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 +682,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 +707,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 +771,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 +815,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
@@ -1122,18 +916,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 +928,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 +936,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 +945,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 +955,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,16 +968,24 @@ 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);
 
@@ -1290,9 +993,10 @@ class CoreExport Module : public classbase, public usecountbase
         * @param source The user running the /WHO query
         * @param params The parameters to the /WHO query
         * @param user The user that this line of the query is about
+        * @param memb The member shown in this line, NULL if no channel is in this line
         * @param line The raw line to send; modifiable, if empty no line will be returned.
         */
-       virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line);
+       virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line);
 
        /** 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,173 +1005,6 @@ 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;
@@ -1479,17 +1016,16 @@ 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 +1037,17 @@ class CoreExport ModuleManager
                PRIO_STATE_LAST
        } prioritizationState;
 
-       /** Internal unload module hook */
-       bool CanUnload(Module*);
+       /** Loads all core modules (cmd_*)
+        */
+       void LoadCoreModules(std::map<std::string, ServiceList>& servicemap);
+
+       /** Calls the Prioritize() method in all loaded modules
+        * @return True if all went well, false if a dependency loop was detected
+        */
+       bool PrioritizeHooks();
+
  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 +1057,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 +1100,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 +1108,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 +1140,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 +1164,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 +1190,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 +1211,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
@@ -1689,11 +1255,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 +1280,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 " " INSPIRCD_REVISION;
 
 #else
 
@@ -1727,11 +1289,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 " " INSPIRCD_REVISION;
 #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..e6f9340
--- /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());
+               }
+
+               /** 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..1ba54cc
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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,
+               /* 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) { }
+       };
+
+       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, public Question
+       {
+        protected:
+               Manager* const manager;
+        public:
+               /* 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->dns_timeout ? ServerInstance->Config->dns_timeout : 5))
+                       , Question(addr, qt)
+                       , manager(mgr)
+                       , 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);
+                       rr.error = ERROR_TIMEDOUT;
+                       this->OnError(&rr);
+                       delete this;
+                       return false;
+               }
+       };
+
+} // namespace DNS
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..3f378d8
--- /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->host;
+               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..67bfc7b
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * 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:
+       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 "";
+       }
+};
+
+/** 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)
+       {
+               IOHook* iohook = sock->GetIOHook();
+               if ((!iohook) || (iohook->prov->type != IOHookProvider::IOH_SSL))
+                       return NULL;
+
+               SSLIOHook* ssliohook = static_cast<SSLIOHook*>(iohook);
+               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/whois.h b/include/modules/whois.h
new file mode 100644 (file)
index 0000000..b64d464
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 The numeric of the line being sent
+        * @param text The text of the numeric, including any parameters
+        * @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, unsigned int& numeric, std::string& text) = 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
+        * @param numeric Numeric to send
+        * @param format Format string for the numeric
+        * @param ... Parameters for the format string
+        */
+       void SendLine(unsigned int numeric, const char* format, ...) CUSTOM_PRINTF(3, 4)
+       {
+               std::string textbuffer;
+               VAFORMAT(textbuffer, format, format)
+               SendLine(numeric, textbuffer);
+       }
+
+       /** Send a line of WHOIS data to the source of the WHOIS
+        * @param numeric Numeric to send
+        * @param text Text of the numeric
+        */
+       virtual void SendLine(unsigned int numeric, const std::string& text) = 0;
+};
diff --git a/include/numericbuilder.h b/include/numericbuilder.h
new file mode 100644 (file)
index 0000000..36cfeed
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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 Numeric
+{
+       class WriteNumericSink;
+
+       template <char Sep, bool SendEmpty, typename Sink>
+       class GenericBuilder;
+
+       template <char Sep = ',', bool SendEmpty = false>
+       class Builder;
+}
+
+class Numeric::WriteNumericSink
+{
+       LocalUser* const user;
+
+ public:
+       WriteNumericSink(LocalUser* u)
+               : user(u)
+       {
+       }
+
+       void operator()(unsigned int numeric, const std::string& params) const
+       {
+               user->WriteNumeric(numeric, params);
+       }
+};
+
+template <char Sep, bool SendEmpty, typename Sink>
+class Numeric::GenericBuilder
+{
+       Sink sink;
+       std::string data;
+       const unsigned int numeric;
+       const std::string::size_type max;
+       std::string::size_type beginpos;
+
+       bool HasRoom(const std::string::size_type additional) const
+       {
+               return (data.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 - 9)
+       {
+               if (addparam)
+                       data.push_back(':');
+               SaveBeginPos();
+       }
+
+       std::string& GetNumeric() { return data; }
+
+       void Add(const std::string& entry)
+       {
+               if (!HasRoom(entry.size()))
+                       Flush();
+               data.append(entry).push_back(Sep);
+       }
+
+       void Add(const std::string& entry1, const std::string& entry2)
+       {
+               if (!HasRoom(entry1.size() + entry2.size()))
+                       Flush();
+               data.append(entry1).append(entry2).push_back(Sep);
+       }
+
+       void Flush()
+       {
+               if (IsEmpty())
+               {
+                       if (!SendEmpty)
+                               return;
+               }
+               else
+               {
+                       data.erase(data.size()-1);
+               }
+
+               sink(numeric, data);
+               if (data.size() > beginpos)
+                       data.erase(beginpos);
+       }
+
+       bool IsEmpty() const { return (data.size() == beginpos); }
+       void SaveBeginPos() { beginpos = data.size(); }
+};
+
+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())
+       {
+       }
+};
index 4fce4cb6de53b746c9ef6a8e6798fdca00aa8a1b..0447df35308a3ab1a8202a6028926e974ea892f4 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!
@@ -44,76 +40,112 @@ enum Numerics
        /*
         * 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_MAP                         = 6, // unrealircd
+       RPL_ENDMAP                      = 7, // unrealircd
+       RPL_SNOMASKIS                   = 8, // unrealircd
+       RPL_REDIR                       = 10,
+
+       RPL_YOURUUID                    = 42, // taken from ircnet
+
+       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_MAPUSERS                    = 270, // insp-specific
+
+       RPL_AWAY                        = 301,
+
+       RPL_SYNTAX                      = 304, // insp-specific
+
+       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_INVITING                    = 341,
+       RPL_INVITELIST                  = 346, // insp-specific (stolen from ircu)
+       RPL_ENDOFINVITELIST             = 347, // insp-specific (stolen from ircu)
+       RPL_VERSION                     = 351,
+       RPL_NAMREPLY                    = 353,
+       RPL_LINKS                       = 364,
+       RPL_ENDOFLINKS                  = 365,
+       RPL_ENDOFNAMES                  = 366,
+       RPL_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_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 +163,37 @@ 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,
+
+       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_COMMANDS                    = 702, // insp-specific
+       RPL_COMMANDSEND                 = 703, // insp-specific
+
+       ERR_CHANOPEN                    = 713,
+       ERR_KNOCKONCHAN                 = 714,
+
+       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
 };
-
-#endif
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..2b1ffb7536a6f67543b3e41992ab2d6ec241e21f 100644 (file)
@@ -18,8 +18,7 @@
  */
 
 
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
+#pragma once
 
 #include "hashcomp.h"
 
@@ -27,40 +26,77 @@ 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 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 an object to other linked servers.
-        * @param target The object to send metadata for.
+       /** 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 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 SendMetaData(User* user, const std::string& key, const std::string& data) { }
+
+       /** 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 SendMetaData(const std::string& key, const std::string& data) { }
 
        /** Send a topic change for a channel
         * @param channel The channel to change the topic for.
@@ -68,34 +104,11 @@ class ProtocolInterface
         */
        virtual void SendTopic(Channel* channel, std::string &topic) { }
 
-       /** 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
-        */
-       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);
-       }
-
        /** 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) { }
+       virtual void SendSNONotice(char snomask, const std::string& text) { }
 
        /** Send raw data to a remote client.
         * @param target The user to push data to.
@@ -107,34 +120,39 @@ class ProtocolInterface
         * @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..bd08773e9972be82a4f0dd591ee110ace0177cc8 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 remote If true the message will go to the uppercase variant of this snomask
         */
-       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..9d69b5d22cf8e70267e9764888bcb4e9bb26e035 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
@@ -128,16 +124,10 @@ namespace irc
                 * @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(); }
        }
 }
 
+#include "iohook.h"
 #include "socketengine.h"
 /** This class handles incoming connections on client ports.
  * It will create a new User for every valid connection
@@ -151,20 +141,26 @@ class CoreExport ListenSocket : public EventHandler
        int bind_port;
        /** Human-readable bind description */
        std::string bind_desc;
+
+       /** The IOHook provider which handles connections on this socket,
+        * NULL if there is none.
+        */
+       dynamic_reference_nocheck<IOHookProvider> iohookprov;
+
        /** 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.
+        * @return True if the IO hook provider was found or none was given, false otherwise.
+        */
+       bool ResetIOHookProvider();
+};
index 37b7d637350b833e46d38f13d932f253008f36d6..ddc48f94d159a359ae8d8f27bfc5534a515aa314 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
@@ -128,7 +118,7 @@ enum EventMask
        /** Add a trial write. During the next DispatchEvents invocation, this
         * will call HandleEvent with EVENT_WRITE 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.
@@ -137,7 +127,7 @@ enum EventMask
        /** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE.
         * Reset by SE before running EVENT_WRITE
         */
-       FD_WRITE_WILL_BLOCK = 0x8000, 
+       FD_WRITE_WILL_BLOCK = 0x8000,
 
        /** Mask for trial read/trial write */
        FD_TRIAL_NOTE_MASK = 0x5000
@@ -166,6 +156,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 +190,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;
 };
@@ -231,33 +228,84 @@ class CoreExport EventHandler : public classbase
  */
 class CoreExport SocketEngine
 {
+ 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) { }
+
+               /** Increase the counters for bytes sent/received in this second.
+                * @param len_in Bytes received, 0 if updating number of bytes written.
+                * @param len_out Bytes sent, 0 if updating number of bytes read.
+                */
+               void Update(size_t len_in, size_t len_out);
+
+               /** Get data transfer statistics.
+                * @param kbitspersec_in Filled with incoming traffic in this second in kbit/s.
+                * @param kbitspersec_out Filled with outgoing traffic in this second in kbit/s.
+                * @param kbitspersec_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
+        **/
+       static std::vector<EventHandler*> ref;
+
  protected:
        /** Current number of descriptors in the engine
         */
-       int CurrentSetSize;
-       /** Reference table, contains all current handlers
-        */
-       EventHandler** ref;
+       static size_t CurrentSetSize;
        /** List of handlers that want a trial read/write
         */
-       std::set<int> trials;
+       static std::set<int> trials;
 
-       int MAX_DESCRIPTORS;
+       static int MAX_DESCRIPTORS;
 
-       size_t indata;
-       size_t outdata;
-       time_t lastempty;
+       /** Socket engine statistics: count of various events, bandwidth usage
+        */
+       static Statistics stats;
 
-       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);
+
+       static void DelFdRef(EventHandler* eh);
 
-       unsigned long TotalEvents;
-       unsigned long ReadEvents;
-       unsigned long WriteEvents;
-       unsigned long ErrorEvents;
+       template <typename T>
+       static void ResizeDouble(std::vector<T>& vect)
+       {
+               if (SocketEngine::CurrentSetSize > vect.size())
+                       vect.resize(vect.size() * 2);
+       }
+
+public:
+#ifndef _WIN32
+       typedef iovec IOVector;
+#else
+       typedef WindowsIOVec IOVector;
+#endif
 
        /** Constructor.
         * The constructor transparently initializes
@@ -266,14 +314,16 @@ 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
@@ -282,7 +332,7 @@ public:
         * @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 +345,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 positive, the number of file descriptors that the system reported that we
+        * may use. Otherwise (<= 0) this number could not be determined.
         */
-       inline int GetMaxFds() const { return MAX_DESCRIPTORS; }
+       static int GetMaxFds() { return MAX_DESCRIPTORS; }
 
        /** 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,21 +367,21 @@ 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
@@ -339,23 +391,17 @@ public:
         * value.
         * @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..3e00a4c
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace 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;
+               }
+       }
+
+       /**
+        * 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 06f704120126309a93114d0996ea20d24139e75e..dfecb04837a5ce4220047764e8457e505db2d888 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 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
-
-/** A list holding local users, this is the type of UserManager::local_users
- */
-typedef std::list<LocalUser*> LocalUserList;
+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 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
+/** List of channels to consider when building the neighbor list of a user
  */
-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
  */
@@ -112,23 +78,9 @@ typedef ConfigDataHash::const_iterator ConfigIter;
 /** Iterator pair, used for tag-name ranges */
 typedef std::pair<ConfigIter,ConfigIter> ConfigTagList;
 
-/** Index of valid oper blocks and types */
-typedef std::map<std::string, reference<OperInfo> > OperIndex;
-
 /** Files read by the configuration */
 typedef std::map<std::string, file_cache> ConfigFileCache;
 
-/** A hash of commands used by the core
- */
-typedef nspace::hash_map<std::string,Command*> Commandtable;
-
-/** Membership list of a channel */
-typedef std::map<User*, Membership*> UserMembList;
-/** Iterator of UserMembList */
-typedef UserMembList::iterator UserMembIter;
-/** const Iterator of UserMembList */
-typedef UserMembList::const_iterator UserMembCIter;
-
 /** Generic user list, used for exceptions */
 typedef std::set<User*> CUList;
 
@@ -159,7 +111,3 @@ typedef XLineContainer::iterator ContainerIter;
 /** An interator in an XLineLookup
  */
 typedef XLineLookup::iterator LookupIter;
-
-
-#endif
-
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..941569e8c02e7af1813ec95468fd7e924a3d36d8 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;
-       }
+       /** Destructor, destroys all users in clientlist
+        */
+       ~UserManager();
 
        /** Client list, a hash_map containing all clients, local and remote
         */
-       user_hash* clientlist;
+       user_hash clientlist;
 
        /** Client list stored by UUID. Contains all clients, and is updated
         * automatically by the constructor and destructor of User.
         */
-       user_hash* uuidlist;
-
-       /** Local client list, a list containing only local clients
-        */
-       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 such as PING checks
         */
-       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,
@@ -90,20 +112,15 @@ class CoreExport UserManager
        /** Disconnect a user gracefully
         * @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
+        * @param operreason The quit reason to show to opers, can be NULL if same as quitreason
         * @return Although this function has no return type, on exit the user provided will no longer exist.
         */
-       void QuitUser(User *user, const std::string &quitreason, const char* operreason = "");
-
-       /** Add a user to the local clone map
-        * @param user The user to add
-        */
-       void AddLocalClone(User *user);
+       void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL);
 
-       /** Add a user to the global clone map
+       /** Add a user to the clone map
         * @param user The user to add
         */
-       void AddGlobalClone(User *user);
+       void AddClone(User* user);
 
        /** Remove all clone counts from the user, you should
         * use this if you change the user's IP address
@@ -116,49 +133,53 @@ class CoreExport UserManager
         */
        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 +187,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..fa346a32968f2164a93aed14c447ef9ba25d7e42 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,10 @@ struct CoreExport ConnectClass : public refcountbase
         */
        unsigned long limit;
 
+       /** If set to true, no user DNS lookups are to be performed
+        */
+       bool resolvehostnames;
+
        /** Create a new connect class with no settings.
         */
        ConnectClass(ConfigTag* tag, char type, const std::string& mask);
@@ -250,7 +239,31 @@ class CoreExport User : public Extensible
         */
        std::string cachedip;
 
+       /** 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;
+       };
+
+       /** List of Memberships for this user
+        */
+       typedef insp::intrusive_list<Membership> ChanList;
 
        /** Hostname of connection.
         * This should be valid as per RFC1035.
@@ -267,10 +280,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.
@@ -302,18 +311,6 @@ class CoreExport User : public Extensible
         */
        std::string fullname;
 
-       /** The user's mode list.
-        * NOT a null terminated string.
-        * Also NOT an array.
-        * Much love to the STL for giving us an easy to use bitset, saving us RAM.
-        * if (modes[modeletter-65]) is set, then the mode is
-        * set, for example, to work out if mode +s is set, we  check the field
-        * User::modes['s'-65] != 0.
-        * The following RFC characters o, w, s, i have constants defined via an
-        * enum, such as UM_SERVERNOTICE and UM_OPETATOR.
-        */
-       std::bitset<64> modes;
-
        /** What snomasks are set on this user.
         * This functions the same as the above modes.
         */
@@ -321,11 +318,11 @@ class CoreExport User : public Extensible
 
        /** Channels this user is on
         */
-       UserChanList chans;
+       ChanList chans;
 
        /** The server the user is connected to.
         */
-       const std::string server;
+       Server* server;
 
        /** The user's away message.
         * If this string is empty, the user is not marked as away.
@@ -333,7 +330,7 @@ class CoreExport User : public Extensible
        std::string awaymsg;
 
        /** Time the user last went away.
-        * This is ONLY RELIABLE if user IS_AWAY()!
+        * This is ONLY RELIABLE if user IsAway()!
         */
        time_t awaytime;
 
@@ -347,16 +344,6 @@ class CoreExport User : public Extensible
         */
        unsigned int registered:3;
 
-       /** True when DNS lookups are completed.
-        * The UserResolver classes res_forward and res_reverse will
-        * set this value once they complete.
-        */
-       unsigned int dns_done:1;
-
-       /** Whether or not to send an snotice about this user's quitting
-        */
-       unsigned int quietquit:1;
-
        /** If this is set to true, then all socket operations for the user
         * are dropped into the bit-bucket.
         * This value is set by QuitUser, and is not needed seperately from that call.
@@ -364,27 +351,13 @@ 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();
 
        /** Get CIDR mask, using default range, for this user
         */
@@ -400,13 +373,7 @@ class CoreExport User : public Extensible
        /** 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 +395,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,12 +413,6 @@ 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
         */
@@ -463,12 +423,16 @@ class CoreExport User : public Extensible
         * @return True if the mode is set
         */
        bool IsModeSet(unsigned char m);
+       bool IsModeSet(ModeHandler* mh);
+       bool IsModeSet(ModeHandler& mh) { return IsModeSet(&mh); }
+       bool IsModeSet(UserModeReference& moderef);
 
        /** Set a specific usermode to on or off
         * @param m 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
@@ -497,12 +461,6 @@ class CoreExport User : public Extensible
         */
        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();
-
        /** Creates a usermask with real host.
         * Takes a buffer to use and fills the given buffer with the hostmask in the format user\@host
         * @return the usermask in the format user\@host
@@ -515,24 +473,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,6 +508,17 @@ class CoreExport User : public Extensible
         */
        void WriteServ(const char* text, ...) CUSTOM_PRINTF(2, 3);
 
+       /** 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); }
+
        void WriteNumeric(unsigned int numeric, const char* text, ...) CUSTOM_PRINTF(3, 4);
 
        void WriteNumeric(unsigned int numeric, const std::string &text);
@@ -580,19 +536,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,24 +548,21 @@ 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
+       /** Execute a function once for each local neighbor of this user. By default, the neighbors of a user are the users
+        * who have at least one common channel with the user. Modules are allowed to alter the set of neighbors freely.
+        * This function is used for example to send something conditionally to neighbors, or to send different messages
+        * to different users depending on their oper status.
+        * @param handler Function object to call, inherited from ForEachNeighborHandler.
+        * @param include_self True to include this user in the set of neighbors, false otherwise.
+        * Modules may override this. Has no effect if this user is not local.
         */
-       void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text);
+       void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true);
 
        /** 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
+        * @param linePrefix text to prefix each complete line with
+        * @param textStream the text to send to the user
         */
-       void SendText(const std::string &LinePrefix, std::stringstream &TextStream);
+       void SendText(const std::string& linePrefix, std::stringstream& textStream);
 
        /** Write to the user, routing the line if the user is remote.
         */
@@ -638,32 +578,24 @@ 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
-        */
-       void DoHostCycle(const std::string &quitline);
-
        /** 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.
+        * remote servers.
         * @param host The new hostname to set
         * @return True if the change succeeded, false if it didn't
+        * (a module vetoed the change).
         */
-       bool ChangeDisplayedHost(const char* host);
+       bool ChangeDisplayedHost(const std::string& host);
 
        /** 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,49 +604,19 @@ 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.
         * @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();
@@ -739,7 +641,7 @@ 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);
@@ -747,10 +649,6 @@ class CoreExport LocalUser : public User, public InviteBase
 
        UserIOHandler eh;
 
-       /** Position in UserManager::local_users
-        */
-       LocalUserList::iterator localuseriter;
-
        /** Stats counter for bytes inbound
         */
        unsigned int bytes_in;
@@ -777,11 +675,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 +693,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.
@@ -833,32 +747,6 @@ class CoreExport LocalUser : public User, public InviteBase
        void Write(const std::string& text);
        void Write(const char*, ...) CUSTOM_PRINTF(2, 3);
 
-       /** Returns the list of channels this user has been invited to but has not yet joined.
-        * @return A list of channels the user is invited to
-        */
-       InviteList& GetInviteList();
-
-       /** Returns true if a user is invited to a channel.
-        * @param channel A channel name to look up
-        * @return True if the user is invited to the given channel
-        */
-       bool IsInvited(const irc::string &channel);
-
-       /** Adds a channel to a users invite list (invites them to a channel)
-        * @param channel A channel name to add
-        * @param timeout When the invite should expire (0 == never)
-        */
-       void InviteTo(const irc::string &channel, time_t timeout);
-
-       /** Removes a channel from a users invite list.
-        * This member function is called on successfully joining an invite only channel
-        * to which the user has previously been invited, to clear the invitation.
-        * @param channel The channel to remove the invite to
-        */
-       void RemoveInvite(const irc::string &channel);
-
-       void RemoveExpiredInvites();
-
        /** 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
         * this to their oper classes and checking the commands they can execute.
@@ -890,7 +778,7 @@ class CoreExport LocalUser : public User, public InviteBase
 class CoreExport 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);
@@ -899,9 +787,15 @@ class CoreExport RemoteUser : public User
 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->GetName();
+       }
+
+       FakeUser(const std::string& uid, const std::string& sname, const std::string& sdesc)
+               : User(uid, new Server(sname, sdesc), USERTYPE_SERVER)
        {
-               nick = srv;
+               nick = sname;
        }
 
        virtual CullResult cull();
@@ -926,42 +820,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(ModeHandler* mh)
 {
- 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)
+{
+       if (!moderef)
+               return false;
+       return IsModeSet(*moderef);
+}
 
-#endif
+inline void User::SetMode(ModeHandler* mh, bool value)
+{
+       modes[mh->GetId()] = value;
+}
index 2a49d8b80efe1c49230137a847a4838a049485be..fe044d0f2d07b7ef119606dc0d7fd8464e979473 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
         */
@@ -536,5 +525,3 @@ class CoreExport XLineManager
         */
        void InvokeStats(const std::string &type, int numeric, User* user, string_list &results);
 };
-
-#endif
index 4a759a24a163692ca19682cad0abb44ecc7ff11c..65e19773e0e29fe1c08d84b408a59954464f8750 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,19 +48,14 @@ run;
 exit 0;
 
 sub run() {
-       my $build = $ENV{BUILDPATH};
-       mkdir $build;
-       chdir $build or die "Could not open build directory: $!";
+       mkdir BUILDPATH;
+       chdir BUILDPATH or die "Could not open build directory: $!";
        unlink 'include';
-       symlink "$ENV{SOURCEPATH}/include", 'include';
+       symlink "${\SOURCEPATH}/include", 'include';
        mkdir $_ for qw/bin modules obj/;
-# BSD make has a horribly annoying bug resulting in an extra chdir of the make process
-# Create symlinks to work around it
-       symlink "../$_", "obj/$_" for qw/bin modules obj/;
 
-       $build = getcwd();
        open MAKE, '>real.mk' or die "Could not write real.mk: $!";
-       chdir "$ENV{SOURCEPATH}/src";
+       chdir "${\SOURCEPATH}/src";
 
        if ($ENV{PURE_STATIC}) {
                run_static();
@@ -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,43 +77,45 @@ 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;
 
@@ -116,11 +124,9 @@ bin/inspircd: $core_mk
 
 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;
        }
@@ -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;
@@ -231,20 +237,13 @@ sub dep_cpp($$$) {
 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\$(VERBOSE) \$\@ ${\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..f5bbedb
--- /dev/null
@@ -0,0 +1,91 @@
+#
+# 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;
+}
+
+package make::common;
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use Exporter              qw(import);
+use File::Spec::Functions qw(rel2abs);
+
+our @EXPORT = qw(get_cpu_count
+                 get_version
+                 module_installed);
+
+sub get_version {
+       state %version;
+       return %version if %version;
+
+       # Attempt to retrieve version information from src/version.sh
+       chomp(my $vf = `sh src/version.sh 2>/dev/null`);
+       if ($vf =~ /^InspIRCd-([0-9]+)\.([0-9]+)\.([0-9]+)(?:\+(\w+))?$/) {
+               %version = ( MAJOR => $1, MINOR => $2, PATCH => $3, LABEL => $4 );
+       }
+
+       # Attempt to retrieve missing version information from Git
+       chomp(my $gr = `git describe --tags 2>/dev/null`);
+       if ($gr =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\d+-g(\w+))?$/) {
+               $version{MAJOR} //= $1;
+               $version{MINOR} //= $2;
+               $version{PATCH} //= $3;
+               $version{LABEL} = $4 if defined $4;
+       }
+
+       # The user is using a stable release which does not have
+       # a label attached.
+       $version{LABEL} //= 'release';
+
+       # 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';
+
+       return %version;
+}
+
+sub module_installed($) {
+       my $module = shift;
+       eval("use $module;");
+       return !$@;
+}
+
+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;
+}
+
+1;
index 9b8e2d0e4b411f8c4174686d35565f5120168f94..e314e6d159305cfe0a6082072f576a04616874f8 100644 (file)
@@ -1,7 +1,7 @@
 #
 # InspIRCd -- Internet Relay Chat Daemon
 #
-#   Copyright (C) 2012 Peter Powell <petpow@saberuk.com>
+#   Copyright (C) 2012-2014 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 Cwd            qw(getcwd);
+use Exporter       qw(import);
+use File::Basename qw(basename);
+
+use make::common;
+use make::console;
 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";
-       }
-       else
-       {
-               $main::config{$flag} = "n";
+
+use constant CONFIGURE_CACHE_FILE    => '.configure.cache';
+use constant CONFIGURE_CACHE_VERSION => '1';
+
+our @EXPORT = qw(CONFIGURE_CACHE_FILE
+                 CONFIGURE_CACHE_VERSION
+                 cmd_clean
+                 cmd_help
+                 cmd_update
+                 run_test
+                 test_file
+                 test_header
+                 read_configure_cache
+                 write_configure_cache
+                 get_compiler_info
+                 find_compiler
+                 get_property
+                 parse_templates);
+
+sub __get_socketengines {
+       my @socketengines;
+       foreach (<src/socketengines/socketengine_*.cpp>) {
+               s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
+               push @socketengines, $1;
        }
-       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($$$) {
 
-sub getrevision {
-       if ($no_git)
-       {
-               return "0";
+       # These are actually hash references
+       my ($config, $compiler, $version) = @_;
+
+       # 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_CACHE_FILE} = CONFIGURE_CACHE_FILE;
+       $settings{SYSTEM_NAME} = lc $^O;
+       chomp($settings{SYSTEM_NAME_VERSION} = `uname -sr 2>/dev/null`);
+
+       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 >/dev/null 2>&1");
+       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]
+
+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.
+
+
+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_configure_cache();
+       my %compiler = get_compiler_info($config{CXX});
+       my %version = get_version();
+       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 >/dev/null 2>&1";
+       $status ||= system "./__test_$file >/dev/null 2>&1";
+       unlink "./__test_$file";
+       return !$status;
+}
 
-       if (!$silent)
-       {
-               print "Detecting modules ";
-       }
+sub test_header($$;$) {
+       my ($compiler, $header, $args) = @_;
+       $args //= '';
+       open(COMPILER, "| $compiler -E - $args >/dev/null 2>&1") or return 0;
+       print COMPILER "#include <$header>";
+       close(COMPILER);
+       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 read_configure_cache {
+       my %config;
+       open(CACHE, CONFIGURE_CACHE_FILE) or return %config;
+       while (my $line = <CACHE>) {
+               next if $line =~ /^\s*($|\#)/;
+               my ($key, $value) = ($line =~ /^(\S+)="(.*)"$/);
+               $config{$key} = $value;
        }
-       closedir(DIRHANDLE);
+       close(CACHE);
+       return %config;
+}
 
-       if (!$silent)
-       {
-               print "\nOk, $i modules.\n";
+sub write_configure_cache(%) {
+       print_format "Writing <|GREEN ${\CONFIGURE_CACHE_FILE}|> ...\n";
+       my %config = @_;
+       open(CACHE, '>', CONFIGURE_CACHE_FILE) or print_error "unable to write ${\CONFIGURE_CACHE_FILE}: $!";
+       while (my ($key, $value) = each %config) {
+               $value //= '';
+               say CACHE "$key=\"$value\"";
        }
+       close(CACHE);
 }
 
-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 $version = `$binary -v 2>&1`;
+       if ($version =~ /Apple\sLLVM\sversion\s(\d+\.\d+)/i) {
+               # Apple version their LLVM releases slightly differently to the mainline LLVM.
+               # See https://trac.macports.org/wiki/XcodeVersionInfo for more information.
+               return (NAME => 'AppleClang', VERSION => $1);
+       } elsif ($version =~ /clang\sversion\s(\d+\.\d+)/i) {
+               return (NAME => 'Clang', VERSION => $1);
+       } elsif ($version =~ /gcc\sversion\s(\d+\.\d+)/i) {
+               return (NAME => 'GCC', VERSION => $1);
+       } elsif ($version =~ /(?:icc|icpc)\sversion\s(\d+\.\d+).\d+\s\(gcc\sversion\s(\d+\.\d+).\d+/i) {
+               return (NAME => 'ICC', VERSION => $1);
        }
+       return (NAME => $binary, VERSION => '0.0');
 }
 
-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($$)
+sub get_property($$;$)
 {
-       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;
+       my ($file, $property, $default) = @_;
+       open(MODULE, $file) or return $default;
+       while (<MODULE>) {
+               if ($_ =~ /^\/\* \$(\S+): (.+) \*\/$/) {
+                       next unless $1 eq $property;
+                       close(MODULE);
+                       return translate_functions($2, $file);
+               }
+       }
+       close(MODULE);
+       return $default // '';
 }
 
-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";
-}
+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(TEMPLATE, $_) or print_error "unable to read $_: $!";
+               my (@lines, $mode, @platforms, %targets);
+
+               # First pass: parse template variables and directives.
+               while (my $line = <TEMPLATE>) {
+                       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 is_dir
-{
-       my ($path) = @_;
-       if (chdir($path))
-       {
-               chdir($main::this);
-               return 1;
-       }
-       else
-       {
-               # Just in case..
-               chdir($main::this);
-               return 0;
-       }
-}
+                       # 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') {
+                                       if ($2 =~ /(\w+)\s(.+)/) {
+                                               $targets{$1} = $2;
+                                       } else {
+                                               $targets{DEFAULT} = $2;
+                                       }
+                               } else {
+                                       print_warning "unknown template command '$1' in $_!";
+                                       push @lines, $line;
+                               }
+                               next;
+                       }
+                       push @lines, $line;
+               }
+               close(TEMPLATE);
 
-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
+               # Only proceed if this file should be templated on this platform.
+               if ($#platforms < 0 || grep { $_ eq $^O } @platforms) {
 
-EOH
-       exit(0);
+                       # Add a default target if the template has not defined one.
+                       unless (scalar keys %targets) {
+                               $targets{DEFAULT} = basename $_;
+                       }
+
+                       # Second pass: parse makefile junk and write files.
+                       while (my ($name, $target) = each %targets) {
+
+                               # TODO: when buildtool is done this mess can be removed completely.
+                               my @final_lines;
+                               foreach my $line (@lines) {
+
+                                       # Are we parsing a makefile and does this line match a statement?
+                                       if ($name =~ /(?:BSD|GNU)_MAKE/ && $line =~ /^\s*\@(\w+)(?:\s+(.+))?$/) {
+                                               my @tokens = split /\s/, $2 if defined $2;
+                                               if ($1 eq 'DO_EXPORT' && defined $2) {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               foreach my $variable (@tokens) {
+                                                                       push @final_lines, "MAKEENV += $variable='\${$variable}'";
+                                                               }
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "export $2";
+                                                       }
+                                               } elsif ($1 eq 'ELSE') {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".else";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "else";
+                                                       }
+                                               } elsif ($1 eq 'ENDIF') {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".endif";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "endif";
+                                                       }
+                                               } elsif ($1 eq 'ELSIFEQ' && defined $2) {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".elif $tokens[0] == $tokens[1]";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "else ifeq ($tokens[0], $tokens[1])";
+                                                       }
+                                               } elsif ($1 eq 'IFDEF' && defined $2) {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".if defined($2)";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "ifdef $2";
+                                                       }
+                                               } elsif ($1 eq 'IFEQ' && defined $2) {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".if $tokens[0] == $tokens[1]";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "ifeq ($tokens[0],$tokens[1])";
+                                                       }
+                                               } elsif ($1 eq 'IFNEQ' && defined $2) {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".if $tokens[0] != $tokens[1]";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "ifneq ($tokens[0],$tokens[1])";
+                                                       }
+                                               } elsif ($1 eq 'IFNDEF' && defined $2) {
+                                                       if ($name eq 'BSD_MAKE') {
+                                                               push @final_lines, ".if !defined($2)";
+                                                       } elsif ($name eq 'GNU_MAKE') {
+                                                               push @final_lines, "ifndef $2";
+                                                       }
+                                               } elsif ($1 eq 'TARGET' && defined $2) {
+                                                       if ($tokens[0] eq $name) {
+                                                               push @final_lines, substr($2, length($tokens[0]) + 1);
+                                                       }
+                                               } elsif ($1 !~ /[A-Z]/) {
+                                                       # HACK: silently ignore if lower case as these are probably make commands.
+                                                       push @final_lines, $line;
+                                               } else {
+                                                       print_warning "unknown template command '$1' in $_!";
+                                                       push @final_lines, $line;
+                                               }
+                                               next;
+                                       }
+
+                                       push @final_lines, $line;
+                               }
+
+                               # Write the template file.
+                               print_format "Writing <|GREEN $target|> ...\n";
+                               open(TARGET, '>', $target) or print_error "unable to write $_: $!";
+                               foreach (@final_lines) {
+                                       say TARGET $_;
+                               }
+                               close(TARGET);
+
+                               # 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..4e7b32d
--- /dev/null
@@ -0,0 +1,114 @@
+#
+# 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/>.
+#
+
+
+package make::console;
+
+BEGIN {
+       require 5.10.0;
+}
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use File::Path            qw(mkpath);
+use File::Spec::Functions qw(rel2abs);
+use Exporter              qw(import);
+
+our @EXPORT = qw(print_format
+                 print_error
+                 print_warning
+                 prompt_bool
+                 prompt_dir
+                 prompt_string);
+
+my %FORMAT_CODES = (
+       DEFAULT => "\e[0m",
+       BOLD    => "\e[1m",
+
+       RED    => "\e[1;31m",
+       GREEN  => "\e[1;32m",
+       YELLOW => "\e[1;33m",
+       BLUE   => "\e[1;34m"
+);
+
+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($) {
+       my $message = shift;
+       print_format "<|RED Error:|> $message\n", *STDERR;
+       exit 1;
+}
+
+sub print_warning($) {
+       my $message = shift;
+       print_format "<|YELLOW Warning:|> $message\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) {
+                       my $mkpath = eval {
+                               mkpath($answer, 0, 0750);
+                               return 1;
+                       };
+                       unless (defined $mkpath) {
+                               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;
+}
+
+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/config.h b/make/template/config.h
new file mode 100644 (file)
index 0000000..513c550
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@"
+#define INSPIRCD_REVISION "@VERSION_LABEL@"
+#define INSPIRCD_SYSTEM   "@SYSTEM_NAME_VERSION@"
+
+#define INSPIRCD_CONFIG_PATH "@CONFIG_DIR@"
+#define INSPIRCD_DATA_PATH   "@DATA_DIR@"
+#define INSPIRCD_LOG_PATH    "@LOG_DIR@"
+#define INSPIRCD_MODULE_PATH "@MODULE_DIR@"
+
+#define INSPIRCD_SOCKETENGINE_NAME "@SOCKETENGINE@"
+
+#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 7cd83a8e15fdd0615e020592e102277f5117a9f6..138de29a9cfdb4ef6eb060bff65fe11bbfca4007 100644 (file)
@@ -1,3 +1,4 @@
+%mode 0750
 #!/usr/bin/env perl
 
 #
@@ -29,17 +30,35 @@ use strict;
 use POSIX;
 use Fcntl;
 
+# From http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
+use constant {
+    STATUS_EXIT_SUCCESS => 0,
+    STATUS_EXIT_DEAD_WITH_PIDFILE => 1,
+    STATUS_EXIT_DEAD_WITH_LOCKFILE => 2,
+    STATUS_EXIT_NOT_RUNNING => 3,
+    STATUS_EXIT_UNKNOWN => 4,
+
+    GENERIC_EXIT_SUCCESS => 0,
+    GENERIC_EXIT_UNSPECIFIED => 1,
+    GENERIC_EXIT_INVALID_ARGUMENTS => 2,
+    GENERIC_EXIT_UNIMPLEMENTED => 3,
+    GENERIC_EXIT_INSUFFICIENT_PRIVILEGE => 4,
+    GENERIC_EXIT_NOT_INSTALLED => 5,
+    GENERIC_EXIT_NOT_CONFIGURED => 6,
+    GENERIC_EXIT_NOT_RUNNING => 7
+};
+
 my $basepath   =       "@BASE_DIR@";
 my $confpath   =       "@CONFIG_DIR@/";
 my $binpath    =       "@BINARY_DIR@";
 my $runpath    =       "@BASE_DIR@";
 my $datadir    =       "@DATA_DIR@";
 my $valgrindlogpath    =       "$basepath/valgrindlogs";
-my $executable =       "@EXECUTABLE@";
-my $version    =       "@VERSION@";
+my $executable =       "inspircd";
+my $version    =       "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@";
 my $uid = "@UID@";
 
-if ($< == 0 || $> == 0) {
+if (!("--runasroot" ~~ @ARGV) && ($< == 0 || $> == 0)) {
        if ($uid !~ /^\d+$/) {
                # Named UID, look it up
                $uid = getpwnam $uid;
@@ -87,12 +106,11 @@ if (!defined($sub))
 {
        print STDERR "Invalid command or none given.\n";
        cmd_help();
-       exit 1;
+       exit GENERIC_EXIT_UNIMPLEMENTED;
 }
 else
 {
-       $sub->(@ARGV);
-       exit 0;
+       exit $sub->(@ARGV); # Error code passed through return value
 }
 
 sub cmd_help()
@@ -105,7 +123,7 @@ sub cmd_help()
        $_ =~ s/_/-/g foreach (@cmds, @devs);
        print STDERR "Usage: ./inspircd (" . join("|", @cmds) . ")\n";
        print STDERR "Developer arguments: (" . join("|", @devs) . ")\n";
-       exit 0;
+       exit GENERIC_EXIT_SUCCESS;
 }
 
 sub cmd_status()
@@ -113,10 +131,10 @@ sub cmd_status()
        if (getstatus() == 1) {
                my $pid = getprocessid();
                print "InspIRCd is running (PID: $pid)\n";
-               exit();
+               exit STATUS_EXIT_SUCCESS;
        } else {
                print "InspIRCd is not running. (Or PID File not found)\n";
-               exit();
+               exit STATUS_EXIT_NOT_RUNNING;
        }
 }
 
@@ -126,23 +144,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 +174,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");
@@ -224,7 +242,7 @@ sub dev_valdebug(@)
        # If we are still alive here.. Try starting the IRCd..
        # May want to do something with these args at some point: --suppressions=.inspircd.sup --gen-suppressions=yes
        # Could be useful when we want to stop it complaining about things we're sure aren't issues.
-       exec qw(valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
+       exec qw(valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=30), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
        die "Failed to start valgrind: $!\n";
 }
 
@@ -258,7 +276,7 @@ sub dev_valdebug_unattended(@)
                sysopen STDERR, "$valgrindlogpath/valdebug.$suffix", O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND, 0666 or die "Can't open $valgrindlogpath/valdebug.$suffix: $!\n";
        # May want to do something with these args at some point: --suppressions=.inspircd.sup --gen-suppressions=yes
        # Could be useful when we want to stop it complaining about things we're sure aren't issues.
-               exec qw(valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=15 --track-fds=yes),
+               exec qw(valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=30 --track-fds=yes),
                        "--suppressions=$binpath/valgrind.sup", qw(--gen-suppressions=all),
                        qw(--leak-resolution=med --time-stamp=yes --log-fd=2 --),
                        "$binpath/$executable", qw(--nofork --debug --nolog), @_;
@@ -283,13 +301,13 @@ sub dev_screenvaldebug(@)
        # If we are still alive here.. Try starting the IRCd..
        print "Starting InspIRCd in `screen`, type `screen -r` when the ircd crashes to view the valgrind and gdb output and get a backtrace.\n";
        print "Once you're inside the screen session press ^C + d to re-detach from the session\n";
-       exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
+       exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=30), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
        die "Failed to start screen: $!\n";
 }
 
 sub cmd_stop()
 {
-       if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return 0; }
+       if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return GENERIC_EXIT_SUCCESS; }
        # Get to here, we have something to kill.
        my $pid = getprocessid();
        print "Stopping InspIRCd (pid: $pid)...\n";
@@ -299,12 +317,12 @@ sub cmd_stop()
                sleep 1;
                if (getstatus() == 0) {
                        print "InspIRCd Stopped.\n";
-                       return;
+                       return GENERIC_EXIT_SUCCESS;
                }
        }
        print "InspIRCd not dying quietly -- forcing kill\n";
        kill KILL => $pid;
-       return 0;
+       return GENERIC_EXIT_SUCCESS;
 }
 
 ###
@@ -415,7 +433,7 @@ sub checkvalgrind
        unless(`valgrind --version`)
        {
                print "Couldn't start valgrind: $!\n";
-               exit;
+               exit GENERIC_EXIT_UNSPECIFIED;
        }
 }
 
@@ -424,7 +442,7 @@ sub checkgdb
        unless(`gdb --version`)
        {
                print "Couldn't start gdb: $!\n";
-               exit;
+               exit GENERIC_EXIT_UNSPECIFIED;
        }
 }
 
@@ -433,6 +451,6 @@ sub checkscreen
        unless(`screen --version`)
        {
                print "Couldn't start screen: $!\n";
-               exit;
+               exit GENERIC_EXIT_UNSPECIFIED;
        }
 }
diff --git a/make/template/inspircd-genssl.1 b/make/template/inspircd-genssl.1
new file mode 100644 (file)
index 0000000..4be5f39
--- /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_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@" "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..463db5c
--- /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_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@" "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..e5f28a6
--- /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=@BASE_DIR@/inspircd rehash
+ExecStart=@BASE_DIR@/inspircd start
+ExecStop=@BASE_DIR@/inspircd stop
+PIDFile=@DATA_DIR@/inspircd.pid
+Restart=on-failure
+Type=forking
+
+[Install]
+WantedBy=multi-user.target
index fa2375ac139ba3ed34b3997e8b74cefbb4abc132..cc201a12654c726207af7680e5d974de9df45604 100644 (file)
@@ -1,3 +1,5 @@
+%target BSD_MAKE BSDmakefile
+%target GNU_MAKE GNUmakefile
 #
 # InspIRCd -- Internet Relay Chat Daemon
 #
 #
 
 
-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@"
 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
+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
@@ -71,19 +78,11 @@ INSTMODE_LIB = 0644
   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
   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
@@ -91,50 +90,48 @@ INSTMODE_LIB = 0644
 
 DBGOK=0
 @IFEQ $(D) 0
-  CXXFLAGS += -O2
-@IFEQ $(CC) g++
-    CXXFLAGS += -g1
+  CORECXXFLAGS += -fno-rtti -O2
+@IFEQ $(COMPILER) GCC
+    CORECXXFLAGS += -g1
 @ENDIF
   HEADER = std-header
   DBGOK=1
 @ENDIF
 @IFEQ $(D) 1
-  CXXFLAGS += -O0 -g3 -Werror
+  CORECXXFLAGS += -O0 -g3 -Werror -DINSPIRCD_ENABLE_RTTI
   HEADER = debug-header
   DBGOK=1
 @ENDIF
 @IFEQ $(D) 2
-  CXXFLAGS += -O2 -g3
+  CORECXXFLAGS += -fno-rtti -O2 -g3
   HEADER = debug-header
   DBGOK=1
 @ENDIF
 FOOTER = finishmessage
 
-CXXFLAGS += -Iinclude
+@TARGET GNU_MAKE MAKEFLAGS += --no-print-directory
 
-@GNU_ONLY MAKEFLAGS += --no-print-directory
-
-@GNU_ONLY SOURCEPATH = $(shell /bin/pwd)
-@BSD_ONLY SOURCEPATH != /bin/pwd
+@TARGET GNU_MAKE SOURCEPATH = $(shell /bin/pwd)
+@TARGET BSD_MAKE SOURCEPATH != /bin/pwd
 
 @IFDEF V
-  RUNCC = $(CC)
-  RUNLD = $(CC)
   VERBOSE = -v
 @ELSE
-  @GNU_ONLY MAKEFLAGS += --silent
-  @BSD_ONLY MAKE += -s
-  RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
-  RUNLD = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
+  @TARGET GNU_MAKE MAKEFLAGS += --silent
+  @TARGET BSD_MAKE MAKE += -s
   VERBOSE =
 @ENDIF
 
 @IFDEF PURE_STATIC
-  CXXFLAGS += -DPURE_STATIC
+  CORECXXFLAGS += -DPURE_STATIC
 @ENDIF
 
-@DO_EXPORT RUNCC RUNLD CXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
-@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC SPLIT_CC
+# Add the users CXXFLAGS to the base ones to allow them to override
+# things like -Wfatal-errors if they wish to.
+CORECXXFLAGS += $(CXXFLAGS)
+
+@DO_EXPORT CXX CORECXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
+@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC
 
 # Default target
 TARGET = all
@@ -142,8 +139,8 @@ TARGET = all
 @IFDEF M
     HEADER = mod-header
     FOOTER = mod-footer
-    @BSD_ONLY TARGET = modules/${M:S/.so$//}.so
-    @GNU_ONLY TARGET = modules/$(M:.so=).so
+    @TARGET BSD_MAKE TARGET = modules/${M:S/.so$//}.so
+    @TARGET GNU_MAKE TARGET = modules/$(M:.so=).so
 @ENDIF
 
 @IFDEF T
@@ -226,14 +223,25 @@ install: target
        @-$(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) $(MANPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MODPATH)
        [ $(BUILDPATH)/bin/ -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) $(BUILDPATH)/bin/inspircd $(BINPATH)
 @IFNDEF PURE_STATIC
        [ $(BUILDPATH)/modules/ -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) $(BUILDPATH)/modules/*.so $(MODPATH)
 @ENDIF
-       -$(INSTALL) -m $(INSTMODE_BIN) @STARTSCRIPT@ $(BASE) 2>/dev/null
-       -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 2>/dev/null
+       -$(INSTALL) -m $(INSTMODE_BIN) inspircd $(BASE) 2>/dev/null
+       -$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(BASE)/.gdbargs 2>/dev/null
+@IFEQ $(SYSTEM) darwin
+       -$(INSTALL) -m $(INSTMODE_BIN) org.inspircd.plist $(BASE) 2>/dev/null
+@ENDIF
+@IFEQ $(SYSTEM) linux
+       -$(INSTALL) -m $(INSTMODE_LIB) inspircd.service $(BASE) 2>/dev/null
+@ENDIF
+       -$(INSTALL) -m $(INSTMODE_LIB) inspircd.1 $(MANPATH) 2>/dev/null
+       -$(INSTALL) -m $(INSTMODE_LIB) 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) *.pem $(CONPATH) 2>/dev/null
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
        @echo ""
@@ -250,11 +258,9 @@ install: target
        @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 BSDmakefile: make/template/main.mk src/version.sh configure @CONFIGURE_CACHE_FILE@
+       ./configure --update
+@TARGET BSD_MAKE .MAKEFILEDEPS: BSDmakefile
 
 clean:
        @echo Cleaning...
@@ -267,20 +273,23 @@ clean:
 deinstall:
        -rm -f $(BINPATH)/inspircd
        -rm -rf $(CONPATH)/examples
+       -rm -f $(MANPATH)/inspircd.1
+       -rm -f $(MANPATH)/inspircd-genssl.1
        -rm -f $(MODPATH)/*.so
        -rm -f $(BASE)/.gdbargs
+       -rm -f $(BASE)/inspircd.service
        -rm -f $(BASE)/org.inspircd.plist
 
-squeakyclean: distclean
-
 configureclean:
-       rm -f .config.cache
        rm -f BSDmakefile
        rm -f GNUmakefile
-       rm -f include/inspircd_config.h
-       rm -f include/inspircd_version.h
+       rm -f include/config.h
        rm -f inspircd
+       rm -f inspircd.1
+       rm -f inspircd-genssl.1
+       -rm -f inspircd.service
        -rm -f org.inspircd.plist
+       -rm -f @CONFIGURE_CACHE_FILE@
 
 distclean: clean configureclean
        -rm -rf $(SOURCEPATH)/run
@@ -316,4 +325,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..91d8cd4
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#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..edf08b8
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <iostream>
+#if defined _LIBCPP_VERSION
+# include <type_traits>
+# include <unordered_map>
+#else
+# 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/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..aba14a0bc461b5dd338d59ae91cf31e98abee6c0 100755 (executable)
 #
 
 
+BEGIN {
+       push @INC, $ENV{SOURCEPATH};
+       require 5.10.0;
+}
+
 use strict;
-use warnings;
-BEGIN { push @INC, $ENV{SOURCEPATH}; }
+use warnings FATAL => qw(all);
+
+use File::Spec::Functions qw(abs2rel);
+
 use make::configure;
+use make::console;
 
 chdir $ENV{BUILDPATH};
 
@@ -30,21 +38,7 @@ 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
+our %config = read_configure_cache();
 
 if ($type eq 'gen-ld') {
        do_static_find(@ARGV);
@@ -65,10 +59,19 @@ if ($type eq 'gen-ld') {
 }
 exit 1;
 
+sub message($$$) {
+       my ($type, $file, $command) = @_;
+       if ($verbose) {
+               print "$command\n";
+       } else {
+               print_format "\t<|GREEN $type:|>\t\t$file\n";
+       }
+}
+
 sub do_static_find {
        my @flags;
        for my $file (@ARGV) {
-               push @flags, getlinkerflags($file);
+               push @flags, get_property($file, 'LinkerFlags');
        }
        open F, '>', $out;
        print F join ' ', @flags;
@@ -77,7 +80,7 @@ sub do_static_find {
 }
 
 sub do_static_link {
-       my $execstr = "$ENV{RUNLD} -o $out $ENV{CORELDFLAGS}";
+       my $execstr = "$ENV{CXX} -o $out $ENV{CORELDFLAGS}";
        for (@ARGV) {
                if (/\.cmd$/) {
                        open F, '<', $_;
@@ -90,19 +93,23 @@ sub do_static_link {
                }
        }
        $execstr .= ' '.$ENV{LDLIBS};
-       print "$execstr\n" if $verbose;
+       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 .= get_property($file, 'LinkerFlags') . ' ';
+       }
+       my $execstr = "$ENV{CXX} -o $out $ENV{PICLDFLAGS} $link_flags @_";
+       message 'LINK', $out, $execstr;
        exec $execstr;
 }
 
@@ -111,27 +118,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_property($file, 'CompileFlags');
 
-               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 = get_property($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;
 }
index baba584ad240754030525f0d60fcaae7ec975302..f2d645f3312cdd37bce35f601108fbb52474dddc 100644 (file)
 #
 
 
-package make::utilities;
+BEGIN {
+       require 5.8.0;
+}
 
-require 5.8.0;
+package make::utilities;
 
 use strict;
 use warnings FATAL => qw(all);
 
 use Exporter 'import';
-use POSIX;
+use Fcntl;
+use File::Path;
 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.
+use POSIX;
 
-# \e[1;32msrc/Makefile\e[0m
+our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
 
 my %already_added = ();
-my $if_skip_lines = 0;
 
 sub promptstring($$$$$)
 {
@@ -94,34 +91,10 @@ sub make_rpath($;$)
        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`;
@@ -194,8 +167,6 @@ 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`;
@@ -227,17 +198,6 @@ 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`;
@@ -310,25 +270,6 @@ sub translate_functions($$)
                $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;
index af5bf113caa6f75b1d16b43bfbb5c8b74d193bb0..e859f683be0e043f195937122e991fb54a69a897 100755 (executable)
@@ -22,7 +22,7 @@
 use strict;
 use warnings FATAL => qw(all);
 
-use make::configure;
+use make::common;
 
 BEGIN {
        unless (module_installed("LWP::Simple")) {
@@ -31,12 +31,12 @@ BEGIN {
        unless (module_installed("Crypt::SSLeay") || module_installed("IO::Socket::SSL")) {
                die "Your system is missing the Crypt::SSLeay or IO::Socket::SSL Perl modules!";
        }
+
 }
 
+use File::Basename;
 use LWP::Simple;
 
-our @modlist;
-
 my %installed;
 # $installed{name} = $version
 
@@ -131,8 +131,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;
@@ -156,9 +154,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;
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..99da0070810b1f6b4463977e58c078c436b651d4 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)
 {
-       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
-       {
-               custom_mode_params[mode] = parameter;
-               modes[mode-65] = true;
-       }
-}
-
-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 "";
-}
-
-std::string Channel::GetModeParameter(ModeHandler* mode)
-{
-       CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
-       if (n != custom_mode_params.end())
-               return n->second;
-       return "";
-}
-
-int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
-{
-       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;
-}
-
-long Channel::GetUserCounter()
-{
-       return userlist.size();
+       FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
 }
 
 Membership* Channel::AddUser(User* user)
 {
-       Membership* memb = new Membership(user, this);
-       userlist[user] = memb;
+       std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
+       if (!ret.second)
+               return NULL;
+
+       Membership* memb = new(ret.first->second) Membership(user, this);
        return memb;
 }
 
 void Channel::DelUser(User* user)
 {
-       UserMembIter a = userlist.find(user);
+       MemberMap::iterator it = userlist.find(user);
+       if (it != userlist.end())
+               DelUser(it);
+}
 
-       if (a != userlist.end())
-       {
-               a->second->cull();
-               delete a->second;
-               userlist.erase(a);
-       }
+void Channel::CheckDestroy()
+{
+       if (!userlist.empty())
+               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);
-               }
+       ModResult res;
+       FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
+       if (res == MOD_RES_DENY)
+               return;
 
-               ClearInvites();
-               ServerInstance->GlobalCulls.AddItem(this);
-       }
+       // 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;
+
+       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,6 +127,9 @@ void Channel::SetDefaultModes()
                ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
                if (mode)
                {
+                       if (mode->IsPrefixMode())
+                               continue;
+
                        if (mode->GetNumParams(true))
                        {
                                list.GetToken(parameter);
@@ -223,204 +152,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)
+                               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, "%s :You are on too many channels", cname.c_str());
+                       return NULL;
                }
        }
 
-       std::string cname;
-       cname.assign(std::string(cn), 0, ServerInstance->Config->Limits.ChanMax);
-       Ptr = ServerInstance->FindChan(cname);
-       bool created_by_local = false;
+       // Crop channel name if it's too long
+       if (cname.length() > ServerInstance->Config->Limits.ChanMax)
+               cname.resize(ServerInstance->Config->Limits.ChanMax);
 
-       if (!Ptr)
+       Channel* chan = ServerInstance->FindChan(cname);
+       bool created_by_local = (chan == NULL); // Flag that will be passed to modules in the OnUserJoin() hook later
+       std::string privs; // Prefix mode(letter)s to give to the joining user
+
+       if (!chan)
        {
-               /*
-                * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
-                */
-               if (!IS_LOCAL(user))
-               {
-                       if (!TS)
-                               ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
-               }
-               else
-               {
-                       privs = "o";
-                       created_by_local = true;
-               }
+               privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' '));
 
-               if (IS_LOCAL(user) && override == false)
+               if (override == false)
                {
+                       // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created
                        ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname.c_str(), privs, key ? key : ""));
+                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
                        if (MOD_RESULT == MOD_RES_DENY)
-                               return NULL;
+                               return NULL; // A module wasn't happy with the join, abort
                }
 
-               Ptr = new Channel(cname, TS);
+               chan = new Channel(cname, ServerInstance->Time());
+               // Set the default modes on the channel (<options:defaultmodes>)
+               chan->SetDefaultModes();
        }
        else
        {
                /* Already on the channel */
-               if (Ptr->HasUser(user))
+               if (chan->HasUser(user))
                        return NULL;
 
-               /*
-                * remote users are allowed us to bypass channel modes
-                * and bans (used by servers)
-                */
-               if (IS_LOCAL(user) && override == false)
+               if (override == false)
                {
                        ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname.c_str(), privs, key ? key : ""));
+                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key));
+
+                       // A module explicitly denied the join and (hopefully) generated a message
+                       // describing the situation, so we may stop here without sending anything
                        if (MOD_RESULT == MOD_RES_DENY)
-                       {
                                return NULL;
-                       }
-                       else if (MOD_RESULT == MOD_RES_PASSTHRU)
-                       {
-                               std::string ckey = Ptr->GetModeParameter('k');
-                               bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
-                               bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
 
+                       // If 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, "%s :Cannot join channel (Incorrect channel key)", chan->name.c_str());
                                                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, "%s :Cannot join channel (Invite only)", chan->name.c_str());
                                                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, "%s :Cannot join channel (Channel is full)", chan->name.c_str());
                                                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, "%s :Cannot join channel (You're banned)", chan->name.c_str());
                                        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 = Ptr->AddUser(user);
-       user->chans.insert(Ptr);
+       Membership* memb = this->AddUser(user);
+       if (!memb)
+               return NULL; // Already on the channel
 
-       for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
+       user->chans.push_front(memb);
+
+       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 +340,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,12 +368,11 @@ 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);
+               std::string suffix(mask, at + 1);
                if (InspIRCd::Match(user->host, suffix, NULL) ||
                        InspIRCd::Match(user->dhost, suffix, NULL) ||
                        InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
@@ -474,12 +387,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 +402,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];
+       const std::string message = ":" + user->GetFullHost() + " " + text;
 
-       if (!user)
-               return;
-
-       snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
-       std::string out = tb;
-
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+       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];
+       const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
 
-       snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
-       std::string out = tb;
-
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+       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 +479,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 +503,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 +527,57 @@ 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()
+const char* Channel::ChanModes(bool showkey)
 {
-       int count = 0;
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
-       {
-               if (!i->first->quitting && !i->first->IsModeSet('i'))
-                       count++;
-       }
-
-       return count;
-}
+       static std::string scratch;
+       std::string sparam;
 
-char* Channel::ChanModes(bool showkey)
-{
-       static char scratch[MAXBUF];
-       static char sparam[MAXBUF];
-       char* offset = scratch;
-       std::string extparam;
-
-       *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;
-}
-
-/* compile a userlist of a channel into a string, each nick seperated by
- * spaces and op, voice etc status shown as @ and +, and send it to 'user'
- */
-void Channel::UserList(User *user)
-{
-       if (!IS_LOCAL(user))
-               return;
-
-       bool has_privs = user->HasPrivPermission("channels/auspex");
-
-       if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
-       {
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
-               return;
-       }
-
-       std::string list = user->nick;
-       list.push_back(' ');
-       list.push_back(this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=');
-       list.push_back(' ');
-       list.append(this->name).append(" :");
-       std::string::size_type pos = list.size();
-
-       bool has_one = false;
-
-       /* Improvement by Brain - this doesnt change in value, so why was it inside
-        * the loop?
-        */
-       bool has_user = this->HasUser(user);
-
-       const size_t maxlen = MAXBUF - 10 - ServerInstance->Config->ServerName.size();
-       std::string prefixlist;
-       std::string nick;
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
-       {
-               if (i->first->quitting)
-                       continue;
-               if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
-               {
-                       /*
-                        * user is +i, and source not on the channel, does not show
-                        * nick in NAMES list
-                        */
-                       continue;
-               }
-
-               prefixlist = this->GetPrefixChar(i->first);
-               nick = i->first->nick;
-
-               FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
-
-               /* Nick was nuked, a module wants us to skip it */
-               if (nick.empty())
-                       continue;
-
-               if (list.size() + prefixlist.length() + nick.length() + 1 > maxlen)
-               {
-                       /* list overflowed into multiple numerics */
-                       user->WriteNumeric(RPL_NAMREPLY, list);
-
-                       // Erase all nicks, keep the constant part
-                       list.erase(pos);
-                       has_one = false;
-               }
-
-               list.append(prefixlist).append(nick).push_back(' ');
-
-               has_one = true;
-       }
-
-       /* if whats left in the list isnt empty, send it */
-       if (has_one)
-       {
-               user->WriteNumeric(RPL_NAMREPLY, list);
-       }
-
-       user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
-}
-
-long Channel::GetMaxBans()
-{
-       /* Return the cached value if there is one */
-       if (this->maxbans)
-               return this->maxbans;
-
-       /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
-       for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
-       {
-               if (InspIRCd::Match(this->name, n->first, NULL))
-               {
-                       this->maxbans = n->second;
-                       return n->second;
-               }
-       }
-
-       /* Screw it, just return the default of 64 */
-       this->maxbans = 64;
-       return this->maxbans;
-}
-
-void Channel::ResetMaxBans()
-{
-       this->maxbans = 0;
+       scratch += sparam;
+       return scratch.c_str();
 }
 
 /* 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,28 +589,23 @@ 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)
+const char* Membership::GetAllPrefixChars() const
 {
        static char prefix[64];
        int ctr = 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++)
-               {
-                       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())
+                       prefix[ctr++] = mh->GetPrefix();
        }
        prefix[ctr] = 0;
 
@@ -929,130 +614,28 @@ const char* Channel::GetAllPrefixChars(User* user)
 
 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..c123353b0a96b94da8c6ded03762afdc7c92be3e 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<irc::string> 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.c_str()).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, "%s :Unknown command",command.c_str());
+                       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,141 @@ 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, ":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, "%s :Unknown command", command.c_str());
                }
                else
                {
-                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
-                                                                               user->nick.c_str(), command.c_str());
+                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str());
                }
 
                ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
                                command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
-               return do_more;
+               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, "%s :Not enough parameters.", command.c_str());
+               if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
+                       user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str());
+               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, "%s :You have not registered",command.c_str());
        }
        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",
+       ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I :%s %s",
                user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
-       return ProcessCommand(user,buffer);
+       ProcessCommand(user,buffer);
 }
 
 bool CommandParser::AddCommand(Command *f)
@@ -406,88 +410,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 d805a1f6591df580feb874adf6e82b4990747f12..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::Match(u->second->MakeHost(), mask, ascii_case_insensitive_map)) ||
-                   (InspIRCd::Match(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::Match(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 99ac39d..0000000
+++ /dev/null
@@ -1,156 +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
-                       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 (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 2c420d1..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 /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());
-
-       /* Work around mIRC suckyness. YOU SUCK, KHALED! */
-       if (parameters.size() == 1)
-       {
-               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 1a5e7e1..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())
-       {
-               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 bdbcfed..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 execv() 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
-
-               execv(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 d547635..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) { 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 90c26a9..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd.  InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WHO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWho : public Command
-{
-       bool CanView(Channel* chan, User* user);
-       bool opt_viewopersonly;
-       bool opt_showrealhost;
-       bool opt_realname;
-       bool opt_mode;
-       bool opt_ident;
-       bool opt_metadata;
-       bool opt_port;
-       bool opt_away;
-       bool opt_local;
-       bool opt_far;
-       bool opt_time;
-
- public:
-       /** Constructor for who.
-        */
-       CommandWho ( Module* parent) : Command(parent,"WHO", 1) {
-               syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [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 *u)
-{
-       UCListIter i = u->chans.begin();
-       while (i != u->chans.end())
-       {
-               Channel* c = *i++;
-               if (!c->IsModeSet('s'))
-                       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(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 94192a71bb9166c2e8bda7124793f96c260fbe91..3be1ac9c53d39ce44f0a79dcd6d81defa55735f9 100644 (file)
@@ -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')
@@ -183,7 +203,10 @@ struct Parser
                std::set<std::string> seen;
                tag = ConfigTag::create(name, current.filename, current.line, items);
 
-               while (kv(items, seen));
+               while (kv(items, seen))
+               {
+                       // Do nothing here (silences a GCC warning).
+               }
 
                if (name == mandatory_tag)
                {
@@ -211,7 +234,7 @@ struct Parser
                }
                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 +246,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 +320,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 +331,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 +343,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,49 +364,20 @@ 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)
+bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec)
 {
-       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)");
-               }
-       }
+       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(fopen(name.c_str(), "r"));
+       FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
        if (!file)
-               throw CoreException("Could not read \"" + name + "\" for include");
+               throw CoreException("Could not read \"" + path + "\" 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)
-{
-       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)");
-               }
-       }
-
-       /* It's not already included, add it to the list of files we've loaded */
-
-       FileWrapper file(popen(name.c_str(), "r"), true);
-       if (!file)
-               throw CoreException("Could not open executable \"" + name + "\" 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;
@@ -390,17 +385,6 @@ bool ParseStack::ParseExec(const std::string& name, int flags, const std::string
 
 bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
 {
-#ifdef __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wunknown-pragmas"
-# pragma clang diagnostic ignored "-Wundefined-bool-conversion"
-#endif
-       // TODO: this is undefined behaviour but changing the API is too risky for 2.0.
-       if (!this)
-               return false;
-#ifdef __clang__
-# pragma clang diagnostic pop
-#endif
        for(std::vector<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
        {
                if(j->first != key)
@@ -408,7 +392,7 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo
                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')
@@ -426,7 +410,7 @@ std::string ConfigTag::getString(const std::string& key, const std::string& 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))
@@ -440,18 +424,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;
@@ -471,7 +478,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;
 }
index bcee938d55208e1aefc3dcc5d0db93cf81820175..2c31fa0ae97f5e67774d1fca6f226502eaa11056 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
+
+ServerLimits::ServerLimits(ConfigTag* tag)
+       : NickMax(tag->getInt("maxnick", 32))
+       , ChanMax(tag->getInt("maxchan", 64))
+       , MaxModes(tag->getInt("maxmodes", 20))
+       , IdentMax(tag->getInt("maxident", 11))
+       , MaxQuit(tag->getInt("maxquit", 255))
+       , MaxTopic(tag->getInt("maxtopic", 307))
+       , MaxKick(tag->getInt("maxkick", 255))
+       , MaxGecos(tag->getInt("maxgecos", 128))
+       , MaxAway(tag->getInt("maxaway", 200))
+       , MaxLine(tag->getInt("maxline", 512))
+       , MaxHost(tag->getInt("maxhost", 64))
+{
+}
+
+static ConfigTag* CreateEmptyTag()
+{
+       std::vector<KeyVal>* items;
+       return ConfigTag::create("empty", "<auto>", 0, items);
+}
 
 ServerConfig::ServerConfig()
-       : NoSnoticeStack(false)
+       : EmptyTag(CreateEmptyTag())
+       , Limits(EmptyTag)
+       , NoSnoticeStack(false)
 {
-       WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
-       RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false;
-       WildcardIPv6 = CycleHosts = InvBypassModes = true;
+       RawLog = HideBans = HideSplits = false;
+       WildcardIPv6 = 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()
@@ -59,60 +72,6 @@ ServerConfig::~ServerConfig()
        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)
-{
-       for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
-               user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
-}
-
-template<typename T, typename V>
-static void range(T& value, V min, V max, V def, const char* msg)
-{
-       if (value >= (T)min && value <= (T)max)
-               return;
-       ServerInstance->Logs->Log("CONFIG", DEFAULT,
-               "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
-               msg, (long)value, (long)min, (long)max, (long)def);
-       value = def;
-}
-
-
-static void ValidIP(const std::string& ip, const std::string& key)
-{
-       irc::sockets::sockaddrs dummy;
-       if (!irc::sockets::aptosa(ip, 0, dummy))
-               throw CoreException("The value of "+key+" is not an IP address");
-}
-
 static void ValidHost(const std::string& p, const std::string& msg)
 {
        int num_dots = 0;
@@ -139,79 +98,20 @@ bool ServerConfig::ApplyDisabledCommands(const std::string& data)
        std::string thiscmd;
 
        /* Enable everything first */
-       for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
+       const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+       for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
                x->second->Disable(false);
 
        /* Now disable all the ones which the user wants disabled */
        while (dcmds >> thiscmd)
        {
-               Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
-               if (cm != ServerInstance->Parser->cmdlist.end())
-               {
-                       cm->second->Disable(true);
-               }
+               Command* handler = ServerInstance->Parser.GetHandler(thiscmd);
+               if (handler)
+                       handler->Disable(true);
        }
        return true;
 }
 
-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);
-               }
-
-               if(!server.empty())
-               {
-                       ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str());
-                       return;
-               }
-       }
-
-       ServerInstance->Logs->Log("CONFIG",DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
-#else
-       // attempt to look up their nameserver from /etc/resolv.conf
-       ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
-
-       std::ifstream resolv("/etc/resolv.conf");
-
-       while (resolv >> server)
-       {
-               if (server == "nameserver")
-               {
-                       resolv >> server;
-                       if (server.find_first_not_of("0123456789.") == std::string::npos)
-                       {
-                               ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
-                               return;
-                       }
-               }
-       }
-
-       ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
-#endif
-       server = "127.0.0.1";
-}
-
 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
 {
        ConfigTagList tags = conf->ConfTags(tag);
@@ -250,13 +150,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;
+               OperTypes[name] = ifo;
                ifo->name = name;
                ifo->type_block = tag;
 
@@ -281,8 +179,8 @@ 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());
@@ -305,7 +203,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;
                        }
@@ -428,6 +326,7 @@ 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);
 
                        ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
                        if (oldMask != oldBlocksByMask.end())
@@ -445,42 +344,31 @@ 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 2.2" },
+       { "link",        "autoconnect", "",                 "2.0+ does not use this attribute - define <autoconnect> tags instead" },
+       { "link",        "transport",   "",                 "has been moved to <link:ssl> as of 2.0" },
+       { "module",      "name",        "m_chanprotect.so", "has been replaced with m_customprefix as of 2.2" },
+       { "module",      "name",        "m_halfop.so",      "has been replaced with m_customprefix as of 2.2" },
+       { "options",     "cyclehosts",  "",                 "has been replaced with m_hostcycle as of 2.2" },
+       { "performance", "nouserdns",   "",                 "has been moved to <connect:resolvehostnames> as of 2.2" }
 };
 
 void ServerConfig::Fill()
@@ -489,89 +377,59 @@ void ServerConfig::Fill()
        ConfigTag* security = ConfValue("security");
        if (sid.empty())
        {
-               ServerName = ConfValue("server")->getString("name");
-               sid = ConfValue("server")->getString("id");
+               ServerName = ConfValue("server")->getString("name", "irc.example.com");
                ValidHost(ServerName, "<server:name>");
-               if (!sid.empty() && !ServerInstance->IsSID(sid))
+
+               sid = ConfValue("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.");
        }
        else
        {
                if (ServerName != ConfValue("server")->getString("name"))
-                       throw CoreException("You must restart to change the server name or SID");
+                       throw CoreException("You must restart to change the server name");
+
                std::string nsid = ConfValue("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");
+       }
+       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!");
+       XLineMessage = options->getString("xlinemessage", 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);
+       NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
        dns_timeout = ConfValue("dns")->getInt("timeout", 5);
        DisabledCommands = ConfValue("disabled")->getString("commands", "");
        DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
        UserStats = security->getString("userstats");
-       CustomVersion = security->getString("customversion", Network + " IRCd");
+       CustomVersion = security->getString("customversion");
        HideSplits = security->getBool("hidesplits");
        HideBans = security->getBool("hidebans");
        HideWhoisServer = security->getString("hidewhois");
        HideKillsServer = security->getString("hidekills");
        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.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
+       Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
+       Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
+       Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
        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>");
+       if (Network.find(' ') != std::string::npos)
+               throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
 
        std::string defbind = options->getString("defaultbind");
        if (assign(defbind) == "ipv4")
@@ -589,26 +447,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"));
@@ -635,11 +474,6 @@ void ServerConfig::Fill()
                DisabledCModes[*p - 'A'] = 1;
        }
 
-       memset(HideModeLists, 0, sizeof(HideModeLists));
-       modes = ConfValue("security")->getString("hidemodes");
-       for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
-               HideModeLists[(unsigned char) *p] = true;
-
        std::string v = security->getString("announceinvites");
 
        if (v == "ops")
@@ -674,12 +508,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;
        }
 }
 
@@ -699,16 +528,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;
+                               }
                        }
                }
 
@@ -730,6 +569,11 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
        if (valid)
                ServerInstance->WritePID(this->PID);
 
+       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
@@ -737,14 +581,14 @@ 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.empty() ? "<all>" : i->first.c_str()) << "\tReason: "
+                                       << i->second << std::endl;
                        }
                }
        }
@@ -753,7 +597,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();
        }
 
@@ -782,15 +626,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 */
@@ -819,13 +659,8 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
 
 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)
@@ -834,6 +669,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)
@@ -841,30 +677,27 @@ 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 (modname.c_str()[0] == 'c')
                        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, "%s :Module %s successfully unloaded.", modname.c_str(), 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, "%s :Failed to unload module %s: %s", modname.c_str(), 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());
                }
        }
 
@@ -874,25 +707,20 @@ void ServerConfig::ApplyModules(User* user)
                {
                        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, "%s :Module %s successfully loaded.", adding->c_str(), 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, "%s :Failed to load module %s: %s", adding->c_str(), 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);
@@ -901,7 +729,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;
 }
@@ -911,35 +739,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()
@@ -951,7 +772,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);
 
@@ -963,15 +784,23 @@ 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();
+               ChanModeReference ban(NULL, "ban");
+               static_cast<ListModeBase*>(*ban)->DoRehash();
                Config->ApplyDisabledCommands(Config->DisabledCommands);
                User* user = ServerInstance->FindNick(TheUserUID);
-               FOREACH_MOD(I_OnRehash, OnRehash(user));
-               ServerInstance->BuildISupport();
+
+               ConfigStatus status(user);
+               const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+               for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+                       i->second->ReadConfig(status);
+
+               // The description of this server may have changed - update it for WHOIS etc.
+               ServerInstance->FakeClient->server->description = Config->ServerDesc;
+
+               ServerInstance->ISupport.Build();
 
                ServerInstance->Logs->CloseLogs();
                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..c1f1b00
--- /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(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[0].c_str() : parameters[1].c_str());
+                       return CMD_FAILURE;
+               }
+
+               // Verify channel timestamp if the INVITE is coming from a remote server
+               if (!IS_LOCAL(user))
+               {
+                       // Remote INVITE commands must carry a channel timestamp
+                       if (parameters.size() < 3)
+                               return CMD_INVALID;
+
+                       // Drop the invite if our channel TS is lower
+                       time_t RemoteTS = ConvToInt(parameters[2]);
+                       if (c->age < RemoteTS)
+                               return CMD_FAILURE;
+               }
+
+               if ((IS_LOCAL(user)) && (!c->HasUser(user)))
+               {
+                       user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str());
+                       return CMD_FAILURE;
+               }
+
+               if (c->HasUser(u))
+               {
+                       user->WriteNumeric(ERR_USERONCHANNEL, "%s %s :is already on channel", u->nick.c_str(), c->name.c_str());
+                       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 :You must be a channel %soperator",
+                                               c->name.c_str(), (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, "%s %s", u->nick.c_str(),c->name.c_str());
+                       if (u->IsAway())
+                               user->WriteNumeric(RPL_AWAY, "%s :%s", u->nick.c_str(), u->awaymsg.c_str());
+               }
+
+               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, ":%s", (*i)->chan->name.c_str());
+               }
+               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..1945bf5
--- /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, "%s :Invalid channel name", parameters[0].c_str());
+       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..e2fdd78
--- /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(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[1].c_str() : parameters[0].c_str());
+               return CMD_FAILURE;
+       }
+
+       Membership* srcmemb = NULL;
+       if (IS_LOCAL(user))
+       {
+               srcmemb = c->GetUser(user);
+               if (!srcmemb)
+               {
+                       user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", parameters[0].c_str());
+                       return CMD_FAILURE;
+               }
+
+               if (u->server->IsULine())
+               {
+                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You may not kick a u-lined client", c->name.c_str());
+                       return CMD_FAILURE;
+               }
+       }
+
+       const Channel::MemberMap::iterator victimiter = c->userlist.find(u);
+       if (victimiter == c->userlist.end())
+       {
+               user->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s :They are not on that channel", u->nick.c_str(), c->name.c_str());
+               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() > req)
+                                       req = mh->GetLevelRequired();
+                       }
+
+                       if (them < req)
+                       {
+                               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator",
+                                       this->name.c_str(), 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..986dbe0
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+       return CMD_FAILURE;
+}
+
+void CommandNames::SendNames(LocalUser* user, Channel* chan, bool show_invisible)
+{
+       Numeric::Builder<' '> reply(user, RPL_NAMREPLY, false);
+       std::string& list = reply.GetNumeric();
+       if (chan->IsModeSet(secretmode))
+               list.push_back('@');
+       else if (chan->IsModeSet(privatemode))
+               list.push_back('*');
+       else
+               list.push_back('=');
+
+       list.push_back(' ');
+       list.append(chan->name).append(" :");
+       reply.SaveBeginPos();
+
+       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, "%s :End of /NAMES list.", chan->name.c_str());
+}
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..8d65d76
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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)
+       , 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+               return CMD_FAILURE;
+       }
+
+       if (parameters.size() == 1)
+       {
+               if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
+               {
+                       user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c->name.c_str());
+                       return CMD_FAILURE;
+               }
+
+               if (c->topic.length())
+               {
+                       Topic::ShowTopic(user, c);
+               }
+               else
+               {
+                       user->WriteNumeric(RPL_NOTOPICSET, "%s :No topic is set.", c->name.c_str());
+               }
+               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, "%s :You're not on that channel!", c->name.c_str());
+                       return CMD_FAILURE;
+               }
+               if (c->IsModeSet(topiclockmode) && !ServerInstance->OnCheckExemption(user, c, "topiclock").check(c->GetPrefixValue(user) >= HALFOP_VALUE))
+               {
+                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to change the topic on this channel", c->name.c_str());
+                       return CMD_FAILURE;
+               }
+       }
+
+       c->SetTopic(user, t);
+       return CMD_SUCCESS;
+}
+
+void Topic::ShowTopic(LocalUser* user, Channel* chan)
+{
+       user->WriteNumeric(RPL_TOPIC, "%s :%s", chan->name.c_str(), chan->topic.c_str());
+       user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", chan->name.c_str(), chan->setby.c_str(), (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..aba4d97
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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"
+
+class CoreModChannel : public Module
+{
+       Invite::APIImpl invapi;
+       CommandInvite cmdinvite;
+       CommandJoin cmdjoin;
+       CommandKick cmdkick;
+       CommandNames cmdnames;
+       CommandTopic cmdtopic;
+
+       ModResult IsInvited(User* user, Channel* chan)
+       {
+               LocalUser* localuser = IS_LOCAL(user);
+               if ((localuser) && (invapi.IsInvited(localuser, chan)))
+                       return MOD_RES_ALLOW;
+               return MOD_RES_PASSTHRU;
+       }
+
+ public:
+       CoreModChannel()
+               : invapi(this)
+               , cmdinvite(this, invapi), cmdjoin(this), cmdkick(this), cmdnames(this), cmdtopic(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
+               Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan };
+               if (optionstag->getBool("invitebypassmodes", true))
+                       ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation));
+               else
+               {
+                       for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++)
+                               ServerInstance->Modules.Detach(events[i], this);
+               }
+       }
+
+       void OnPostJoin(Membership* memb) CXX11_OVERRIDE
+       {
+               Channel* const chan = memb->chan;
+               LocalUser* const localuser = IS_LOCAL(memb->user);
+               if (localuser)
+               {
+                       // Remove existing invite, if any
+                       invapi.Remove(localuser, chan);
+
+                       if (chan->topicset)
+                               Topic::ShowTopic(localuser, chan);
+
+                       // Show all members of the channel, including invisible (+i) users
+                       cmdnames.SendNames(localuser, chan, true);
+               }
+       }
+
+       ModResult OnCheckKey(User* user, Channel* chan, const std::string& keygiven) CXX11_OVERRIDE
+       {
+               // Hook only runs when being invited bypasses +bkl
+               return IsInvited(user, chan);
+       }
+
+       ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               // Hook only runs when being invited bypasses +bkl
+               return IsInvited(user, chan);
+       }
+
+       ModResult OnCheckLimit(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               // Hook only runs when being invited bypasses +bkl
+               return IsInvited(user, chan);
+       }
+
+       ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               // Hook always runs
+               return IsInvited(user, chan);
+       }
+
+       void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+       {
+               invapi.RemoveAll(user);
+       }
+
+       void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
+       {
+               // Make sure the channel won't appear in invite lists from now on, don't wait for cull to unset the ext
+               invapi.RemoveAll(chan);
+       }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               ServerInstance->Modules.SetPriority(this, I_OnPostJoin, PRIORITY_FIRST);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               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..0dafde8
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+namespace 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
+{
+       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..1e8bb75
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+ * 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;
+
+               //record.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;
+                       }
+                       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;
+
+       /** 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)
+       {
+               for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+                       requests[i] = NULL;
+               ServerInstance->Timers.AddTimer(this);
+       }
+
+       ~MyManager()
+       {
+               for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+               {
+                       DNS::Request* request = requests[i];
+                       if (!request)
+                               continue;
+
+                       Query rr(*request);
+                       rr.error = ERROR_UNKNOWN;
+                       request->OnError(&rr);
+
+                       delete request;
+               }
+       }
+
+       void Process(DNS::Request* req)
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing request to lookup " + req->name + " of type " + ConvToStr(req->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;
+
+               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->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)
+       {
+               if (requests[req->id] == req)
+                       requests[req->id] = NULL;
+       }
+
+       std::string GetErrorStr(Error e)
+       {
+               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 (static_cast<Question&>(*request) != 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->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)
+       {
+               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)
+       {
+               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;
+                       memset(&bindto, 0, sizeof(bindto));
+                       bindto.sa.sa_family = myserver.sa.sa_family;
+
+                       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);
+                       }
+               }
+               else
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
+               }
+       }
+};
+
+class ModuleDNS : public Module
+{
+       MyManager manager;
+       std::string DNSServer;
+
+       void FindDNSServer()
+       {
+#ifdef _WIN32
+               // attempt to look up their nameserver from the system
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
+
+               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("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
+                               return;
+                       }
+               }
+
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
+#else
+               // attempt to look up their nameserver from /etc/resolv.conf
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+
+               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("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
+                                       return;
+                               }
+                       }
+               }
+
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
+#endif
+               DNSServer = "127.0.0.1";
+       }
+
+ public:
+       ModuleDNS() : manager(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               std::string oldserver = DNSServer;
+               DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server");
+               if (DNSServer.empty())
+                       FindDNSServer();
+
+               if (oldserver != DNSServer)
+                       this->manager.Rehash(DNSServer);
+       }
+
+       void OnUnloadModule(Module* mod)
+       {
+               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);
+                               rr.error = ERROR_UNLOADED;
+                               req->OnError(&rr);
+
+                               delete req;
+                       }
+               }
+       }
+
+       Version GetVersion()
+       {
+               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..f6e0539
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 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->answers[0];
+
+               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->host.assign(*hostname, 0, ServerInstance->Config->Limits.MaxHost);
+                                       bound_user->dhost = bound_user->host;
+
+                                       /* Invalidate cache */
+                                       bound_user->InvalidateCache();
+                               }
+                               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 OnUserInit(LocalUser *user)
+       {
+               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)
+       {
+               return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+       }
+
+       Version GetVersion()
+       {
+               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..722ef86
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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)
+       : Command(parent, "ADMIN", 0, 0)
+{
+       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->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 (!AdminName.empty())
+               user->SendText(":%s %03d %s :Name     - %s", ServerInstance->Config->ServerName.c_str(),
+                       RPL_ADMINLOC1, user->nick.c_str(), AdminName.c_str());
+       user->SendText(":%s %03d %s :Nickname - %s", ServerInstance->Config->ServerName.c_str(),
+               RPL_ADMINLOC2, user->nick.c_str(), AdminNick.c_str());
+       user->SendText(":%s %03d %s :E-Mail   - %s", ServerInstance->Config->ServerName.c_str(),
+               RPL_ADMINEMAIL, user->nick.c_str(), AdminEmail.c_str());
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandAdmin::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       if (parameters.size() > 0)
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
diff --git a/src/coremods/core_info/cmd_commands.cpp b/src/coremods/core_info/cmd_commands.cpp
new file mode 100644 (file)
index 0000000..3ff6728
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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"
+
+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 %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));
+       }
+       std::sort(list.begin(), list.end());
+       for(unsigned int i=0; i < list.size(); i++)
+               user->Write(list[i]);
+       user->WriteNumeric(RPL_COMMANDSEND, ":End of COMMANDS list");
+       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..8dad994
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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)
+       : Command(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           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",
+       "   jackmcbarn      ChrisTX         Shawn",
+       " ",
+       "\2Thanks To\2:",
+       "   Asmo            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(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;
+}
+
+RouteDescriptor CommandInfo::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       if (parameters.size() > 0)
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
diff --git a/src/coremods/core_info/cmd_modules.cpp b/src/coremods/core_info/cmd_modules.cpp
new file mode 100644 (file)
index 0000000..0a1420e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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"
+
+CommandModules::CommandModules(Module* parent)
+       : Command(parent, "MODULES", 0, 0)
+{
+       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("vcC");
+                       int pos = 0;
+                       for (int mult = 2; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
+                               if (!(V.Flags & mult))
+                                       flags[pos] = '-';
+
+#ifdef PURE_STATIC
+                       user->SendText(":%s 702 %s :%s %s :%s", ServerInstance->Config->ServerName.c_str(),
+                               user->nick.c_str(), m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str());
+#else
+                       std::string srcrev = m->ModuleDLLManager->GetVersion();
+                       user->SendText(":%s 702 %s :%s %s :%s - %s", ServerInstance->Config->ServerName.c_str(),
+                               user->nick.c_str(), m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str(), srcrev.c_str());
+#endif
+               }
+               else
+               {
+                       user->SendText(":%s 702 %s :%s %s", ServerInstance->Config->ServerName.c_str(),
+                               user->nick.c_str(), m->ModuleSourceFile.c_str(), V.description.c_str());
+               }
+       }
+       user->SendText(":%s 703 %s :End of MODULES list", ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandModules::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       if (parameters.size() >= 1)
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
diff --git a/src/coremods/core_info/cmd_motd.cpp b/src/coremods/core_info/cmd_motd.cpp
new file mode 100644 (file)
index 0000000..5761609
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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)
+       : Command(parent, "MOTD", 0, 1)
+{
+       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->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;
+}
+
+RouteDescriptor CommandMotd::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       if (parameters.size() > 0)
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
diff --git a/src/coremods/core_info/cmd_time.cpp b/src/coremods/core_info/cmd_time.cpp
new file mode 100644 (file)
index 0000000..95cfb12
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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)
+       : Command(parent, "TIME", 0, 0)
+{
+       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->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),
+               ServerInstance->Config->ServerName.c_str(), InspIRCd::TimeString(ServerInstance->Time()).c_str());
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandTime::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       if (parameters.size() > 0)
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
diff --git a/src/coremods/core_info/cmd_version.cpp b/src/coremods/core_info/cmd_version.cpp
new file mode 100644 (file)
index 0000000..eb3ab2c
--- /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, ":%s", version.c_str());
+       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..56c3c95
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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"
+
+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..f5dd9e6
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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"
+
+/** Handle /ADMIN.
+ */
+class CommandAdmin : public Command
+{
+ 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);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** 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 Command
+{
+ 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);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /MODULES.
+ */
+class CommandModules : public Command
+{
+ 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);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /MOTD.
+ */
+class CommandMotd : public Command
+{
+ 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);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /TIME.
+ */
+class CommandTime : public Command
+{
+ 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);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** 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..49da893
--- /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, 303)
+       {
+       }
+
+       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..278e604
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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");
+
+       /* Work around mIRC suckyness. YOU SUCK, KHALED! */
+       if (parameters.size() == 1)
+       {
+               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, "* %ld :", users);
+                       }
+                       else
+                       {
+                               /* User is in the channel/privileged, channel is not +s */
+                               user->WriteNumeric(RPL_LIST, "%s %ld :[+%s] %s", chan->name.c_str(), users, 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..1d49d89
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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, "%s :Module successfully loaded.", parameters[0].c_str());
+               return CMD_SUCCESS;
+       }
+       else
+       {
+               user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+               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, "%s :You cannot unload core commands!", parameters[0].c_str());
+               return CMD_FAILURE;
+       }
+
+       Module* m = ServerInstance->Modules->Find(parameters[0]);
+       if (m == creator)
+       {
+               user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload module loading commands!", parameters[0].c_str());
+               return CMD_FAILURE;
+       }
+
+       if (m && ServerInstance->Modules->Unload(m))
+       {
+               ServerInstance->SNO->WriteGlobalSno('a', "MODULE UNLOADED: %s unloaded %s", user->nick.c_str(), parameters[0].c_str());
+               user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module successfully unloaded.", parameters[0].c_str());
+       }
+       else
+       {
+               user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(),
+                       m ? ServerInstance->Modules->LastError().c_str() : "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()
+       {
+               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..2529ca4
--- /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, ":There are %d users and %d invisible on %d servers",
+                       n_users - counters.invisible, counters.invisible, n_serv);
+
+       if (ServerInstance->Users->OperCount())
+               user->WriteNumeric(RPL_LUSEROP, "%d :operator(s) online", ServerInstance->Users->OperCount());
+
+       if (ServerInstance->Users->UnregisteredUserCount())
+               user->WriteNumeric(RPL_LUSERUNKNOWN, "%d :unknown connections", ServerInstance->Users->UnregisteredUserCount());
+
+       user->WriteNumeric(RPL_LUSERCHANNELS, "%lu :channels formed", (unsigned long)ServerInstance->GetChans().size());
+       user->WriteNumeric(RPL_LUSERME, ":I have %d clients and %d servers", ServerInstance->Users->LocalUserCount(),n_local_servs);
+       user->WriteNumeric(RPL_LOCALUSERS, ":Current local users: %d  Max: %d", ServerInstance->Users->LocalUserCount(), counters.max_local);
+       user->WriteNumeric(RPL_GLOBALUSERS, ":Current global users: %d  Max: %d", n_users, counters.max_global);
+
+       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)
+       {
+               counters.UpdateMaxUsers();
+               if (user->IsModeSet(invisiblemode))
+                       counters.invisible++;
+       }
+
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+       {
+               if (user->IsModeSet(invisiblemode))
+                       counters.invisible--;
+       }
+
+       Version GetVersion()
+       {
+               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..4bc6c25
--- /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 = "<password>";
+}
+
+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("COMMAND", LOG_SPARSE, diebuf);
+                       DieRestart::SendError(diebuf);
+               }
+
+               QuitAll();
+               ServerInstance->Exit(EXIT_STATUS_DIE);
+       }
+       else
+       {
+               ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
+               ServerInstance->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..4a0cb2f
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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 *u = ServerInstance->FindNick(parameters[0]);
+       if (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
+                */
+
+               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 (!user->server->IsULine())
+                               ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+                       this->lastuuid = u->uuid;
+               }
+               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 (!user->server->IsULine())
+                       {
+                               if (IS_LOCAL(user))
+                                       ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+                               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", LOG_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());
+
+                       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());
+
+                       this->lastuuid.clear();
+               }
+
+               // send the quit out
+               ServerInstance->Users->QuitUser(u, killreason);
+       }
+       else
+       {
+               user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+               return CMD_FAILURE;
+       }
+
+       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..e4ba695
--- /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->host;
+       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..19d2fa8
--- /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, "%s :Rehashing", FileSystem::GetFileName(ServerInstance->ConfigFileName).c_str());
+               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..3e21972
--- /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 = "<password>";
+}
+
+CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
+{
+       ServerInstance->Logs->Log("COMMAND", 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 execv() 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
+
+               execv(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..0fc82df
--- /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);
+               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..d691464
--- /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, "%s :Cannot send to channel (no external messages)", chan->name.c_str());
+                                       return CMD_FAILURE;
+                               }
+
+                               if (chan->IsModeSet(moderatedmode))
+                               {
+                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (+m)", chan->name.c_str());
+                                       return CMD_FAILURE;
+                               }
+
+                               if (ServerInstance->Config->RestrictBannedUsers)
+                               {
+                                       if (chan->IsBanned(user))
+                                       {
+                                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're banned)", 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, 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(ERR_NOSUCHNICK, "%s :No such nick/channel", 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(ERR_NOSUCHNICK, "%s :No such nick/channel", 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(ERR_NOTEXTTOSEND, ":No text to send");
+                       return CMD_FAILURE;
+               }
+
+               if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
+               {
+                       /* auto respond with aweh msg */
+                       user->WriteNumeric(RPL_AWAY, "%s :%s", dest->nick.c_str(), dest->awaymsg.c_str());
+               }
+
+               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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+               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()
+       {
+               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..0d01d9e
--- /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;
+                       if ((mh->IsPrefixMode()) && (memb->hasMode(mh->GetModeChar())))
+                               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, "%s :Module %ssuccessfully reloaded.", passedname.c_str(), 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, "%s :You cannot reload core_reloadmodule.so (unload and load it)",
+                       parameters[0].c_str());
+               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, "%s :Could not find module by that name", parameters[0].c_str());
+               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..9fb232b
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * 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(char statschar, User* user, string_list &results);
+ public:
+       /** Constructor for stats.
+        */
+       CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { 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)
+                       return ROUTE_UNICAST(parameters[1]);
+               return ROUTE_LOCALONLY;
+       }
+};
+
+static void GenerateStatsLl(User* user, string_list& results, char c)
+{
+       results.push_back(InspIRCd::Format("211 %s nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", user->nick.c_str(), (c == '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;
+               results.push_back("211 "+user->nick+" "+u->nick+"["+u->ident+"@"+(c == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->signon));
+       }
+}
+
+void CommandStats::DoStats(char statschar, User* user, string_list &results)
+{
+       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->host.c_str());
+               results.push_back("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("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("*");
+                               else if (ip.find_first_of(':') != std::string::npos)
+                               {
+                                       ip.insert(ip.begin(), '[');
+                                       ip.insert(ip.end(),  ']');
+                               }
+                               std::string type = ls->bind_tag->getString("type", "clients");
+                               std::string hook = ls->bind_tag->getString("ssl", "plaintext");
+
+                               results.push_back("249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
+                                       " (" + type + ", " + hook + ")");
+                       }
+               }
+               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;
+                               std::stringstream res;
+                               res << "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 (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+                       {
+                               ConnectClass* c = *i;
+                               results.push_back("215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : SocketEngine::GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
+                               results.push_back("218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
+                                               ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
+                               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);
+                                       results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+                                                       (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
+                                       idx++;
+                               }
+                       }
+                       results.push_back("249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
+               }
+               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':
+               {
+                       const SocketEngine::Statistics& stats = SocketEngine::GetStats();
+                       results.push_back("249 "+user->nick+" :Total events: "+ConvToStr(stats.TotalEvents));
+                       results.push_back("249 "+user->nick+" :Read events:  "+ConvToStr(stats.ReadEvents));
+                       results.push_back("249 "+user->nick+" :Write events: "+ConvToStr(stats.WriteEvents));
+                       results.push_back("249 "+user->nick+" :Error events: "+ConvToStr(stats.ErrorEvents));
+                       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 */
+                                       results.push_back("212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count));
+                               }
+                       }
+               }
+               break;
+
+               /* stats z (debug and memory info) */
+               case 'z':
+               {
+                       results.push_back("249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
+                       results.push_back("249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->GetChans().size()));
+                       results.push_back("249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
+
+                       float kbitpersec_in, kbitpersec_out, kbitpersec_total;
+                       char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
+
+                       SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
+
+                       snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
+                       snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
+                       snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
+
+                       results.push_back("249 "+user->nick+" :Bandwidth total:  "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
+                       results.push_back("249 "+user->nick+" :Bandwidth out:    "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
+                       results.push_back("249 "+user->nick+" :Bandwidth in:     "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
+
+#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("249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
+                               results.push_back("249 "+user->nick+" :Signals:          "+ConvToStr(R.ru_nsignals));
+                               results.push_back("249 "+user->nick+" :Page faults:      "+ConvToStr(R.ru_majflt));
+                               results.push_back("249 "+user->nick+" :Swaps:            "+ConvToStr(R.ru_nswap));
+                               results.push_back("249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
+
+                               char percent[30];
+
+                               float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
+                                       + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
+                               float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
+                               float per = (n_eaten / n_elapsed) * 100;
+
+                               snprintf(percent, 30, "%03.5f%%", per);
+                               results.push_back("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("249 "+user->nick+" :CPU Use (total):  "+percent);
+                       }
+#else
+                       PROCESS_MEMORY_COUNTERS MemCounters;
+                       if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
+                       {
+                               results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
+                               results.push_back("249 "+user->nick+" :Pagefile usage:   "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
+                               results.push_back("249 "+user->nick+" :Page faults:      "+ConvToStr(MemCounters.PageFaultCount));
+                       }
+
+                       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("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("249 "+user->nick+" :CPU Use (total):  "+percent);
+                       }
+#endif
+               }
+               break;
+
+               case 'T':
+               {
+                       results.push_back("249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
+                       results.push_back("249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
+                       results.push_back("249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
+                       results.push_back("249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
+                       results.push_back("249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats.Connects));
+                       results.push_back(InspIRCd::Format("249 %s :bytes sent %5.2fK recv %5.2fK", user->nick.c_str(),
+                               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;
+                               results.push_back("243 "+user->nick+" 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);
+                               }
+                               results.push_back("243 "+user->nick+" O "+tag->name.c_str() + " " + 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(user, results, statschar);
+               break;
+
+               /* stats u (show server uptime) */
+               case 'u':
+               {
+                       unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
+                       results.push_back(InspIRCd::Format("242 %s :Server up %u days, %.2u:%.2u:%.2u", user->nick.c_str(),
+                               up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
+               }
+               break;
+
+               default:
+               break;
+       }
+
+       results.push_back("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) && (!user->IsOper()))
+                       localuser->CommandFloodPenalty += 2000;
+               return CMD_SUCCESS;
+       }
+       string_list values;
+       char search = parameters[0][0];
+       DoStats(search, user, values);
+
+       const std::string p = ":" + ServerInstance->Config->ServerName + " ";
+       for (size_t i = 0; i < values.size(); i++)
+               user->SendText(p + values[i]);
+
+       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..20f6888
--- /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, "%s %s :0 %s", ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerDesc.c_str());
+               user->WriteNumeric(RPL_ENDOFLINKS, "* :End of /LINKS list.");
+               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()
+       {
+               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..adc6e6c
--- /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..a62bf51
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str());
+               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);
+       }
+}
+
+void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
+{
+       if (targetchannel)
+       {
+               // Display channel's current mode string
+               user->WriteNumeric(RPL_CHANNELMODEIS, "%s +%s", targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
+               user->WriteNumeric(RPL_CHANNELCREATED, "%s %lu", targetchannel->name.c_str(), (unsigned long)targetchannel->age);
+       }
+       else
+       {
+               if (targetuser == user || user->HasPrivPermission("users/auspex"))
+               {
+                       // Display user's current mode string
+                       // XXX: Use WriteServ() because WriteNumeric() assumes the target (i.e. next word after the number)
+                       // is 'user' and puts his nick there which is not what we want
+                       user->WriteServ("%03d %s :+%s", RPL_UMODEIS, targetuser->nick.c_str(), targetuser->FormatModes());
+                       if (targetuser->IsOper())
+                       {
+                               ModeHandler* 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.
+                               user->WriteServ("%03d %s %s%s :Server notice mask", RPL_SNOMASKIS, targetuser->nick.c_str(), (snomaskstr.empty() ? "+" : ""), snomaskstr.c_str());
+                       }
+               }
+               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..a62e1c2
--- /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_ERRONEUSNICKNAME, "* :Erroneous Nickname");
+               return CMD_FAILURE;
+       }
+
+       if (newnick == "0")
+       {
+               newnick = user->uuid;
+       }
+       else if (!ServerInstance->IsNick(newnick))
+       {
+               user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Erroneous Nickname", newnick.c_str());
+               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, "%s :Cannot send to channel (you're banned)", chan->name.c_str());
+                               return CMD_FAILURE;
+                       }
+               }
+       }
+
+       if (!user->ChangeNick(newnick))
+               return CMD_FAILURE;
+
+       if (user->registered < REG_NICKUSER)
+       {
+               user->registered = (user->registered | REG_NICK);
+               return CommandUser::CheckRegister(user);
+       }
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_user/cmd_part.cpp b/src/coremods/core_user/cmd_part.cpp
new file mode 100644 (file)
index 0000000..77aafd1
--- /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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+               return CMD_FAILURE;
+       }
+
+       if (!c->PartUser(user, reason))
+       {
+               user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel", c->name.c_str());
+               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..c4e127d
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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)
+{
+       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* operquit = ServerInstance->OperQuit.get(user);
+       ServerInstance->Users->QuitUser(user, quitmsg, operquit);
+
+       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..cbf4f5e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+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, "USER :Your username is not valid");
+                       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]);
+                       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..dd77854
--- /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..0418588
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#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
+{
+ 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..eae6e51
--- /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 = "302 " + user->nick + " :";
+
+       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 == user) || (has_privs)) ? u->host : u->dhost);
+                       retbuf += ' ';
+               }
+       }
+
+       user->WriteServ(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..8b9258d
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * 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 invisiblemode;
+
+       Membership* get_first_visible_channel(User* u)
+       {
+               for (User::ChanList::iterator i = u->chans.begin(); i != u->chans.end(); ++i)
+               {
+                       Membership* memb = *i;
+                       if (!memb->chan->IsModeSet(secretmode))
+                               return memb;
+               }
+               return NULL;
+       }
+
+ public:
+       /** Constructor for who.
+        */
+       CommandWho(Module* parent)
+               : Command(parent, "WHO", 1)
+               , secretmode(parent, "secret")
+               , privatemode(parent, "private")
+               , invisiblemode(parent, "invisible")
+       {
+               syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
+       }
+
+       void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults);
+       /** 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->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 = 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->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->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, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults)
+{
+       if (!memb)
+               memb = get_first_visible_channel(u);
+
+       std::string wholine = initial + (memb ? memb->chan->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->GetName());
+
+       wholine.append(" " + u->nick + " ");
+
+       /* away? */
+       if (u->IsAway())
+       {
+               wholine.append("G");
+       }
+       else
+       {
+               wholine.append("H");
+       }
+
+       /* oper? */
+       if (u->IsOper())
+       {
+               wholine.push_back('*');
+       }
+
+       if (memb)
+       {
+               char prefix = memb->GetPrefixChar();
+               if (prefix)
+                       wholine.push_back(prefix);
+       }
+
+       wholine.append(" :0 " + u->fullname);
+
+       FOREACH_MOD(OnSendWhoLine, (user, parms, u, memb, 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;
+
+       std::vector<std::string> whoresults;
+       std::string initial = "352 " + user->nick + " ";
+
+       /* 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, initial, 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, initial, 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, 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(RPL_ENDOFWHO, "%s :End of /WHO list.", *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/coremods/core_whois.cpp b/src/coremods/core_whois.cpp
new file mode 100644 (file)
index 0000000..ecc406a
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * 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(unsigned int numeric, const std::string& text) CXX11_OVERRIDE;
+};
+
+void WhoisContextImpl::SendLine(unsigned int numeric, const std::string& text)
+{
+       std::string copy_text = target->nick;
+       copy_text.push_back(' ');
+       copy_text.append(text);
+
+       ModResult MOD_RESULT;
+       FIRST_MOD_RESULT_CUSTOM(lineevprov, Whois::LineEventListener, OnWhoisLine, MOD_RESULT, (*this, numeric, copy_text));
+
+       if (MOD_RESULT != MOD_RES_DENY)
+               source->WriteNumeric(numeric, copy_text);
+}
+
+/** 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()(unsigned int numeric, const std::string& text) const
+       {
+               whois.SendLine(numeric, text);
+       }
+};
+
+class WhoisChanListNumericBuilder : public Numeric::GenericBuilder<' ', false, WhoisNumericSink>
+{
+ public:
+       WhoisChanListNumericBuilder(WhoisContextImpl& whois)
+               : Numeric::GenericBuilder<' ', false, WhoisNumericSink>(WhoisNumericSink(whois), 319, true, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1)
+       {
+       }
+};
+
+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, "%s %s * :%s", dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
+       if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+       {
+               whois.SendLine(378, ":is connecting from %s@%s %s", dest->ident.c_str(), dest->host.c_str(), dest->GetIPString().c_str());
+       }
+
+       SendChanList(whois);
+
+       if (!whois.IsSelfWhois() && !ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+       {
+               whois.SendLine(312, "%s :%s", ServerInstance->Config->HideWhoisServer.c_str(), ServerInstance->Config->Network.c_str());
+       }
+       else
+       {
+               whois.SendLine(312, "%s :%s", dest->server->GetName().c_str(), dest->server->GetDesc().c_str());
+       }
+
+       if (dest->IsAway())
+       {
+               whois.SendLine(301, ":%s", dest->awaymsg.c_str());
+       }
+
+       if (dest->IsOper())
+       {
+               if (ServerInstance->Config->GenericOper)
+                       whois.SendLine(313, ":is an IRC operator");
+               else
+                       whois.SendLine(313, ":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, ":is using modes +%s %s", dest->FormatModes(), snomaskmode->GetUserParameter(dest).c_str());
+               }
+               else
+               {
+                       whois.SendLine(379, ":is using modes +%s", dest->FormatModes());
+               }
+       }
+
+       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, "%lu %lu :seconds idle, signon time", idle, signon);
+       }
+
+       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(ERR_NOSUCHNICK, "%s :No such nick/channel", !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
+               user->WriteNumeric(RPL_ENDOFWHOIS, "%s :End of /WHOIS list.", !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
+               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..79ce94c
--- /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, "%s :This command has been disabled.", name.c_str());
+               return CMD_FAILURE;
+       }
+
+       const WhoWas::Nick* const nick = manager.FindNick(parameters[0]);
+       if (!nick)
+       {
+               user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str());
+       }
+       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, "%s %s %s * :%s", parameters[0].c_str(), u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
+
+                       if (user->HasPrivPermission("users/auspex"))
+                               user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s", parameters[0].c_str(), u->host.c_str());
+
+                       std::string signon = InspIRCd::TimeString(u->signon);
+                       bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
+                       user->WriteNumeric(RPL_WHOISSERVER, "%s %s :%s", parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
+               }
+       }
+
+       user->WriteNumeric(RPL_ENDOFWHOWAS, "%s :End of WHOWAS", parameters[0].c_str());
+       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->host)
+       , dhost(user->dhost)
+       , ident(user->ident)
+       , server(user->server->GetName())
+       , gecos(user->fullname)
+       , signon(user->signon)
+{
+}
+
+WhoWas::Nick::Nick(const std::string& nickname)
+       : addtime(ServerInstance->Time())
+       , nick(nickname)
+{
+}
+
+WhoWas::Nick::~Nick()
+{
+       stdalgo::delete_all(entries);
+}
+
+class ModuleWhoWas : public Module
+{
+       CommandWhowas cmd;
+
+ public:
+       ModuleWhoWas() : cmd(this)
+       {
+       }
+
+       void OnGarbageCollect()
+       {
+               // Remove all entries older than MaxKeep
+               cmd.manager.Maintain();
+       }
+
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+       {
+               cmd.manager.Add(user);
+       }
+
+       ModResult OnStats(char symbol, User* user, string_list &results)
+       {
+               if (symbol == 'z')
+                       results.push_back("249 "+user->nick+" :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()
+       {
+               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..3f042c3
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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';
+       Penalty = 0;
+       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..ee85fdd
--- /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"
+
+CommandKline::CommandKline(Module* parent)
+       : Command(parent, "KLINE", 1, 3)
+{
+       flags_needed = 'o';
+       Penalty = 0;
+       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..955efea
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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';
+       Penalty = 0;
+       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..859be10
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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';
+       Penalty = 0;
+       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::Match(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..7fa7da0
--- /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::Match(user->MakeHost(), mask, ascii_case_insensitive_map)) ||
+                       (InspIRCd::Match(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, "%s :Invalid nickname: %s", newnick.c_str(), 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..d4ad498
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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 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);
+};
+
+/** 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 3a6a151cb5d047db124b4caca75d1a8063c0a1bf..9984f4dbeabe5c8873bdb91b2ecc6bb1382c1f1b 100644 (file)
@@ -22,7 +22,7 @@
 
 
 #include "inspircd.h"
-#include "dynamic.h"
+
 #ifndef _WIN32
 #include <dlfcn.h>
 #else
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..35e5f3671cec15aa37c1387bfc460a6eaf54eb76 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.
+/**
+ * A case sensitive mapping of characters from upper case to lower case for the
+ * character set of RFC 1459. This is identical to ASCII.
  */
-void nspace::strlower(char *n)
-{
-       if (n)
-       {
-               for (char* t = n; *t; t++)
-                       *t = national_case_insensitive_map[(unsigned char)*t];
-       }
-}
-
-#ifdef HASHMAP_DEPRECATED
-       size_t CoreExport nspace::insensitive::operator()(const std::string &s) const
-#else
-       size_t nspace::hash<std::string>::operator()(const std::string &s) const
-#endif
-
-{
-       /* XXX: NO DATA COPIES! :)
-        * The hash function here is practically
-        * a copy of the one in STL's hash_fun.h,
-        * only with *x replaced with national_case_insensitive_map[*x].
-        * This avoids a copy to use hash<const char*>
-        */
-       size_t t = 0;
-       for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
-               t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
-       return t;
-}
-
+unsigned const char rfc_case_sensitive_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,  65,  66,  67,  68,  69,  // 60-69
+       70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  // 70-79
+       80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  // 80-89
+       90,  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-239
+       240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+       250, 251, 252, 253, 254, 255,                     // 250-255
+};
 
 size_t CoreExport irc::hash::operator()(const irc::string &s) const
 {
@@ -165,6 +169,39 @@ bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2)
        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
+{
+       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++)
+       {
+               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);
+}
+
+size_t irc::insensitive::operator()(const std::string &s) const
+{
+       /* XXX: NO DATA COPIES! :)
+        * The hash function here is practically
+        * a copy of the one in STL's hash_fun.h,
+        * only with *x replaced with national_case_insensitive_map[*x].
+        * This avoids a copy to use hash<const char*>
+        */
+       size_t t = 0;
+       for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
+               t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
+       return t;
+}
+
 /******************************************************
  *
  * This is the implementation of our special irc::string
@@ -217,60 +254,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++;
-
-               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;
-               }
+       bool first = !pos;
 
-               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;
+
+       return true;
 }
 
 bool irc::tokenstream::GetToken(irc::string &token)
@@ -297,182 +304,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 (this->StreamEnd())
        {
-               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)
-       {
-               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 +412,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 +432,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..c9135679cfd0e8d6a31d4aeab4b484d1ee0685dd 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);
 
-       return (Config->ulines.find(sserver.c_str()) != Config->ulines.end());
+               if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
+               {
+                       break;
+               }
+
+               formatBuffer.resize(formatBuffer.size() * 2);
+       }
+
+       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 %H:%M:%S %Y";
 
-// 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)
@@ -588,11 +444,11 @@ ModResult OnCheckExemptionHandler::Call(User* user, Channel* chan, const std::st
                std::string::size_type pos = current.find(':');
                if (pos == std::string::npos)
                        continue;
-               if (current.substr(0,pos) == restriction)
+               if (!current.compare(0, pos, restriction))
                        minmode = current[pos+1];
        }
 
-       ModeHandler* mh = ServerInstance->Modes->FindMode(minmode, MODETYPE_CHANNEL);
+       PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(minmode);
        if (mh && mypfx >= mh->GetPrefixRank())
                return MOD_RES_ALLOW;
        if (mh || minmode == '*')
index 656be220f6c60d37469dd88f793a5b00bcc7daa2..eb24cdea0a2b0d924e8a116f6e4bbd7639c17bd5 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,120 +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();
-       }
+       SocketEngine::Deinit();
+       Logs->CloseLogs();
 }
 
 void InspIRCd::SetSignals()
@@ -258,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)
        {
@@ -282,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
@@ -299,7 +199,7 @@ void InspIRCd::WritePID(const std::string &filename)
 #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())
        {
@@ -309,89 +209,50 @@ void InspIRCd::WritePID(const std::string &filename)
        else
        {
                std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
-               this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
+               this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
                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),
+        OperQuit("operquit", ExtensionItem::EXT_USER, NULL),
         GenRandom(&HandleGenRandom),
         IsChannel(&HandleIsChannel),
-        IsSID(&HandleIsSID),
-        Rehash(&HandleRehash),
         IsNick(&HandleIsNick),
         IsIdent(&HandleIsIdent),
-        FloodQuitUser(&HandleFloodQuitUser),
         OnCheckExemption(&HandleOnCheckExemption)
 {
        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;
@@ -420,28 +281,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 */
@@ -451,19 +310,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 << " " << INSPIRCD_REVISION << std::endl;
                Exit(EXIT_STATUS_NOERROR);
        }
 
@@ -477,28 +338,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);
+               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;
                }
@@ -506,34 +361,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 << ", compiled on " __DATE__ " at " __TIME__ << std::endl;
+       std::cout << "For contributors & authors: " << con_green << "See /INFO Output" << con_reset << std::endl;
 
 #ifndef _WIN32
        if (!do_root)
                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
@@ -545,47 +393,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);
@@ -594,8 +427,8 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
        this->Modules->LoadAll();
 
-       /* Just in case no modules were loaded - fix for bug #101 */
-       this->BuildISupport();
+       // Build ISupport as ModuleManager::LoadAll() does not do it
+       this->ISupport.Build();
        Config->ApplyDisabledCommands(Config->DisabledCommands);
 
        if (!pl.empty())
@@ -612,7 +445,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                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)
@@ -620,7 +453,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));
                }
        }
 
@@ -634,7 +467,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);
 
@@ -643,16 +476,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 */
@@ -664,10 +497,10 @@ 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], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SocketEngine::GetMaxFds());
 
 #ifndef _WIN32
        std::string SetUser = Config->ConfValue("security")->getString("runasuser");
@@ -681,7 +514,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
                if (ret == -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);
                }
 
@@ -693,7 +526,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
                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);
                }
 
@@ -701,7 +534,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
                if (ret == -1)
                {
-                       this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
                        this->QuickExit(0);
                }
        }
@@ -716,7 +549,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
                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);
                }
 
@@ -724,7 +557,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
                if (ret == -1)
                {
-                       this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
                        this->QuickExit(0);
                }
        }
@@ -753,15 +586,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;
@@ -776,7 +611,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();
 
@@ -796,18 +631,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
 
@@ -820,21 +655,18 @@ 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, ());
 
-                       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();
                        }
                }
@@ -846,8 +678,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();
@@ -859,25 +691,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..89c3a71a93a691850b70b882f5db0fddb8db8679 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "inspstring.h"
-#include "socketengine.h"
-
-#ifndef DISABLE_WRITEV
-#include <sys/uio.h>
-#endif
-
-#ifndef IOV_MAX
-#define IOV_MAX 1024
-#endif
+#include "iohook.h"
 
 BufferedSocket::BufferedSocket()
 {
@@ -47,7 +37,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 +56,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 +82,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 +96,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 +112,14 @@ void StreamSocket::Close()
        {
                // final chance, dump as much of the sendq as we can
                DoWrite();
-               if (IOHook)
+               if (GetIOHook())
                {
-                       try
-                       {
-                               IOHook->OnStreamSocketClose(this);
-                       }
-                       catch (CoreException& modexcept)
-                       {
-                               ServerInstance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s",
-                                       modexcept.GetSource(), modexcept.GetReason());
-                       }
-                       IOHook = NULL;
+                       GetIOHook()->OnStreamSocketClose(this);
+                       delete iohook;
+                       DelIOHook();
                }
-               ServerInstance->SE->Shutdown(this, 2);
-               ServerInstance->SE->DelFd(this);
-               ServerInstance->SE->Close(this);
-               fd = -1;
+               SocketEngine::Shutdown(this, 2);
+               SocketEngine::Close(this);
        }
 }
 
@@ -153,27 +134,16 @@ 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()
 {
-       if (IOHook)
+       if (GetIOHook())
        {
-               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;
-               }
+               int rv = GetIOHook()->OnStreamSocketRead(this, recvq);
                if (rv > 0)
                        OnDataReady();
                if (rv < 0)
@@ -182,36 +152,36 @@ void StreamSocket::DoRead()
        else
        {
                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);
+                       SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
                        recvq.append(ReadBuffer, n);
                        OnDataReady();
                }
                else if (n > 0)
                {
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ);
+                       SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ);
                        recvq.append(ReadBuffer, n);
                        OnDataReady();
                }
                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);
                }
                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);
                }
                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);
                }
                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);
                }
        }
 }
@@ -223,107 +193,21 @@ void StreamSocket::DoWrite()
 {
        if (sendq.empty())
                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
+       if (GetIOHook())
        {
-               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 = GetIOHook()->OnStreamSocketWrite(this);
+               if (rv < 0)
+                       SetError("Write Error"); // will not overwrite a better error message
 
-                                               // 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
-                       }
-               }
-               catch (CoreException& modexcept)
-               {
-                       ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s",
-                               modexcept.GetSource(), modexcept.GetReason());
-               }
+               // rv == 0 means the socket has blocked. Stop trying to send data.
+               // IOHook has requested unblock notification from the socketengine.
        }
-#ifndef DISABLE_WRITEV
        else
        {
                // don't even try if we are known to be blocking
@@ -331,7 +215,7 @@ void StreamSocket::DoWrite()
                        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() && !sendq.empty() && eventChange == FD_WANT_EDGE_WRITE)
                {
                        // Prepare a writev() call to write all buffers efficiently
                        int bufcount = sendq.size();
@@ -343,21 +227,24 @@ 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 = sendq.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)sendq.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();
                        }
                        else if (rv > 0)
@@ -368,10 +255,9 @@ 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())
                                {
-                                       std::string& front = sendq.front();
+                                       const SendQueue::Element& front = sendq.front();
                                        if (front.length() <= (size_t)rv)
                                        {
                                                // this string got fully written out
@@ -381,7 +267,7 @@ void StreamSocket::DoWrite()
                                        else
                                        {
                                                // stopped in the middle of this string
-                                               front = front.substr(rv);
+                                               sendq.erase_front(rv);
                                                rv = 0;
                                        }
                                }
@@ -407,38 +293,39 @@ 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 +341,96 @@ 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);
        }
 }
-
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..fa43e6827240b8ed113b55003a356c6a51507b84 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
+
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#endif
 
 ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to)
        : bind_tag(tag)
+       , iohookprov(NULL, std::string())
 {
        irc::sockets::satoap(bind_to, bind_addr, bind_port);
-       bind_desc = irc::sockets::satouser(bind_to);
+       bind_desc = bind_to.str();
 
        fd = socket(bind_to.sa.sa_family, SOCK_STREAM, 0);
 
@@ -51,23 +54,38 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t
        }
 #endif
 
-       ServerInstance->SE->SetReuse(fd);
-       int rv = ServerInstance->SE->Bind(this->fd, bind_to);
+       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);
+
+       int timeout = tag->getInt("defer", 0);
+       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,57 +93,35 @@ 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;
 
        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_desc.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));
+               ServerInstance->Logs->Log("SOCKET", LOG_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;
-       }
-
        if (client.sa.sa_family == AF_INET6)
        {
                /*
@@ -156,7 +152,7 @@ void ListenSocket::AcceptInternal()
                }
        }
 
-       ServerInstance->SE->NonBlocking(incomingSockfd);
+       SocketEngine::NonBlocking(incomingSockfd);
 
        ModResult res;
        FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server));
@@ -171,29 +167,26 @@ 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",
+               ServerInstance->stats.Refused++;
+               ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Refusing connection on %s - %s",
                        bind_desc.c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
-               ServerInstance->SE->Close(incomingSockfd);
+               SocketEngine::Close(incomingSockfd);
        }
 }
 
-void ListenSocket::HandleEvent(EventType e, int err)
+bool ListenSocket::ResetIOHookProvider()
 {
-       switch (e)
-       {
-               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;
-       }
+       std::string provname = bind_tag->getString("ssl");
+       if (!provname.empty())
+               provname.insert(0, "ssl/");
+
+       // Set the new provider name, dynref handles the rest
+       iohookprov.SetProvider(provname);
+
+       // Return true if no provider was set, or one was set and it was also found
+       return (provname.empty() || iohookprov);
 }
diff --git a/src/listmode.cpp b/src/listmode.cpp
new file mode 100644 (file)
index 0000000..35964df
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * 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, "%s %s %s %lu", channel->name.c_str(), it->mask.c_str(), it->setter.c_str(), (unsigned long) it->time);
+               }
+       }
+       user->WriteNumeric(endoflistnumeric, "%s :%s", channel->name.c_str(), endofliststring.c_str());
+}
+
+void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
+{
+       user->WriteNumeric(endoflistnumeric, "%s :%s", channel->name.c_str(), endofliststring.c_str());
+}
+
+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("*", 64));
+
+       // 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 64;
+}
+
+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);
+}
+
+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, "%s %s :Channel ban list is full", channel->name.c_str(), parameter.c_str());
+}
+
+void ListModeBase::TellAlreadyOnList(User*, Channel*, std::string&)
+{
+}
+
+void ListModeBase::TellNotSet(User*, Channel*, std::string&)
+{
+}
index 89b2be019721289c241b4839bb591820215b1b84..8bd5f7f88ac6697eb1ee09049db777f39b87bd80 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 " (" INSPIRCD_REVISION ", " MODULE_INIT_STR ")"
+       " - compiled on " INSPIRCD_SYSTEM;
+
 LogManager::LogManager()
+       : Logging(false)
 {
-       Logging = false;
 }
 
 LogManager::~LogManager()
@@ -82,41 +84,41 @@ 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);
                        logmap.insert(std::make_pair(target, fw));
@@ -126,7 +128,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 +137,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 +192,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 +207,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 +230,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 +267,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 +288,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;
                }
@@ -354,8 +321,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 % 20 == 0)
        {
                fflush(log);
        }
index 89ff37fa1e7fd0b709bd411c220abdf15cf6b1f4..6038f6f5b0a04771516ae3f90f922fd7c37d5fdd 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), levelrequired(HALFOP_VALUE)
 {
 }
 
 CullResult ModeHandler::cull()
 {
-       if (ServerInstance->Modes)
+       if (ServerInstance)
                ServerInstance->Modes->DelMode(this);
        return classbase::cull();
 }
@@ -67,16 +44,6 @@ ModeHandler::~ModeHandler()
 {
 }
 
-bool ModeHandler::IsListMode()
-{
-       return list;
-}
-
-unsigned int ModeHandler::GetPrefixRank()
-{
-       return 0;
-}
-
 int ModeHandler::GetNumParams(bool adding)
 {
        switch (parameters_taken)
@@ -123,11 +90,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 +109,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 +119,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,42 +128,20 @@ 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)
-{
-       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;
-}
-
-bool ParamChannelModeHandler::ParamValidate(std::string& parameter)
-{
-       return true;
-}
-
-ModeWatcher::ModeWatcher(Module* Creator, char modeletter, ModeType type)
-       : mode(modeletter), m_type(type), creator(Creator)
+ModeWatcher::ModeWatcher(Module* Creator, const std::string& modename, ModeType type)
+       : mode(modename), m_type(type), creator(Creator)
 {
+       ServerInstance->Modes->AddModeWatcher(this);
 }
 
 ModeWatcher::~ModeWatcher()
 {
-}
-
-char ModeWatcher::GetModeChar()
-{
-       return mode;
+       ServerInstance->Modes->DelModeWatcher(this);
 }
 
 ModeType ModeWatcher::GetModeType()
@@ -198,77 +149,91 @@ ModeType ModeWatcher::GetModeType()
        return m_type;
 }
 
-bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool, ModeType)
+bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
 {
        return true;
 }
 
-void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool, ModeType)
+void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
 {
 }
 
-User* ModeParser::SanityChecks(User *user, const char *dest, Channel *chan, int)
+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)
 {
-       User *d;
-       if ((!user) || (!dest) || (!chan) || (!*dest))
-       {
-               return NULL;
-       }
-       d = ServerInstance->FindNick(dest);
-       if (!d)
+       list = true;
+}
+
+ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
+{
+       User* target;
+       if (IS_LOCAL(source))
+               target = ServerInstance->FindNickOnly(parameter);
+       else
+               target = ServerInstance->FindNick(parameter);
+
+       if (!target)
        {
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), dest);
-               return NULL;
+               source->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameter.c_str());
+               return MODEACTION_DENY;
        }
-       return d;
+
+       Membership* memb = chan->GetUser(target);
+       if (!memb)
+               return MODEACTION_DENY;
+
+       parameter = target->nick;
+       return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY);
 }
 
-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);
+       ModeHandler* mh = mcitem.mh;
+       bool adding = mcitem.adding;
        int pcnt = mh->GetNumParams(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);
@@ -286,10 +251,10 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
                        unsigned int ourrank = chan->GetPrefixValue(user);
                        if (ourrank < neededrank)
                        {
-                               ModeHandler* neededmh = NULL;
+                               PrefixMode* neededmh = NULL;
                                for(char c='A'; c <= 'z'; c++)
                                {
-                                       ModeHandler *privmh = FindMode(c, MODETYPE_CHANNEL);
+                                       PrefixMode* privmh = FindPrefixMode(c);
                                        if (privmh && privmh->GetPrefixRank() >= neededrank)
                                        {
                                                // this mode is sufficient to allow this action
@@ -298,34 +263,39 @@ 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, "%s :You must have channel %s access or above to %sset channel mode %c",
+                                               chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar);
                                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, "%s :You cannot %sset channel mode %c",
+                                               chan->name.c_str(), 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 (pcnt && 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'])
                {
-                       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, ":Permission Denied - %s mode %c has been locked by the administrator",
+                               type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
                        return MODEACTION_DENY;
                }
        }
@@ -333,43 +303,19 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
        if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
        {
                /* 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, ":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, ":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);
 
@@ -379,67 +325,26 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
        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;
+       if (endindex > parameters.size())
+               endindex = parameters.size();
 
-       LastParse.clear();
-       LastParseParams.clear();
-       LastParseTranslate.clear();
-
-       if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
-       {
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),target.c_str());
-               return;
-       }
-       if (parameters.size() == 1)
-       {
-               this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str());
-               return;
-       }
-
-       ModResult MOD_RESULT;
-       FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters));
-
-       bool SkipAccessChecks = false;
-
-       if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW)
-               SkipAccessChecks = true;
-       else if (MOD_RESULT == MOD_RES_DENY)
-               return;
-
-       if (targetuser && !SkipAccessChecks && user != targetuser)
-       {
-               user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str());
-               return;
-       }
-
-       std::string mode_sequence = parameters[1];
-
-       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 +359,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, "%c :is unknown mode char to me", modechar);
                        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->GetNumParams(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->GetNumParams(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 +526,6 @@ void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_s
        }
 }
 
-const std::string& ModeParser::GetLastParse()
-{
-       return LastParse;
-}
-
 void ModeParser::CleanMask(std::string &mask)
 {
        std::string::size_type pos_of_pling = mask.find_first_of('!');
@@ -639,50 +562,83 @@ 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;
+       }
+
+       throw ModuleException("Out of ModeIds");
+}
 
+void ModeParser::AddMode(ModeHandler* mh)
+{
        /* 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;
+       if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
+               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'))
                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 +647,104 @@ 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;
+       ModeHandlerMap& mhmap = modehandlersbyname[mt];
+       ModeHandlerMap::const_iterator it = mhmap.find(modename);
+       if (it != mhmap.end())
+               return it->second;
 
+       return NULL;
+}
+
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
+{
        if ((modeletter < 'A') || (modeletter > 'z'))
                return NULL;
 
-       mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
-       pos = (modeletter-65) | mask;
-
-       return modehandlers[pos];
+       return modehandlers[mt][modeletter-65];
 }
 
-std::string ModeParser::UserModeList()
+PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
 {
-       char modestr[256];
-       int pointer = 0;
-
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
-       {
-               unsigned char pos = (mode-65) | MASK_USER;
-
-               if (modehandlers[pos])
-                       modestr[pointer++] = mode;
-       }
-       modestr[pointer++] = 0;
-       return modestr;
+       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->GetNumParams(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 = CreateModeList(MODETYPE_USER) + " " + CreateModeList(MODETYPE_CHANNEL) + " " + 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 +753,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->GetNumParams(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->GetNumParams(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();
                        }
                }
        }
@@ -841,20 +796,17 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes)
 {
        std::string mletters;
        std::string mprefixes;
-       std::map<int,std::pair<char,char> > prefixes;
+       insp::flat_map<int, std::pair<char, char> > 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[modehandlers[pos]->GetPrefixRank()] = std::make_pair(
-                               modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetModeChar());
-               }
+               PrefixMode* pm = *i;
+               if (pm->GetPrefix())
+                       prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar());
        }
 
-       for(std::map<int,std::pair<char,char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); n++)
+       for (insp::flat_map<int, std::pair<char, char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
        {
                mletters = mletters + n->second.first;
                mprefixes = mprefixes + n->second.second;
@@ -863,103 +815,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->GetNumParams(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->GetModeChar()))
+                       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;
 
@@ -967,45 +884,42 @@ 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();
+}
+
 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..6e9517a4fbba74d6573a7be7dcc7352d611bf696 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 */
@@ -46,8 +43,7 @@ ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, st
         * 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..08ee7f562486dc4190a9f6cf94867106402d8bf3 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 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, "%c :is unknown snomask char to me", *i);
+                                               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..9a687ad2bf1af38002134ff30aefc3f288ff6603 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>
 
 
 #ifndef PURE_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)
                {
@@ -74,28 +73,35 @@ bool ModuleManager::Load(const std::string& filename, bool defer)
                        std::string version = newhandle->GetVersion();
                        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 +111,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 +154,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..98ed26c672c1fe787512e912aa13ba455b400d2a 100644 (file)
@@ -17,7 +17,7 @@
  */
 
 
-#define MODNAME cmd_all
+#define MODNAME "cmd_all"
 
 #include "inspircd.h"
 #include "exitcodes.h"
@@ -58,7 +58,6 @@ class AllModule : public Module
                        {
                                Command* c = (*i)(this);
                                cmds.push_back(c);
-                               ServerInstance->AddCommand(c);
                        }
                }
                catch (...)
@@ -70,8 +69,7 @@ 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()
@@ -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 (modname[0] == 'c')
                {
-                       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 b2d2f23c68af1acf45b429bd3de819298f0393de..292986df500327b8ae4ed1584e14584a0ef2d5f9 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::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(char, User*, string_list&) { 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(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&) { }
+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*, unsigned int, const std::string&) { DetachEvent(I_OnNumeric); return MOD_RES_PASSTHRU; }
+ModResult   Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { DetachEvent(I_OnAcceptConnection); return MOD_RES_PASSTHRU; }
+void           Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, std::string&) { DetachEvent(I_OnSendWhoLine); }
+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);
+}
+
+void ServiceProvider::DisableAutoRegister()
+{
+       if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+               stdalgo::erase(*ServerInstance->Modules->NewServices, this);
+}
 
-ModuleManager::ModuleManager() : ModCount(0)
+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;
        }
 
@@ -347,22 +344,30 @@ bool ModuleManager::CanUnload(Module* mod)
 
 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);
 
        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);
                chan->doUnhookExtensions(items);
-               const UserMembList* users = chan->GetUsers();
-               for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
+               const Channel::MemberMap& users = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator mi = users.begin(); mi != users.end(); ++mi)
                        mi->second->doUnhookExtensions(items);
        }
-       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
@@ -370,16 +375,25 @@ void ModuleManager::DoSafeUnload(Module* mod)
                mod->OnCleanup(TYPE_USER, user);
                user->doUnhookExtensions(items);
        }
-       for(char m='A'; m <= 'z'; m++)
+
+       const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
+       for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); )
+       {
+               ModeHandler* mh = i->second;
+               ++i;
+               if (mh->creator == mod)
+                       this->DelService(*mh);
+       }
+
+       const ModeParser::ModeHandlerMap& chanmodes = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+       for (ModeParser::ModeHandlerMap::const_iterator i = chanmodes.begin(); i != chanmodes.end(); )
        {
-               ModeHandler* mh;
-               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);
+               ModeHandler* mh = i->second;
+               ++i;
+               if (mh->creator == mod)
+                       this->DelService(*mh);
        }
+
        for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
        {
                std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
@@ -389,19 +403,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 +435,105 @@ 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 = tag->getString("name");
+               this->NewServices = &servicemap[ExpandModName(name)];
+               std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
+
+               if (!this->Load(name, true))
+               {
+                       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 +541,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 +556,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 +586,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,178 +653,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)
-{
-       std::string result = default_value;
-       if (!SlowGetTag(tag, index)->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");
-       return SlowGetTag(tag, index)->getBool(name, 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());
-       int result = SlowGetTag(tag, index)->getInt(name, 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()
+void ModuleManager::AddReferent(const std::string& name, ServiceProvider* service)
 {
-       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..d21a82149b423cbbe84fca796fff24f5dbddff5f 100644 (file)
@@ -27,7 +27,6 @@
 # pragma comment(lib, "GeoIP.lib")
 #endif
 
-/* $ModDesc: Provides a way to restrict users by country using GeoIP lookup */
 /* $LinkerFlags: -lGeoIP */
 
 class ModuleGeoIP : public Module
@@ -37,7 +36,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 +46,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 +75,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 +97,16 @@ class ModuleGeoIP : public Module
                return MOD_RES_DENY;
        }
 
-       ModResult OnStats(char symbol, User* user, string_list &out)
+       ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
        {
                if (symbol != 'G')
                        return MOD_RES_PASSTHRU;
 
                unsigned int unknown = 0;
                std::map<std::string, unsigned int> results;
-               for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+
+               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,7 +115,7 @@ class ModuleGeoIP : public Module
                                unknown++;
                }
 
-               std::string p = ServerInstance->Config->ServerName + " 801 " + user->nick + " :GeoIPSTATS ";
+               std::string p = "801 " + user->nick + " :GeoIPSTATS ";
                for (std::map<std::string, unsigned int>::const_iterator i = results.begin(); i != results.end(); ++i)
                {
                        out.push_back(p + i->first + " " + ConvToStr(i->second));
@@ -129,4 +129,3 @@ class ModuleGeoIP : public Module
 };
 
 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..c110258
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * 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/>.
+ */
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+#include <ldap.h>
+
+#ifdef _WIN32
+# pragma comment(lib, "libldap_r.lib")
+# pragma comment(lib, "liblber.lib")
+#endif
+
+/* $LinkerFlags: -lldap_r */
+
+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;
+       time_t last_timeout_check;
+
+ 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), last_timeout_check(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->getInt("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 6c765fb..0000000
+++ /dev/null
@@ -1,436 +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 = (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)
-               {
-                       // Do a second search, based on password, if it contains a :
-                       // That is, PASS <user>:<password> will work.
-                       size_t pos = user->password.find(":");
-                       if (pos != std::string::npos)
-                       {
-                               // manpage says we must deallocate regardless of success or failure
-                               // since we're about to do another query (and reset msg), first
-                               // free the old one.
-                               msg.dealloc();
-
-                               std::string cutpassword = user->password.substr(0, pos);
-                               res = ldap_search_ext_s(conn, base.c_str(), searchscope, cutpassword.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg);
-
-                               if (res == LDAP_SUCCESS)
-                               {
-                                       // Trim the user: prefix, leaving just 'pass' for later password check
-                                       user->password = user->password.substr(pos + 1);
-                               }
-                       }
-
-                       // It may have found based on user:pass check above.
-                       if (res != 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))
-                       {
-                               std::string::size_type 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)
index 598f9aac9c88ae5ef4045a327d7ae180d5d331f8..0e8c8cf5528cd768421e51b62073ac39870e6a15 100644 (file)
 #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 insp::flat_map<std::string, SQLConn*> ConnMap;
 typedef std::deque<MsSQLResult*> ResultQueue;
 
 unsigned long count(const char * const str, char a)
@@ -64,8 +59,8 @@ class QueryThread : public SocketThread
   public:
        QueryThread(ModuleMsSQL* mod) : Parent(mod) { }
        ~QueryThread() { }
-       virtual void Run();
-       virtual void OnNotify();
+       void Run();
+       void OnNotify();
 };
 
 class MsSQLResult : public SQLresult
@@ -88,10 +83,6 @@ class MsSQLResult : public SQLresult
        {
        }
 
-       ~MsSQLResult()
-       {
-       }
-
        void AddRow(int colsnum, char **dat, char **colname)
        {
                colnames.clear();
@@ -111,17 +102,17 @@ class MsSQLResult : public SQLresult
                rows++;
        }
 
-       virtual int Rows()
+       int Rows()
        {
                return rows;
        }
 
-       virtual int Cols()
+       int Cols()
        {
                return cols;
        }
 
-       virtual std::string ColName(int column)
+       std::string ColName(int column)
        {
                if (column < (int)colnames.size())
                {
@@ -134,7 +125,7 @@ class MsSQLResult : public SQLresult
                return "";
        }
 
-       virtual int ColNum(const std::string &column)
+       int ColNum(const std::string &column)
        {
                for (unsigned int i = 0; i < colnames.size(); i++)
                {
@@ -145,7 +136,7 @@ class MsSQLResult : public SQLresult
                return 0;
        }
 
-       virtual SQLfield GetValue(int row, int column)
+       SQLfield GetValue(int row, int column)
        {
                if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
                {
@@ -158,7 +149,7 @@ class MsSQLResult : public SQLresult
                return SQLfield("",true);
        }
 
-       virtual SQLfieldList& GetRow()
+       SQLfieldList& GetRow()
        {
                if (currentrow < rows)
                        return fieldlists[currentrow];
@@ -166,7 +157,7 @@ class MsSQLResult : public SQLresult
                        return emptyfieldlist;
        }
 
-       virtual SQLfieldMap& GetRowMap()
+       SQLfieldMap& GetRowMap()
        {
                /* In an effort to reduce overhead we don't actually allocate the map
                 * until the first time it's needed...so...
@@ -192,7 +183,7 @@ class MsSQLResult : public SQLresult
                return *fieldmap;
        }
 
-       virtual SQLfieldList* GetRowPtr()
+       SQLfieldList* GetRowPtr()
        {
                fieldlist = new SQLfieldList();
 
@@ -207,7 +198,7 @@ class MsSQLResult : public SQLresult
                return fieldlist;
        }
 
-       virtual SQLfieldMap* GetRowMapPtr()
+       SQLfieldMap* GetRowMapPtr()
        {
                fieldmap = new SQLfieldMap();
 
@@ -223,12 +214,12 @@ class MsSQLResult : public SQLresult
                return fieldmap;
        }
 
-       virtual void Free(SQLfieldMap* fm)
+       void Free(SQLfieldMap* fm)
        {
                delete fm;
        }
 
-       virtual void Free(SQLfieldList* fl)
+       void Free(SQLfieldList* fl)
        {
                delete fl;
        }
@@ -258,7 +249,7 @@ class SQLConn : public classbase
                                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);
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
                                        LoggingMutex->Unlock();
                                        CloseDB();
                                }
@@ -266,7 +257,7 @@ class SQLConn : public classbase
                        else
                        {
                                LoggingMutex->Lock();
-                               ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
                                LoggingMutex->Unlock();
                                CloseDB();
                        }
@@ -274,7 +265,7 @@ class SQLConn : public classbase
                else
                {
                        LoggingMutex->Lock();
-                       ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
                        LoggingMutex->Unlock();
                        CloseDB();
                }
@@ -433,7 +424,7 @@ class SQLConn : public classbase
 
                char* msquery = strdup(req->query.q.data());
                LoggingMutex->Lock();
-               ServerInstance->Logs->Log("m_mssql",DEBUG,"doing Query: %s",msquery);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "doing Query: %s",msquery);
                LoggingMutex->Unlock();
                if (tds_submit_query(sock, msquery) != TDS_SUCCEED)
                {
@@ -449,8 +440,8 @@ class SQLConn : public classbase
                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);
+                       //ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "<******> result type: %d", tds_res);
+                       //ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "AFFECTED ROWS: %d", sock->rows_affected);
                        switch (tds_res)
                        {
                                case TDS_ROWFMT_RESULT:
@@ -476,8 +467,8 @@ class SQLConn : public classbase
                                                if (sock->res_info->row_count > 0)
                                                {
                                                        int cols = sock->res_info->num_cols;
-                                                       char** name = new char*[MAXBUF];
-                                                       char** data = new char*[MAXBUF];
+                                                       char** name = new char*[512];
+                                                       char** data = new char*[512];
                                                        for (int j=0; j<cols; j++)
                                                        {
                                                                TDSCOLUMN* col = sock->current_results->columns[j];
@@ -516,7 +507,7 @@ class SQLConn : public classbase
        {
                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);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
                LoggingMutex->Unlock();
                return 0;
        }
@@ -525,7 +516,7 @@ class SQLConn : public classbase
        {
                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);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
                LoggingMutex->Unlock();
                return 0;
        }
@@ -657,18 +648,14 @@ class ModuleMsSQL : public Module
                queryDispatcher = new QueryThread(this);
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ReadConf();
 
-               ServerInstance->Threads->Start(queryDispatcher);
-
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               ServerInstance->Modules->AddService(sqlserv);
+               ServerInstance->Threads.Start(queryDispatcher);
        }
 
-       virtual ~ModuleMsSQL()
+       ~ModuleMsSQL()
        {
                queryDispatcher->join();
                delete queryDispatcher;
@@ -753,7 +740,7 @@ class ModuleMsSQL : public Module
                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());
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
                        LoggingMutex->Unlock();
                        return;
                }
@@ -787,14 +774,14 @@ class ModuleMsSQL : public Module
                connections.clear();
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                queryDispatcher->LockQueue();
                ReadConf();
                queryDispatcher->UnlockQueueWakeup();
        }
 
-       void OnRequest(Request& request)
+       void OnRequest(Request& request) CXX11_OVERRIDE
        {
                if(strcmp(SQLREQID, request.id) == 0)
                {
@@ -825,7 +812,7 @@ class ModuleMsSQL : public Module
                return ++currid;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("MsSQL provider", VF_VENDOR);
        }
index 01b1553b0b1d36b0e15433f5b8bb17793ca22cd7..d8dda27a4ef6c91cb4d609f32e8ddfe97ea707ec 100644 (file)
@@ -25,7 +25,7 @@
 
 #include "inspircd.h"
 #include <mysql.h>
-#include "sql.h"
+#include "modules/sql.h"
 
 #ifdef _WIN32
 # pragma comment(lib, "libmysql.lib")
@@ -33,7 +33,6 @@
 
 /* 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") */
 
@@ -90,7 +89,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;
 
@@ -105,11 +104,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
@@ -119,8 +118,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
@@ -186,21 +185,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()))
                {
@@ -209,7 +204,7 @@ class MySQLresult : public SQLResult
                return SQLEntry();
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQLEntries& result)
        {
                if (currentrow < rows)
                {
@@ -260,6 +255,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))
                {
@@ -383,12 +384,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()
@@ -405,7 +401,7 @@ ModuleSQL::~ModuleSQL()
        }
 }
 
-void ModuleSQL::OnRehash(User* user)
+void ModuleSQL::ReadConfig(ConfigStatus& status)
 {
        ConnMap conns;
        ConfigTagList tags = ServerInstance->Config->ConfTags("database");
index ac247548ac35c303a805734c483f112bb0c3f3f5..ff8c1174c8e72937a93c6f800d5b4c7d6beabee3 100644 (file)
 
 #include "inspircd.h"
 #include <cstdlib>
-#include <sstream>
 #include <libpq-fe.h>
-#include "sql.h"
+#include "modules/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 */
 
@@ -43,7 +41,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 +57,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 +95,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 +109,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 +118,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,7 +150,7 @@ 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();
                }
        }
@@ -180,18 +178,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 +241,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 +256,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 +349,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:
@@ -417,7 +416,7 @@ restart:
                                        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");
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
 #else
                                        size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
 #endif
@@ -452,7 +451,7 @@ restart:
                                        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");
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
 #else
                                        size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
 #endif
@@ -488,7 +487,7 @@ restart:
 
        void Close()
        {
-               ServerInstance->SE->DelFd(this);
+               SocketEngine::DelFd(this);
 
                if(sql)
                {
@@ -505,25 +504,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 +555,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 +583,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 +608,7 @@ void SQLConn::DelayReconnect()
                if (!mod->retimer)
                {
                        mod->retimer = new ReconnectTimer(mod);
-                       ServerInstance->Timers->AddTimer(mod->retimer);
+                       ServerInstance->Timers.AddTimer(mod->retimer);
                }
        }
 }
index cba234c8cae74977c9930bbfde1a462ea63649f0..9ae6719ba96a89d35adc9d67ca7d5c5f7bd6932f 100644 (file)
 
 #include "inspircd.h"
 #include <pcre.h>
-#include "m_regex.h"
+#include "modules/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 */
 
 # 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 +41,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 +61,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 +69,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..c4657bf
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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/>.
+ */
+
+
+#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>
+
+/* $LinkerFlags: -lre2 */
+
+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..8e7bd0da25f99375ca7ab2b5304dc44c89805aa2 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 "modules/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 */
-
-class StdRegexException : public ModuleException
-{
-public:
-       StdRegexException(const std::string& rx, const std::string& error)
-               : ModuleException(std::string("Error in regex ") + rx + ": " + error)
-       {
-       }
-};
 
 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 +34,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 +49,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 +59,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..8a1d5424880d6d479ea9286494a2a0ec921f5433 100644 (file)
 
 
 #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 +48,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 +76,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 1e3a65a18fdea27e4483daff11f05fd366534116..05203da39c6ae7fb464a33ffe33f7ed3781cb047 100644 (file)
 
 
 #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 +54,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 +74,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 +82,6 @@ class SQLite3Result : public SQLResult
 
 class SQLConn : public SQLProvider
 {
- private:
        sqlite3* conn;
        reference<ConfigTag> config;
 
@@ -90,7 +91,7 @@ class SQLConn : public SQLProvider
                std::string host = tag->getString("hostname");
                if (sqlite3_open_v2(host.c_str(), &conn, SQLITE_OPEN_READWRITE, 0) != SQLITE_OK)
                {
-                       ServerInstance->Logs->Log("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"));
                        conn = NULL;
                }
        }
@@ -152,13 +153,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;
@@ -179,7 +180,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++)
@@ -209,23 +210,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();
        }
@@ -241,7 +229,7 @@ class ModuleSQLite3 : public Module
                conns.clear();
        }
 
-       void ReadConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ClearConns();
                ConfigTagList tags = ServerInstance->Config->ConfTags("database");
@@ -255,12 +243,7 @@ class ModuleSQLite3 : public Module
                }
        }
 
-       void OnRehash(User* user)
-       {
-               ReadConf();
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("sqlite3 provider", VF_VENDOR);
        }
index 813a8ecfaa98990698339c32ac17ff15dd660ffa..6a653ddedd60f6f2dbb94e95be4097d4be8f7f94 100644 (file)
 
 
 #include "inspircd.h"
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#include "ssl.h"
-#include "m_cap.h"
-
-#ifdef _WIN32
-# pragma comment(lib, "libgnutls-30.lib")
+#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;
-#endif
-
-       SSLConfig()
-               : x509_cred(NULL)
-               , x509_key(NULL)
-               , dh_params(NULL)
-#ifdef GNUTLS_NEW_PRIO_API
-               , priority(NULL)
+#ifdef _WIN32
+# pragma comment(lib, "libgnutls-30.lib")
 #endif
-       {
-       }
-
-       ~SSLConfig()
-       {
-               ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Destroying SSLConfig %p", (void*)this);
 
-               if (x509_cred)
-                       gnutls_certificate_free_credentials(x509_cred);
+/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") eval("print `libgcrypt-config --cflags | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
+/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") eval("print `libgcrypt-config --libs | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
 
-               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);
+// These don't exist in older GnuTLS versions
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 1, 7)
+#define GNUTLS_NEW_PRIO_API
+#endif
 
-#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
 
 class RandGen : public HandlerBase2<void, char*, size_t>
 {
  public:
-       RandGen() {}
        void Call(char* buffer, size_t len)
        {
 #ifdef GNUTLS_HAS_RND
@@ -143,842 +114,1252 @@ class RandGen : public HandlerBase2<void, char*, size_t>
        }
 };
 
-/** Represents an SSL user's extra data
- */
-class issl_session
-{
-public:
-       StreamSocket* socket;
-       gnutls_session_t sess;
-       issl_status status;
-       reference<ssl_cert> cert;
-       reference<SSLConfig> config;
-
-       issl_session() : socket(NULL), sess(NULL), status(ISSL_NONE) {}
-};
-
-static SSLConfig* GetSessionConfig(gnutls_session_t sess)
+namespace GnuTLS
 {
-       issl_session* session = reinterpret_cast<issl_session*>(gnutls_transport_get_ptr(sess));
-       return session->config;
-}
+       class Init
+       {
+        public:
+               Init() { gnutls_global_init(); }
+               ~Init() { gnutls_global_deinit(); }
+       };
 
-class CommandStartTLS : public SplitCommand
-{
- public:
-       bool enabled;
-       CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS")
+       class Exception : public ModuleException
        {
-               enabled = true;
-               works_before_reg = true;
-       }
+        public:
+               Exception(const std::string& reason)
+                       : ModuleException(reason) { }
+       };
 
-       CmdResult HandleLocal(const std::vector<std::string> &parameters, LocalUser *user)
+       void ThrowOnError(int errcode, const char* msg)
        {
-               if (!enabled)
+               if (errcode < 0)
                {
-                       user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
-                       return CMD_FAILURE;
+                       std::string reason = msg;
+                       reason.append(" :").append(gnutls_strerror(errcode));
+                       throw Exception(reason);
                }
+       }
 
-               if (user->registered == REG_ALL)
-               {
-                       user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
-               }
-               else
+       /** Used to create a gnutls_datum_t* from a std::string
+        */
+       class Datum
+       {
+               gnutls_datum_t datum;
+
+        public:
+               Datum(const std::string& dat)
                {
-                       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());
+                       datum.data = (unsigned char*)dat.data();
+                       datum.size = static_cast<unsigned int>(dat.length());
                }
 
-               return CMD_FAILURE;
-       }
-};
-
-class ModuleSSLGnuTLS : public Module
-{
-       issl_session* sessions;
-
-       gnutls_digest_algorithm_t hash;
+               const gnutls_datum_t* get() const { return &datum; }
+       };
 
-       std::string sslports;
-       int dh_bits;
+       class Hash
+       {
+               gnutls_digest_algorithm_t hash;
 
-       RandGen randhandler;
-       CommandStartTLS starttls;
+        public:
+               // Nothing to deallocate, constructor may throw freely
+               Hash(const std::string& hashname)
+               {
+                       // 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
+                       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
+               }
 
-       GenericCap capHandler;
-       ServiceProvider iohook;
+               gnutls_digest_algorithm_t get() const { return hash; }
+       };
 
-       inline static const char* UnknownIfNULL(const char* str)
+       class DHParams
        {
-               return str ? str : "UNKNOWN";
-       }
+               gnutls_dh_params_t dh_params;
 
-       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)
+               DHParams()
                {
-#ifdef _WIN32
-                       gnutls_transport_set_errno(session->sess, EAGAIN);
-#else
-                       errno = EAGAIN;
-#endif
-                       return -1;
+                       ThrowOnError(gnutls_dh_params_init(&dh_params), "gnutls_dh_params_init() failed");
                }
 
-               int rv = ServerInstance->SE->Recv(session->socket, reinterpret_cast<char *>(buffer), size, 0);
+        public:
+               /** Import */
+               static std::auto_ptr<DHParams> Import(const std::string& dhstr)
+               {
+                       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;
+               }
 
-#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_READ_WILL_BLOCK);
-               return rv;
-       }
+               const gnutls_dh_params_t& get() const { return dh_params; }
+       };
 
-       static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
+       class X509Key
        {
-               issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
-               if (session->socket->GetEventMask() & FD_WRITE_WILL_BLOCK)
+               /** Ensure that the key is deinited in case the constructor of X509Key throws
+                */
+               class RAIIKey
                {
-#ifdef _WIN32
-                       gnutls_transport_set_errno(session->sess, EAGAIN);
-#else
-                       errno = EAGAIN;
-#endif
-                       return -1;
-               }
+                public:
+                       gnutls_x509_privkey_t key;
 
-               int rv = ServerInstance->SE->Send(session->socket, reinterpret_cast<const char *>(buffer), size, 0);
+                       RAIIKey()
+                       {
+                               ThrowOnError(gnutls_x509_privkey_init(&key), "gnutls_x509_privkey_init() failed");
+                       }
 
-#ifdef _WIN32
-               if (rv < 0)
+                       ~RAIIKey()
+                       {
+                               gnutls_x509_privkey_deinit(key);
+                       }
+               } key;
+
+        public:
+               /** Import */
+               X509Key(const std::string& keystr)
                {
-                       /* 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);
+                       int ret = gnutls_x509_privkey_import(key.key, Datum(keystr).get(), GNUTLS_X509_FMT_PEM);
+                       ThrowOnError(ret, "Unable to import private key");
                }
-#endif
-
-               if (rv < (int)size)
-                       ServerInstance->SE->ChangeEventMask(session->socket, FD_WRITE_WILL_BLOCK);
-               return rv;
-       }
 
- public:
+               gnutls_x509_privkey_t& get() { return key.key; }
+       };
 
-       ModuleSSLGnuTLS()
-               : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK)
+       class X509CertList
        {
-#ifndef GNUTLS_HAS_RND
-               gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
+               std::vector<gnutls_x509_crt_t> certs;
 
-               sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+        public:
+               /** Import */
+               X509CertList(const std::string& certstr)
+               {
+                       unsigned int certcount = 3;
+                       certs.resize(certcount);
+                       Datum datum(certstr);
 
-               gnutls_global_init(); // This must be called once in the program
-       }
+                       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)
+                       {
+                               // 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 init()
-       {
-               currconf = new SSLConfig;
-               InitSSLConfig(currconf);
+                       ThrowOnError(ret, "Unable to load certificates");
 
-               ServerInstance->GenRandom = &randhandler;
+                       // Resize the vector to the actual number of certs because we rely on its size being correct
+                       // when deallocating the certs
+                       certs.resize(certcount);
+               }
 
-               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));
+               ~X509CertList()
+               {
+                       for (std::vector<gnutls_x509_crt_t>::iterator i = certs.begin(); i != certs.end(); ++i)
+                               gnutls_x509_crt_deinit(*i);
+               }
 
-               ServerInstance->Modules->AddService(iohook);
-               ServerInstance->Modules->AddService(starttls);
-       }
+               gnutls_x509_crt_t* raw() { return &certs[0]; }
+               unsigned int size() const { return certs.size(); }
+       };
 
-       void OnRehash(User* user)
+       class X509CRL : public refcountbase
        {
-               sslports.clear();
+               class RAIICRL
+               {
+                public:
+                       gnutls_x509_crl_t crl;
+
+                       RAIICRL()
+                       {
+                               ThrowOnError(gnutls_x509_crl_init(&crl), "gnutls_x509_crl_init() failed");
+                       }
+
+                       ~RAIICRL()
+                       {
+                               gnutls_x509_crl_deinit(crl);
+                       }
+               } crl;
+
+        public:
+               /** Import */
+               X509CRL(const std::string& crlstr)
+               {
+                       int ret = gnutls_x509_crl_import(get(), Datum(crlstr).get(), GNUTLS_X509_FMT_PEM);
+                       ThrowOnError(ret, "Unable to load certificate revocation list");
+               }
+
+               gnutls_x509_crl_t& get() { return crl.crl; }
+       };
 
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
-               starttls.enabled = Conf->getBool("starttls", true);
+#ifdef GNUTLS_NEW_PRIO_API
+       class Priority
+       {
+               gnutls_priority_t priority;
 
-               if (Conf->getBool("showports", true))
+        public:
+               Priority(const std::string& priorities)
                {
-                       sslports = Conf->getString("advertisedports");
-                       if (!sslports.empty())
-                               return;
+                       // 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;
 
-                       for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+                       int ret = gnutls_priority_init(&priority, priocstr, &prioerror);
+                       if (ret < 0)
                        {
-                               ListenSocket* port = ServerInstance->ports[i];
-                               if (port->bind_tag->getString("ssl") != "gnutls")
-                                       continue;
+                               // 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)));
+                       }
+               }
+
+               ~Priority()
+               {
+                       gnutls_priority_deinit(priority);
+               }
 
-                               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());
+               void SetupSession(gnutls_session_t sess)
+               {
+                       gnutls_priority_set(sess, priority);
+               }
+
+               static const char* GetDefault()
+               {
+                       return "NORMAL:%SERVER_PRECEDENCE:-VERS-SSL3.0";
+               }
 
-                               if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+               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
                                {
-                                       /*
-                                        * 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;
+                                       // Worked
+                                       gnutls_priority_deinit(test);
                                }
                        }
+                       return ret;
                }
-       }
-
-       void OnModuleRehash(User* user, const std::string &param)
+       };
+#else
+       /** Dummy class, used when gnutls_priority_set() is not available
+        */
+       class Priority
        {
-               if(param != "ssl")
-                       return;
+        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");
+               }
 
-               reference<SSLConfig> newconf = new SSLConfig;
-               try
+               static void SetupSession(gnutls_session_t sess)
                {
-                       InitSSLConfig(newconf);
+                       // Always set the default priorities
+                       gnutls_set_default_priority(sess);
                }
-               catch (ModuleException& ex)
+
+               static const char* GetDefault()
                {
-                       ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Not applying new config. %s", ex.GetReason());
-                       return;
+                       return "NORMAL";
                }
 
-               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;
-       }
+               static std::string RemoveUnknownTokens(const std::string& prio)
+               {
+                       // We don't do anything here because only NORMAL is accepted
+                       return prio;
+               }
+       };
+#endif
 
-       void InitSSLConfig(SSLConfig* config)
+       class CertCredentials
        {
-               ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Initializing new SSLConfig %p", (void*)config);
+               /** DH parameters associated with these credentials
+                */
+               std::auto_ptr<DHParams> dh;
 
-               std::string keyfile;
-               std::string certfile;
-               std::string cafile;
-               std::string crlfile;
-               OnRehash(NULL);
+        protected:
+               gnutls_certificate_credentials_t cred;
 
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
+        public:
+               CertCredentials()
+               {
+                       ThrowOnError(gnutls_certificate_allocate_credentials(&cred), "Cannot allocate certificate credentials");
+               }
 
-               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");
+               ~CertCredentials()
+               {
+                       gnutls_certificate_free_credentials(cred);
+               }
 
-               // 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");
+               /** Associates these credentials with the session
+                */
+               void SetupSession(gnutls_session_t sess)
+               {
+                       gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred);
+               }
 
-               if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
-                       dh_bits = 1024;
+               /** 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());
+               }
+       };
 
-               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 X509Credentials : public CertCredentials
+       {
+               /** Private key
+                */
+               X509Key key;
 
+               /** Certificate list, presented to the peer
+                */
+               X509CertList certs;
 
-               int ret;
+               /** Trusted CA, may be NULL
+                */
+               std::auto_ptr<X509CertList> trustedca;
 
-               gnutls_certificate_credentials_t& x509_cred = config->x509_cred;
+               /** Certificate revocation list, may be NULL
+                */
+               std::auto_ptr<X509CRL> crl;
 
-               ret = gnutls_certificate_allocate_credentials(&x509_cred);
-               if (ret < 0)
+               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);
+
+        public:
+               X509Credentials(const std::string& certstr, const std::string& keystr)
+                       : key(keystr)
+                       , certs(certstr)
                {
-                       // 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)));
-               }
+                       // 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((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));
+#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
+               }
 
-               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));
+               /** 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");
 
-               FileReader reader;
+                               if (CRL.get())
+                               {
+                                       ret = gnutls_certificate_set_x509_crl(cred, &CRL->get(), 1);
+                                       ThrowOnError(ret, "gnutls_certificate_set_x509_crl() failed");
+                               }
 
-               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()) };
+                               trustedca = certlist;
+                               crl = CRL;
+                       }
+               }
+       };
 
-               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()) };
+       class DataReader
+       {
+               int retval;
+#ifdef INSPIRCD_GNUTLS_HAS_RECV_PACKET
+               gnutls_packet_t packet;
 
-               std::vector<gnutls_x509_crt_t>& x509_certs = config->x509_certs;
+        public:
+               DataReader(gnutls_session_t sess)
+               {
+                       // Using the packet API avoids the final copy of the data which GnuTLS does if we supply
+                       // our own buffer. Instead, we get the buffer containing the data from GnuTLS and copy it
+                       // to the recvq directly from there in appendto().
+                       retval = gnutls_record_recv_packet(sess, &packet);
+               }
 
-               // 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)
+               void appendto(std::string& recvq)
                {
-                       // 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);
+                       // Copy data from GnuTLS buffers to recvq
+                       gnutls_datum_t datum;
+                       gnutls_packet_get(packet, &datum, NULL);
+                       recvq.append(reinterpret_cast<const char*>(datum.data), datum.size);
+
+                       gnutls_packet_deinit(packet);
                }
+#else
+               char* const buffer;
 
-               if (ret <= 0)
+        public:
+               DataReader(gnutls_session_t sess)
+                       : buffer(ServerInstance->GetReadBuffer())
                {
-                       // 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"));
+                       // Read data from GnuTLS buffers into ReadBuffer
+                       retval = gnutls_record_recv(sess, buffer, ServerInstance->Config->NetBufferSize);
                }
-               x509_certs.resize(ret);
 
-               gnutls_x509_privkey_t& x509_key = config->x509_key;
-               if (gnutls_x509_privkey_init(&x509_key) < 0)
+               void appendto(std::string& recvq)
                {
-                       // Make sure the destructor does not try to deallocate this, see above
-                       x509_key = NULL;
-                       throw ModuleException("Unable to initialize private key");
+                       // Copy data from ReadBuffer to recvq
+                       recvq.append(buffer, retval);
                }
+#endif
 
-               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)));
+               int ret() const { return retval; }
+       };
 
-               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)));
+       class Profile : public refcountbase
+       {
+               /** Name of this profile
+                */
+               const std::string name;
 
-               #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;
+               /** X509 certificate(s) and key
+                */
+               X509Credentials x509cred;
 
-               gnutls_priority_t& priority = config->priority;
-               if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0)
-               {
-                       // 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);
-               }
+               /** The minimum length in bits for the DH prime to be accepted as a client
+                */
+               unsigned int min_dh_bits;
 
-               #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
+               /** Hashing algorithm to use when generating certificate fingerprints
+                */
+               Hash hash;
 
-               #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
+               /** Priorities for ciphers, compression methods, etc.
+                */
+               Priority priority;
 
-               gnutls_dh_params_t& dh_params = config->dh_params;
-               ret = gnutls_dh_params_init(&dh_params);
-               if (ret < 0)
+               /** Rough max size of records to send
+                */
+               const unsigned int outrecsize;
+
+               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)
+                       : name(profilename)
+                       , x509cred(certstr, keystr)
+                       , min_dh_bits(mindh)
+                       , hash(hashstr)
+                       , priority(priostr)
+                       , outrecsize(recsize)
                {
-                       // 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;
+                       x509cred.SetDH(DH);
+                       x509cred.SetCA(CA, CRL);
                }
 
-               std::string dhfile = Conf->getString("dhfile");
-               if (!dhfile.empty())
+               static std::string ReadFile(const std::string& filename)
                {
-                       // 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()) };
+                       FileReader reader(filename);
+                       std::string ret = reader.GetString();
+                       if (ret.empty())
+                               throw Exception("Cannot read file " + filename);
+                       return ret;
+               }
 
-                       if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0)
+               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))
                        {
-                               // 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);
+                               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;
                }
-               else
+
+        public:
+               static reference<Profile> Create(const std::string& profilename, ConfigTag* tag)
                {
-                       GenerateDHParams(dh_params);
-               }
+                       std::string certstr = ReadFile(tag->getString("certfile", "cert.pem"));
+                       std::string keystr = ReadFile(tag->getString("keyfile", "key.pem"));
 
-               gnutls_certificate_set_dh_params(x509_cred, dh_params);
-       }
+                       std::auto_ptr<DHParams> dh = DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem")));
 
-       void GenerateDHParams(gnutls_dh_params_t dh_params)
-       {
-               // 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.
+                       std::string priostr = GetPrioStr(profilename, tag);
+                       unsigned int mindh = tag->getInt("mindhbits", 1024);
+                       std::string hashstr = tag->getString("hash", "md5");
 
-               int ret;
+                       // 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())
+                       {
+                               ca.reset(new X509CertList(ReadFile(filename)));
 
-               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));
-       }
+                               filename = tag->getString("crlfile");
+                               if (!filename.empty())
+                                       crl.reset(new X509CRL(ReadFile(filename)));
+                       }
 
-       ~ModuleSSLGnuTLS()
-       {
-               currconf = NULL;
+#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
+                       return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl, outrecsize);
+               }
 
-               gnutls_global_deinit();
-               delete[] sessions;
-               ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
+               /** Set up the given session with the settings in this profile
+                */
+               void SetupSession(gnutls_session_t sess)
+               {
+                       priority.SetupSession(sess);
+                       x509cred.SetupSession(sess);
+                       gnutls_dh_set_prime_bits(sess, min_dh_bits);
+
+                       // Request client certificate if we are a server, no-op if we're a client
+                       gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST);
+               }
+
+               const std::string& GetName() const { return name; }
+               X509Credentials& GetX509Credentials() { return x509cred; }
+               gnutls_digest_algorithm_t GetHash() const { return hash.get(); }
+               unsigned int GetOutgoingRecordSize() const { return outrecsize; }
+       };
+}
+
+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
+
+       void CloseSession()
+       {
+               if (this->sess)
+               {
+                       gnutls_bye(this->sess, GNUTLS_SHUT_WR);
+                       gnutls_deinit(this->sess);
+               }
+               sess = NULL;
+               certificate = NULL;
+               status = ISSL_NONE;
        }
 
-       void OnCleanup(int target_type, void* item)
+       // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+       int Handshake(StreamSocket* user)
        {
-               if(target_type == TYPE_USER)
+               int ret = gnutls_handshake(this->sess);
+
+               if (ret < 0)
                {
-                       LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+                       if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+                       {
+                               // Handshake needs resuming later, read() or write() would have blocked.
+                               this->status = ISSL_HANDSHAKING;
 
-                       if (user && user->eh.GetIOHook() == this)
+                               if (gnutls_record_get_direction(this->sess) == 0)
+                               {
+                                       // gnutls_handshake() wants to read() again.
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                               }
+                               else
+                               {
+                                       // gnutls_handshake() wants to write() again.
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+                               }
+
+                               return 0;
+                       }
+                       else
                        {
-                               // 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");
+                               user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
+                               CloseSession();
+                               return -1;
                        }
                }
-       }
+               else
+               {
+                       // Change the seesion state
+                       this->status = ISSL_HANDSHAKEN;
 
-       Version GetVersion()
-       {
-               return Version("Provides SSL support for clients", VF_VENDOR);
-       }
+                       VerifyCertificate();
 
+                       // Finish writing, if any left
+                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 
-       void On005Numeric(std::string &output)
-       {
-               if (!sslports.empty())
-                       output.append(" SSL=" + sslports);
-               if (starttls.enabled)
-                       output.append(" STARTTLS");
+                       return 1;
+               }
        }
 
-       void OnHookIO(StreamSocket* user, ListenSocket* lsb)
+       void VerifyCertificate()
        {
-               if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
+               unsigned int certstatus;
+               const gnutls_datum_t* cert_list;
+               int ret;
+               unsigned int cert_list_size;
+               gnutls_x509_crt_t cert;
+               char str[512];
+               unsigned char digest[512];
+               size_t digest_size = sizeof(digest);
+               size_t name_size = sizeof(str);
+               ssl_cert* certinfo = new ssl_cert;
+               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(this->sess, &certstatus);
+
+               if (ret < 0)
                {
-                       /* Hook the user with our module */
-                       user->AddIOHook(this);
+                       certinfo->error = std::string(gnutls_strerror(ret));
+                       return;
                }
-       }
 
-       void OnRequest(Request& request)
-       {
-               if (strcmp("GET_SSL_CERT", request.id) == 0)
+               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(this->sess) != GNUTLS_CRT_X509)
                {
-                       SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
-                       int fd = req.sock->GetFd();
-                       issl_session* session = &sessions[fd];
+                       certinfo->error = "No X509 keys sent";
+                       return;
+               }
 
-                       req.cert = session->cert;
+               ret = gnutls_x509_crt_init(&cert);
+               if (ret < 0)
+               {
+                       certinfo->error = gnutls_strerror(ret);
+                       return;
                }
-               else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+
+               cert_list_size = 0;
+               cert_list = gnutls_certificate_get_peers(this->sess, &cert_list_size);
+               if (cert_list == NULL)
                {
-                       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->error = "No certificate was found";
+                       goto info_done_dealloc;
                }
-       }
 
-       void InitSession(StreamSocket* user, bool me_server)
-       {
-               issl_session* session = &sessions[user->GetFd()];
+               /* This is not a real world example, since we only check the first
+                * certificate in the given chain.
+                */
+
+               ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
+               if (ret < 0)
+               {
+                       certinfo->error = gnutls_strerror(ret);
+                       goto info_done_dealloc;
+               }
+
+               if (gnutls_x509_crt_get_dn(cert, str, &name_size) == 0)
+               {
+                       std::string& dn = certinfo->dn;
+                       dn = str;
+                       // Make sure there are no chars in the string that we consider invalid
+                       if (dn.find_first_of("\r\n") != std::string::npos)
+                               dn.clear();
+               }
 
-               gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
-               session->socket = user;
-               session->config = currconf;
+               name_size = sizeof(str);
+               if (gnutls_x509_crt_get_issuer_dn(cert, str, &name_size) == 0)
+               {
+                       std::string& issuer = certinfo->issuer;
+                       issuer = str;
+                       if (issuer.find_first_of("\r\n") != std::string::npos)
+                               issuer.clear();
+               }
 
-               #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 ((ret = gnutls_x509_crt_get_fingerprint(cert, profile->GetHash(), digest, &digest_size)) < 0)
+               {
+                       certinfo->error = gnutls_strerror(ret);
+               }
+               else
+               {
+                       certinfo->fingerprint = BinToHex(digest, digest_size);
+               }
 
-               if (me_server)
-                       gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+               /* Beware here we do not check for errors.
+                */
+               if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
+               {
+                       certinfo->error = "Not activated, or expired certificate";
+               }
 
-               Handshake(session, user);
+info_done_dealloc:
+               gnutls_x509_crt_deinit(cert);
        }
 
-       void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+       // 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)
        {
-               issl_session* session = &sessions[user->GetFd()];
+               if (status == ISSL_HANDSHAKEN)
+                       return 1;
+               else if (status == ISSL_HANDSHAKING)
+               {
+                       // The handshake isn't finished, try to finish it
+                       return Handshake(sock);
+               }
 
-               /* For STARTTLS: Don't try and init a session on a socket that already has a session */
-               if (session->sess)
-                       return;
+               CloseSession();
+               sock->SetError("No SSL session");
+               return -1;
+       }
 
-               InitSession(user, true);
+#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
 
-       void OnStreamSocketConnect(StreamSocket* user)
+       int HandleWriteRet(StreamSocket* sock, int ret)
        {
-               InitSession(user, false);
+               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;
+               }
        }
 
-       void OnStreamSocketClose(StreamSocket* user)
+       static const char* UnknownIfNULL(const char* str)
        {
-               CloseSession(&sessions[user->GetFd()]);
+               return str ? str : "UNKNOWN";
        }
 
-       int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+       static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
        {
-               issl_session* session = &sessions[user->GetFd()];
+               StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+               GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+#endif
 
-               if (!session->sess)
+               if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
                {
-                       CloseSession(session);
-                       user->SetError("No SSL session");
+#ifdef _WIN32
+                       gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+                       errno = EAGAIN;
+#endif
                        return -1;
                }
 
-               if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
-               {
-                       // The handshake isn't finished, try to finish it.
+               int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0);
 
-                       if(!Handshake(session, user))
-                       {
-                               if (session->status != ISSL_CLOSING)
-                                       return 0;
-                               return -1;
-                       }
+#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 we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
+               if (rv < (int)size)
+                       SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+               return rv;
+       }
 
-               if (session->status == ISSL_HANDSHAKEN)
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+       static ssize_t VectorPush(gnutls_transport_ptr_t transportptr, const giovec_t* iov, int iovcnt)
+       {
+               StreamSocket* sock = reinterpret_cast<StreamSocket*>(transportptr);
+#ifdef _WIN32
+               GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+#endif
+
+               if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
                {
-                       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);
-                               return 1;
-                       }
-                       else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
-                       {
-                               return 0;
-                       }
-                       else if (ret == 0)
-                       {
-                               user->SetError("Connection closed");
-                               CloseSession(session);
-                               return -1;
-                       }
-                       else
-                       {
-                               user->SetError(gnutls_strerror(ret));
-                               CloseSession(session);
-                               return -1;
-                       }
-               }
-               else if (session->status == ISSL_CLOSING)
+#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;
 
-               return 0;
+               if (ret < size)
+                       SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+               return ret;
        }
 
-       int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
+#else // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+       static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
        {
-               issl_session* session = &sessions[user->GetFd()];
+               StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+               GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+#endif
 
-               if (!session->sess)
+               if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
                {
-                       CloseSession(session);
-                       user->SetError("No SSL session");
+#ifdef _WIN32
+                       gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+                       errno = EAGAIN;
+#endif
                        return -1;
                }
 
-               if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
+               int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0);
+
+#ifdef _WIN32
+               if (rv < 0)
                {
-                       // The handshake isn't finished, try to finish it.
-                       Handshake(session, user);
-                       if (session->status != ISSL_CLOSING)
-                               return 0;
-                       return -1;
+                       /* 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
 
-               int ret = 0;
+               if (rv < (int)size)
+                       SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+               return rv;
+       }
+#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 
-               if (session->status == ISSL_HANDSHAKEN)
-               {
-                       ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
+ 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);
 
-                       if (ret == (int)sendq.length())
+               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)
                        {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
+                               reader.appendto(recvq);
                                return 1;
                        }
-                       else if (ret > 0)
+                       else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
                        {
-                               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)
+                       else if (ret == 0)
                        {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
-                               return 0;
+                               user->SetError("Connection closed");
+                               CloseSession();
+                               return -1;
                        }
-                       else // (ret < 0)
+                       else
                        {
                                user->SetError(gnutls_strerror(ret));
-                               CloseSession(session);
+                               CloseSession();
                                return -1;
                        }
                }
-
-               return 0;
        }
 
-       bool Handshake(issl_session* session, StreamSocket* user)
+       int OnStreamSocketWrite(StreamSocket* user) CXX11_OVERRIDE
        {
-               int ret = gnutls_handshake(session->sess);
+               // Finish handshake if needed
+               int prepret = PrepareIO(user);
+               if (prepret <= 0)
+                       return prepret;
 
-               if (ret < 0)
+               // Session is ready for transferring application data
+               StreamSocket::SendQueue& sendq = user->GetSendQ();
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+               while (true)
                {
-                       if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+                       // 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()))
                        {
-                               // Handshake needs resuming later, read() or write() would have blocked.
-
-                               if(gnutls_record_get_direction(session->sess) == 0)
+                               const StreamSocket::SendQueue::Element& elem = sendq.front();
+                               gbuffersize += elem.length();
+                               ret = gnutls_record_send(this->sess, elem.data(), elem.length());
+                               if (ret < 0)
                                {
-                                       // gnutls_handshake() wants to read() again.
-                                       session->status = ISSL_HANDSHAKING_READ;
-                                       ServerInstance->SE->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);
+                                       CloseSession();
+                                       return -1;
                                }
+                               sendq.pop_front();
                        }
-                       else
+               }
+#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())
                        {
-                               user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
-                               CloseSession(session);
-                               session->status = ISSL_CLOSING;
+                               sendq.erase_front(ret);
+                               SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+                               return 0;
                        }
 
-                       return false;
+                       // Wrote entire record, continue sending
+                       sendq.pop_front();
                }
-               else
-               {
-                       // Change the seesion state
-                       session->status = ISSL_HANDSHAKEN;
+#endif
 
-                       VerifyCertificate(session,user);
+               SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
+               return 1;
+       }
 
-                       // Finish writing, if any left
-                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+       void TellCiphersAndFingerprint(LocalUser* user)
+       {
+               if (sess)
+               {
+                       std::string text = "*** You are connected using SSL cipher '";
+                       GetCiphersuite(text);
+                       text += '\'';
+                       if (!certificate->fingerprint.empty())
+                               text += " and your SSL certificate fingerprint is " + certificate->fingerprint;
 
-                       return true;
+                       user->WriteNotice(text);
                }
        }
 
-       void OnUserConnect(LocalUser* user)
+       void GetCiphersuite(std::string& out) const
        {
-               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());
-                       }
-               }
+               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))));
        }
 
-       void CloseSession(issl_session* session)
+       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->GetIOHook())->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)
        {
-               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;
+               ServerInstance->Modules->AddService(*this);
        }
 
-       void VerifyCertificate(issl_session* session, StreamSocket* user)
+       ~GnuTLSIOHookProvider()
        {
-               if (!session->sess || !user)
-                       return;
+               ServerInstance->Modules->DelService(*this);
+       }
 
-               unsigned int status;
-               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];
-               size_t digest_size = sizeof(digest);
-               size_t name_size = sizeof(name);
-               ssl_cert* certinfo = new ssl_cert;
-               session->cert = certinfo;
+       void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+       {
+               new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile);
+       }
 
-               /* 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);
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile);
+       }
+};
 
-               if (ret < 0)
-               {
-                       certinfo->error = std::string(gnutls_strerror(ret));
-                       return;
-               }
+class ModuleSSLGnuTLS : public Module
+{
+       typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
 
-               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);
+       // First member of the class, gets constructed first and destructed last
+       GnuTLS::Init libinit;
+       RandGen randhandler;
+       ProfileList profiles;
 
-               /* 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)
-               {
-                       certinfo->error = "No X509 keys sent";
-                       return;
-               }
+       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;
 
-               ret = gnutls_x509_crt_init(&cert);
-               if (ret < 0)
+               ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+               if (tags.first == tags.second)
                {
-                       certinfo->error = gnutls_strerror(ret);
-                       return;
+                       // 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());
+                       }
                }
 
-               cert_list_size = 0;
-               cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
-               if (cert_list == NULL)
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
-                       certinfo->error = "No certificate was found";
-                       goto info_done_dealloc;
-               }
+                       ConfigTag* tag = i->second;
+                       if (tag->getString("provider") != "gnutls")
+                               continue;
 
-               /* This is not a real world example, since we only check the first
-                * certificate in the given chain.
-                */
+                       std::string name = tag->getString("name");
+                       if (name.empty())
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+                               continue;
+                       }
 
-               ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
-               if (ret < 0)
-               {
-                       certinfo->error = gnutls_strerror(ret);
-                       goto info_done_dealloc;
-               }
+                       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());
+                       }
 
-               if (gnutls_x509_crt_get_dn(cert, name, &name_size) == 0)
-               {
-                       std::string& dn = certinfo->dn;
-                       dn = name;
-                       // 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();
+                       newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
                }
 
-               name_size = sizeof(name);
-               if (gnutls_x509_crt_get_issuer_dn(cert, name, &name_size) == 0)
-               {
-                       std::string& issuer = certinfo->issuer;
-                       issuer = name;
-                       if (issuer.find_first_of("\r\n") != std::string::npos)
-                               issuer.clear();
-               }
+               // 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
+       }
 
-               if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
+       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
                {
-                       certinfo->error = gnutls_strerror(ret);
+                       ReadProfiles();
                }
-               else
+               catch (ModuleException& ex)
                {
-                       certinfo->fingerprint = irc::hex(digest, digest_size);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
                }
+       }
 
-               /* Beware here we do not check for errors.
-                */
-               if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
+       ~ModuleSSLGnuTLS()
+       {
+               ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
+       }
+
+       void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+       {
+               if(target_type == TYPE_USER)
                {
-                       certinfo->error = "Not activated, or expired certificate";
+                       LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+
+                       if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == 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");
+                       }
                }
+       }
 
-info_done_dealloc:
-               gnutls_x509_crt_deinit(cert);
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides SSL support for clients", VF_VENDOR);
        }
 
-       void OnEvent(Event& ev)
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               if (starttls.enabled)
-                       capHandler.HandleEvent(ev);
+               IOHook* hook = user->eh.GetIOHook();
+               if (hook && hook->prov->creator == this)
+                       static_cast<GnuTLSIOHook*>(hook)->TellCiphersAndFingerprint(user);
        }
 
-       ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
-               if ((user->eh.GetIOHook() == this) && (sessions[user->eh.GetFd()].status != ISSL_HANDSHAKEN))
-                       return MOD_RES_DENY;
+               if ((user->eh.GetIOHook()) && (user->eh.GetIOHook()->prov->creator == this))
+               {
+                       GnuTLSIOHook* iohook = static_cast<GnuTLSIOHook*>(user->eh.GetIOHook());
+                       if (!iohook->IsHandshakeDone())
+                               return MOD_RES_DENY;
+               }
+
                return MOD_RES_PASSTHRU;
        }
 };
index b21091d3fddc6f2b6c5ca53bdd26ebd3a0b95c70..c9ae14e11bad0b31547494d3b19cd9d3a052d199 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
+
 #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") */
+/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
+/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto") */
 
-/* $NoPedantic */
-
-
-class ModuleSSLOpenSSL;
+#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
 
 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;
-       }
-};
+        public:
+               Exception(const std::string& reason)
+                       : ModuleException(reason) { }
+       };
 
-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 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);
 
-               // Clear everything
-               SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
+                       ctx_options = SSL_CTX_set_options(ctx, opts);
 
-               // 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);
-       }
+                       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_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+                       SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+                       SSL_CTX_set_info_callback(ctx, StaticSSLInfoCallback);
+               }
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
-       void SetupECDH(ConfigTag* tag)
-       {
-               std::string curvename = tag->getString("ecdhcurve", "prime256v1");
-               if (curvename.empty())
-                       return;
+               ~Context()
+               {
+                       SSL_CTX_free(ctx);
+               }
 
-               int nid = OBJ_sn2nid(curvename.c_str());
-               if (nid == 0)
+               bool SetDH(DHParams& dh)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unknown curve: \"%s\"", curvename.c_str());
-                       return;
+                       ERR_clear_error();
+                       return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
                }
 
-               EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
-               if (!eckey)
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+               void SetECDH(const std::string& curvename)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unable to create EC key object");
-                       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
 
-               ERR_clear_error();
-               if (SSL_CTX_set_tmp_ecdh(ctx, eckey) < 0)
+               bool SetCiphers(const std::string& ciphers)
                {
-                       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_set_cipher_list(ctx, ciphers.c_str());
                }
 
-               EC_KEY_free(eckey);
-       }
-#endif
+               bool SetCerts(const std::string& filename)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
+               }
 
-#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 SetPrivateKey(const std::string& filename)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
+               }
 
-               if ((where & SSL_CB_HANDSHAKE_START) && (session.status == ISSL_OPEN))
+               bool SetCA(const std::string& filename)
                {
-                       // The other side is trying to renegotiate, kill the connection and change status
-                       // to ISSL_NONE so CheckRenego() closes the session
-                       session.status = ISSL_NONE;
-                       ServerInstance->SE->Shutdown(fd, 2);
+                       ERR_clear_error();
+                       return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
                }
-       }
 
-       bool CheckRenego(StreamSocket* sock, issl_session* session)
-       {
-               if (session->status != ISSL_NONE)
-                       return true;
+               long GetDefaultContextOptions() const
+               {
+                       return ctx_options;
+               }
 
-               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
+               long SetRawContextOptions(long setoptions, long clearoptions)
+               {
+                       // Clear everything
+                       SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
 
- public:
+                       // 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);
+               }
 
-       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", "dh.pem")))
+                       , ctx(SSL_CTX_new(SSLv23_server_method()))
+                       , clictx(SSL_CTX_new(SSLv23_client_method()))
+                       , allowrenego(tag->getBool("renegotiation")) // Disallow by default
+                       , outrecsize(tag->getInt("outrecsize", 2048, 512, 16384))
+               {
+                       if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
+                               throw Exception("Couldn't set DH parameters");
+
+                       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());
                        }
                }
 
-               /* 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())))
-               {
-                       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);
-               }
+               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; }
+       };
 
-               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)))
+       namespace BIOMethod
+       {
+               static int create(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);
+                       bio->init = 1;
+                       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 int destroy(BIO* bio)
                {
-                       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);
+                       // 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;
                }
 
-#ifdef _WIN32
-               BIO* dhpfile = BIO_new_file(dhfile.c_str(), "r");
-#else
-               FILE* dhpfile = fopen(dhfile.c_str(), "r");
-#endif
-               DH* ret;
-
-               if (dhpfile == NULL)
+               static long ctrl(BIO* bio, int cmd, long num, void* ptr)
                {
-                       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));
+                       if (cmd == BIO_CTRL_FLUSH)
+                               return 1;
+                       return 0;
                }
-               else
-               {
-#ifdef _WIN32
-                       ret = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
-                       BIO_free(dhpfile);
-#else
-                       ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
-#endif
 
-                       ERR_clear_error();
-                       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);
-               }
+               static int read(BIO* bio, char* buf, int len);
+               static int write(BIO* bio, const char* buf, int len);
+       }
+}
 
-#ifndef _WIN32
-               fclose(dhpfile);
-#endif
+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
+};
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
-               SetupECDH(conf);
-#endif
-       }
+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);
 
-       void On005Numeric(std::string &output)
-       {
-               if (!sslports.empty())
-                       output.append(" SSL=" + sslports);
-       }
+       SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
 
-       ~ModuleSSLOpenSSL()
-       {
-               SSL_CTX_free(ctx);
-               SSL_CTX_free(clictx);
-               delete[] sessions;
-       }
+       return 1;
+}
 
-       void OnUserConnect(LocalUser* user)
-       {
-               if (user->eh.GetIOHook() == this)
-               {
-                       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));
-                       }
-               }
-       }
+class OpenSSLIOHook : public SSLIOHook
+{
+ private:
+       SSL* sess;
+       issl_status status;
+       bool data_to_write;
+       reference<OpenSSL::Profile> profile;
 
-       void OnCleanup(int target_type, void* item)
+       // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+       int Handshake(StreamSocket* user)
        {
-               if (target_type == TYPE_USER)
+               ERR_clear_error();
+               int ret = SSL_do_handshake(sess);
+               if (ret < 0)
                {
-                       LocalUser* user = IS_LOCAL((User*)item);
+                       int err = SSL_get_error(sess, ret);
 
-                       if (user && user->eh.GetIOHook() == this)
+                       if (err == SSL_ERROR_WANT_READ)
                        {
-                               // 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");
+                               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
+                       {
+                               CloseSession();
+                               return -1;
                        }
                }
-       }
+               else if (ret > 0)
+               {
+                       // Handshake complete.
+                       VerifyCertificate();
 
-       Version GetVersion()
-       {
-               return Version("Provides SSL support for clients", VF_VENDOR);
-       }
+                       status = ISSL_OPEN;
 
-       void OnRequest(Request& request)
-       {
-               if (strcmp("GET_SSL_CERT", request.id) == 0)
-               {
-                       SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
-                       int fd = req.sock->GetFd();
-                       issl_session* session = &sessions[fd];
+                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 
-                       req.cert = session->cert;
+                       return 1;
                }
-               else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+               else if (ret == 0)
                {
-                       SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
-                       if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
-                               req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+                       CloseSession();
                }
+               return -1;
        }
 
-       void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+       void CloseSession()
        {
-               int fd = user->GetFd();
-
-               issl_session* session = &sessions[fd];
+               if (sess)
+               {
+                       SSL_shutdown(sess);
+                       SSL_free(sess);
+               }
+               sess = NULL;
+               certificate = NULL;
+               status = ISSL_NONE;
+       }
 
-               session->sess = SSL_new(ctx);
-               session->status = ISSL_NONE;
-               session->outbound = false;
-               session->data_to_write = false;
+       void VerifyCertificate()
+       {
+               X509* cert;
+               ssl_cert* certinfo = new ssl_cert;
+               this->certificate = certinfo;
+               unsigned int n;
+               unsigned char md[EVP_MAX_MD_SIZE];
 
-               if (session->sess == NULL)
-                       return;
+               cert = SSL_get_peer_certificate(sess);
 
-               if (SSL_set_fd(session->sess, fd) == 0)
+               if (!cert)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
+                       certinfo->error = "Could not get peer certificate: "+std::string(get_error());
                        return;
                }
 
-               Handshake(user, session);
-       }
+               certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK);
 
-       void OnStreamSocketConnect(StreamSocket* user)
-       {
-               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 (!SelfSigned)
+               {
+                       certinfo->unknownsigner = false;
+                       certinfo->trusted = true;
+               }
+               else
+               {
+                       certinfo->unknownsigner = true;
+                       certinfo->trusted = false;
+               }
 
-               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(clictx);
-               session->status = ISSL_NONE;
-               session->outbound = true;
-               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 OnStreamSocketClose(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;
 
-               CloseSession(&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->ptr);
+                       SocketEngine::Shutdown(eh, 2);
+               }
        }
 
-       int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+       bool CheckRenego(StreamSocket* sock)
        {
-               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;
+               if (status != ISSL_NONE)
+                       return true;
 
-               issl_session* session = &sessions[fd];
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Session %p killed, attempted to renegotiate", (void*)sess);
+               CloseSession();
+               sock->SetError("Renegotiation is not allowed");
+               return false;
+       }
 
-               if (!session->sess)
+       // 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)
                {
-                       CloseSession(session);
-                       return -1;
+                       // The handshake isn't finished, try to finish it
+                       return Handshake(sock);
                }
 
-               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();
+               return -1;
+       }
+
+       // Calls our private SSLInfoCallback()
+       friend void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
 
-               // If we resumed the handshake then session->status will be ISSL_OPEN
+ 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)
+       {
+               // Create BIO instance and store a pointer to the socket in it which will be used by the read and write functions
+               BIO* bio = BIO_new(&biomethods);
+               bio->ptr = sock;
+               SSL_set_bio(sess, bio, bio);
+
+               SSL_set_ex_data(sess, exdataindex, this);
+               sock->AddIOHook(this);
+               Handshake(sock);
+       }
+
+       void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
+       {
+               CloseSession();
+       }
 
-               if (session->status == 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 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);
-                               if (session->data_to_write)
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
+                               if (data_to_write)
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
                                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) CXX11_OVERRIDE
        {
-               int fd = user->GetFd();
-
-               issl_session* session = &sessions[fd];
-
-               if (!session->sess)
-               {
-                       CloseSession(session);
-                       return -1;
-               }
-
-               session->data_to_write = true;
+               // Finish handshake if needed
+               int prepret = PrepareIO(user);
+               if (prepret <= 0)
+                       return prepret;
 
-               if (session->status == ISSL_HANDSHAKING)
-               {
-                       if (!Handshake(user, session))
-                       {
-                               // Couldn't resume handshake.
-                               if (session->status == ISSL_NONE)
-                                       return -1;
-                               return 0;
-                       }
-               }
+               data_to_write = true;
 
-               if (session->status == ISSL_OPEN)
+               // Session is ready for transferring application data
+               StreamSocket::SendQueue& sendq = user->GetSendQ();
+               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 TellCiphersAndFingerprint(LocalUser* user)
        {
-               int ret;
+               if (sess)
+               {
+                       std::string text = "*** You are connected using SSL cipher '";
+                       GetCiphersuite(text);
+                       text += '\'';
+                       const std::string& fingerprint = certificate->fingerprint;
+                       if (!fingerprint.empty())
+                               text += " and your SSL certificate fingerprint is " + fingerprint;
 
-               ERR_clear_error();
-               if (session->outbound)
-                       ret = SSL_connect(session->sess);
-               else
-                       ret = SSL_accept(session->sess);
+                       user->WriteNotice(text);
+               }
+       }
 
-               if (ret < 0)
+       void GetCiphersuite(std::string& out) const
+       {
+               out.append(SSL_get_version(sess)).push_back('-');
+               out.append(SSL_get_cipher(sess));
+       }
+
+       bool IsHandshakeDone() const { return (status == ISSL_OPEN); }
+};
+
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
+{
+       OpenSSLIOHook* hook = static_cast<OpenSSLIOHook*>(SSL_get_ex_data(ssl, exdataindex));
+       hook->SSLInfoCallback(where, rc);
+}
+
+static int OpenSSL::BIOMethod::write(BIO* bio, const char* buffer, int size)
+{
+       BIO_clear_retry_flags(bio);
+
+       StreamSocket* sock = static_cast<StreamSocket*>(bio->ptr);
+       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->ptr);
+       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);
-               }
-
-               session->sess = NULL;
-               session->status = ISSL_NONE;
-               session->cert = NULL;
+               // Initialize OpenSSL
+               SSL_library_init();
+               SSL_load_error_strings();
        }
 
-       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();
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
+       {
+               IOHook* hook = user->eh.GetIOHook();
+               if (hook && hook->prov->creator == this)
+                       static_cast<OpenSSLIOHook*>(hook)->TellCiphersAndFingerprint(user);
+       }
 
-               if (!X509_digest(cert, digest, md, &n))
-               {
-                       certinfo->error = "Out of memory generating fingerprint";
-               }
-               else
+       void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+       {
+               if (target_type == TYPE_USER)
                {
-                       certinfo->fingerprint = irc::hex(md, n);
+                       LocalUser* user = IS_LOCAL((User*)item);
+
+                       if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == 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");
+                       }
                }
+       }
 
-               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))
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+       {
+               if ((user->eh.GetIOHook()) && (user->eh.GetIOHook()->prov->creator == this))
                {
-                       certinfo->error = "Not activated, or expired certificate";
+                       OpenSSLIOHook* iohook = static_cast<OpenSSLIOHook*>(user->eh.GetIOHook());
+                       if (!iohook->IsHandshakeDone())
+                               return MOD_RES_DENY;
                }
 
-               X509_free(cert);
+               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..d2fa09c4ebad1106a5f37776c50c85a5832ea6e7 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()
        {
                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, ":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..3a4bf5348c2de554d0323aaa62615cebc80f010c 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;
@@ -59,25 +57,27 @@ 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];
+               std::string fpre = fantasy->getString("prefix");
+               fprefix = fpre.empty() ? "!" : fpre;
 
                Aliases.clear();
                ConfigTagList tags = ServerInstance->Config->ConfTags("alias");
@@ -85,8 +85,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");
@@ -99,20 +99,12 @@ class ModuleAlias : public Module
                }
        }
 
- 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 +134,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 +143,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 +161,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,54 +192,44 @@ 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())
                {
@@ -270,24 +245,22 @@ class ModuleAlias : public Module
                        }
                }
 
-               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;
                        }
                }
@@ -316,7 +289,7 @@ class ModuleAlias : public Module
        void DoCommand(const std::string& newline, User* user, Channel *chan, const std::string &original_line)
        {
                std::string result;
-               result.reserve(MAXBUF);
+               result.reserve(newline.length());
                for (unsigned int i = 0; i < newline.length(); i++)
                {
                        char c = newline[i];
@@ -329,28 +302,28 @@ 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);
                                        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);
                                        i += 5;
@@ -367,23 +340,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()
        {
                // 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..05e76113affb7fda59fe3f0311c57d5d85961299 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, "%s :You are banned from using INVITE", channel->name.c_str());
                                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..075064c62465be4b131cdb9c6a1f647165667f52 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;
 
@@ -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..7ad7ba1a3d8d684fc9609cdd764f206572d66687 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */
-
-class AuditoriumMode : public ModeHandler
+class AuditoriumMode : public SimpleChannelModeHandler
 {
  public:
-       AuditoriumMode(Module* Creator) : ModeHandler(Creator, "auditorium", 'u', PARAM_NONE, MODETYPE_CHANNEL)
+       AuditoriumMode(Module* Creator) : SimpleChannelModeHandler(Creator, "auditorium", 'u')
        {
                levelrequired = OP_VALUE;
        }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               if (channel->IsModeSet(this) == adding)
-                       return MODEACTION_DENY;
-               channel->SetMode(this, adding);
-               return MODEACTION_ALLOW;
-       }
 };
 
 class ModuleAuditorium : public Module
 {
- private:
        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()
-       {
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("auditorium");
                OpsVisible = tag->getBool("opvisible");
@@ -78,7 +51,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);
        }
@@ -112,19 +85,16 @@ class ModuleAuditorium : public Module
                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 +103,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,13 +149,11 @@ class ModuleAuditorium : public Module
                }
        }
 
-       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
        {
-               Channel* channel = ServerInstance->FindChan(params[0]);
-               if (!channel)
+               if (!memb)
                        return;
-               Membership* memb = channel->GetUser(user);
-               if ((!memb) || (IsVisible(memb)))
+               if (IsVisible(memb))
                        return;
                if (CanSee(source, memb))
                        return;
index 0c0e8f579a1e458094b758557332904db4c4d9b3..2b9c2e1c21c28edfaf62adc6d3513a1ffa0ee08f 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
  */
@@ -34,17 +32,13 @@ class AutoOpList : public ListModeBase
                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,13 @@ 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, "%s :Cannot find prefix mode '%s' for autoop",
+                               mid.c_str(), mid.c_str());
                        return MOD_RES_DENY;
                }
                else if (!mh)
@@ -70,8 +64,8 @@ class AutoOpList : public ListModeBase
                        return MOD_RES_DENY;
                if (mh->GetLevelRequired() > 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, "%s :You must be able to set mode '%s' to include it in an autoop",
+                               channel->name.c_str(), mid.c_str());
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
@@ -82,62 +76,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..d3490acc062aab9eeb569ff4d4ebf9c4fd791916 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, "%s :Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), 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, "%s :Invalid channel name in redirection (%s)", channel->name.c_str(), 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, ":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, ":You must be opped on %s to set it as a redirect.", mask[CHAN].c_str());
                                                return false;
                                        }
 
                                        if (assign(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, "%s :You cannot set a ban redirection to the channel the ban is on", channel->name.c_str());
                                                return false;
                                        }
                                }
@@ -224,26 +228,19 @@ 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(int target_type, void* item) CXX11_OVERRIDE
        {
                if(target_type == TYPE_CHANNEL)
                {
@@ -252,33 +249,21 @@ class ModuleBanRedirect : public Module
 
                        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 +307,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, "%s :Cannot join channel (You are banned)", chan->name.c_str());
                                                        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, "%s :Cannot join channel (You are banned)", chan->name.c_str());
+                                                       user->WriteNumeric(470, "%s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", chan->name.c_str(), redir->targetchan.c_str());
                                                        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 +330,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..8a025a0
--- /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()
+       {
+               return Version("Implements bcrypt hashing", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleBCrypt)
index be861447f36effcab132852d2f8e32836fe45387..9614203c350c04a8f3d9f950c9a8085c4d3bb405 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;
        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.c_str()), 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);
                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,21 +116,21 @@ 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]) && (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];
@@ -161,7 +139,7 @@ class ModuleBlockAmsg : public Module
                        }
                        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 2006936992aa333723efa750548e38acd964030a..57f86afd125b1f30e208b9f1fbc8086ed41d18f1 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support to block all-CAPS channel messages and notices */
-
 
 /** Handles the +B channel mode
  */
@@ -36,34 +34,21 @@ class BlockCaps : public SimpleChannelModeHandler
 class ModuleBlockCAPS : public Module
 {
        BlockCaps bc;
-       int percent;
+       unsigned int percent;
        unsigned int minlen;
        char capsmap[256];
-public:
 
+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)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('B');
+               tokens["EXTBAN"].push_back('B');
        }
 
-       virtual void OnRehash(User* user)
-       {
-               ReadConf();
-       }
-
-       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)
                {
@@ -76,28 +61,20 @@ public:
                        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, "%s :Your message cannot contain %d%% or more capital letters if it's longer than %d characters", c->name.c_str(), percent, minlen);
                                        return MOD_RES_DENY;
                                }
                        }
@@ -105,37 +82,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..a08ad7c6f0dabca419445a767703489070824139 100644 (file)
@@ -23,8 +23,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +c to block color */
-
 /** Handles the +c channel mode
  */
 class BlockColor : public SimpleChannelModeHandler
@@ -42,19 +40,12 @@ class ModuleBlockColor : public Module
        {
        }
 
-       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)))
                {
@@ -64,7 +55,7 @@ class ModuleBlockColor : public Module
                        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 +67,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, "%s :Can't send colors to channel (+c set)", c->name.c_str());
                                                        return MOD_RES_DENY;
                                                break;
                                        }
@@ -86,16 +77,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..419af0153dfcc7264c45594638bcfdc105a35797 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..0eb208138276a6b7d4ee3673b2d3af18d4b3b776 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(ERR_NOSUCHNICK, "%s :No such nick/channel", tok.c_str());
+                       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, ":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, "%s :is already on your accept list", whotoadd->nick.c_str());
                        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, "%s :is not on your accept list", whotoremove->nick.c_str());
                        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, "%s :is not on your accept list", whotoremove->nick.c_str());
                        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, "%s :is in +g mode (server-side ignore).", dest->nick.c_str());
                        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, "%s :has been informed that you messaged them.", dest->nick.c_str());
+                               dest->SendText(":%s %03d %s %s %s@%s :is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
+                                               ServerInstance->Config->ServerName.c_str(), RPL_UMODEGMSG, dest->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), user->nick.c_str());
                                dat->lastnotify = now;
                        }
                        return MOD_RES_DENY;
@@ -414,34 +408,18 @@ 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);
@@ -449,8 +427,12 @@ public:
                tracknick = tag->getBool("tracknick");
                notify_cooldown = tag->getInt("cooldown", 60);
        }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               // Want to be after modules like silence or services_account
+               ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
+       }
 };
 
 MODULE_INIT(ModuleCallerID)
-
-
index 6b4387fdd96ccec55db075d0391be07a6478c02f..09d5e604dd12c3e617c3b88a9b799f503a4cdabb 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)
+       {
+               if ((usercaps & cap->GetMask()) == 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.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, "%s :Invalid CAP subcommand", subcommand.c_str());
+                       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..4fb0653a91b1014e6da68ff377f9d17dfa5947df 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:
+private:
+       std::string displaytext;
        irc::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->displaytext = ch;
                this->matchtext = ch.c_str();
        }
 
-       ~CBan()
-       {
-       }
-
        // XXX I shouldn't have to define this
        bool Matches(User *u)
        {
@@ -55,15 +52,9 @@ public:
                return false;
        }
 
-       void DisplayExpiry()
+       const std::string& Displayable()
        {
-               ServerInstance->SNO->WriteToSnoMask('x',"Removing expired CBan %s (set by %s %ld seconds ago)",
-                       this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
-       }
-
-       const char* Displayable()
-       {
-               return matchtext.c_str();
+               return displaytext;
        }
 };
 
@@ -95,7 +86,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 +101,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 +121,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,22 +154,18 @@ 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(char symbol, User* user, string_list &out) CXX11_OVERRIDE
        {
                if (symbol != 'C')
                        return MOD_RES_PASSTHRU;
@@ -188,27 +174,26 @@ class ModuleCBan : public Module
                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, "%s :Cannot join channel, CBANed (%s)", cname.c_str(), rl->reason.c_str());
                        ServerInstance->SNO->WriteGlobalSno('a', "%s tried to join %s which is CBANed (%s)",
-                                user->nick.c_str(), cname, rl->reason.c_str());
+                                user->nick.c_str(), cname.c_str(), rl->reason.c_str());
                        return MOD_RES_DENY;
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Gives /cban, aka C:lines. Think Q:lines, for channels.", VF_COMMON | VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleCBan)
-
index 50c8e22a7212d738b33075f15f74a848167f1ea5..da22b5153627745b2e44824406610123ca9df013 100644 (file)
  */
 
 
-/* $ModDesc: Provides user and channel +G mode */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
 #include "inspircd.h"
-#include <iostream>
 
-typedef std::map<irc::string,irc::string> censor_t;
+typedef insp::flat_map<irc::string, irc::string> censor_t;
 
 /** Handles usermode +G
  */
@@ -55,24 +49,8 @@ class ModuleCensor : public Module
  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()
-       {
-       }
-
        // 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,11 +58,11 @@ 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;
+                       active = c->IsModeSet(cc);
                        ModResult res = ServerInstance->OnCheckExemption(user,c,"censor");
 
                        if (res == MOD_RES_ALLOW)
@@ -101,7 +79,7 @@ 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(), ((Channel*)dest)->name.c_str(), index->first.c_str());
+                                       user->WriteNumeric(ERR_WORDFILTERED, "%s %s :Your message contained a censored word, and was blocked", ((Channel*)dest)->name.c_str(), index->first.c_str());
                                        return MOD_RES_DENY;
                                }
 
@@ -112,12 +90,7 @@ class ModuleCensor : 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 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 +109,7 @@ class ModuleCensor : public Module
                }
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides user and channel +G mode",VF_VENDOR);
        }
index cce2e785531ad3c38b6fd3283fdeb173a8712aa0..721d6ba081a693b0237d326b7aab742cd7416e0e 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
-
-/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
+#include "modules/dns.h"
 
 enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
 
+// 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.c_str());
+       ServerInstance->Users->AddClone(user);
+}
 
 /** Holds a CGI site's details
  */
@@ -64,14 +70,12 @@ class CommandWebirc : public Command
        bool notify;
        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)
+                 realhost("cgiirc_realhost", ExtensionItem::EXT_USER, Creator)
+                 , realip("cgiirc_realip", ExtensionItem::EXT_USER, Creator)
                {
                        works_before_reg = true;
                        this->syntax = "password client hostname ip";
@@ -90,25 +94,25 @@ class CommandWebirc : public Command
                                                realhost.set(user, user->host);
                                                realip.set(user, user->GetIPString());
 
-                                               bool host_ok = (parameters[2].length() < 64);
+                                               // 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
+                                               bool host_ok = (parameters[2].length() <= ServerInstance->Config->Limits.MaxHost);
                                                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());
+                                                       ServerInstance->SNO->WriteGlobalSno('w', "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);
+                                               // Where the magic happens - change their IP
+                                               ChangeIP(user, parameters[3]);
+                                               // And follow this up by changing their host
+                                               user->host = user->dhost = newhost;
+                                               user->InvalidateCache();
 
-                                               webirc_ip.set(user, parameters[3]);
                                                return CMD_SUCCESS;
                                        }
                                }
                        }
 
-                       ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
+                       ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
                        return CMD_FAILURE;
                }
 };
@@ -116,39 +120,44 @@ class CommandWebirc : public Command
 
 /** Resolver for CGI:IRC hostnames encoded in ident/GECOS
  */
-class CGIResolver : public Resolver
+class CGIResolver : public DNS::Request
 {
        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),
+       CGIResolver(DNS::Manager *mgr, Module* me, bool NotifyOpers, const std::string &source, LocalUser* u,
+                       const std::string &ttype, LocalIntExt& ext)
+               : DNS::Request(mgr, me, source, DNS::QUERY_PTR), typ(ttype), theiruid(u->uuid),
                waiting(ext), notify(NotifyOpers)
        {
        }
 
-       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 */
                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());
+                       LocalUser* lu = IS_LOCAL(them);
+                       if (!lu)
+                               return;
 
-                       if (result.length() > 64)
+                       const DNS::ResourceRecord &ans_record = r->answers[0];
+                       if (ans_record.rdata.empty() || ans_record.rdata.length() > ServerInstance->Config->Limits.MaxHost)
                                return;
-                       them->host = result;
-                       them->dhost = result;
+
+                       if (notify)
+                               ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), ans_record.rdata.c_str(), typ.c_str());
+
+                       them->host = them->dhost = ans_record.rdata;
                        them->InvalidateCache();
-                       them->CheckLines(true);
+                       lu->CheckLines(true);
                }
        }
 
-       virtual void OnError(ResolverError e, const std::string &errormessage)
+       void OnError(const DNS::Query *r) CXX11_OVERRIDE
        {
                if (!notify)
                        return;
@@ -156,11 +165,11 @@ class CGIResolver : public Resolver
                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());
+                       ServerInstance->SNO->WriteToSnoMask('w', "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());
                }
        }
 
-       virtual ~CGIResolver()
+       ~CGIResolver()
        {
                User* them = ServerInstance->FindUUID(theiruid);
                if (!them)
@@ -175,6 +184,7 @@ class ModuleCgiIRC : public Module
 {
        CommandWebirc cmd;
        LocalIntExt waiting;
+       dynamic_reference<DNS::Manager> DNS;
 
        static void RecheckClass(LocalUser* user)
        {
@@ -183,14 +193,6 @@ class ModuleCgiIRC : public Module
                user->CheckClass();
        }
 
-       static void ChangeIP(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);
@@ -199,40 +201,42 @@ class ModuleCgiIRC : public Module
                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)
+               if (user->quitting || !DNS || !user->MyClass->resolvehostnames)
                        return;
 
+               CGIResolver* r = new CGIResolver(*this->DNS, this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), waiting);
                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);
+                       this->DNS->Process(r);
                }
-               catch (...)
+               catch (DNS::Exception &ex)
                {
+                       int count = waiting.get(user);
+                       if (count)
+                               waiting.set(user, count - 1);
+                       delete r;
                        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());
+                                ServerInstance->SNO->WriteToSnoMask('w', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname; %s", user->nick.c_str(), user->host.c_str(), ex.GetReason().c_str());
                }
        }
 
 public:
-       ModuleCgiIRC() : cmd(this), waiting("cgiirc-delay", this)
+       ModuleCgiIRC()
+               : cmd(this)
+               , waiting("cgiirc-delay", ExtensionItem::EXT_USER, this)
+               , DNS(this, "DNS")
        {
        }
 
-       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();
 
@@ -251,7 +255,7 @@ public:
                        {
                                if (type == "webirc" && password.empty())
                                {
-                                       ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
+                                       ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
                                }
                                else
                                {
@@ -267,7 +271,7 @@ public:
                                        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());
+                                               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
                                        }
 
                                        cmd.Hosts.push_back(CGIhost(hostmask, cgitype, password));
@@ -275,27 +279,20 @@ public:
                        }
                        else
                        {
-                               ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
+                               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
                                continue;
                        }
                }
        }
 
-       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;
@@ -304,13 +301,10 @@ 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 OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                for(CGIHostlist::iterator iter = cmd.Hosts.begin(); iter != cmd.Hosts.end(); iter++)
                {
@@ -388,7 +382,7 @@ public:
 
        bool IsValidHost(const std::string &host)
        {
-               if(!host.size() || host.size() > 64)
+               if(!host.size() || host.size() > ServerInstance->Config->Limits.MaxHost)
                        return false;
 
                for(unsigned int i = 0; i < host.size(); i++)
@@ -407,11 +401,10 @@ public:
                return true;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Change user's hosts connecting from known CGI:IRC hosts",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..53428a5a871af08f19274155a4a7ebb31ba810bb 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"
 
 /** Handles channel mode +g
  */
@@ -38,31 +33,30 @@ 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, "%s %s :word is too long for censor list", chan->name.c_str(), word.c_str());
                        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, "%s %s :Channel spamfilter list is full", chan->name.c_str(), word.c_str());
        }
 
-       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, "%s :The word %s is already on the spamfilter list", chan->name.c_str(), 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, "%s :No such spamfilter word is set", chan->name.c_str());
        }
 };
 
@@ -78,42 +72,35 @@ class ModuleChanFilter : public Module
        {
        }
 
-       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
        {
+               if (target_type != TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
+
+               Channel* chan = static_cast<Channel*>(dest);
                ModResult res = ServerInstance->OnCheckExemption(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, "%s :Cannot send to channel (your message contained a censored word)", chan->name.c_str());
                                        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, "%s %s :Cannot send to channel (your message contained a censored word)", chan->name.c_str(), i->mask.c_str());
                                        return MOD_RES_DENY;
                                }
                        }
@@ -122,33 +109,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..0624b4a86294ef2f3eb2cd497538f4506b301add 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("CONFIG", 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..189c0d0bcd895e4199e05ecb42a19d4bd6d32261 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]);
                        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(status))
                                                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..22dcf730fd7bfce252299574500e67982f13f8f1 100644 (file)
  */
 
 
-/* $ModDesc: Provides the /CHECK command to retrieve information on a user, channel, hostname or IP address */
-
 #include "inspircd.h"
+#include "listmode.h"
 
 /** Handle /CHECK
  */
 class CommandCheck : public Command
 {
+       UserModeReference snomaskmode;
+
+       std::string GetSnomasks(User* user)
+       {
+               std::string ret;
+               if (snomaskmode)
+                       ret = snomaskmode->GetUserParameter(user);
+
+               if (ret.empty())
+                       ret = "+";
+               return ret;
+       }
+
+       static void dumpListMode(User* user, const std::string& checkstr, const ListModeBase::ModeList* list)
+       {
+               if (!list)
+                       return;
+
+               std::string buf = checkstr + " modelist";
+               const std::string::size_type headlen = buf.length();
+               const size_t maxline = ServerInstance->Config->Limits.MaxLine;
+               for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+               {
+                       if (buf.size() + i->mask.size() + 1 > maxline)
+                       {
+                               user->SendText(buf);
+                               buf.erase(headlen);
+                       }
+                       buf.append(" ").append(i->mask);
+               }
+               if (buf.length() > headlen)
+                       user->SendText(buf);
+       }
+
  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>";
        }
@@ -92,26 +127,26 @@ class CommandCheck : public Command
                        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 + " snomasks " + GetSnomasks(targuser));
+                       user->SendText(checkstr + " server " + targuser->server->GetName());
                        user->SendText(checkstr + " uid " + targuser->uuid);
                        user->SendText(checkstr + " signon " + timestring(targuser->signon));
                        user->SendText(checkstr + " nickts " + timestring(targuser->age));
                        if (loctarg)
-                               user->SendText(checkstr + " lastmsg " + timestring(targuser->idle_lastmsg));
+                               user->SendText(checkstr + " 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);
                        }
 
-                       if (IS_OPER(targuser))
+                       if (targuser->IsOper())
                        {
                                OperInfo* oper = targuser->oper;
                                /* user is an oper of type ____ */
-                               user->SendText(checkstr + " opertype " + oper->NameStr());
+                               user->SendText(checkstr + " opertype " + oper->name);
                                if (loctarg)
                                {
                                        std::string umodes;
@@ -127,7 +162,7 @@ class CommandCheck : public Command
                                        }
                                        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++)
+                                       for (OperInfo::PrivSet::const_iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); ++i)
                                        {
                                                opcmds.push_back(' ');
                                                opcmds.append(*i);
@@ -135,7 +170,7 @@ class CommandCheck : public Command
                                        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++)
+                                       for (OperInfo::PrivSet::const_iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); ++i)
                                        {
                                                privs.push_back(' ');
                                                privs.append(*i);
@@ -147,8 +182,8 @@ class CommandCheck : public Command
 
                        if (loctarg)
                        {
-                               user->SendText(checkstr + " clientaddr " + irc::sockets::satouser(loctarg->client_sa));
-                               user->SendText(checkstr + " serveraddr " + irc::sockets::satouser(loctarg->server_sa));
+                               user->SendText(checkstr + " clientaddr " + loctarg->client_sa.str());
+                               user->SendText(checkstr + " serveraddr " + loctarg->server_sa.str());
 
                                std::string classname = loctarg->GetClass()->name;
                                if (!classname.empty())
@@ -157,10 +192,14 @@ class CommandCheck : public Command
                        else
                                user->SendText(checkstr + " onip " + targuser->GetIPString());
 
-                       for (UCListIter i = targuser->chans.begin(); i != targuser->chans.end(); i++)
+                       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).push_back(' ');
                        }
 
                        std::stringstream dump(chliststr);
@@ -187,32 +226,25 @@ class CommandCheck : public Command
 
                        /* 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);
+                               user->SendText("%s member %-3u %s%s (%s@%s) %s ",
+                                       checkstr.c_str(), clonecount.global,
+                                       i->second->GetAllPrefixChars(), i->first->nick.c_str(),
+                                       i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
                        }
 
-                       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));
+                       const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+                       for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
+                               dumpListMode(user, checkstr, (*i)->GetList(targchan));
+
                        dumpExt(user, checkstr, targchan);
                }
                else
@@ -221,7 +253,8 @@ 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))
                                {
@@ -252,42 +285,15 @@ class CommandCheck : public Command
        }
 };
 
-
 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..43b2a323ba4fb84478d2e8c65de77a11f6052852 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,16 +32,16 @@ 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;
                }
 
@@ -52,7 +49,7 @@ class CommandChghost : public Command
                {
                        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,15 +57,15 @@ 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
                        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);
@@ -92,20 +89,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 +104,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..c855216bfd4d91028e4df35e3134a2e9012bdded 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
                        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());
                }
 
@@ -79,7 +77,6 @@ class CommandChgident : public Command
        }
 };
 
-
 class ModuleChgIdent : public Module
 {
        CommandChgident cmd;
@@ -89,21 +86,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..830d5070b60c1b47ed50bfd0ac2031026663ce39 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
                        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());
                }
 
@@ -75,7 +73,6 @@ class CommandChgname : public Command
        }
 };
 
-
 class ModuleChgName : public Module
 {
        CommandChgname cmd;
@@ -85,20 +82,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_clearchan.cpp b/src/modules/m_clearchan.cpp
new file mode 100644 (file)
index 0000000..5fcec36
--- /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->host);
+                                       xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
+                               }
+                               catch (ModuleException& ex)
+                               {
+                                       // 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()
+       {
+               // 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 105d688337d20d0afb767059815bf10ace8c5671..1534043ce9cff081b20c6047615a49528e0e91ff 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 */
@@ -106,8 +99,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
@@ -118,12 +111,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->SetMode(this, false);
                        user->ChangeDisplayedHost(user->host.c_str());
                        return MODEACTION_ALLOW;
                }
        }
-
 };
 
 class CommandCloak : public Command
@@ -147,7 +139,6 @@ class ModuleCloaking : public Module
        std::string prefix;
        std::string suffix;
        std::string key;
-       unsigned int compatkey[4];
        const char* xtab[4];
        dynamic_reference<HashProvider> Hash;
 
@@ -155,18 +146,6 @@ class ModuleCloaking : public Module
        {
        }
 
-       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.
         *
@@ -213,7 +192,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
@@ -224,63 +203,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;
@@ -348,7 +270,7 @@ class ModuleCloaking : public Module
                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)
@@ -359,9 +281,8 @@ class ModuleCloaking : public Module
                /* Check if they have a cloaked host, but are not using it */
                if (cloak && *cloak != user->dhost)
                {
-                       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;
@@ -375,32 +296,22 @@ 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;
                                        break;
@@ -411,82 +322,23 @@ 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;
                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");
-
-               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;
+                       throw ModuleException("Bad value for <cloak:mode>; must be half or full");
 
-                       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)
@@ -495,29 +347,6 @@ class ModuleCloaking : public Module
 
                switch (mode)
                {
-                       case MODE_COMPAT_HOST:
-                       {
-                               if (ipstr != host)
-                               {
-                                       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 (ipstr != host)
@@ -533,7 +362,7 @@ 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)
@@ -554,7 +383,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..c51c8d3b4f13e7d7427f017d60fb435ac951d557 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
@@ -50,11 +48,12 @@ class CommandClones : public Command
                user->WriteServ(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->WriteServ(clonesstr + " " + ConvToStr(counts.global) + " " + i->first.str());
                }
 
                user->WriteServ(clonesstr + " END");
@@ -63,31 +62,18 @@ class CommandClones : public Command
        }
 };
 
-
 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..eab53b9bc92573a973e15bcbcc71be69d680cb3b 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, "%s :You are not permitted to send private messages to this user (+c set)", t->nick.c_str());
                                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..b22dbdf4dc69bee8b5078b725b8b452f2a0c4592 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->getInt("delay", 0, 0, 60);
+       }
 
-               void OnPostConnect(User* user)
-               {
-                       if (!IS_LOCAL(user))
-                               return;
+       void Prioritize()
+       {
+               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->getInt("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..7a8d66ae72dcd24812612e01499c3db1efe6fab6 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()
        {
                // 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];
@@ -62,26 +51,14 @@ class ModuleModesOnConnect : public Module
                        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);
-
                        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 >> buf)
+                               modes.push_back(buf);
 
-                       ServerInstance->Parser->CallHandler("MODE", modes, user);
+                       ServerInstance->Parser.CallHandler("MODE", modes, user);
                }
 
                memcpy(ServerInstance->Config->DisabledUModes, save, 64);
index 1d48220a6b096330cbafecd9e077216149042f35..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..fcb4b09ed8dcb9c2a69473808458801007fd35ac 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()
        {
-               ServerInstance->Logs->Log("m_connectban",DEBUG, "Clearing map.");
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Clearing map.");
                connects.clear();
        }
 };
index f77691e3276a8efe2f6649b4da04d31a588b79fc..2ab906e27c42d1e2f34b583dfaf0898e8b4f3314 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,19 +36,12 @@ 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");
@@ -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..f6f9a84f691220925fd04342a27b2301b897dc9a 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)
+               : PrefixMode(parent, Tag->getString("name"), 0, Tag->getInt("rank"))
+               , 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);
+               levelrequired = tag->getInt("ranktoset", prefixrank);
                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)
-       {
-       }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               return MODEACTION_ALLOW;
-       }
 };
 
 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)
@@ -110,7 +58,7 @@ class ModuleCustomPrefix : public Module
                        tags.first++;
                        CustomPrefixMode* mh = new CustomPrefixMode(this, tag);
                        modes.push_back(mh);
-                       if (mh->rank <= 0)
+                       if (mh->GetPrefixRank() == 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());
@@ -120,18 +68,17 @@ class ModuleCustomPrefix : public Module
                        }
                        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..b86bf1809182f6e7041c02b8a0b7d86c62e360ea 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->host;
+               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, unsigned int& numeric, std::string& text) CXX11_OVERRIDE
        {
                /* We use this and not OnWhois because this triggers for remote, too */
                if (numeric == 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, ":%s", ctitle->c_str());
                        }
                }
                /* 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..c8b6bd8b4b82ec923e433c602a313da3ffa7d812 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, "%s :No such channel", parameters[0].c_str());
                        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, "%s :You're not on that channel", channel->name.c_str());
                }
 
                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 043486283365bd3f6467c388eb8244f5989aea62..0e57896f3b78c78d7317536b745660e202277b72 100644 (file)
@@ -25,8 +25,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the /DCCALLOW command */
-
 class BannedFileList
 {
  public:
@@ -53,13 +51,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 +97,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 +120,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, "%s :Removed %s from your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
                                                                break;
                                                        }
                                                }
@@ -128,22 +130,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, "%s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str());
                                                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, "%s :Too many nicks on DCCALLOW list", user->nick.c_str());
                                                return CMD_FAILURE;
                                        }
 
@@ -151,7 +153,7 @@ 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, "%s :%s is already on your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
                                                        return CMD_FAILURE;
                                                }
                                        }
@@ -159,10 +161,10 @@ class CommandDccallow : public Command
                                        std::string mask = target->nick+"!"+target->ident+"@"+target->dhost;
                                        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 +172,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 +184,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, "%s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), 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, "%s :Added %s to DCCALLOW list for this session", user->nick.c_str(), target->nick.c_str());
                                        }
 
                                        /* route it. */
@@ -196,7 +198,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(401, "%s :No such nick/channel", nick.c_str());
                                return CMD_FAILURE;
                        }
                }
@@ -210,26 +212,26 @@ 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());
+               user->WriteNumeric(998, ":DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
+               user->WriteNumeric(998, ":You may allow DCCs from specific users by specifying a");
+               user->WriteNumeric(998, ":DCC allow for the user you want to receive DCCs from.");
+               user->WriteNumeric(998, ":For example, to allow the user Brain to send you inspircd.exe");
+               user->WriteNumeric(998, ":you would type:");
+               user->WriteNumeric(998, ":/DCCALLOW +Brain");
+               user->WriteNumeric(998, ":Brain would then be able to send you files. They would have to");
+               user->WriteNumeric(998, ":resend the file again if the server gave them an error message");
+               user->WriteNumeric(998, ":before you added them to your DCCALLOW list.");
+               user->WriteNumeric(998, ":DCCALLOW entries will be temporary by default, if you want to add");
+               user->WriteNumeric(998, ":them to your DCCALLOW list until you leave IRC, type:");
+               user->WriteNumeric(998, ":/DCCALLOW +Brain 0");
+               user->WriteNumeric(998, ":To remove the user from your DCCALLOW list, type:");
+               user->WriteNumeric(998, ":/DCCALLOW -Brain");
+               user->WriteNumeric(998, ":To see the users in your DCCALLOW list, type:");
+               user->WriteNumeric(998, ":/DCCALLOW LIST");
+               user->WriteNumeric(998, ":NOTE: If the user leaves IRC or changes their nickname");
+               user->WriteNumeric(998, ":  they will be removed from your DCCALLOW list.");
+               user->WriteNumeric(998, ":  your DCCALLOW list will be deleted when you leave IRC.");
+               user->WriteNumeric(999, ":End of DCCALLOW HELP");
 
                LocalUser* localuser = IS_LOCAL(user);
                if (localuser)
@@ -239,78 +241,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, "%s :%s (%s)", user->nick.c_str(), 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 +309,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)
@@ -384,16 +361,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->dhost + ") 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))
                                        {
-                                               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->dhost + ") 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;
                                        }
                                }
@@ -407,7 +384,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())
@@ -417,7 +394,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, "%s :DCCALLOW entry for %s has expired", u->nick.c_str(), iter2->nickname.c_str());
                                                        iter2 = dl->erase(iter2);
                                                }
                                                else
@@ -441,7 +418,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())
@@ -451,8 +428,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, "%s :Removed %s from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
                                                        dl->erase(i);
                                                        break;
                                                }
@@ -481,8 +458,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)
@@ -494,12 +474,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..fd9b322c0cf947ff1a34518f63b47c6383757774 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)
-       {
-               if (target_type == TYPE_CHANNEL)
-               {
-                       Channel* chan = (Channel*)dest;
-                       if (chan)
-                               this->BuildDeafList(MSG_NOTICE, chan, user, status, text, exempt_list);
-               }
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       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_PRIVMSG, chan, user, status, text, exempt_list);
-               }
+               if (target_type != TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
 
-               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,20 @@ class ModuleDeaf : public Module
                        if (is_bypasschar && !is_a_uline)
                                continue; /* deliver message */
 
-                       if (status && !strchr(chan->GetAllPrefixChars(i->first), status))
+                       if (status && !strchr(i->second->GetAllPrefixChars(), 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..e864a8289c75e0f8d6be6b174fc7f33f4c65524c 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)
@@ -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..f64297e15b8c678ce29d0969b8583400175e2ebf 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;
        }
@@ -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,14 @@ 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, "%s :You must wait %d seconds after joining to send to channel (+d)",
+                               channel->name.c_str(), len);
                        return MOD_RES_DENY;
                }
        }
@@ -147,19 +131,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..6378ba2733c75b0f2aa23e283d31ec0896fd3a44 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, "%s :Channel %s is forbidden, redirecting to %s: %s", cname.c_str(),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, "%s :Channel %s is forbidden: %s", cname.c_str(),cname.c_str(),reason.c_str());
                                        return MOD_RES_DENY;
                                }
                        }
index 2b5de2bd66dd2d8747e6de147a829d82893ac9c2..4e4b3a354b4efdb0bd7bf8c97584b36e61498251 100644 (file)
@@ -24,8 +24,6 @@
  *  Syntax: /DEVOICE <#chan>
  */
 
-/* $ModDesc: Provides voiced users with the ability to devoice themselves. */
-
 #include "inspircd.h"
 
 /** Handle /DEVOICE
@@ -36,24 +34,17 @@ class CommandDevoice : public Command
        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;
-               }
+               std::vector<std::string> modes;
+               modes.push_back(parameters[0]);
+               modes.push_back("-v");
+               modes.push_back(user->nick);
 
-               return CMD_FAILURE;
+               ServerInstance->Parser.CallHandler("MODE", modes, ServerInstance->FakeClient);
+               return CMD_SUCCESS;
        }
 };
 
@@ -65,16 +56,7 @@ class ModuleDeVoice : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleDeVoice()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides voiced users with the ability to devoice themselves.", VF_VENDOR);
        }
index d4101686a03c1daaba9115da2a8244e2898838d0..5eaa8c279abce71526b1ff611930d2e89f503163 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,170 +53,167 @@ 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 &ans_record = r->answers[0];
+
+               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_aton(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);
-                       // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
-                       if(result.length())
+                       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:
-                                               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;
+                               }
+                               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;
                                }
-                               else
-                                       ConfEntry->stats_misses++;
+                               case DNSBLConfEntry::I_UNKNOWN:
+                               default:
+                                       break;
                        }
-                       else
-                               ConfEntry->stats_misses++;
+
+                       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;
 
@@ -241,25 +236,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();
 
@@ -291,7 +282,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 */
 
@@ -316,11 +307,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())
@@ -336,14 +322,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)
@@ -352,40 +333,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))
@@ -396,15 +398,15 @@ 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(char symbol, User* user, string_list &results) CXX11_OVERRIDE
        {
                if (symbol != 'd')
                        return MOD_RES_PASSTHRU;
@@ -416,12 +418,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 " +
+                       results.push_back("304 " + user->nick + " :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));
+               results.push_back("304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
+               results.push_back("304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
 
                return MOD_RES_PASSTHRU;
        }
index 8d6d65af2329c050dff0fa4e6c319909da0731b7..076445644ba2bfa84644d94eadff7b170a3ea5ed 100644 (file)
@@ -18,9 +18,7 @@
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides the ability to allow channel operators to be exempt from certain modes. */
+#include "listmode.h"
 
 /** Handles channel mode +X
  */
@@ -31,30 +29,42 @@ 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, "%s %s :Invalid exemptchanops entry, format is <restriction>:<prefix>", chan->name.c_str(), word.c_str());
+                       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, "%s %s :Unknown restriction", chan->name.c_str(), restriction.c_str());
                        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, "%s %s :Channel exemptchanops list is full", chan->name.c_str(), word.c_str());
        }
 
        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, "%s :The word %s is already on the exemptchanops list", chan->name.c_str(), 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, "%s :No such exemptchanops word is set", chan->name.c_str());
        }
 };
 
@@ -63,18 +73,14 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std:
  public:
        ExemptChanOps ec;
        ExemptHandler(Module* me) : ec(me) {}
-       
-       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 Call(User* user, Channel* chan, const std::string& restriction)
@@ -82,21 +88,21 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std:
                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 == "*")
@@ -108,23 +114,16 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std:
 
 class ModuleExemptChanOps : public Module
 {
-       std::string defaults;
        ExemptHandler eh;
 
  public:
-
        ModuleExemptChanOps() : eh(this)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               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()
@@ -132,20 +131,15 @@ class ModuleExemptChanOps : public Module
                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..34d0bebb3cce91d865a14cde6f912b69ee4175b6 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);
+       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(char symbol, User* user, string_list &results) 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, "%s :Message to channel blocked and opers notified (%s)", target.c_str(), 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, "%s :Message to channel blocked (%s)", target.c_str(), 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, "");
@@ -695,7 +673,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,12 +683,12 @@ 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());
                }
        }
 }
@@ -719,13 +697,17 @@ ModResult ModuleFilter::OnStats(char symbol, User* user, string_list &results)
 {
        if (symbol == '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++)
+               {
+                       results.push_back("223 "+user->nick+" :"+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);
+                       results.push_back("223 "+user->nick+" :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));
+                       results.push_back("223 "+user->nick+" :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..8f847e1
--- /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->getInt("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_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()
+       {
+               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..294187fa5c81bea51b6b8d775c0c72fa374861ac 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, "%s :Module successfully loaded.", parameters[0].c_str());
                        }
                        else
                        {
-                               user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+                               user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
                        }
                }
                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, "%s :You cannot unload core commands!", parameters[0].c_str());
+                       return CMD_FAILURE;
+               }
+
                std::string servername = parameters.size() > 1 ? parameters[1] : "*";
 
                if (InspIRCd::Match(ServerInstance->Config->ServerName.c_str(), servername))
@@ -94,11 +98,11 @@ class CommandGunloadmodule : public Command
                                }
                                else
                                {
-                                       user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+                                       user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
                                }
                        }
                        else
-                               user->SendText(":%s 972 %s %s :No such module", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
+                               user->SendText(":%s %03d %s %s :No such module", ServerInstance->Config->ServerName.c_str(), ERR_CANTUNLOADMODULE, user->nick.c_str(), parameters[0].c_str());
                }
                else
                        ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBAL UNLOAD BY '%s' (not unloaded here)",parameters[0].c_str(), user->nick.c_str());
@@ -112,25 +116,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 +135,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, "%s :Could not find module by that name", parameters[0].c_str());
                                return CMD_FAILURE;
                        }
                }
@@ -185,22 +168,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..ef9ae5e22e8e7d8ffcccaa8396f9c04bd6723e54 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, ":  %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, ":*** 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, ":%s", token.c_str());
                        }
 
-                       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..431b7b9688da80547a9c43d32d0faeaf47692593 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,41 +28,31 @@ 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, unsigned int& numeric, std::string& text) CXX11_OVERRIDE
        {
                /* always show to self */
-               if (user == dest)
+               if (whois.IsSelfWhois())
                        return MOD_RES_PASSTHRU;
 
                /* don't touch anything except 319 */
@@ -72,7 +60,7 @@ class ModuleHideChans : public Module
                        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..cde8371
--- /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, "%s :You do not have access to view the %s list", chan->name.c_str(), 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 9d50bd2e5342b410ef4715c492c6ffdf50502944..03f6745ee9796ed887b4ff843a3a10e9b2aa9d1b 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,41 +48,30 @@ 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, unsigned int numeric, const std::string& text) CXX11_OVERRIDE
        {
                if (numeric != 252 || active || user->HasPrivPermission("users/auspex"))
                        return MOD_RES_PASSTHRU;
@@ -94,13 +81,13 @@ class ModuleHideOper : public Module
                if (opercount)
                {
                        active = true;
-                       user->WriteNumeric(252, "%s %lu :operator(s) online", user->nick.c_str(),  opercount);
+                       user->WriteNumeric(252, "%lu :operator(s) online", opercount);
                        active = false;
                }
                return MOD_RES_DENY;
        }
 
-       ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+       ModResult OnWhoisLine(Whois::Context& whois, unsigned int& numeric, std::string& text) CXX11_OVERRIDE
        {
                /* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
                 * person doing the WHOIS is not an oper
@@ -108,18 +95,18 @@ class ModuleHideOper : public Module
                if (numeric != 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)
+       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
        {
-               if (user->IsModeSet('H') && !source->HasPrivPermission("users/auspex"))
+               if (user->IsModeSet(hm) && !source->HasPrivPermission("users/auspex"))
                {
                        // hide the "*" that marks the user as an oper from the /WHO line
                        std::string::size_type spcolon = line.find(" :");
@@ -135,27 +122,28 @@ class ModuleHideOper : public Module
                }
        }
 
-       ModResult OnStats(char symbol, User* user, string_list &results)
+       ModResult OnStats(char symbol, User* user, string_list& results) CXX11_OVERRIDE
        {
                if (symbol != '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() && (user->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);
+                               results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+                                               (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
                                count++;
                        }
                }
-               results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
+               results.push_back("249 "+user->nick+" :"+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..b33c101
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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));
+
+               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->dhost, "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..760647d47f1ae796636e8de03bedf1045cf3ae6c 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,29 @@ 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;
+
+       bool Tick(time_t currtime) CXX11_OVERRIDE
+       {
+               AddToCull();
+               return false;
+       }
 
-       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())
+ public:
+       HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
+               : BufferedSocket(newfd)
+               , Timer(timeoutsec)
+               , InternalState(HTTP_SERVE_WAIT_REQUEST)
+               , ip(IP)
+               , postsize(0)
+               , waitingcull(false)
        {
-               InternalState = HTTP_SERVE_WAIT_REQUEST;
+               ServerInstance->Timers.AddTimer(this);
 
-               FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
-               if (GetIOHook())
-                       GetIOHook()->OnStreamSocketAccept(this, client, server);
+               if (via->iohookprov)
+                       via->iohookprov->OnAccept(this, client, server);
        }
 
        ~HttpServerSocket()
@@ -77,9 +87,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 +198,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)
@@ -225,7 +230,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 +270,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 +301,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 +323,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 +342,78 @@ 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->getInt("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));
+               sockets.push_front(new HttpServerSocket(nfd, incomingip, from, client, server, timeoutsec));
                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;
-                       }
-               }
-       }
-
        void OnUnloadModule(Module* mod)
        {
-               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->GetIOHook() && sock->GetIOHook()->prov->creator == mod)
                        {
                                sock->cull();
                                delete sock;
@@ -411,20 +421,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..866fa0e866a137f80df22ca45384bb77a79c0fc4 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,20 +204,25 @@ 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);
        }
index 62314cd7ea0eef94282e3b4c8f94eaf8d0855f4a..6fd7f405013e6fcc22092bd4c32ac5833bb5712b 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/"))
                        {
@@ -95,18 +92,21 @@ 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);
        }
index 2fc7ca7dea33656e4c79dfcf5c7723176daf75f1..541e080f56a7ad999f4c7632fe85f88f5dfe5533 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,19 @@ 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><socketengine>" INSPIRCD_SOCKETENGINE_NAME "</socketengine>";
+                               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<std::string>& isupport = ServerInstance->ISupport.GetLines();
+                               for (std::vector<std::string>::const_iterator it = isupport.begin(); it != isupport.end(); it++)
+                               {
+                                       data << Sanitize(*it) << 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 +133,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 +174,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))
+                                               << 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>";
+                                       if (u->IsOper())
+                                               data << "<opertype>" << Sanitize(u->oper->name) << "</opertype>";
                                        data << "<modes>" << u->FormatModes() << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
                                        LocalUser* lu = IS_LOCAL(u);
                                        if (lu)
                                                data << "<port>" << lu->GetServerPort() << "</port><servaddr>"
-                                                       << irc::sockets::satouser(lu->server_sa) << "</servaddr>";
+                                                       << lu->server_sa.str() << "</servaddr>";
                                        data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
 
                                        DumpMeta(data, u);
@@ -205,10 +201,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 +217,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);
        }
 };
 
-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 +259,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..0e5aa43aea55b74d5c61a94ff4e3ba06b0e5cd8f 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->getInt("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..9e94e556dbc4e2052e85fe5b1edef8ce6ce65c59 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);
                        }
@@ -236,7 +174,7 @@ class ModuleIRCv3 : public Module
                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..af35031
--- /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->dhost);
+       }
+
+       void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+       {
+               DoChgHost(user, user->ident, newhost);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the chghost IRCv3.2 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3ChgHost)
diff --git a/src/modules/m_ircv3_echomessage.cpp b/src/modules/m_ircv3_echomessage.cpp
new file mode 100644 (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..52802f168884fd2a1783b2b41b0cae08b984b001 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +j (join flood protection) */
-
 /** 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 +63,87 @@ 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;
        }
 
+       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, "%s :Invalid flood parameter",channel->name.c_str());
+                       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, "%s :Invalid flood parameter",channel->name.c_str());
+                       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()
-       {
-               ServerInstance->Modules->AddService(jf);
-               ServerInstance->Modules->AddService(jf.ext);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserJoin };
-               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)
                {
                        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, "%s :This channel is temporarily unavailable (+j). Please try again later.",chan->name.c_str());
                                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)
@@ -235,11 +164,7 @@ class ModuleJoinFlood : public Module
                }
        }
 
-       ~ModuleJoinFlood()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel mode +j (join flood protection)", VF_VENDOR);
        }
index dce8f0bd5dfcf2856f184cdb6495b244e636733a..e9c07f45fda18816a003a1017d8f3d150a7ff184 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, "%s %d :Please use this Server/Port instead", parameters[0].c_str(), GetPort(t));
                                                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 = (SSLClientCert::GetCertificate(&user->eh) ? sslport : port);
+               if (p == 0)
+                       p = user->GetServerPort();
+               return p;
+       }
+};
 
 class ModuleJumpServer : public Module
 {
@@ -143,40 +155,30 @@ 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, "%s %d :Please use this Server/Port instead",
+                               js.redirect_to.c_str(), port);
                        ServerInstance->Users->QuitUser(user, js.reason);
                        return MOD_RES_PASSTHRU;
                }
                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..b8a7766675e8e191ea0cb45331ba495058f695d9 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, "%s :You must wait %u seconds after being kicked to rejoin (+J)", chan->name.c_str(), 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..26397bc9c17f94e135e4f2604a173e30480bcd81 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,25 +45,25 @@ 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(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
                        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, "%s :Can't KNOCK on %s, you are already on that channel.", c->name.c_str(), 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, ":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, "%s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!", c->name.c_str(), c->name.c_str());
                        return CMD_FAILURE;
                }
 
@@ -70,7 +73,7 @@ class CommandKnock : public Command
                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,33 +83,19 @@ 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());
@@ -128,7 +117,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..7da6328
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * 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;
+               }
+
+               try
+               {
+                       std::string what = attribute + "=" + (useusername ? user->ident : user->nick);
+                       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..9deb9a2
--- /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->host;
+                       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..65b9aa036349141cdcfe4164a37b1ae1f0a6b589 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
-*/
-
+ */
 
 class CommandLockserv : public Command
 {
        bool& locked;
-public:
+
+ public:
        CommandLockserv(Module* Creator, bool& lock) : Command(Creator, "LOCKSERV", 0), locked(lock)
        {
                flags_needed = 'o';
@@ -41,12 +39,12 @@ public:
        {
                if (locked)
                {
-                       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());
+               user->WriteNumeric(988, "%s :Closed for new connections", user->server->GetName().c_str());
                ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used LOCKSERV to temporarily disallow new connections", user->nick.c_str());
                return CMD_SUCCESS;
        }
@@ -54,10 +52,9 @@ public:
 
 class CommandUnlockserv : public Command
 {
-private:
        bool& locked;
 
-public:
+ public:
        CommandUnlockserv(Module* Creator, bool &lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
        {
                flags_needed = 'o';
@@ -67,12 +64,12 @@ public:
        {
                if (!locked)
                {
-                       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());
+               user->WriteNumeric(989, "%s :Open for new connections", user->server->GetName().c_str());
                ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used UNLOCKSERV to allow new connections", user->nick.c_str());
                return CMD_SUCCESS;
        }
@@ -80,37 +77,28 @@ public:
 
 class ModuleLockserv : public Module
 {
-private:
        bool locked;
        CommandLockserv lockcommand;
        CommandUnlockserv unlockcommand;
 
-public:
+ public:
        ModuleLockserv() : lockcommand(this, locked), unlockcommand(this, locked)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                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 = false;
        }
 
-       virtual ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                if (locked)
                {
@@ -120,12 +108,12 @@ public:
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
                return locked ? 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..6cec05a18e1b48efff4f012904178fcf00c620fe 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;
 
@@ -253,36 +244,15 @@ class MD5Provider : public HashProvider
                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);
                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 +261,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..1faf3bfb99e5a1d04c449b9798201088f72edb6d 100644 (file)
@@ -25,8 +25,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +f (message flood protection) */
-
 /** Holds flood settings and state for mode +f
  */
 class floodsettings
@@ -36,7 +34,7 @@ class floodsettings
        unsigned int secs;
        unsigned int lines;
        time_t reset;
-       std::map<User*, unsigned int> counters;
+       insp::flat_map<User*, unsigned int> counters;
 
        floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
        {
@@ -56,64 +54,50 @@ 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, "%s :Invalid flood parameter", channel->name.c_str());
+                       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, "%s :Invalid flood parameter", channel->name.c_str());
+                       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));
        }
 };
 
@@ -128,17 +112,13 @@ class ModuleMsgFlood : public Module
        {
        }
 
-       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)
@@ -153,17 +133,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->dhost);
+                                       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 (limit is " + ConvToStr(f->lines) +
+                                       " in " + ConvToStr(f->secs) + " secs)";
 
-                               dest->KickUser(ServerInstance->FakeClient, user, kickmessage);
+                               dest->KickUser(ServerInstance->FakeClient, user, kickMessage);
 
                                return MOD_RES_DENY;
                        }
@@ -172,30 +150,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()
        {
                // 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..9b0fa8dab0b8438a49fe8e1b83f608bc936ea3ba 100644 (file)
  */
 
 
-/* $ModDesc: Implements the ability to have server-side MLOCK enforcement. */
-
 #include "inspircd.h"
 
 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)
        {
                if (!channel)
                        return MOD_RES_PASSTHRU;
@@ -52,6 +46,7 @@ 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)
                {
@@ -62,7 +57,6 @@ public:
 
                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..139c70e
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * 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;
+};
+
+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, "%u %s%s%s :Monitor list is full", maxmonitor, nick.c_str(), (ss.StreamEnd() ? "" : ","), ss.GetRemaining().c_str());
+                               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..72c4acd47ca12c57baf8f577880542dcab279f20 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, "%s :Cannot send to channel (you're muted)", chan->name.c_str());
                        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..617ee43b368f5a3f6f48e588e4ab15ea5f227b35 100644 (file)
  */
 
 
-/* $ModDesc: Provides the ability to manipulate modes via long names. */
-
 #include "inspircd.h"
 
 static void DisplayList(User* user, Channel* channel)
 {
        std::stringstream items;
-       for(char letter = 'A'; letter <= 'z'; letter++)
+       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))
                {
-                       if ((letter == 'k') && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
+                       if ((mh->name == "key") && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
                                items << " <key>";
                        else
-                               items << " " << channel->GetModeParameter(letter);
+                               items << " " << 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());
+       const std::string line = ":" + ServerInstance->Config->ServerName + " 961 " + user->nick + " " + channel->name;
+       user->SendText(line, items);
+       user->WriteNumeric(960, "%s :End of mode list", channel->name.c_str());
 }
 
 class CommandProp : public Command
@@ -56,17 +52,20 @@ class CommandProp : public Command
 
        CmdResult Handle(const std::vector<std::string> &parameters, User *src)
        {
+               Channel* const chan = ServerInstance->FindChan(parameters[0]);
+               if (!chan)
+               {
+                       src->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+                       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 +75,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->GetNumParams(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 +99,12 @@ class DummyZ : public ModeHandler
        {
                list = true;
        }
+
+       // Handle /MODE #chan Z
+       void DisplayList(User* user, Channel* chan)
+       {
+               ::DisplayList(user, chan);
+       }
 };
 
 class ModuleNamedModes : public Module
@@ -113,16 +116,7 @@ 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);
        }
@@ -132,80 +126,59 @@ class ModuleNamedModes : public Module
                ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_FIRST);
        }
 
-       ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters)
+       ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
        {
                if (!channel)
                        return MOD_RES_PASSTHRU;
-               if (parameters[1].find('Z') == std::string::npos)
-                       return MOD_RES_PASSTHRU;
-               if (parameters.size() <= 2)
-               {
-                       DisplayList(source, channel);
-                       return MOD_RES_DENY;
-               }
-
-               std::vector<std::string> newparms;
-               newparms.push_back(parameters[0]);
-               newparms.push_back(parameters[1]);
 
-               std::string modelist = newparms[1];
-               bool adding = true;
-               unsigned int param_at = 2;
-               for(unsigned int i = 0; i < modelist.length(); i++)
+               Modes::ChangeList::List& list = modes.getlist();
+               for (Modes::ChangeList::List::iterator i = list.begin(); i != list.end(); )
                {
-                       unsigned char modechar = modelist[i];
-                       if (modechar == '+' || modechar == '-')
-                       {
-                               adding = (modechar == '+');
-                               continue;
-                       }
-                       ModeHandler *mh = ServerInstance->Modes->FindMode(modechar, MODETYPE_CHANNEL);
-                       if (modechar == 'Z')
+                       Modes::Change& curr = *i;
+                       // Replace all namebase (dummyZ) modes being changed with the actual
+                       // mode handler and parameter. The parameter format of the namebase mode is
+                       // <modename>[=<parameter>].
+                       if (curr.mh == &dummyZ)
                        {
-                               modechar = 0;
-                               std::string name, value;
-                               if (param_at < parameters.size())
-                                       name = parameters[param_at++];
+                               std::string name = curr.param;
+                               std::string value;
                                std::string::size_type eq = name.find('=');
                                if (eq != std::string::npos)
                                {
-                                       value = name.substr(eq + 1);
-                                       name = name.substr(0, eq);
+                                       value.assign(name, eq + 1, std::string::npos);
+                                       name.erase(eq);
+                               }
+
+                               ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+                               if (!mh)
+                               {
+                                       // Mode handler not found
+                                       i = list.erase(i);
+                                       continue;
                                }
-                               for(char letter = 'A'; modechar == 0 && letter <= 'z'; letter++)
+
+                               curr.param.clear();
+                               if (mh->GetNumParams(curr.adding))
                                {
-                                       mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
-                                       if (mh && mh->name == name)
+                                       if (value.empty())
                                        {
-                                               if (mh->GetNumParams(adding))
-                                               {
-                                                       if (!value.empty())
-                                                       {
-                                                               newparms.push_back(value);
-                                                               modechar = letter;
-                                                               break;
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       modechar = letter;
-                                                       break;
-                                               }
+                                               // Mode needs a parameter but there wasn't one
+                                               i = list.erase(i);
+                                               continue;
                                        }
+
+                                       // Change parameter to the text after the '='
+                                       curr.param = value;
                                }
-                               if (modechar)
-                                       modelist[i] = modechar;
-                               else
-                                       modelist.erase(i--, 1);
-                       }
-                       else if (mh && mh->GetNumParams(adding) && param_at < parameters.size())
-                       {
-                               newparms.push_back(parameters[param_at++]);
+
+                               // Put the actual ModeHandler in place of the namebase handler
+                               curr.mh = mh;
                        }
+
+                       ++i;
                }
-               newparms[1] = modelist;
-               ServerInstance->Modes->Process(newparms, source, false);
-               return MOD_RES_DENY;
+
+               return MOD_RES_PASSTHRU;
        }
 };
 
index 82d311773c2b52ba0d7ce14b50140e65efe868dd..c906322bf529967fba09f4413bc0e0fef0b8afb3 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,28 +52,24 @@ 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)
+       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
        {
-               if (!cap.ext.get(source))
+               if ((!memb) || (!cap.get(source)))
                        return;
 
                // Channel names can contain ":", and ":" as a 'start-of-token' delimiter is
@@ -101,31 +84,16 @@ class ModuleNamesX : public Module
                        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;
+               //                                                            pos
 
                // 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;
 
                line.erase(pos, 1);
                line.insert(pos, prefixes);
        }
-
-       void OnEvent(Event& ev)
-       {
-               cap.HandleEvent(ev);
-       }
 };
 
 MODULE_INIT(ModuleNamesX)
index bc90c9fad5a620ce6050cbd83430f59a8b5a3c5c..8e836c407d59e3e0be3c810b787439c1e3e0322d 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;
        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,28 @@ 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)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               std::string tmp(casemapping);
-               tmp.insert(0, "CASEMAPPING=");
-               SearchAndReplace(output, std::string("CASEMAPPING=rfc1459"), tmp);
+               tokens["CASEMAPPING"] = casemapping;
        }
 
-       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()));
+               casemapping = tag->getString("casemapping", FileSystem::GetFileName(charset));
                if (casemapping.find(' ') != std::string::npos)
                        throw ModuleException("<nationalchars:casemapping> must not contain any spaces!");
 #if defined _WIN32
-               if (!ServerInstance->Config->StartsWithWindowsDriveLetter(charset))
+               if (!FileSystem::StartsWithWindowsDriveLetter(charset))
                        charset.insert(0, "./locales/");
 #else
                if(charset[0] != '/')
@@ -307,16 +294,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 +314,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 +330,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 +348,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..cade0b1db751ace5a5dd7829510b0d58275094e5 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +F (nick flood protection) */
-
 /** Holds settings and state associated with channel mode +F
  */
 class nickfloodsettings
@@ -80,53 +78,41 @@ class nickfloodsettings
 
 /** 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, "%s :Invalid flood parameter",channel->name.c_str());
+                       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, "%s :Invalid flood parameter",channel->name.c_str());
+                       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));
        }
 };
 
@@ -135,28 +121,16 @@ class ModuleNickFlood : public Module
        NickFlood nf;
 
  public:
-
        ModuleNickFlood()
                : nf(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(nf);
-               ServerInstance->Modules->AddService(nf.ext);
-               Implementation eventlist[] = { I_OnUserPreNick, I_OnUserPostNick };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       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);
@@ -168,7 +142,7 @@ class ModuleNickFlood : public Module
 
                                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, ":%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", channel->name.c_str(), f->nicks, f->secs);
                                        return MOD_RES_DENY;
                                }
 
@@ -188,14 +162,14 @@ 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);
@@ -214,11 +188,7 @@ class ModuleNickFlood : public Module
                }
        }
 
-       ~ModuleNickFlood()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Channel mode F - nick flood protection", VF_VENDOR);
        }
index 4f5e5941cea5d653bb2453e19e154b4fcb68d82b..a99628bf1258396e1bb5d82f21935f4755a9a155 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit */
-
 /** Handle /NICKLOCK
  */
 class CommandNicklock : public Command
@@ -35,7 +33,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 +42,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(947, "%s :Nickname now locked.", parameters[1].c_str());
                }
 
                /* If we made it this far, extend the user */
@@ -66,7 +64,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
                        {
@@ -98,7 +96,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 +105,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;
                }
 
@@ -139,7 +137,6 @@ class CommandNickunlock : public Command
        }
 };
 
-
 class ModuleNickLock : public Module
 {
        LocalIntExt locked;
@@ -147,38 +144,22 @@ 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;
@@ -187,7 +168,7 @@ class ModuleNickLock : public Module
        void Prioritize()
        {
                Module *nflood = ServerInstance->Modules->Find("m_nickflood.so");
-               ServerInstance->Modules->SetPriority(this, I_OnUserPreNick, PRIORITY_BEFORE, &nflood);
+               ServerInstance->Modules->SetPriority(this, I_OnUserPreNick, PRIORITY_BEFORE, nflood);
        }
 };
 
index 1dd6fe34ad467d71617f55b52316685cc9b3e9b7..953557d90ff165549acab2a3fd2a76734a8186cf 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +C to block CTCPs */
-
 class NoCTCP : public SimpleChannelModeHandler
 {
  public:
@@ -31,38 +29,20 @@ class NoCTCP : public SimpleChannelModeHandler
 
 class ModuleNoCTCP : public Module
 {
-
        NoCTCP nc;
 
  public:
-
        ModuleNoCTCP()
                : nc(this)
        {
        }
 
-       void init()
-       {
-               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)))
                {
@@ -74,18 +54,18 @@ class ModuleNoCTCP : public Module
                        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, "%s :Can't send CTCP to channel (+C set)", c->name.c_str());
                                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..0acf841187a7f3e91c0e4039fd377b25ed9040e1 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, "%s :Can't kick user %s from channel (+Q set)", memb->chan->name.c_str(), 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..63815f2bf7ef329675082187470e1ff14cbac773 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for channel mode +N & extban +b N: which prevents nick changes on channel */
-
 class NoNicks : public SimpleChannelModeHandler
 {
  public:
@@ -38,54 +36,34 @@ class ModuleNoNickChange : public Module
        {
        }
 
-       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()
-       {
-       }
-
-       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");
 
                        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, ":Can't change nickname while on %s (+N is set)",
+                                       curr->name.c_str());
                                return MOD_RES_DENY;
                        }
                }
@@ -93,7 +71,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..cab367ad9e6abe49e879a275911a1feb79d57c3a 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +T to block notices to the channel */
-
 class NoNotice : public SimpleChannelModeHandler
 {
  public:
@@ -39,32 +37,25 @@ class ModuleNoNotice : public Module
        {
        }
 
-       void init()
-       {
-               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");
                                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, "%s :Can't send NOTICE to channel (+T set)", c->name.c_str());
                                        return MOD_RES_DENY;
                                }
                        }
@@ -72,11 +63,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..1444f93ad691a3ab37174b02e10f51e0dfdcf755 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)
+       bool notice;
+       bool op;
+       ModeHandler* npmh;
+       CommandOjoin(Module* parent, ModeHandler& mode)
+               : SplitCommand(parent, "OJOIN", 1)
+               , npmh(&mode)
        {
                flags_needed = 'o'; Penalty = 0; 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)
@@ -82,15 +65,17 @@ class CommandOjoin : public Command
                }
                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)
+       NetworkPrefix(Module* parent, char NPrefix)
+               : PrefixMode(parent, "official-join", 'Y', NETWORK_VALUE, NPrefix)
        {
-               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)
-       {
        }
 
        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.GetModeChar()))
                        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()
        {
                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..3c6b4cd59c96f72de160d9a208da70b6ef9bc009 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,33 @@ 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, "%s :Only IRC operators may join %s (+O is set)",
+                               chan->name.c_str(), 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..ac7178a93385ac12881ef467b481997d54e19a40 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, ":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..33ebb57a04c37dee58af5306916585f4abe891e3 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;
@@ -71,7 +60,7 @@ class ModuleModesOnOper : public Module
                while (ss >> buf)
                        modes.push_back(buf);
 
-               ServerInstance->SendMode(modes, u);
+               ServerInstance->Parser.CallHandler("MODE", modes, u);
        }
 };
 
index 989f97689d20c45f45aa41e8340520e52d5b2043..bd1853d43da43a37a78739e1420aa9ddc47d6e97 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
@@ -82,34 +80,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..51281a528b6867e102c8ccd0dfdad43ed5835528 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)
+               OperPrefixMode(Module* Creator)
+                       : PrefixMode(Creator, "operprefix", 'y', OPERPREFIX_VALUE)
                {
                        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)
-               {
+                       levelrequired = INT_MAX;
                }
 };
 
@@ -97,106 +42,65 @@ 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)
        {
-               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()))
                        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);
        }
@@ -209,10 +113,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 3e42c4f7927885f023bc9b5bf05775b21f8a28a8..8bf1d30792f01a9a136d3caa3abc96f5581dc3de 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;
-
+                       ModeHandler* mh = i->mh;
                        if (mh->GetLevelRequired() > 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->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass %s", chan->name.c_str(), 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))
@@ -110,104 +130,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 e86b3cbf6d171dece2453c8e50e110680ad9c980..22513abeafdb8c20fc27da671425e4925f43d1ca 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,19 +203,23 @@ 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->topic = tag->getString("topic");
+
+                               if ((topicset != 0) || (!c->topic.empty()))
+                               {
+                                       if (topicset == 0)
+                                               topicset = ServerInstance->Time();
                                        c->topicset = topicset;
+                                       c->setby = tag->getString("topicsetby");
+                                       if (c->setby.empty())
+                                               c->setby = ServerInstance->Config->ServerName;
+                               }
 
-                               ServerInstance->Logs->Log("m_permchannels", DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str());
 
                                if (modes.empty())
                                        continue;
@@ -325,24 +248,24 @@ public:
                }
        }
 
-       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;
        }
 
@@ -361,7 +284,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)
                {
@@ -371,38 +294,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..e822676bf7357c102233cbcd69f5768110a4daf7 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, "%s :Invalid channel name", parameter.c_str());
+                               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, ":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, ":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, "%s * :You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.", cname.c_str());
                                                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, "%s %s :Force redirection stopped.", cname.c_str(), channel.c_str());
                                                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, "%s %s :You may not join this channel, so you are automatically being transferred to the redirect channel.", cname.c_str(), channel.c_str());
+                                               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..0ffe5e085010f43a62bdc846d4d2334e11c1f553 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, "%s :You must have a registered nickname to create a new channel", cname.c_str());
                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..8d0009d71330baca2816c421a412c102d7cbbadf 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(ERR_NOSUCHNICK, "%s :No such nick/channel", !channel ? channame.c_str() : username.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, "%s :Only a u-line may remove a u-line from a channel.", channame.c_str());
                        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 2.2. 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;
 
@@ -121,26 +131,25 @@ class RemoveBase : public Command
                                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());
+                               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, "%s :Can't remove user %s from channel (nokicks mode is set)", channel->name.c_str(), 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..21bca0f
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * 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 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->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
+{
+       RepeatMode rm;
+
+ public:
+       RepeatModule() : 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;
+
+               if (ServerInstance->OnCheckExemption(user, chan, "repeat") == 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->dhost);
+                               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..9e660e8ed16f0d1843b480384ff5a6a772fdc212 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, "%s :Only IRC operators may create new channels", cname.c_str());
                                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..279775d480570621bb58a9f3464c390271a0d0b7 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, "%s :You are not permitted to send private messages to this user", u->nick.c_str());
                        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..2aee89ad282b300053dbbf4aca3adb93a49c94bc 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,7 +58,8 @@ 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;
@@ -79,7 +78,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 +89,9 @@ class RLine : public XLine
                DefaultApply(u, "R", false);
        }
 
-       void DisplayExpiry()
+       const std::string& Displayable()
        {
-               ServerInstance->SNO->WriteToSnoMask('x',"Removing expired R-line %s (set by %s %ld seconds ago)",
-                       this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
-       }
-
-       const char* Displayable()
-       {
-               return matchtext.c_str();
+               return matchtext;
        }
 
        std::string matchtext;
@@ -116,7 +109,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 +122,6 @@ class RLineFactory : public XLineFactory
 
                return new RLine(set_time, duration, source, reason, xline_specific_mask, rxfactory);
        }
-
-       ~RLineFactory()
-       {
-       }
 };
 
 /** Handle /RLINE
@@ -156,7 +145,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 +154,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 +168,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 +177,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 +189,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 +207,6 @@ class CommandRLine : public Command
 
 class ModuleRLine : public Module
 {
- private:
        dynamic_reference<RegexFactory> rxfactory;
        RLineFactory f;
        CommandRLine r;
@@ -233,29 +221,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 +251,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,7 +284,7 @@ class ModuleRLine : public Module
                initing = false;
        }
 
-       virtual ModResult OnStats(char symbol, User* user, string_list &results)
+       ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
        {
                if (symbol != 'R')
                        return MOD_RES_PASSTHRU;
@@ -311,7 +293,7 @@ class ModuleRLine : public Module
                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 +310,7 @@ class ModuleRLine : public Module
                }
        }
 
-       virtual void OnBackgroundTimer(time_t curtime)
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                if (added_zline)
                {
@@ -337,7 +319,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)
diff --git a/src/modules/m_rmode.cpp b/src/modules/m_rmode.cpp
new file mode 100644 (file)
index 0000000..feb1738
--- /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())
+               {
+                       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(modeletter) && !((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..d1321947b2bb54979ecf8618a19556ae05581321 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'; Penalty = 0; 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->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+                               user->WriteNotice("*** You are not allowed to /SAJOIN other users (the privilege users/sajoin-others is needed to /SAJOIN others).");
                                return CMD_FAILURE;
                        }
-                       if (IS_LOCAL(user) && !ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+
+                       if (dest->server->IsULine())
+                       {
+                               user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+                               return CMD_FAILURE;
+                       }
+                       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;
+                       }
+
+                       Channel* chan = ServerInstance->FindChan(channel);
+                       if ((chan) && (chan->HasUser(dest)))
+                       {
+                               user->SendText(":" + user->server->GetName() + " NOTICE " + user->nick + " :*** " + dest->nick + " is already on " + channel);
                                return CMD_FAILURE;
                        }
 
-                       /* For local users, we send the JoinUser which may create a channel and set its TS.
+                       /* 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,7 +96,7 @@ class CommandSajoin : public Command
                }
                else
                {
-                       user->WriteServ("NOTICE "+user->nick+" :*** No such nickname "+parameters[0]);
+                       user->WriteNotice("*** No such nickname "+nickname);
                        return CMD_FAILURE;
                }
        }
@@ -110,20 +119,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 7dfcd8904db3b7e8531059e270274a6eac547cb5..911b826dc0ac25190bd74b6a04c5a0199d185d31 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides a SAKICK command */
-
 /** Handle /SAKICK
  */
 class CommandSakick : public Command
@@ -30,29 +28,27 @@ class CommandSakick : public Command
        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);
+               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,28 +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);
-
-                               Channel *n = ServerInstance->FindChan(parameters[1]);
-                               if (n && n->HasUser(dest))
-                               {
-                                       /* Sort-of-bug: If the command was issued remotely, this message won't be sent */
-                                       user->WriteServ("NOTICE %s :*** Unable to kick %s from %s", user->nick.c_str(), dest->nick.c_str(), parameters[0].c_str());
-                                       return CMD_FAILURE;
-                               }
-                       }
-
-                       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;
@@ -107,21 +91,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..689189229c8ed80b7d542199607aa9ede2037cd5 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
@@ -47,13 +45,32 @@ class CommandSamode : public Command
                                user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
                                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,22 +84,12 @@ 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;
@@ -92,7 +99,7 @@ class ModuleSaMode : public Module
        void Prioritize()
        {
                Module *override = ServerInstance->Modules->Find("m_override.so");
-               ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, &override);
+               ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, override);
        }
 };
 
index 4e4be77ae635c6e7f6d73d452dee4c6e88057e81..ba265fddd29acaeb2d9e7059f2d2d074634e081d 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for SANICK command */
-
 /** Handle /SANICK
  */
 class CommandSanick : public Command
@@ -32,7 +30,7 @@ class CommandSanick : public Command
        {
                allow_empty_last_param = false;
                flags_needed = 'o'; Penalty = 0; syntax = "<nick> <new-nick>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               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]);
                        }
@@ -98,20 +96,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..730bf08234f4aba5c6e00d704355a1ab03ff08f9 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'; Penalty = 0; 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;
@@ -109,21 +97,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..fb1c67564350144ae7acefc0ff27e146098b3773 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
@@ -31,24 +29,24 @@ class CommandSaquit : public Command
        CommandSaquit(Module* Creator) : Command(Creator, "SAQUIT", 2, 2)
        {
                flags_needed = 'o'; Penalty = 0; syntax = "<nick> <reason>";
-               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)) && (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,7 +54,7 @@ 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;
                }
        }
@@ -79,20 +77,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 32c9afc79ec05e36be5eeecd07f91f6f75e94ca3..d7c97c835b0f8579c6d741a030e0c7cb2676c5e1 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"
 
-/* $ModDesc: Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE. */
+class SASLCap : public Cap::Capability
+{
+       std::string mechlist;
+
+       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);
+       }
+
+       const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
+       {
+               return &mechlist;
+       }
+
+ public:
+       SASLCap(Module* mod)
+               : Cap::Capability(mod, "sasl")
+       {
+       }
+
+       void SetMechlist(const std::string& newmechlist)
+       {
+               if (mechlist == newmechlist)
+                       return;
+
+               mechlist = newmechlist;
+               NotifyValueChange();
+       }
+};
 
 enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
 enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
 
 static std::string sasl_target = "*";
+static Events::ModuleEventProvider* saslevprov;
 
 static void SendSASL(const parameterlist& params)
 {
-       if (!ServerInstance->PI->SendEncapsulatedData(params))
+       if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
        {
-               SASLFallback(NULL, params);
+               FOREACH_MOD_CUSTOM(*saslevprov, SASLEventListener, OnSASLAuth, (params));
        }
 }
 
@@ -56,17 +90,15 @@ class SaslAuthenticator
                : user(user_), state(SASL_INIT), state_announced(false)
        {
                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_))
+               LocalUser* localuser = IS_LOCAL(user);
+               if (method == "EXTERNAL" && localuser)
                {
-                       SocketCertificateRequest req(&((LocalUser*)user_)->eh, ServerInstance->Modules->Find("m_sasl.so"));
-                       std::string fp = req.GetFingerprint();
+                       std::string fp = SSLClientCert::GetFingerprint(&localuser->eh);
 
                        if (fp.size())
                                params.push_back(fp);
@@ -112,13 +144,13 @@ class SaslAuthenticator
                        else if (msg[2] == "M")
                                this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
                        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;
                }
 
@@ -137,8 +169,6 @@ class SaslAuthenticator
                        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");
@@ -164,13 +194,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(903, ":SASL authentication successful");
                        break;
                 case SASL_ABORT:
-                       this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
+                       this->user->WriteNumeric(906, ":SASL authentication aborted");
                        break;
                 case SASL_FAIL:
-                       this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
+                       this->user->WriteNumeric(904, ":SASL authentication failed");
                        break;
                 default:
                        break;
@@ -184,8 +214,8 @@ class CommandAuthenticate : public Command
 {
  public:
        SimpleExtItem<SaslAuthenticator>& authExt;
-       GenericCap& cap;
-       CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, GenericCap& Cap)
+       Cap::Capability& cap;
+       CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, Cap::Capability& Cap)
                : Command(Creator, "AUTHENTICATE", 1), authExt(ext), cap(Cap)
        {
                works_before_reg = true;
@@ -196,7 +226,7 @@ class CommandAuthenticate : public Command
                /* Only allow AUTHENTICATE on unregistered clients */
                if (user->registered != REG_ALL)
                {
-                       if (!cap.ext.get(user))
+                       if (!cap.get(user))
                                return CMD_FAILURE;
 
                        SaslAuthenticator *sasl = authExt.get(user);
@@ -223,10 +253,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;
                }
 
@@ -252,34 +282,34 @@ class CommandSASL : public Command
 class ModuleSASL : public Module
 {
        SimpleExtItem<SaslAuthenticator> authExt;
-       GenericCap cap;
+       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)
+               , cap(this)
+               , 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_OnUserRegister, 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.so and m_cap.so are not loaded! m_sasl.so 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", "*");
        }
 
-       ModResult OnUserRegister(LocalUser *user)
+       ModResult OnUserRegister(LocalUser *user) CXX11_OVERRIDE
        {
                SaslAuthenticator *sasl_ = authExt.get(user);
                if (sasl_)
@@ -291,14 +321,15 @@ class ModuleSASL : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string& extdata) CXX11_OVERRIDE
        {
-               return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
+               if ((target == NULL) && (extname == "saslmechlist"))
+                       cap.SetMechlist(extdata);
        }
 
-       void OnEvent(Event &ev)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               cap.HandleEvent(ev);
+               return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
        }
 };
 
index ae1c19d9148022e2324d27ba96ec19182b051f99..4a6f855369276dba2b3fb6fd865571e0454a9713 100644 (file)
@@ -17,8 +17,6 @@
  */
 
 
-/* $ModDesc: Provides a SATOPIC command */
-
 #include "inspircd.h"
 
 /** Handle /SATOPIC
@@ -40,17 +38,15 @@ class CommandSATopic : public Command
 
                if(target)
                {
-                       std::string newTopic = parameters[1];
-
-                       // 3rd parameter overrides access checks
-                       target->SetTopic(user, newTopic, true);
+                       const std::string& newTopic = parameters[1];
+                       target->SetTopic(user, newTopic);
                        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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
                        return CMD_FAILURE;
                }
        }
@@ -65,16 +61,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 6013d1fd7543400bfcadf0a4f55f1269f8b058ad..f4042b8f6299cbdfec0d51a9773b6e43a0c3b84d 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();
 
@@ -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 crap clients (read: 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..48ab511f08c8a557cccbd1af4892ea425c488e26 100644 (file)
  */
 
 
-/* $ModDesc: Provides support for ircu-style services accounts, including chmode +R, etc. */
-
 #include "inspircd.h"
-#include "account.h"
+#include "modules/account.h"
 
 /** Channel mode +r - mark a channel as identified
  */
@@ -40,15 +38,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 +64,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 +102,91 @@ 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 (!value.empty())
+               {
+                       // Logged in
+                       if (IS_LOCAL(user))
+                       {
+                               user->WriteNumeric(900, "%s %s :You are now logged in as %s",
+                                       user->GetFullHost().c_str(), value.c_str(), value.c_str());
+                       }
+               }
+               // If value is empty then logged out
+
+               FOREACH_MOD_CUSTOM(eventprov, AccountEventListener, OnAccountChange, (user, value));
        }
+};
 
-       void init()
+class ModuleServicesAccount : public Module, public Whois::EventListener
+{
+       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)
+               , 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, "%s :is logged in as", account->c_str());
                }
 
-               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) && assign(user->nick) != oldnick)
+                       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;
@@ -196,10 +199,10 @@ class ModuleServicesAccount : public Module
                        Channel* c = (Channel*)dest;
                        ModResult res = ServerInstance->OnCheckExemption(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(477, c->name+" :You need to be identified to a registered account to message this channel");
                                return MOD_RES_DENY;
                        }
                }
@@ -207,17 +210,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(477, 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 +256,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(477, chan->name + " :You need to be identified to a registered account to join this channel");
                                        return MOD_RES_DENY;
                                }
                        }
@@ -281,56 +276,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..0445235dc795f6821de7853ee98c0fae38b5fc89 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,46 +42,36 @@ class ServProtectMode : public ModeHandler
        }
 };
 
-class ModuleServProtectMode : public Module
+class ModuleServProtectMode : public Module, public Whois::EventListener, public Whois::LineEventListener
 {
        ServProtectMode bm;
  public:
        ModuleServProtectMode()
-               : bm(this)
+               : Whois::EventListener(this)
+               , Whois::LineEventListener(this)
+               , bm(this)
        {
        }
 
-       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()
-       {
-       }
-
-       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())
                {
                        /* Check if the parameter is a valid nick/uuid
                         */
@@ -95,10 +83,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(mh->GetModeChar()))
                                {
                                        /* 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, "%s :You are not permitted to remove privileges from %s services", chan->name.c_str(), ServerInstance->Config->Network.c_str());
                                        return MOD_RES_DENY;
                                }
                        }
@@ -107,37 +95,36 @@ 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, ":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, "%s :You are not permitted to kick services",
+                               memb->chan->name.c_str());
                        return MOD_RES_DENY;
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnWhoisLine(User* src, User* dst, int &numeric, std::string &text)
+       ModResult OnWhoisLine(Whois::Context& whois, unsigned int& numeric, std::string& text) CXX11_OVERRIDE
        {
-               return ((src != dst) && (numeric == 319) && dst->IsModeSet('k')) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+               return ((numeric == 319) && whois.GetTarget()->IsModeSet(bm)) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
        }
 };
 
-
 MODULE_INIT(ModuleServProtectMode)
index 2ef0c054865a6fcf9a19ee5b1631f6324e0c4938..75dbe1c6a6c00f84a4ce09661b720c4327d60210 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)
@@ -44,18 +41,18 @@ class CommandSethost : public Command
                {
                        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)
+               if (len > ServerInstance->Config->Limits.MaxHost)
                {
-                       user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick.c_str());
+                       user->WriteNotice("*** SETHOST: Host too long");
                        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);
                        return CMD_SUCCESS;
@@ -70,21 +67,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 +83,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..dd82aef29ebf33f19247ebad525b5bf6138225d4 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Allows opers to set their idle time */
-
 /** 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(948, ":Invalid idle time.");
                        return CMD_FAILURE;
                }
                user->idle_lastmsg = (ServerInstance->Time() - idle);
@@ -47,7 +44,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(944, ":Idle time set.");
 
                return CMD_SUCCESS;
        }
@@ -63,16 +60,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);
        }
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..cb51c43
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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
+       {
+               const std::string& sn = ServerInstance->Config->ServerName;
+               if (method == SF_NUMERIC)
+               {
+                       if (!introtext.empty())
+                               user->SendText(":%s %03d %s :%s %s", sn.c_str(), intronumeric, user->nick.c_str(), sn.c_str(), introtext.c_str());
+
+                       for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+                               user->SendText(":%s %03d %s :- %s", sn.c_str(), textnumeric, user->nick.c_str(), i->c_str());
+
+                       user->SendText(":%s %03d %s :%s", sn.c_str(), endnumeric, user->nick.c_str(), endtext.c_str());
+               }
+               else
+               {
+                       const char* msgcmd = (method == SF_MSG ? "PRIVMSG" : "NOTICE");
+                       std::string header = InspIRCd::Format(":%s %s %s :", sn.c_str(), msgcmd, user->nick.c_str());
+                       for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+                               user->SendText(header + *i);
+               }
+               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..3cb85f3fb1b18d502ee393cddbc1c01d9694351f 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 + "@" +
+                       (dest->HasPrivPermission("users/auspex") ? src->host : src->dhost) +
+                       ") 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 8bf4d30e7bf341d4d6ce773bc7c1734455bb586e..a3a2909a08bb9427704d3b2eb3498da6070eb943 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,14 +34,11 @@ 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))
@@ -59,15 +54,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 +96,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,18 +109,18 @@ class CommandShun : public Command
                        }
                        else
                        {
-                               user->WriteServ("NOTICE %s :*** Shun %s not found in list, try /stats H.",user->nick.c_str(),target.c_str());
+                               user->WriteNotice("*** Shun " + target + " 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
@@ -151,7 +140,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());
                                }
@@ -159,7 +148,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;
                        }
                }
@@ -179,7 +168,7 @@ class ModuleShun : public Module
 {
        CommandShun cmd;
        ShunFactory f;
-       std::set<std::string> ShunEnabledCommands;
+       insp::flat_set<std::string> ShunEnabledCommands;
        bool NotifyOfShun;
        bool affectopers;
 
@@ -188,17 +177,12 @@ 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);
@@ -207,10 +191,10 @@ class ModuleShun : public Module
        void Prioritize()
        {
                Module* alias = ServerInstance->Modules->Find("m_alias.so");
-               ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, &alias);
+               ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, alias);
        }
 
-       virtual ModResult OnStats(char symbol, User* user, string_list& out)
+       ModResult OnStats(char symbol, User* user, string_list& out) CXX11_OVERRIDE
        {
                if (symbol != 'H')
                        return MOD_RES_PASSTHRU;
@@ -219,7 +203,7 @@ class ModuleShun : public Module
                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");
@@ -242,7 +226,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;
@@ -253,18 +237,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;
                }
 
@@ -283,11 +265,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..f2ac26fb3692a8617cdb5fc19590b1903b5eee57 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;
@@ -108,11 +106,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 +125,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, "%s %s %s", user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
                                }
                        }
-                       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 +147,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;
                        }
 
@@ -176,7 +174,7 @@ class CommandSilence : public Command
                                                if (listitem == mask && i->second == pattern)
                                                {
                                                        sl->erase(i);
-                                                       user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+                                                       user->WriteNumeric(950, "%s :Removed %s %s from silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
                                                        if (!sl->size())
                                                        {
                                                                ext.unset(user);
@@ -185,7 +183,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, "%s :%s %s does not exist on your silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
                        }
                        else if (action == '+')
                        {
@@ -198,7 +196,7 @@ 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, "%s :Your silence list is full",user->nick.c_str());
                                        return CMD_FAILURE;
                                }
 
@@ -208,19 +206,19 @@ class CommandSilence : public Command
                                        irc::string listitem = n->first.c_str();
                                        if (listitem == mask && n->second == pattern)
                                        {
-                                               user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+                                               user->WriteNumeric(952, "%s :%s %s is already on your silence list", user->nick.c_str(), 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, "%s :Added %s %s to silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
                                return CMD_SUCCESS;
                        }
                }
@@ -293,6 +291,7 @@ class CommandSilence : public Command
 class ModuleSilence : public Module
 {
        unsigned int maxsilence;
+       bool ExemptULine;
        CommandSilence cmdsilence;
        CommandSVSSilence cmdsvssilence;
  public:
@@ -302,36 +301,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 +335,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 +371,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..f14c8ac987937f735f488c4d12052f4d38cbd8ce 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 7b64358980d6bb41e49dbadeee5b437ea327fd5b..b75fbc3cc52ac7d915b5bedd36dacacaba60ca25 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
 
-#include "treesocket.h"
 #include "treeserver.h"
 #include "utils.h"
 #include "link.h"
 #include "main.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;
+               // 2.2 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;
 }
@@ -66,16 +89,18 @@ static std::string BuildModeList(ModeType type)
                {
                        std::string mdesc = mh->name;
                        mdesc.push_back('=');
-                       if (mh->GetPrefix())
-                               mdesc.push_back(mh->GetPrefix());
-                       if (mh->GetModeChar())
-                               mdesc.push_back(mh->GetModeChar());
+                       PrefixMode* pm = mh->IsPrefixMode();
+                       if (pm)
+                       {
+                               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)
@@ -90,7 +115,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 */
@@ -134,13 +159,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->Find("m_sha256.so")))
+       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)+
@@ -152,18 +179,18 @@ 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)+
                        // 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");
 }
@@ -208,7 +235,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")
@@ -218,7 +261,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.";
@@ -256,21 +299,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.";
 
@@ -292,7 +320,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.";
                }
 
@@ -314,13 +342,13 @@ 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.";
                }
 
                /* 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->Find("m_sha256.so")))
+               if ((n != this->capab->CapKeys.end()) && (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256")))
                {
                        /* Challenge-response is on now */
                        this->SetTheirChallenge(n->second);
@@ -332,7 +360,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);
@@ -354,7 +382,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                }
                else
                {
-                       capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ',');
+                       capab->ModuleList.push_back(');
                        capab->ModuleList.append(params[1]);
                }
        }
@@ -388,12 +416,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..26eb458
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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);
+       }
+
+       bool Unicast(const std::string& target) const
+       {
+               return Utils->DoOneToOne(*this, target);
+       }
+
+       void Unicast(User* target) const
+       {
+               Utils->DoOneToOne(*this, target->server);
+       }
+};
index 3b5b499c11d8cf8d3f3c83e2a6c9060f88720e22..1f7456426e3d6a9df87aa35a2ba956468e234470 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_COMMANDS_H
-#define M_SPANNINGTREE_COMMANDS_H
+#pragma once
 
-#include "main.h"
+#include "servercommand.h"
+#include "commandbuilder.h"
 
 /** 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 CommandFName : public UserOnlyServerCommand<CommandFName>
+{
+ public:
+       CommandFName(Module* Creator) : UserOnlyServerCommand<CommandFName>(Creator, "FNAME", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
+};
+
+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:
+       CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
+};
+
+class CommandAway : public UserOnlyServerCommand<CommandAway>
+{
+ public:
+       CommandAway(Module* Creator) : UserOnlyServerCommand<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 XLine;
+class CommandAddLine : public ServerCommand
+{
+ public:
+       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 CommandDelLine : public ServerCommand
+{
+ public:
+       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 CommandNick : public UserOnlyServerCommand<CommandNick>
+{
+ public:
+       CommandNick(Module* Creator) : UserOnlyServerCommand<CommandNick>(Creator, "NICK", 2) { }
+       CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+};
+
+class 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 CommandPong : public ServerOnlyServerCommand<CommandPong>
+{
+ public:
+       CommandPong(Module* Creator) : ServerOnlyServerCommand<CommandPong>(Creator, "PONG", 1) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandPush : public ServerCommand
+{
+ public:
+       CommandPush(Module* Creator) : ServerCommand(Creator, "PUSH", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
+       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandSave : public ServerCommand
+{
+ public:
+       /** Timestamp of the uuid nick of all users who collided and got their nick changed to uuid
+        */
+       static const time_t SavedTimestamp = 100;
+
+       CommandSave(Module* Creator) : ServerCommand(Creator, "SAVE", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
 };
-class CommandFMode : public Command
+
+class CommandServer : public ServerOnlyServerCommand<CommandServer>
 {
+       static void HandleExtra(TreeServer* newserver, const std::vector<std::string>& params);
+
  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; }
+       CommandServer(Module* Creator) : ServerOnlyServerCommand<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 CommandFTopic : public Command
+
+class CommandSQuit : public ServerOnlyServerCommand<CommandSQuit>
 {
  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; }
+       CommandSQuit(Module* Creator) : ServerOnlyServerCommand<CommandSQuit>(Creator, "SQUIT", 2) { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
 };
-class CommandFHost : public Command
+
+class CommandSNONotice : public ServerCommand
 {
  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; }
+       CommandSNONotice(Module* Creator) : ServerCommand(Creator, "SNONOTICE", 2) { }
+       CmdResult Handle(User* user, std::vector<std::string>& parameters);
 };
-class CommandFIdent : public Command
+
+class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
 {
  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; }
+       CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
+       CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
 };
-class CommandFName : public Command
+
+class CommandSInfo : public ServerOnlyServerCommand<CommandSInfo>
 {
  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; }
+       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 SpanningTreeCommands
 {
  public:
-       CommandRConnect rconnect;
-       CommandRSQuit rsquit;
        CommandSVSJoin svsjoin;
        CommandSVSPart svspart;
        CommandSVSNick svsnick;
@@ -144,12 +379,27 @@ class SpanningTreeCommands
        CommandUID uid;
        CommandOpertype opertype;
        CommandFJoin fjoin;
+       CommandIJoin ijoin;
+       CommandResync resync;
        CommandFMode fmode;
        CommandFTopic ftopic;
        CommandFHost fhost;
        CommandFIdent fident;
        CommandFName fname;
+       CommandAway away;
+       CommandAddLine addline;
+       CommandDelLine delline;
+       CommandEncap encap;
+       CommandIdle idle;
+       CommandNick nick;
+       CommandPing ping;
+       CommandPong pong;
+       CommandPush push;
+       CommandSave save;
+       CommandServer server;
+       CommandSQuit squit;
+       CommandSNONotice snonotice;
+       CommandEndBurst endburst;
+       CommandSInfo sinfo;
        SpanningTreeCommands(ModuleSpanningTree* module);
 };
-
-#endif
index ec0cdb03661d3e99a45d15a6487828d0e59bded1..41b5488f0e7a10ec6b7b5a347b99afc0b9be9de8 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")
+                       if (proto_version < 1205)
                        {
-                               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 (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)
+                               else if (command == "RESYNC")
                                        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);
-                               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;
+
+                                       std::string::size_type c = line.find(' ', b + 1);
+                                       if (c == std::string::npos)
+                                               return;
 
-                               if (subcmd == "CHGIDENT" && d != std::string::npos)
+                                       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;
 
-                               Command* thiscmd = ServerInstance->Parser->GetHandler(subcmd);
-                               if (thiscmd && subcmd != "WHOISNOTICE")
+                                       std::string::size_type c = line.find(' ', b + 1);
+                                       if (c == std::string::npos)
+                                               return;
+
+                                       std::string::size_type d = line.find(' ', c + 1);
+                                       if ((d < line.size()-1) && (original_line[d+1] != ':'))
+                                       {
+                                               // There is a third parameter which doesn't begin with a colon, erase it
+                                               std::string::size_type e = line.find(' ', d + 1);
+                                               line.erase(d, e-d);
+                                       }
+                               }
+                               else if (command == "SINFO")
+                               {
+                                       // :22D SINFO version :InspIRCd-2.2
+                                       //     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")
                                {
-                                       Version ver = thiscmd->creator->GetVersion();
-                                       if (ver.Flags & VF_OPTCOMMON)
+                                       // :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();
                                        }
                                }
                        }
+                       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 2.2.
+               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;
+       }
+
+       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..95f8f4e4a7da0ea4b7f7f6da73cfc7efda3185c7 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
 
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.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());
+               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 (params[0].find_first_of("*?") != std::string::npos)
-               {
-                       Utils->DoOneToAllButSender(who->uuid, "ENCAP", params, who->server);
-               }
-               else
-                       Utils->DoOneToOne(who->uuid, "ENCAP", params, params[0]);
+               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 47b39452268dfa8cc34923a98cfc36184726bb25..0879e730a0551616533178ebfab4ce0ff798fc8b 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,271 @@ 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. :-)
+        *
+        * 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 2.2 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;
 
-       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)
+                       {
+                               apply_other_sides_modes = false;
+                       }
+                       else if (ourTS > TS)
                        {
-                               chan = new Channel(channel, 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;
-                       }
+       TreeServer* const sourceserver = TreeServer::Get(srcuser);
 
-                       stack.Push(*i, modeparam);
-               }
+       // 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);
 
-               std::vector<std::string> modelist;
+       // 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);
+       }
 
-               // Mode parser needs to know what channel to act on.
-               modelist.push_back(params[0]);
+       fwdfjoin.finalize();
+       fwdfjoin.Forward(sourceserver);
 
-               while (stack.GetStackedLine(modelist))
-               {
-                       ServerInstance->Modes->Process(modelist, srcuser, true);
-                       modelist.erase(modelist.begin() + 1, modelist.end());
-               }
+       // 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);
+
+       return CMD_SUCCESS;
+}
 
-               ServerInstance->Modes->Process(modelist, srcuser, true);
+void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin)
+{
+       std::string::size_type comma = item.find(',');
+
+       // Comma not required anymore if the user has no modes
+       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)
+       {
+               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))
        {
-               const char* usr = item.c_str();
-               if (usr && *usr)
+               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 nonexistant 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());
-               }
+void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
+{
+       if (Utils->AnnounceTSChange)
+               chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), newname.c_str(), (unsigned long) chan->age, (unsigned long) TS);
+
+       // 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, if it isn't empty then send a topic change message to local users
+       if (!chan->topic.empty())
+       {
+               chan->topic.clear();
+               chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :", chan->name.c_str());
        }
-       return CMD_SUCCESS;
+       chan->setby.clear();
+       chan->topicset = 0;
 }
 
-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..3c76c928aa6a7d773390547f5ee9b46e02516a32 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)
+       {
+               // 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;
+       }
+
+       if (c->topic != newtopic)
        {
-               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;
-               }
+               // Update topic only when it differs from current topic
+               c->topic.assign(newtopic, 0, ServerInstance->Config->Limits.MaxTopic);
+               c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
        }
+
+       // Update setter and settime
+       c->setby.assign(setter, 0, 128);
+       c->topicset = ts;
+
+       FOREACH_MOD(OnPostTopicChange, (user, c, c->topic));
+
        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..78e05db
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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() > 2)
+       {
+               time_t RemoteTS = ServerCommand::ExtractTS(params[2]);
+               if (RemoteTS < chan->age)
+                       throw ProtocolException("Attempted to lower TS via IJOIN. LocalTS=" + ConvToStr(chan->age));
+               apply_modes = ((params.size() > 3) && (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..21213fb3e9e490a20253e032dd56d3595752d7de 100644 (file)
@@ -18,8 +18,7 @@
  */
 
 
-#ifndef M_SPANNINGTREE_LINK_H
-#define M_SPANNINGTREE_LINK_H
+#pragma once
 
 class Link : public refcountbase
 {
@@ -31,7 +30,7 @@ class Link : public refcountbase
        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..7263768443a4f7ab52fa2d9dbf6f6acdd931c360 100644 (file)
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
 #include "socket.h"
 #include "xline.h"
+#include "iohook.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), push(module), save(module),
+       server(module), squit(module), snonotice(module),
+       endburst(module), sinfo(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 +103,40 @@ 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,
+       user->WriteNumeric(RPL_LINKS, "%s %s :%d %s",   Current->GetName().c_str(),
+                       (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
+                       (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops,
                        Current->GetDesc().c_str());
 }
 
-int ModuleSpanningTree::CountServs()
-{
-       return Utils->serverlist.size();
-}
-
 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)
@@ -272,7 +200,7 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
                return;
        }
 
-       QueryType start_type = DNS_QUERY_AAAA;
+       DNS::QueryType start_type = DNS::QUERY_AAAA;
        if (strchr(x->IPAddr.c_str(),':'))
        {
                in6_addr n;
@@ -289,8 +217,8 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
        /* Do we already have an IP? If so, no need to resolve it. */
        if (ipvalid)
        {
-               /* Gave a hook, but it wasnt one we know */
-               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 +230,21 @@ 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 m_dns.so is not loaded, unable to resolve.", x->Name.c_str());
+       }
        else
        {
+               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,20 +288,27 @@ 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, ":%s", Version.c_str());
        }
        else
        {
-               user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),parameters[0].c_str());
+               user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str());
        }
        return MOD_RES_DENY;
 }
@@ -378,15 +317,11 @@ ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& para
  */
 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);
+       std::string text;
+       VAFORMAT(text, format, format);
 
        if (IS_LOCAL(user))
-               user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
+               user->WriteNotice(text);
        else
                ServerInstance->PI->SendUserNotice(user, text);
 }
@@ -413,8 +348,7 @@ ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& para
                        }
                        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());
+                               RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str());
                                return MOD_RES_DENY;
                        }
                }
@@ -423,24 +357,16 @@ ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& para
        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();
        }
 }
 
@@ -450,130 +376,43 @@ void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std:
        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)
-{
-       if (IS_LOCAL(user))
-       {
-               parameterlist params;
-               params.push_back(":"+text);
-               Utils->DoOneToMany(user->uuid,"WALLOPS",params);
-       }
-}
-
-void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
-{
-       /* Server origin */
-       if (user == NULL)
-               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 +421,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 +434,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)
        {
-               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.
+               CommandFJoin::Builder params(memb->chan);
+               params.add(memb);
+               params.finalize();
+               params.Broadcast();
+       }
+       else
+       {
+               CmdBuilder params(memb->user, "IJOIN");
                params.push_back(memb->chan->name);
-               params.push_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 +472,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 +480,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 +488,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 +566,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 +602,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 +618,26 @@ 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);
 
 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->GetIOHook() && sock->GetIOHook()->prov->creator == 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 +645,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->GetIOHook() && sock->GetIOHook()->prov->creator == 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,7 +746,7 @@ 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()
index 17adc928780a4f9f3aa04f01bd4686c5a2acfcbc..3e02ce93f8ba119ae18a47a61e26049160e8df5b 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);
@@ -133,56 +135,44 @@ class ModuleSpanningTree : public Module
         */
        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);
+       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;
+       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(char statschar, User* user, string_list &results) 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();
        ~ModuleSpanningTree();
-       Version GetVersion();
+       Version GetVersion() CXX11_OVERRIDE;
        void Prioritize();
 };
-
-#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 d508c092dd00fea08c6fb87a365fb8b64e6da736..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;
+}
+
+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 with distances as hops.
+/** Recursively send the server tree.
  * This is used during network burst to inform the other server
  * (and any of ITS servers too) of what servers we know about.
  * If at any point any of these servers already exist on the other
- * end, our connection may be terminated. The hopcounts given
- * by this function are relative, this doesn't matter so long as
- * they are all >1, as all the remote servers re-calculate them
- * to be relative too, with themselves as hop 0.
+ * end, our connection may be terminated.
  */
-void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
+void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
 {
-       char command[MAXBUF];
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       SendServerInfo(Current);
+
+       const TreeServer::ChildServers& children = Current->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
-               TreeServer* recursive_server = Current->GetChild(q);
+               TreeServer* recursive_server = *i;
                if (recursive_server != s)
                {
-                       std::string recursive_servername = recursive_server->GetName();
-                       snprintf(command, MAXBUF, ":%s SERVER %s * %d %s :%s", Current->GetID().c_str(), recursive_servername.c_str(), hops,
-                                       recursive_server->GetID().c_str(),
-                                       recursive_server->GetDesc().c_str());
-                       this->WriteLine(command);
-                       this->WriteLine(":"+recursive_server->GetID()+" VERSION :"+recursive_server->GetVersion());
+                       this->WriteLine(CommandServer::Builder(recursive_server));
                        /* down to next level */
-                       this->SendServers(recursive_server, s, hops+1);
+                       this->SendServers(recursive_server, s);
                }
        }
 }
 
 /** Send one or more FJOINs for a channel of users.
- * If the length of a single line is more than 480-NICKMAX
- * in length, it is split over multiple lines.
+ * If the length of a single line is too long, it is split over multiple lines.
  */
 void TreeSocket::SendFJoins(Channel* c)
 {
-       std::string buffer;
-       char list[MAXBUF];
-
-       size_t curlen, headlen;
-       curlen = headlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu +%s :",
-               ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long)c->age, c->ChanModes(true));
-       int numusers = 0;
-       char* ptr = list + curlen;
-       bool looped_once = false;
-
-       const UserMembList *ulist = c->GetUsers();
-       std::string modes;
-       std::string params;
-
-       for (UserMembCIter 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)
-               {
-                       // remove the final space
-                       if (ptr[-1] == ' ')
-                               ptr[-1] = '\0';
-                       buffer.append(list).append("\r\n");
-                       curlen = headlen;
-                       ptr = list + headlen;
-                       numusers = 0;
-               }
-
-               ptrlen = snprintf(ptr, MAXBUF-curlen, "%s,%s ", modestr.c_str(), i->first->uuid.c_str());
-
-               looped_once = true;
-
-               curlen += ptrlen;
-               ptr += ptrlen;
+       CommandFJoin::Builder fjoin(c);
 
-               numusers++;
-       }
-
-       // 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");
-       }
-
-       int linesize = 1;
-       for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
+       const Channel::MemberMap& ulist = c->GetUsers();
+       for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
        {
-               int size = b->data.length() + 2;
-               int currsize = linesize + size;
-               if (currsize <= 350)
+               Membership* memb = i->second;
+               if (!fjoin.has_room(memb))
                {
-                       modes.append("b");
-                       params.append(" ").append(b->data);
-                       linesize += size;
-               }
-               if ((modes.length() >= ServerInstance->Config->Limits.MaxModes) || (currsize > 350))
-               {
-                       /* Wrap at MAXMODES */
-                       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;
+                       // No room for this user, send the line and prepare a new one
+                       this->WriteLine(fjoin.finalize());
+                       fjoin.clear();
                }
+               fjoin.add(memb);
        }
-
-       /* 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)
@@ -192,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());
+}
+
+/** 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);
 
-               FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,Utils->Creator,this));
+       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..1d853a2
--- /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/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..ab531c171e47b31c1df92b040f6c0f8c0463fd73 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
        {
@@ -48,12 +50,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->host.c_str(), opertype.c_str());
        return CMD_SUCCESS;
 }
 
+CommandOpertype::Builder::Builder(User* user)
+       : CmdBuilder(user, "OPERTYPE")
+{
+       push_last(user->oper->name);
+}
index 04fa4bcab31b53a61aaaab3b6b5e9abd3c25c102..612df80f3ef0802b9e8ee04f1d8c83ddf084893f 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(), ' ');
+
+       char buf[16];
+       snprintf(buf, sizeof(buf), "%5d [%5.2f%%]", current->UserCount, percent);
+       buffer += buf;
 
-       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, "%s :No such server", parameters[0].c_str());
+                       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",
+
+       std::vector<std::string> map = GetMap(user, Utils->TreeRoot, max, 0);
+       for (std::vector<std::string>::const_iterator i = map.begin(); i != map.end(); ++i)
+               user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(),
+                       RPL_MAP, user->nick.c_str(), i->c_str());
+
+       size_t totusers = ServerInstance->Users->GetUsers().size();
+       float avg_users = (float) totusers / Utils->serverlist.size();
+
+       user->SendText(":%s %03d %s :%u server%s and %u user%s, average %.2f users per server",
                ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(),
-               line, (line > 1 ? "s" : ""), totusers, (totusers > 1 ? "s" : ""), avg_users);
+               (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users);
        user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(),
                RPL_ENDMAP, user->nick.c_str());
 
-       delete[] names;
-       delete[] stats;
-
-       return true;
+       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..14b3f5ef74b10a6936eb26d9859d4eb6c78421db 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)
 {
@@ -36,12 +31,22 @@ ModResult ModuleSpanningTree::OnStats(char statschar, User* user, string_list &r
                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));
+                       results.push_back("213 "+user->nick+" "+statschar+" *@"+(L->HiddenFromStats ? "<hidden>" : L->IPAddr)+" * "+(*i)->Name.c_str()+" "+ConvToStr(L->Port)+" "+(L->Hook.empty() ? "plaintext" : L->Hook));
                        if (statschar == 'c')
-                               results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+L->Name.c_str());
+                               results.push_back("244 "+user->nick+" H * * "+L->Name.c_str());
+               }
+               return MOD_RES_DENY;
+       }
+       else if (statschar == 'U')
+       {
+               ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       std::string name = i->second->getString("server");
+                       if (!name.empty())
+                               results.push_back("248 "+user->nick+" U "+name);
                }
                return MOD_RES_DENY;
        }
        return MOD_RES_PASSTHRU;
 }
-
index ad8c6a6ef1388afa7151d3657e0be96088e3d5a6..430467dc73319ad810978c2b4841e55d74ab85a5 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[1].c_str());
+               user->WriteNumeric(RPL_ENDOFWHOIS, "%s :End of /WHOIS list.", parameters[1].c_str());
+               return MOD_RES_DENY;
        }
        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 471bbfcb970670c895ec304df5e30bcef15c44d2..ae98be946520608313c18af76cbe8d61173986fc 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 nonexistant server %s",
-                               routing.serverdest.c_str());
-                       return;
+                       sdest = FindServer(routing.serverdest);
+                       if (!sdest)
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistant server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
+                               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 +72,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 +89,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 3ab5dae9df0c9aa5b54fed4fc8c53e4f244bb10d..786f8b74b4056e200c26ec75c78f8c27ff0b303e 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;
+       if (!source)
+               source = ServerInstance->FakeClient;
 
-       params.push_back(channel->name);
-       params.push_back(ConvToStr(ServerInstance->Time()));
-       params.push_back(ServerInstance->Config->ServerName);
-       params.push_back(":" + topic);
-
-       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);
+       CommandMetadata::Builder(key, data).Broadcast();
 }
 
-void SpanningTreeProtocolInterface::SendChannel(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 = status + cname;
-       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);
-       }
+       sock->WriteLine(CommandMetadata::Builder(key, data));
 }
 
+void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
+{
+       CommandFTopic::Builder(ServerInstance->FakeClient, channel).Broadcast();
+}
 
-void SpanningTreeProtocolInterface::SendChannelPrivmsg(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::SendSNONotice(char snomask, const std::string &text)
 {
-       SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" PRIVMSG "+target->name+" :"+text);
+       CmdBuilder("SNONOTICE").push(snomask).push_last(text).Broadcast();
 }
 
-void SpanningTreeProtocolInterface::SendChannelNotice(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
 {
-       SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" NOTICE "+target->name+" :"+text);
+       CmdBuilder("PUSH").push(target->uuid).push_last(rawline).Unicast(target);
 }
 
-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..45742e9eaa0374753959ad61ac3cae822339fd96 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 SendTopic(Channel* channel, std::string &topic);
+       void SendSNONotice(char snomask, const std::string& text) CXX11_OVERRIDE;
+       void PushToClient(User* target, const std::string &rawline);
+       void SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype);
+       void SendMessage(User* target, const std::string& text, MessageType msgtype);
+       void GetServerList(ServerList& sl);
+};
index b791376ea9e65823e555badc4fc1f1817f549a07..b29b780c8eee0f9223c354c94bd57a7384658b76 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"
 
-/* $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)
+CmdResult CommandPush::Handle(User* user, std::vector<std::string>& params)
 {
-       if (params.size() < 2)
-               return true;
        User* u = ServerInstance->FindNick(params[0]);
        if (!u)
-               return true;
+               return CMD_FAILURE;
        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;
+       return CMD_SUCCESS;
 }
-
index d4254cac67f951a3cb20e1551a5f206a2c38437d..c5d3a5b523d9b8ab88d0e3b5a395c1f430dcf4d9 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());
+               ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** 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;
 }
 
index d7c4c52274c02a2dfe51c16eabee2db5aa51dfff..3d04a5085dd0a9bc3c38b76aa961c8e12ab9657e 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 &ans_record = r->answers[0];
+
        /* 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.
@@ -51,7 +51,7 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in
        TreeServer* CheckDupe = Utils->FindServer(MyLink->Name.c_str());
        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 +66,74 @@ 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 (query == DNS::QUERY_AAAA)
        {
-               bool cached = false;
-               ServernameResolver* snr = new ServernameResolver(Utils, host, MyLink, cached, DNS_QUERY_A, myautoconnect);
-               ServerInstance->AddResolver(snr, cached);
-               return;
+               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(), errormessage.c_str() );
+
+       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)
 {
+       const DNS::ResourceRecord &ans_record = r->answers[0];
+
        for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
        {
                Link* L = *i;
                if (L->IPAddr == host)
                {
-                       Utils->ValidIPs.push_back(result);
+                       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)
+       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..45413c33f9e2a244f035d7b6496ba95f318d9bff 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());
+               ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** 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)");
+               ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** 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..bc43841c1cafefd4a9950605b9b91548413505f0 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.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;
-       }
-
-       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);
+       const std::string& servername = params[0];
+       const std::string& sid = params[1];
+       const std::string& description = params.back();
+       TreeSocket* socket = ParentOfThis->GetSocket();
 
-       if (!ParentOfThis)
+       if (!InspIRCd::IsSID(sid))
        {
-               this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
-               return false;
-       }
-       if (!ServerInstance->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++)
@@ -134,22 +123,27 @@ 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)
-               {
-                       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;
-               }
+               if (!CheckDuplicate(sname, sid))
+                       return NULL;
+
+               ServerInstance->SNO->WriteToSnoMask('l',"Verified server connection " + linkID + " ("+description+")");
+               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 +152,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 +172,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 +193,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))
+       const Link* x = AuthRemote(params);
+       if (x)
        {
-               this->SendError("Invalid format server ID: "+sid+"!");
-               return false;
-       }
-
-       for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
-       {
-               Link* x = *i;
-               if (x->Name != servername && x->Name != "*") // open link allowance
-                       continue;
-
-               if (!ComparePass(*x, password))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('l',"Invalid password on link: %s", x->Name.c_str());
-                       continue;
-               }
-
-               if (!CheckDuplicate(sname, sid))
-                       return false;
-
-               ServerInstance->SNO->WriteToSnoMask('l',"Verified incoming server connection " + linkID + " ("+description+")");
-
-               this->SendCapabilities(2);
-
                // Save these for later, so when they accept our credentials (indicated by BURST) we remember them
                this->capab->hidden = x->Hidden;
-               this->capab->sid = sid;
-               this->capab->description = description;
-               this->capab->name = sname;
+               this->capab->sid = params[3];
+               this->capab->description = params.back();
+               this->capab->name = params[0];
 
                // Send our details: Our server name and description and hopcount of 0,
                // along with the sendpass from this block.
@@ -276,8 +211,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..07dfc48
--- /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);
+
+       /**
+        * 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..98443409aa8abd3068207bcf9cef39a1eaf1fed6 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,8 +34,21 @@ 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;
 }
 
index 59973202ddc06cc52a73a214648eeeaae45986dc..b3a612ca350f9b53108ac9c213812ace9070066b 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;
index 3bdf13b25ca1e03ee8cc0859e5f8047404f08314..f86afa367a9e4695aa29dbe92d08d2d72958ae02 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)
diff --git a/src/modules/m_spanningtree/translate.cpp b/src/modules/m_spanningtree/translate.cpp
new file mode 100644 (file)
index 0000000..48c0632
--- /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->GetNumParams(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..48f16c9df80f55ef90c5ecb271f0b4341bf63d89 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,177 @@ TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::strin
         */
 
        this->AddHashEntry();
-
-       SetID(id);
+       Parent->Children.push_back(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"));
+       FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), SpanningTreeEventListener, OnServerLink, (this));
 
-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++)
+       FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), SpanningTreeEventListener, OnServerSplit, (server));
+       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;
-}
-
-TreeSocket* TreeServer::GetSocket()
-{
-       return Socket;
-}
-
-TreeServer* TreeServer::GetParent()
-{
-       return Parent;
-}
+       for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+       {
+               TreeServer* server = *i;
+               server->SQuitInternal(num_lost_servers);
+       }
 
-void TreeServer::SetVersion(const std::string &Version)
-{
-       VersionString = Version;
+       // Mark server as dead
+       isdead = true;
+       num_lost_servers++;
+       RemoveHash();
 }
 
-unsigned int TreeServer::ChildCount()
+unsigned int TreeServer::QuitUsers(const std::string& reason)
 {
-       return Children.size();
-}
+       std::string publicreason = ServerInstance->Config->HideSplits ? "*.net *.split" : reason;
 
-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..b7e9ee9d9bdd865d4de2a09ec54346acbf84dea9 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
         */
-       void SetID(const std::string &id);
+       PingTimer pingtimer;
+
+       /** This method is used to add this TreeServer to the
+        * hash maps. It is only called by the constructors.
+        */
+       void AddHashEntry();
+
+       /** Used by SQuit logic to recursively remove servers
+        */
+       void SQuitInternal(unsigned int& num_lost_servers);
+
+       /** Remove the reference to this server from the hash maps
+        */
+       void RemoveHash();
 
  public:
+       typedef std::vector<TreeServer*> ChildServers;
        FakeUser* const ServerUser;             /* User representing this server */
-       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();
 
-       /** 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 abda283353def3e2fab90d2ea4cdf04593f80f76..4887623c125f1e4aaff4f77a73f6305b152a06b0 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,37 +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 */
+
+       /** 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
         */
@@ -164,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();
 
        /** 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.
@@ -178,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
@@ -195,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.
@@ -230,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
@@ -250,57 +274,11 @@ class TreeSocket : public BufferedSocket
 
        /** 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);
@@ -321,15 +299,12 @@ class TreeSocket : public BufferedSocket
 
        /** Handle socket timeout from connect()
         */
-       virtual void OnTimeout();
+       void OnTimeout();
        /** Handle server quit on close
         */
-       virtual void Close();
+       void Close();
 
-       /** 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..2198d6208c1f6927f2e386eae57dd25543047710 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(assign(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);
+       if (via->iohookprov)
+               via->iohookprov->OnAccept(this, client, server);
        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 +86,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 +121,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 +132,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 +170,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 +195,3 @@ void TreeSocket::OnDataReady()
                SendError("RecvQ overrun (line too long)");
        Utils->Creator->loopCall = false;
 }
-
-bool TreeSocket::Introduced()
-{
-       return (capab == NULL);
-}
index acb822fbfa169419b2021fca91b9c4d2c984b49e..2e6961524d5da43328e89287766bc3bc45077748 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,63 @@ 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")
+       // Translate commands coming from servers using an older protocol
+       if (proto_version < ProtocolVersion)
        {
-               this->OperQuit(prefix,params);
-       }
-       else if (command == "IDLE")
-       {
-               this->Whois(prefix,params);
-       }
-       else if (command == "PUSH")
-       {
-               this->Push(prefix,params);
-       }
-       else if (command == "SQUIT")
-       {
-               if (params.size() == 2)
-               {
-                       this->Squit(Utils->FindServer(params[0]),params[1]);
-               }
-       }
-       else if (command == "SNONOTICE")
-       {
-               if (params.size() >= 2)
-               {
-                       ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + who->nick + ": "+ params[1]);
-                       params[1] = ":" + params[1];
-                       Utils->DoOneToAllButSender(prefix, command, params, prefix);
-               }
-       }
-       else if (command == "BURST")
-       {
-               // Set prefix server as bursting
-               TreeServer* ServerSource = Utils->FindServer(prefix);
-               if (!ServerSource)
-               {
-                       ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
+               if (!PreProcessOldProtocolMessage(who, command, params))
                        return;
-               }
-
-               ServerSource->bursting = true;
-               Utils->DoOneToAllButSender(prefix, command, params, prefix);
        }
-       else if (command == "ENDBURST")
-       {
-               TreeServer* ServerSource = Utils->FindServer(prefix);
-               if (!ServerSource)
-               {
-                       ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
-                       return;
-               }
 
-               ServerSource->FinishBurst();
-               Utils->DoOneToAllButSender(prefix, command, params, prefix);
-       }
-       else if (command == "ENCAP")
-       {
-               this->Encap(who, params);
-       }
-       else if (command == "NICK")
+       ServerCommand* scmd = Utils->Creator->CmdManager.GetHandler(command);
+       CommandBase* cmdbase = scmd;
+       Command* cmd = NULL;
+       if (!scmd)
        {
-               if (params.size() != 2)
-               {
-                       SendError("Protocol violation: Wrong number of parameters for NICK message");
-                       return;
-               }
-
-               if (IS_SERVER(who))
-               {
-                       SendError("Protocol violation: Server changing nick");
-                       return;
-               }
-
-               if ((isdigit(params[0][0])) && (params[0] != who->uuid))
-               {
-                       SendError("Protocol violation: User changing nick to an invalid UID - " + params[0]);
-                       return;
-               }
-
-               /* Update timestamp on user when they change nicks */
-               who->age = atoi(params[1].c_str());
-
-               /*
-                * On nick messages, check that the nick doesnt already exist here.
-                * If it does, perform collision logic.
-                */
-               bool callfnc = true;
-               User* x = ServerInstance->FindNickOnly(params[0]);
-               if ((x) && (x != who) && (x->registered == REG_ALL))
+               // Not a special server-to-server command
+               cmd = ServerInstance->Parser.GetHandler(command);
+               if (!cmd)
                {
-                       int collideret = 0;
-                       /* x is local, who is remote */
-                       collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
-                       if (collideret != 1)
+                       if (command == "ERROR")
                        {
-                               // 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;
                        }
-               }
-               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 +354,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 +365,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..a3b80457927b5aacd6bce3e416328d34f4a9e5c0 100644 (file)
 #include "commands.h"
 
 #include "utils.h"
-#include "link.h"
-#include "treesocket.h"
 #include "treeserver.h"
-#include "resolvers.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());
+               collideswith->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", 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);
+               collideswith->ChangeNick(collideswith->uuid);
        }
        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 RemoteUser(params[0], remoteserver);
+       ServerInstance->Users->clientlist[params[2]] = _new;
        _new->nick = params[2];
        _new->host = params[3];
        _new->dhost = params[4];
        _new->ident = params[5];
-       _new->fullname = params[params.size() - 1];
+       _new->fullname = params.back();
        _new->registered = REG_ALL;
        _new->signon = signon;
        _new->age = age_t;
 
-       /* 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->GetNumParams(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());
 
-       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->host);
+       push(user->dhost);
+       push(user->ident);
+       push(user->GetIPString());
+       push_int(user->signon);
+       push('+').push_raw(user->FormatModes(true));
+       push_last(user->fullname);
+}
index 367a3b921a88da46f5d7477c2c8ea17a004a60ac..bbda2634d5ae6b67c3e7209ff2e346d801cf5104 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;
@@ -83,8 +75,6 @@ TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
  */
 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)
        {
@@ -96,9 +86,7 @@ TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
                User *u = ServerInstance->FindNick(ServerName);
                if (u)
                {
-                       Found = FindServer(u->server);
-                       if (Found)
-                               return Found->GetRoute();
+                       return TreeServer::Get(u)->GetRoute();
                }
 
                return NULL;
@@ -106,10 +94,7 @@ TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
 }
 
 /** 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 +115,20 @@ TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id)
                return NULL;
 }
 
-SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C) : Creator(C)
+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->Logs->Log("m_spanningtree",DEBUG,"***** Using SID for hash: %s *****", ServerInstance->Config->GetSID().c_str());
-
-       this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
-       this->ReadConfiguration();
+       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 +146,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 +168,45 @@ 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++)
+       const std::string& FullLine = params.str();
+
+       const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
-               TreeServer* Route = this->TreeRoot->GetChild(x);
-               // 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))
+               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::DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params)
+bool SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, const std::string& target)
 {
-       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);
-               if (Route && Route->GetSocket())
-               {
-                       TreeSocket* Sock = Route->GetSocket();
-                       if (Sock)
-                               Sock->WriteLine(FullLine);
-               }
-       }
+       TreeServer* Route = this->BestRouteTo(target);
+       if (!Route)
+               return false;
+
+       DoOneToOne(params, Route);
        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 +217,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,7 +251,6 @@ 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");
@@ -339,14 +270,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");
@@ -357,8 +292,8 @@ void SpanningTreeUtilities::ReadConfiguration()
                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");
 
-               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 '"+assign(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");
@@ -375,11 +310,11 @@ void SpanningTreeUtilities::ReadConfiguration()
                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 '" + assign(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 '" + assign(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 +325,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 +335,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 +343,9 @@ void SpanningTreeUtilities::ReadConfiguration()
                AutoconnectBlocks.push_back(A);
        }
 
+       for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+               i->second->CheckULine();
+
        RefreshIPCache();
 }
 
@@ -429,15 +362,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..84637bf01da9d44304d0423b949ae775aab9686d 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;
@@ -124,33 +114,34 @@ class SpanningTreeUtilities : public classbase
         */
        ~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);
+       bool DoOneToOne(const CmdBuilder& params, const std::string& target);
+       void DoOneToOne(const CmdBuilder& params, Server* target);
 
        /** Send a message from this server to all but one other, local or remote
         */
-       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);
 
@@ -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..b5f0d6c470968f4f09c7f7d57f03d1077043adfd 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,16 +72,16 @@ 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;
@@ -119,7 +90,7 @@ class OpMeQuery : public SQLQuery
 
                hostname.append("@").append(user->host);
 
-               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 2bfe0e1c4e0ca285727789b722d48a4056790e6e..523d52abbc9cf21a3d2cde83c3f13deace7cc028 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,90 +95,85 @@ 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(ERR_NOSUCHNICK, "%s :No such nickname", parameters[0].c_str());
                        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());
-               }
-       }
-
-       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;
-                       }
+                       if ((!operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
+                               whois.SendLine(276, ":has client certificate fingerprint %s", cert->fingerprint.c_str());
                }
-               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;
@@ -184,7 +181,7 @@ class ModuleSSLInfo : public Module
 
                                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;
                                }
@@ -192,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;
                                }
@@ -203,21 +200,20 @@ 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())
                        return;
                // find an auto-oper block for this user
-               for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
+               for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
                {
                        OperInfo* ifo = i->second;
                        std::string fp = ifo->oper_block->getString("fingerprint");
@@ -226,33 +222,23 @@ class ModuleSSLInfo : public Module
                }
        }
 
-       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..1a596f5e0424b0332b344566e965ef9011132425 100644 (file)
 
 
 #include "inspircd.h"
-#include "ssl.h"
-
-/* $ModDesc: Provides channel mode +z to allow for Secure/SSL only channels */
+#include "modules/ssl.h"
 
 /** 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, "%s :all members of the channel must be connected via SSL", channel->name.c_str());
                                                        return MODEACTION_DENY;
                                                }
                                        }
                                }
-                               channel->SetMode('z',true);
+                               channel->SetMode(this, true);
                                return MODEACTION_ALLOW;
                        }
                        else
@@ -63,9 +69,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 +91,15 @@ class ModuleSSLModes : public Module
        {
        }
 
-       void init()
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(sslm);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
-       {
-               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 +107,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(489, "%s :Cannot join channel; SSL users only (+z)", cname.c_str());
                                return MOD_RES_DENY;
                        }
                }
@@ -114,33 +115,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::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('z');
+               tokens["EXTBAN"].push_back('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..054f9c3
--- /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..0d4bdb877ff44845e24ff6ddb8ef71e03f69d38c 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel +S mode (strip ansi color) */
-
 /** Handles channel mode +S
  */
 class ChannelStripColor : public SimpleChannelModeHandler
@@ -50,24 +48,12 @@ class ModuleStripColor : public Module
        {
        }
 
-       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()
-       {
-       }
-
-       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,7 +62,7 @@ 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)
                {
@@ -86,7 +72,7 @@ class ModuleStripColor : public Module
                        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 +83,7 @@ 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)
-       {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel +S mode (strip ansi color)", VF_VENDOR);
        }
index e666b0fe24b977c1de8f5845e03c9152e01e12cc..a623e1553746021fdd53fac731e71140897287ce 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,22 +172,18 @@ 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(char symbol, User* user, string_list &out) CXX11_OVERRIDE
        {
                if(symbol != 'S')
                        return MOD_RES_PASSTHRU;
@@ -206,26 +192,26 @@ class ModuleSVSHold : public Module
                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, "%s :Services reserved nickname: %s", newnick.c_str(), 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..80c7ddd3b13d8054dc5ddc587890c6d02b572489 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(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
                        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, unsigned int& numeric, std::string& text) CXX11_OVERRIDE
        {
                /* We use this and not OnWhois because this triggers for remote, too */
                if (numeric == 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, ":%s", swhois->c_str());
                        }
                }
 
@@ -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 b473277046c0af470fbd3afea566c89711090711..8196d37ba6299a01f509c9ed70279a2a99fcec9c 100644 (file)
@@ -20,9 +20,8 @@
  */
 
 
-/* $ModDesc: Adds timed bans */
-
 #include "inspircd.h"
+#include "listmode.h"
 
 /** Holds a timed ban
  */
@@ -42,21 +41,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,51 +72,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(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
                        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, "%s :You do not have permission to set bans on this channel",
+                               channel->name.c_str());
                        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;
@@ -131,6 +135,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;
+
+               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;
+                       }
+               }
+       }
+};
+
 class ChannelMatcher
 {
        Channel* const chan;
@@ -150,37 +182,16 @@ class ChannelMatcher
 class ModuleTimedBans : public Module
 {
        CommandTban cmd;
+       BanWatcher banwatcher;
+
  public:
        ModuleTimedBans()
                : cmd(this)
+               , banwatcher(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnDelBan, I_OnBackgroundTimer, I_OnChannelDelete };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ModResult OnDelBan(User* source, Channel* chan, const std::string &banmask)
-       {
-               irc::string listitem = banmask.c_str();
-               irc::string thischan = chan->name.c_str();
-               for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); i++)
-               {
-                       irc::string target = i->mask.c_str();
-                       irc::string tchan = i->channel.c_str();
-                       if ((listitem == target) && (tchan == thischan))
-                       {
-                               TimedBanList.erase(i);
-                               break;
-                       }
-               }
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual void OnBackgroundTimer(time_t curtime)
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                timedbans expired;
                for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
@@ -201,17 +212,14 @@ class ModuleTimedBans : public Module
                        Channel* cr = ServerInstance->FindChan(chan);
                        if (cr)
                        {
-                               std::vector<std::string> setban;
-                               setban.push_back(chan);
-                               setban.push_back("-b");
-                               setban.push_back(mask);
-
                                CUList empty;
                                std::string expiry = "*** Timed ban on " + chan + " expired.";
                                cr->WriteAllExcept(ServerInstance->FakeClient, true, '@', empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
                                ServerInstance->PI->SendChannelNotice(cr, '@', 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);
                        }
                }
        }
@@ -222,11 +230,10 @@ class ModuleTimedBans : public Module
                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..6053bc849cca9c6ad1c011dca25c9d5801c62653 100644 (file)
@@ -16,8 +16,6 @@
  * 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"
 
 class CommandSVSTOPIC : public Command
@@ -31,7 +29,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,7 +45,7 @@ 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;
                        }
 
@@ -92,11 +90,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,14 +145,7 @@ 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)))
@@ -170,7 +157,7 @@ class ModuleTopicLock : public Module
                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..e92688d1ddfff17171c06c64b2ce5d54f8ad25c3 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(401, "%s :No such nick/channel", parameters[1].c_str());
                        }
                        else
                        {
-                               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNumeric(401, "%s :No such nick/channel", parameters[0].c_str());
                        }
 
                        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, "%s :You must be a channel %soperator", c->name.c_str(), c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-");
                                return CMD_FAILURE;
                        }
                }
@@ -75,16 +77,14 @@ class CommandUninvite : public Command
                LocalUser* lu = IS_LOCAL(u);
                if (lu)
                {
-                       irc::string xname(c->name.c_str());
-                       if (!lu->IsInvited(xname))
+                       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());
+                               user->SendText(":%s 505 %s %s %s :Is not invited to channel %s", user->server->GetName().c_str(), user->nick.c_str(), u->nick.c_str(), c->name.c_str(), c->name.c_str());
                                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());
+                       user->SendText(":%s 494 %s %s %s :Uninvited", user->server->GetName().c_str(), user->nick.c_str(), c->name.c_str(), u->nick.c_str());
+                       lu->WriteNumeric(493, ":You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str());
 
                        std::string msg = "*** " + user->nick + " uninvited " + u->nick + ".";
                        c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE " + c->name + " :" + msg);
@@ -111,20 +111,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..04396739340c2dda7e680d48b06ae45e781ecafe 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for USERIP command */
-
 /** Handle /USERIP
  */
 class CommandUserip : public Command
@@ -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 += "+";
@@ -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..bdd4b2b15414797720e20ac17fb94f24e73e787b 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, "%s %s %s %lu :is away", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long)target->awaytime);
+                       else
+                               user->WriteNumeric(RPL_NOWON, "%s %s %s %lu :is online", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long)target->age);
+               }
+               else if (show_offline)
+                       user->WriteNumeric(RPL_NOWOFF, "%s * * 0 :is offline", nick.c_str());
        }
 
-       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, "%s :Too many WATCH entries", nick.c_str());
+                       return;
                }
+               else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK)
+               {
+                       user->WriteNumeric(942, "%s :Invalid nickname", nick.c_str());
+                       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, "%s %s %s %lu :stopped watching", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long)target->age);
+               else
+                       user->WriteNumeric(RPL_WATCHOFF, "%s * * 0 :stopped watching", nick.c_str());
        }
-};
 
-/** 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, ":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;
+
+               std::string text = InspIRCd::Format("%s %s %s %lu :%s", nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) shownts, numerictext);
+               for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
+               {
+                       LocalUser* curr = *i;
+                       curr->WriteNumeric(numeric, text);
+               }
        }
 
-       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)
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..191a3d30ffd21812a8a0e596ac3399ddd34ea4df 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 + " :" INSPIRCD_SYSTEM " [" INSPIRCD_REVISION "," INSPIRCD_SOCKETENGINE_NAME "," + Config->sid + "]";
+       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,138 @@ 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')
        {
-               // 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++)
+               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';
+               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::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"] = "rfc1459";
+       tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL);
+       tokens["CHANNELLEN"] = ConvToStr(ServerInstance->Config->Limits.ChanMax);
+       tokens["CHANTYPES"] = "#";
+       tokens["ELIST"] = "MU";
+       tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick);
+       tokens["MAXBANS"] = "64"; // TODO: make this a config setting.
+       tokens["MAXCHANNELS"] = ConvToStr(ServerInstance->Config->MaxChans);
+       tokens["MAXTARGETS"] = ConvToStr(ServerInstance->Config->MaxTargets);
+       tokens["MODES"] = ConvToStr(ServerInstance->Config->Limits.MaxModes);
+       tokens["NETWORK"] = ServerInstance->Config->Network;
+       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["FNC"] = 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
+       std::string line;
+       unsigned int token_count = 0;
+       cachedlines.clear();
 
+       for (std::map<std::string, std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
+       {
+               line.append(it->first);
+
+               // If this token has a value then append a '=' char after the name and then the value itself
+               if (!it->second.empty())
+                       line.append(1, '=').append(it->second);
+
+               // Always append a space, even if it's the last token because all lines will be suffixed
+               line.push_back(' ');
+               token_count++;
+
+               if (token_count % 13 == 12 || it == --tokens.end())
+               {
+                       // Reached maximum number of tokens for this line or the current token
+                       // is the last one; finalize the line and store it for later use
+                       line.append(":are supported by this server");
+                       cachedlines.push_back(line);
+                       line.clear();
+               }
+       }
+}
+
+void ISupportManager::SendTo(LocalUser* user)
+{
+       for (std::vector<std::string>::const_iterator i = cachedlines.begin(); i != cachedlines.end(); ++i)
+               user->WriteNumeric(RPL_ISUPPORT, *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..17f13bb8a22c22a47fc7e752a30b406b39bcce41 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;
@@ -106,6 +53,8 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports)
                                if ((**n).bind_desc == bind_readable)
                                {
                                        (*n)->bind_tag = tag; // Replace tag, we know addr and port match, but other info (type, ssl) may not
+                                       (*n)->ResetIOHookProvider();
+
                                        skip = true;
                                        old_ports.erase(n);
                                        break;
@@ -136,11 +85,11 @@ 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.",
+               this->Logs->Log("SOCKET", LOG_DEFAULT, "Port binding %s was removed from the config file, closing.",
                        (**n).bind_desc.c_str());
                delete *n;
 
@@ -219,24 +168,22 @@ bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr,
 
 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, &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, &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 +314,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..4183488b723ac0f063c5af9aa71b76a9d9caf688 100644 (file)
 
 #include "inspircd.h"
 
+
+/** 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;
+
+int SocketEngine::MAX_DESCRIPTORS;
+
+/** Socket engine statistics: count of various events, bandwidth usage
+ */
+SocketEngine::Statistics SocketEngine::stats;
+
 EventHandler::EventHandler()
 {
        fd = -1;
@@ -34,20 +53,12 @@ void EventHandler::SetFd(int FD)
        this->fd = FD;
 }
 
-SocketEngine::SocketEngine()
-{
-       TotalEvents = WriteEvents = ReadEvents = ErrorEvents = 0;
-       lastempty = ServerInstance->Time();
-       indata = outdata = 0;
-}
-
-SocketEngine::~SocketEngine()
+void EventHandler::OnEventHandlerWrite()
 {
 }
 
-void SocketEngine::SetEventMask(EventHandler* eh, int mask)
+void EventHandler::OnEventHandlerError(int errornum)
 {
-       eh->event_mask = mask;
 }
 
 void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
@@ -60,7 +71,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 +99,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 +144,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 +155,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)
@@ -173,7 +204,7 @@ int SocketEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, s
 {
        int nbRecvd = recvfrom(fd->GetFd(), (char*)buf, len, flags, from, fromlen);
        if (nbRecvd > 0)
-               this->UpdateStats(nbRecvd, 0);
+               stats.Update(nbRecvd, 0);
        return nbRecvd;
 }
 
@@ -181,7 +212,7 @@ 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.Update(0, nbSent);
        return nbSent;
 }
 
@@ -189,7 +220,7 @@ 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.Update(nbRecvd, 0);
        return nbRecvd;
 }
 
@@ -197,10 +228,37 @@ int SocketEngine::SendTo(EventHandler* fd, const void *buf, size_t len, int flag
 {
        int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, to, tolen);
        if (nbSent > 0)
-               this->UpdateStats(0, nbSent);
+               stats.Update(0, nbSent);
        return nbSent;
 }
 
+int SocketEngine::WriteV(EventHandler* fd, const IOVector* iovec, int count)
+{
+       int sent = writev(fd->GetFd(), iovec, count);
+       if (sent > 0)
+               stats.Update(0, 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 +289,27 @@ int SocketEngine::Shutdown(int fd, int how)
        return shutdown(fd, how);
 }
 
-void SocketEngine::RecoverFromFork()
+void SocketEngine::Statistics::Update(size_t len_in, size_t len_out)
 {
+       CheckFlush();
+       indata += len_in;
+       outdata += len_out;
 }
 
-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 f2837777ad7ec7a3ac11e6116be84bc70ca9ca0e..8548e08248a19d52617ba5be1fc6c743e8b104a3 100644 (file)
  */
 
 
-#include <vector>
-#include <string>
-#include <map>
 #include "inspircd.h"
 #include "exitcodes.h"
-#include "socketengine.h"
+
 #include <sys/epoll.h>
 #include <ulimit.h>
 #include <iostream>
 
 /** 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;
-       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);
-       }
+       // MAX_DESCRIPTORS is mainly used for display purposes, no problem if ulimit() fails and returns a negative number
+       MAX_DESCRIPTORS = ulimit(4, 0);
 
-       // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
-       EngineHandle = epoll_create(GetMaxFds() / 4);
+       // 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.");
+               ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
+               ServerInstance->Logs->Log("SOCKET", LOG_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()];
-
-       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)
@@ -115,41 +88,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);
@@ -157,75 +130,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)
@@ -235,31 +211,21 @@ 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])
+                       stats.ReadEvents++;
+                       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);
+                       stats.WriteEvents++;
+                       eh->OnEventHandlerWrite();
                }
        }
 
        return i;
 }
-
-std::string EPollEngine::GetName()
-{
-       return "epoll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new EPollEngine;
-}
index 8694a0bdd84d4cc2e5249aba5fa4d3f3ed6f5732..922cb7f2d99d6df40afdf9ec3b76e0a19019764f 100644 (file)
 #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];
@@ -69,21 +57,13 @@ KQueueEngine::KQueueEngine()
        mib[1] = KERN_MAXFILES;
 #endif
        len = sizeof(MAX_DESCRIPTORS);
+       // MAX_DESCRIPTORS is mainly used for display purposes, no problem if the sysctl() below fails
        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*));
+       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.
@@ -93,176 +73,148 @@ 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.");
+               ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
+               ServerInstance->Logs->Log("SOCKET", LOG_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;
 }
 
-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);
+       ke = GetChangeKE();
+       EV_SET(ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
 
-       if (j < 0)
-       {
-               ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
-                                         fd, strerror(errno));
-       }
+       SocketEngine::DelFdRef(eh);
 
-       CurrentSetSize--;
-       ref[fd] = NULL;
-
-       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++;
+                       stats.WriteEvents++;
                        /* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE,
                         * we set a one-shot write, so we need to clear that bit
                         * to detect when it set again.
                         */
                        const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
-                       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);
+                       stats.ReadEvents++;
+                       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..5fd7e6235888f2ef91b9e38627a6b782e1d0c021 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()
+       std::vector<int> fd_mappings(16);
+}
+
+void SocketEngine::Init()
 {
-       CurrentSetSize = 0;
        struct rlimit limits;
        if (!getrlimit(RLIMIT_NOFILE, &limits))
        {
@@ -86,23 +48,17 @@ PollEngine::PollEngine()
        }
        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);
+               // MAX_DESCRIPTORS is mainly used for display purposes, it's not a problem that getrlimit() failed
+               MAX_DESCRIPTORS = -1;
        }
+}
 
-       ref = new EventHandler* [GetMaxFds()];
-       events = new struct pollfd[GetMaxFds()];
-
-       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 +71,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)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
 {
-       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)
-{
-       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 +148,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 (int 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->eh->OnEventHandlerWrite();
                }
        }
 
        return i;
 }
-
-std::string PollEngine::GetName()
-{
-       return "poll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new PollEngine;
-}
index f7c547d4510d79367297db443d3868db868dc579..d94d02664f1b5597d0c2b4b89c9e0f47643054b0 100644 (file)
 
 #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>
+#include <ulimit.h>
 
 /** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports
  */
-class PortsEngine : public SocketEngine
+namespace
 {
-private:
-       /** These are used by epoll() to hold socket events
+       /** These are used by ports to hold socket events
         */
-       port_event_t* events;
+       std::vector<port_event_t> events(16);
        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()
+/** Initialize ports engine
+ */
+void SocketEngine::Init()
 {
-       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);
-       }
+       // MAX_DESCRIPTORS is mainly used for display purposes, no problem if ulimit() fails and returns a negative number
+       MAX_DESCRIPTORS = ulimit(4, 0);
+
        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.");
+               ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: Could not initialize socket engine: %s", strerror(errno));
+               ServerInstance->Logs->Log("SOCKET", LOG_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*));
+/** Shutdown the ports engine
+ */
+void SocketEngine::Deinit()
+{
+       SocketEngine::Close(EngineHandle);
 }
 
-PortsEngine::~PortsEngine()
+void SocketEngine::RecoverFromFork()
 {
-       this->Close(EngineHandle);
-       delete[] ref;
-       delete[] events;
 }
 
 static int mask_to_events(int event_mask)
@@ -113,45 +80,44 @@ static int mask_to_events(int event_mask)
        return rv;
 }
 
-bool PortsEngine::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)
                return false;
 
-       if (ref[fd])
+       if (!SocketEngine::AddFdRef(eh))
                return false;
 
-       ref[fd] = eh;
-       SocketEngine::SetEventMask(eh, event_mask);
+       eh->SetEventMask(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++;
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
+       ResizeDouble(events);
+
        return true;
 }
 
-void PortsEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::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)
+void SocketEngine::DelFd(EventHandler* eh)
 {
        int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
                return;
 
        port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
 
-       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);
 }
 
-int PortsEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
 {
        struct timespec poll_time;
 
@@ -159,62 +125,51 @@ int PortsEngine::DispatchEvents()
        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);
+       int ret = port_getn(EngineHandle, &events[0], events.size(), &nget, &poll_time);
        ServerInstance->UpdateTime();
 
        // first handle an error condition
        if (ret == -1)
                return -1;
 
-       TotalEvents += nget;
+       stats.TotalEvents += nget;
 
        unsigned int i;
        for (i = 0; i < nget; i++)
        {
-               switch (this->events[i].portev_source)
+               port_event_t& ev = events[i];
+
+               if (ev.portev_source != PORT_SOURCE_FD)
+                       continue;
+
+               // Copy these in case the vector gets resized and ev invalidated
+               const int fd = ev.portev_object;
+               const int portev_events = ev.portev_events;
+               EventHandler* eh = static_cast<EventHandler*>(ev.portev_user);
+               if (eh->GetFd() < 0)
+                       continue;
+
+               int mask = eh->GetEventMask();
+               if (portev_events & POLLWRNORM)
+                       mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE);
+               if (portev_events & POLLRDNORM)
+                       mask &= ~FD_READ_WILL_BLOCK;
+               // reinsert port for next time around, pretending to be one-shot for writes
+               eh->SetEventMask(mask);
+               port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
+               if (portev_events & POLLRDNORM)
                {
-                       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;
+                       stats.ReadEvents++;
+                       eh->OnEventHandlerRead();
+                       if (eh != GetRef(fd))
+                               continue;
+               }
+               if (portev_events & POLLWRNORM)
+               {
+                       stats.WriteEvents++;
+                       eh->OnEventHandlerWrite();
                }
        }
 
        return (int)i;
 }
-
-std::string PortsEngine::GetName()
-{
-       return "ports";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new PortsEngine;
-}
index 0b5abaf304792b6d14978992ec1aa53d1cb77b86..6dfbae88e0e452d8f6a1edc7095e99ebd91366b8 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()
+       int MaxFD = 0;
+}
+
+void SocketEngine::Init()
 {
        MAX_DESCRIPTORS = FD_SETSIZE;
-       CurrentSetSize = 0;
-
-       ref = new EventHandler* [GetMaxFds()];
-       memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
 
        FD_ZERO(&ReadSet);
        FD_ZERO(&WriteSet);
        FD_ZERO(&ErrSet);
-       MaxFD = 0;
 }
 
-SelectEngine::~SelectEngine()
+void SocketEngine::Deinit()
+{
+}
+
+void SocketEngine::RecoverFromFork()
 {
-       delete[] ref;
 }
 
-bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
 {
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > GetMaxFds() - 1))
                return false;
 
-       if (ref[fd])
+       if (!SocketEngine::AddFdRef(eh))
                return false;
 
-       ref[fd] = eh;
-
-       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))
                return;
 
-       CurrentSetSize--;
-       ref[fd] = NULL;
+       SocketEngine::DelFdRef(eh);
 
        FD_CLR(fd, &ReadSet);
        FD_CLR(fd, &WriteSet);
@@ -106,10 +84,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 +108,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 +119,50 @@ 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;
+
+               EventHandler* ev = GetRef(i);
+               if (!ev)
+                       continue;
 
-                       if (has_error)
-                       {
-                               ErrorEvents++;
+               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;
 
-                               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)
+               {
+                       stats.ReadEvents++;
+                       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)
+               {
+                       stats.WriteEvents++;
+                       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 76446c5b57fb07371adaa9b6730e6f335d7e0805..ba6bbf36bb8e6a96bd1a2a3c6bd82211df21bf9d 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)
 {
 }
 
+UserManager::~UserManager()
+{
+       for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
+       {
+               delete i->second;
+       }
+}
+
 /* add a client connection to the sockets list */
 void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 {
        /* NOTE: Calling this one parameter constructor for User automatically
         * allocates a new UUID and places it in the hash_map.
         */
-       LocalUser* New = NULL;
-       try
-       {
-               New = new LocalUser(socket, client, server);
-       }
-       catch (...)
-       {
-               ServerInstance->Logs->Log("USERS", DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
-               ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
-               return;
-       }
+       LocalUser* const New = new LocalUser(socket, client, server);
        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())
-       {
-               try
-               {
-                       eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
-               }
-               catch (CoreException& modexcept)
-               {
-                       ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
-               }
-       }
+       // If this listener has an IO hook provider set then tell it about the connection
+       if (via->iohookprov)
+               via->iohookprov->OnAccept(eh, client, server);
 
-       ServerInstance->Logs->Log("USERS", DEBUG,"New user fd: %d", socket);
+       ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
 
        this->unregistered_count++;
 
        /* The users default nick is their UUID */
        New->nick = New->uuid;
-       (*(this->clientlist))[New->nick] = New;
+       this->clientlist[New->nick] = New;
 
        New->registered = REG_NONE;
        New->signon = ServerInstance->Time();
        New->lastping = 1;
 
-       ServerInstance->Users->AddLocalClone(New);
-       ServerInstance->Users->AddGlobalClone(New);
+       this->AddClone(New);
 
-       New->localuseriter = this->local_users.insert(local_users.end(), New);
-       local_count++;
+       this->local_users.push_front(New);
 
-       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");
@@ -98,7 +108,7 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
         * Check connect class settings and initialise settings into User.
         * This will be done again after DNS resolution. -- w00t
         */
-       New->CheckClass();
+       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,276 +150,229 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
                }
        }
 
-       if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
+       if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
        {
-               ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
+               ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
                this->QuitUser(New, "Internal error handling connection");
        }
 
-       /* 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->host.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 (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);
-}
-
+       if (!clientlist.erase(user->nick))
+               ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
 
-void UserManager::AddLocalClone(User *user)
-{
-       local_clones[user->GetCIDRMask()]++;
+       uuidlist.erase(user->uuid);
+       user->PurgeEmptyChannels();
 }
 
-void UserManager::AddGlobalClone(User *user)
+void UserManager::AddClone(User* user)
 {
-       global_clones[user->GetCIDRMask()]++;
+       CloneCounts& counts = clonemap[user->GetCIDRMask()];
+       counts.global++;
+       if (IS_LOCAL(user))
+               counts.local++;
 }
 
 void UserManager::RemoveCloneCounts(User *user)
 {
-       if (IS_LOCAL(user))
+       CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
+       if (it != clonemap.end())
        {
-               clonemap::iterator x = local_clones.find(user->GetCIDRMask());
-               if (x != local_clones.end())
+               CloneCounts& counts = it->second;
+               counts.global--;
+               if (counts.global == 0)
                {
-                       x->second--;
-                       if (!x->second)
-                       {
-                               local_clones.erase(x);
-                       }
+                       // No more users from this IP, remove entry from the map
+                       clonemap.erase(it);
+                       return;
                }
-       }
 
-       clonemap::iterator y = global_clones.find(user->GetCIDRMask());
-       if (y != global_clones.end())
-       {
-               y->second--;
-               if (!y->second)
-               {
-                       global_clones.erase(y);
-               }
+               if (IS_LOCAL(user))
+                       counts.local--;
        }
 }
 
 void UserManager::RehashCloneCounts()
 {
-       local_clones.clear();
-       global_clones.clear();
+       clonemap.clear();
 
-       const user_hash& hash = *ServerInstance->Users->clientlist;
+       const user_hash& hash = ServerInstance->Users.GetUsers();
        for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
        {
                User* u = i->second;
-
-               if (IS_LOCAL(u))
-                       AddLocalClone(u);
-               AddGlobalClone(u);
+               AddClone(u);
        }
 }
 
-unsigned long UserManager::GlobalCloneCount(User *user)
+const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
 {
-       clonemap::iterator x = global_clones.find(user->GetCIDRMask());
-       if (x != global_clones.end())
-               return x->second;
+       CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
+       if (it != clonemap.end())
+               return it->second;
        else
-               return 0;
-}
-
-unsigned long UserManager::LocalCloneCount(User *user)
-{
-       clonemap::iterator x = local_clones.find(user->GetCIDRMask());
-       if (x != local_clones.end())
-               return x->second;
-       else
-               return 0;
-}
-
-/* this function counts all users connected, wether they are registered or NOT. */
-unsigned int UserManager::UserCount()
-{
-       /*
-        * XXX: Todo:
-        *  As part of this restructuring, move clientlist/etc fields into usermanager.
-        *      -- w00t
-        */
-       return this->clientlist->size();
-}
-
-/* this counts only registered users, so that the percentages in /MAP don't mess up */
-unsigned int UserManager::RegisteredUserCount()
-{
-       return this->clientlist->size() - this->UnregisteredUserCount();
-}
-
-/* return how many users are opered */
-unsigned int UserManager::OperCount()
-{
-       return this->all_opers.size();
-}
-
-/* return how many users are unregistered */
-unsigned int UserManager::UnregisteredUserCount()
-{
-       return this->unregistered_count;
-}
-
-/* return how many local registered users there are */
-unsigned int UserManager::LocalUserCount()
-{
-       /* Doesnt count unregistered clients */
-       return (this->local_count - this->UnregisteredUserCount());
+               return zeroclonecounts;
 }
 
 void UserManager::ServerNoticeAll(const char* text, ...)
 {
-       if (!text)
-               return;
-
-       char textbuffer[MAXBUF];
-       char formatbuffer[MAXBUF];
-       va_list argsPtr;
-       va_start (argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
+       std::string message;
+       VAFORMAT(message, text, text);
+       message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
 
-       for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+       for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
        {
                User* t = *i;
-               t->WriteServ(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);
+}
+
+/**
+ * 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 UserManager::DoBackgroundUserStuff()
+{
+       /*
+        * loop over all local users..
+        */
+       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;
 
-       char textbuffer[MAXBUF];
-       char formatbuffer[MAXBUF];
-       va_list argsPtr;
-       va_start (argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
+               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();
+               }
 
-       snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
+               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;
+               }
 
-       for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
-       {
-               User* t = *i;
-               t->WriteServ(std::string(formatbuffer));
+               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 418f2c9aa3557f187e45eb189c892a07b154970c..fd4afbcef7a643b7bd061ed07ca240ba266e63bc 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,55 +33,25 @@ 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)
-{
-       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)
 {
-       static char data[MAXBUF];
+       static std::string data;
        std::string params;
-       int offset = 0;
+       data.clear();
 
        for (unsigned char n = 0; n < 64; n++)
        {
-               if (modes[n])
+               ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
+               if (mh && IsModeSet(mh))
                {
-                       data[offset++] = n + 65;
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
-                       if (showparameters && mh && mh->GetNumParams(true))
+                       data.push_back(n + 65);
+                       if (showparameters && mh->GetNumParams(true))
                        {
                                std::string p = mh->GetUserParameter(this);
                                if (p.length())
@@ -193,36 +59,36 @@ const char* User::FormatModes(bool showparameters)
                        }
                }
        }
-       data[offset] = 0;
-       strlcat(data, params.c_str(), MAXBUF);
-       return data;
+       data += params;
+       return data.c_str();
 }
 
-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)
+       : uuid(uid), server(srv), usertype(type)
 {
        age = ServerInstance->Time();
-       signon = idle_lastmsg = 0;
+       signon = 0;
        registered = 0;
-       quietquit = quitting = exempt = dns_done = false;
-       quitting_sendq = false;
+       quitting = 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()),
+       : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL), eh(this),
        bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
        already_sent(0)
 {
+       exempt = quitting_sendq = false;
+       idle_lastmsg = 0;
        ident = "unknown";
        lastping = 0;
        eh.SetFd(myfd);
@@ -233,8 +99,6 @@ LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::so
 
 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 +106,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 + "@" + host;
        return this->cached_makehost;
 }
 
@@ -262,18 +116,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,98 +126,21 @@ 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 + "@" + dhost;
        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 + "@" + host;
        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)
 {
        return true;
@@ -381,7 +148,7 @@ bool User::HasModePermission(unsigned char, ModeType)
 
 bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
 {
-       if (!IS_OPER(this))
+       if (!this->IsOper())
                return false;
 
        if (mode < 'A' || mode > ('A' + 64)) return false;
@@ -404,7 +171,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 +191,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 +208,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 +235,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 +250,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 +299,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 +308,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 +316,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)
@@ -592,17 +347,17 @@ void User::Oper(OperInfo* info)
        }
 
        ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
-               nick.c_str(), ident.c_str(), host.c_str(), oper->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(), host.c_str(), oper->name.c_str(), opername.c_str());
+       this->WriteNumeric(RPL_YOUAREOPER, ":You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str());
 
-       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 +415,7 @@ void OperInfo::init()
 
 void User::UnOper()
 {
-       if (!IS_OPER(this))
+       if (!this->IsOper())
                return;
 
        /*
@@ -672,44 +427,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 +462,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()))
-       {
-               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()))
+       else if (clone_count)
        {
-               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;
 }
 
-bool User::CheckLines(bool doZline)
+bool LocalUser::CheckLines(bool doZline)
 {
        const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
 
@@ -764,7 +507,7 @@ bool User::CheckLines(bool doZline)
 
 void LocalUser::FullConnect()
 {
-       ServerInstance->stats->statsConnects++;
+       ServerInstance->stats.Connects++;
        this->idle_lastmsg = ServerInstance->Time();
 
        /*
@@ -781,19 +524,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, ":Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
+       this->WriteNumeric(RPL_YOURHOSTIS, ":Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH);
+       this->WriteNumeric(RPL_SERVERCREATED, ":This server was created %s %s", __TIME__, __DATE__);
 
-       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 std::string& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
+       this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH, modelist.c_str());
 
-       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 +539,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 +558,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;
 }
@@ -843,72 +581,25 @@ void User::InvalidateCache()
        cached_fullrealhost.clear();
 }
 
-bool User::ChangeNick(const std::string& newnick, bool force)
+bool User::ChangeNick(const std::string& newnick, time_t newts)
 {
        if (quitting)
        {
-               ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
+               ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
                return false;
        }
 
-       ModResult MOD_RESULT;
-
-       if (force)
-               ServerInstance->NICKForced.set(this, 1);
-       FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
-       ServerInstance->NICKForced.set(this, 0);
-
-       if (MOD_RESULT == MOD_RES_DENY)
-       {
-               ServerInstance->stats->statsCollisions++;
-               return false;
-       }
-
-       if (assign(newnick) == assign(nick))
+       User* const InUse = ServerInstance->FindNickOnly(newnick);
+       if (InUse == this)
        {
-               // case change, don't need to check Q:lines and such
+               // case change, don't need to check campers
                // and, if it's identical including case, we can leave right now
+               // We also don't update the nick TS if it's a case change, either
                if (newnick == nick)
                        return true;
        }
        else
        {
-               /*
-                * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some fucking *moron*
-                * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be.
-                * Thanks Kein for finding this. -- w00t
-                *
-                * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves.
-                *              -- w00t
-                */
-               if (IS_LOCAL(this) && !force)
-               {
-                       XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick);
-                       if (mq)
-                       {
-                               if (this->registered == REG_ALL)
-                               {
-                                       ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
-                                               newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str());
-                               }
-                               this->WriteNumeric(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), newnick.c_str(), mq->reason.c_str());
-                               return false;
-                       }
-
-                       if (ServerInstance->Config->RestrictBannedUsers)
-                       {
-                               for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
-                               {
-                                       Channel *chan = *i;
-                                       if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
-                                       {
-                                               this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), chan->name.c_str());
-                                               return false;
-                                       }
-                               }
-                       }
-               }
-
                /*
                 * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
                 * then we have a potential collide. Check whether someone else is camping on the nick
@@ -918,29 +609,26 @@ 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->WriteFrom(InUse, "NICK %s", InUse->uuid.c_str());
+                               InUse->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", InUse->nick.c_str());
 
-                               InUse->nick = InUse->uuid;
-                               InUse->InvalidateCache();
                                InUse->registered &= ~REG_NICK;
+                               InUse->ChangeNick(InUse->uuid);
                        }
                        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, "%s :Nickname is already in use.", newnick.c_str());
                                return false;
                        }
                }
+
+               age = newts ? newts : ServerInstance->Time();
        }
 
        if (this->registered == REG_ALL)
@@ -949,11 +637,11 @@ 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;
 }
@@ -970,18 +658,18 @@ 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);
                /* 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;
 }
 
 irc::sockets::cidr_mask User::GetCIDRMask()
@@ -1032,7 +720,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));
        }
 }
 
@@ -1048,23 +736,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++;
 }
@@ -1073,14 +761,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)
@@ -1093,32 +776,25 @@ 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::WriteCommand(const char* command, const std::string& text)
+{
+       this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
+}
 
 void User::WriteNumeric(unsigned int numeric, const char* text, ...)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteNumeric(numeric, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteNumeric(numeric, textbuffer);
 }
 
 void User::WriteNumeric(unsigned int numeric, const std::string &text)
 {
-       char textbuffer[MAXBUF];
        ModResult MOD_RESULT;
 
        FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text));
@@ -1126,17 +802,15 @@ void User::WriteNumeric(unsigned int numeric, const std::string &text)
        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 message = InspIRCd::Format(":%s %03u %s %s", ServerInstance->Config->ServerName.c_str(),
+               numeric, this->registered & REG_NICK ? this->nick.c_str() : "*", text.c_str());
+       this->Write(message);
 }
 
 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);
 }
 
 
@@ -1144,150 +818,92 @@ 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, ...)
+namespace
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       va_start(argsPtr, data);
-       vsnprintf(textbuffer, MAXBUF, data, argsPtr);
-       va_end(argsPtr);
+       class WriteCommonRawHandler : public User::ForEachNeighborHandler
+       {
+               const std::string& msg;
 
-       this->WriteTo(dest, std::string(textbuffer));
-}
+               void Execute(LocalUser* user) CXX11_OVERRIDE
+               {
+                       user->Write(msg);
+               }
 
-void User::WriteTo(User *dest, const std::string &data)
-{
-       dest->WriteFrom(this, data);
+        public:
+               WriteCommonRawHandler(const std::string& message)
+                       : msg(message)
+               {
+               }
+       };
 }
 
 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), true);
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
+       this->WriteCommonRaw(textbuffer, true);
 }
 
-void User::WriteCommonExcept(const char* text, ...)
+void User::WriteCommonRaw(const std::string &line, bool include_self)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (this->registered != REG_ALL || quitting)
-               return;
-
-       int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteCommonRaw(std::string(textbuffer), false);
+       WriteCommonRawHandler handler(line);
+       ForEachNeighbor(handler, include_self);
 }
 
-void User::WriteCommonRaw(const std::string &line, bool include_self)
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
 {
-       if (this->registered != REG_ALL || quitting)
-               return;
-
-       LocalUser::already_sent_id++;
-
-       UserChanList include_c(chans);
-       std::map<User*,bool> exceptions;
+       // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
+       // and visit all users on those channels. Because two users may share more than one common channel,
+       // we must skip users that we have already visited.
+       // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
+       // The global counter is incremented every time we do something for each neighbor of a user. Then,
+       // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
+       // skip the member. Otherwise, we set it to the current counter and visit the member.
 
+       // Ask modules to build a list of exceptions.
+       // Mods may also exclude entire channels by erasing them from include_chans.
+       IncludeChanList include_chans(chans.begin(), chans.end());
+       std::map<User*, bool> exceptions;
        exceptions[this] = include_self;
+       FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
 
-       FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
+       // Get next id, guaranteed to differ from the already_sent field of all users
+       const already_sent_t newid = ServerInstance->Users.NextAlreadySentId();
 
-       for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+       // Handle exceptions first
+       for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
        {
-               LocalUser* u = IS_LOCAL(i->first);
-               if (u && !u->quitting)
+               LocalUser* curr = IS_LOCAL(i->first);
+               if (curr)
                {
-                       u->already_sent = LocalUser::already_sent_id;
-                       if (i->second)
-                               u->Write(line);
+                       // Mark as visited to ensure we won't visit again if there is a common channel
+                       curr->already_sent = newid;
+                       // Always treat quitting users as excluded
+                       if ((i->second) && (!curr->quitting))
+                               handler.Execute(curr);
                }
        }
-       for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
-       {
-               Channel* c = *v;
-               const UserMembList* ulist = c->GetUsers();
-               for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
-               {
-                       LocalUser* u = IS_LOCAL(i->first);
-                       if (u && !u->quitting && u->already_sent != LocalUser::already_sent_id)
-                       {
-                               u->already_sent = LocalUser::already_sent_id;
-                               u->Write(line);
-                       }
-               }
-       }
-}
 
-void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
-{
-       char tb1[MAXBUF];
-       char tb2[MAXBUF];
-
-       if (this->registered != REG_ALL)
-               return;
-
-       already_sent_t uniq_id = ++LocalUser::already_sent_id;
-
-       snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),normal_text.c_str());
-       snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),oper_text.c_str());
-       std::string out1 = tb1;
-       std::string out2 = tb2;
-
-       UserChanList include_c(chans);
-       std::map<User*,bool> exceptions;
-
-       FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
-       for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
-       {
-               LocalUser* u = IS_LOCAL(i->first);
-               if (u && !u->quitting)
-               {
-                       u->already_sent = uniq_id;
-                       if (i->second)
-                               u->Write(IS_OPER(u) ? out2 : out1);
-               }
-       }
-       for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
+       // 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);
                        }
                }
        }
@@ -1309,31 +925,26 @@ void FakeUser::SendText(const std::string& line)
 
 void User::SendText(const char *text, ...)
 {
-       va_list argsPtr;
-       char line[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(line, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       SendText(std::string(line));
+       std::string line;
+       VAFORMAT(line, text, text);
+       SendText(line);
 }
 
-void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream)
+void User::SendText(const std::string& linePrefix, std::stringstream& textStream)
 {
        std::string line;
-       std::string Word;
-       while (TextStream >> Word)
+       std::string word;
+       while (textStream >> word)
        {
-               size_t lineLength = LinePrefix.length() + line.length() + Word.length() + 13;
-               if (lineLength > MAXBUF)
+               size_t lineLength = linePrefix.length() + line.length() + word.length() + 3; // "\s\n\r"
+               if (lineLength > ServerInstance->Config->Limits.MaxLine)
                {
-                       SendText(LinePrefix + line);
+                       SendText(linePrefix + line);
                        line.clear();
                }
-               line += " " + Word;
+               line += " " + word;
        }
-       SendText(LinePrefix + line);
+       SendText(linePrefix + line);
 }
 
 /* return 0 or 1 depending if users u and u2 share one or more common channels
@@ -1350,22 +961,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;
@@ -1376,83 +984,14 @@ 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));
-
-       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)
                return true;
@@ -1465,106 +1004,30 @@ bool User::ChangeDisplayedHost(const char* shost)
                        return false;
        }
 
-       FOREACH_MOD(I_OnChangeHost, 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);
+       FOREACH_MOD(OnChangeHost, (this,shost));
 
+       this->dhost.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, "%s :is now your displayed host", this->dhost.c_str());
 
        return true;
 }
 
-bool User::ChangeIdent(const char* newident)
+bool User::ChangeIdent(const std::string& newident)
 {
        if (this->ident == newident)
                return true;
 
-       FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
-
-       std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
+       FOREACH_MOD(OnChangeIdent, (this,newident));
 
        this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
-
        this->InvalidateCache();
 
-       this->DoHostCycle(quitstr);
-
        return true;
 }
 
-void User::SendAll(const char* command, const char* text, ...)
-{
-       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;
-
-       for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
-       {
-               if (line.length() + namelen + pos - start > 510)
-               {
-                       ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
-                       line = prefix.str();
-               }
-
-               line.append(cl.substr(start, pos - start + 1));
-       }
-
-       if (line.length() != prefix.str().length())
-       {
-               ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
-       }
-}
-
 /*
  * Sets a user's connection class.
  * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc.
@@ -1576,27 +1039,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));
@@ -1604,7 +1067,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
                                continue;
                        if (MOD_RESULT == MOD_RES_ALLOW)
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Class forced by module to %s", c->GetName().c_str());
+                               ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
                                found = c;
                                break;
                        }
@@ -1620,7 +1083,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
                        if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
                            !InspIRCd::MatchCIDR(this->host, 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;
                        }
 
@@ -1630,7 +1093,7 @@ 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;
                        }
 
@@ -1638,7 +1101,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
                        int port = c->config->getInt("port");
                        if (port)
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Requires port (%d)", port);
+                               ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires port (%d)", port);
 
                                /* and our port doesn't match, fail. */
                                if (this->GetServerPort() != port)
@@ -1647,9 +1110,9 @@ void LocalUser::SetClass(const std::string &explicit_name)
 
                        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;
                                }
                        }
@@ -1669,27 +1132,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);
        }
 
@@ -1700,20 +1149,21 @@ 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)
 {
 }
 
@@ -1723,7 +1173,7 @@ ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, cons
        softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), recvqmax(parent.recvqmax),
        penaltythreshold(parent.penaltythreshold), commandrate(parent.commandrate),
        maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxconnwarn(parent.maxconnwarn), maxchans(parent.maxchans),
-       limit(parent.limit)
+       limit(parent.limit), resolvehostnames(parent.resolvehostnames)
 {
 }
 
@@ -1746,4 +1196,5 @@ void ConnectClass::Update(const ConnectClass* src)
        maxconnwarn = src->maxconnwarn;
        maxchans = src->maxchans;
        limit = src->limit;
+       resolvehostnames = src->resolvehostnames;
 }
index 29dbd442bf04767f4aefd6f849acb47c5998c58f..d307082f464cf015b79b2fddcd11b83c9e86d164 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/sh
-echo "InspIRCd-2.0.21"
+echo "InspIRCd-2.2.0+pre"
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..6711f953aa9b7a311d0c91a6a741769ebf437422 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)
+{
+       std::stringstream masklist(masks);
+       std::string mask;
+       while (masklist >> mask)
+       {
+               if (InspIRCd::Match(hostname, mask, ascii_case_insensitive_map) ||
+                       InspIRCd::MatchCIDR(ipaddr, mask, ascii_case_insensitive_map))
+               {
+                       return true;
+               }
+       }
+       return false;
+}
index 66d24f439a6b12b600033a17bf40ef3b72ba6efd..dedf8c7a9c917a4c838ee31acce445d0eab3a57a 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;
 
                /* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
                LookupIter safei;
@@ -259,7 +259,7 @@ bool XLineManager::AddLine(XLine* line, User* user)
        ContainerIter x = lookup_lines.find(line->type);
        if (x != lookup_lines.end())
        {
-               LookupIter i = x->second.find(line->Displayable());
+               LookupIter i = x->second.find(line->Displayable().c_str());
                if (i != x->second.end())
                {
                        // XLine propagation bug was here, if the line to be added already exists and
@@ -276,15 +276,15 @@ bool XLineManager::AddLine(XLine* line, User* user)
        if (!xlf)
                return false;
 
-       ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
+       ServerInstance->BanCache.RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
 
        if (xlf->AutoApplyToUserList(line))
                pending_lines.push_back(line);
 
-       lookup_lines[line->type][line->Displayable()] = line;
+       lookup_lines[line->type][line->Displayable().c_str()] = line;
        line->OnAdd();
 
-       FOREACH_MOD(I_OnAddLine,OnAddLine(user, line));
+       FOREACH_MOD(OnAddLine, (user, line));
 
        return true;
 }
@@ -306,15 +306,13 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
        if (simulate)
                return true;
 
-       ServerInstance->BanCache->RemoveEntries(y->second->type, true);
+       ServerInstance->BanCache.RemoveEntries(y->second->type, true);
 
-       FOREACH_MOD(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);
@@ -326,9 +324,10 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
 void ELine::Unset()
 {
        /* remove exempt from everyone and force recheck after deleting eline */
-       for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
        {
-               User* u = (User*)(*u2);
+               LocalUser* u = *u2;
                u->exempt = false;
        }
 
@@ -409,7 +408,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();
@@ -418,9 +417,7 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
         * is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
         * -- Brain
         */
-       std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
-       if (pptr != pending_lines.end())
-               pending_lines.erase(pptr);
+       stdalgo::erase(pending_lines, item->second);
 
        delete item->second;
        container->second.erase(item);
@@ -430,10 +427,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)
@@ -471,7 +468,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()+" "+
+                               results.push_back(ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
                                        ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+i->second->source+" :"+i->second->reason);
                        i = safei;
                }
@@ -531,30 +528,28 @@ 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))
@@ -576,7 +571,8 @@ 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))
@@ -598,7 +594,8 @@ 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))
@@ -615,7 +612,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))
@@ -641,7 +639,7 @@ bool QLine::Matches(User *u)
 void QLine::Apply(User* u)
 {
        /* Force to uuid on apply of qline, no need to disconnect any more :) */
-       u->ForceNickChange(u->uuid.c_str());
+       u->ChangeNick(u->uuid);
 }
 
 
@@ -679,67 +677,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..739f7fc
--- /dev/null
@@ -0,0 +1,152 @@
+#!/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/utilities.pm module will not
+#            be available!
+
+sub prompt($$) {
+       my ($question, $default) = @_;
+       return prompt_string(1, $question, $default) if eval '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 'Syntax: genssl <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-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
+__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 '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 '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..b0eb255
--- /dev/null
@@ -0,0 +1,73 @@
+#!/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 make::common;
+use make::configure;
+
+$ENV{D} = $ENV{V} = 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';
+       push @socketengines, 'ports' if test_header $compiler, 'ports.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;
+               }
+               $ENV{PURE_STATIC} = 1;
+               if (system 'make', '-j'.get_cpu_count, 'install') {
+                       say "Failed to compile with static modules using the $compiler compiler and the $socketengine socket engine!";
+                       exit 1;
+               }
+               delete $ENV{PURE_STATIC};
+               if (system 'make', '-j'.get_cpu_count, 'install') {
+                       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 f53ca0701fdc963b535f866175d9cceeea720fd6..4b875ca61c70ba738b164be0eb573c4120cdd36c 100644 (file)
@@ -1,3 +1,2 @@
-inspircd_version.h\r
-inspircd_config.h\r
+config.h\r
 inspircd.rc\r
index 10653cf74a13692c75f618c444341c463096275a..7682706965c1b2b9bfe9ca0b4770280dd85a6165 100644 (file)
@@ -2,25 +2,30 @@ cmake_minimum_required(VERSION 2.8)
 
 project(InspIRCd CXX)
 
-set(CONF_PATH "conf" CACHE PATH "Configuration file path")
-set(MODULE_PATH "modules" CACHE PATH "Module path")
-set(DATA_PATH "data" CACHE PATH "Data path")
-set(LOG_PATH "logs" CACHE PATH "Log file path")
+set(CONFIG_DIR "conf" CACHE PATH "Configuration file path")
+set(MODULE_DIR "modules" CACHE PATH "Module path")
+set(DATA_DIR "data" CACHE PATH "Data path")
+set(LOG_DIR "logs" CACHE PATH "Log file path")
 
 set(EXTRA_INCLUDES "" CACHE PATH "Extra include paths")
 set(EXTRA_LIBS "" CACHE PATH "Extra library paths")
 
 set(INSPIRCD_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../")
 
+set(SYSTEM_NAME_VERSION ${CMAKE_SYSTEM})
+set(SOCKETENGINE "select")
+
+# Build with multiple processes
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+
 # 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 +61,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,20 +83,20 @@ 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")
        include(InstallRequiredSystemLibraries)
 
        set(CPACK_PACKAGE_NAME "InspIRCd IRC Daemon")
        set(CPACK_PACKAGE_VENDOR "InspIRCd Development Team")
-       set(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION})
-       set(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
-       set(CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION})
-       set(CPACK_PACKAGE_FILE_NAME "InspIRCd-${FULL_VERSION}")
+       set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
+       set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
+       set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH})
+       set(CPACK_PACKAGE_FILE_NAME "InspIRCd-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
        set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../docs/COPYING")
 
        set(CPACK_GENERATOR "NSIS")
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..3e0a264a58366764d93a07f05b00da711323ba9a 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"
@@ -85,7 +86,7 @@ CoreExport int insp_inet_pton(int af, const char *src, void *dst)
                }
                return 1;
        }
-       
+
        return 0;
 }
 
@@ -163,7 +164,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 +190,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..d65d4eb92f84a61486131d1fe52fc238ffd3939d 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
@@ -97,6 +103,10 @@ CoreExport const char * insp_inet_ntop(int af, const void * src, char * dst, soc
 #define vsnprintf _vsnprintf
 #endif
 
+#ifndef va_copy
+#define va_copy(dest, src) (dest = src)
+#endif
+
 /* Unix-style sleep (argument is in seconds) */
 __inline void sleep(int seconds) { Sleep(seconds * 1000); }
 
@@ -104,9 +114,12 @@ __inline void sleep(int seconds) { Sleep(seconds * 1000); }
 #define popen _popen
 #define pclose _pclose
 
+/* _access */
+#define access _access
+
 /* 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;
 };
@@ -191,8 +204,6 @@ CoreExport void closedir(DIR * handle);
 void * ::operator new(size_t iSize);
 void ::operator delete(void * ptr);
 
-#define DISABLE_WRITEV
-
 #include <exception>
 
 class CWin32Exception : public std::exception
@@ -208,5 +219,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 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 */