summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/aliases/anope.conf.example26
-rw-r--r--conf/aliases/atheme.conf.example20
-rw-r--r--conf/aliases/ircservices.conf.example20
-rw-r--r--conf/inspircd.censor.example16
-rw-r--r--conf/inspircd.filter.example53
-rw-r--r--conf/inspircd.helpop-full.example520
-rw-r--r--conf/inspircd.helpop.example84
-rw-r--r--conf/inspircd.motd.example41
-rw-r--r--conf/inspircd.quotes.example176
-rw-r--r--conf/inspircd.rules.example4
-rwxr-xr-xconfigure1803
-rw-r--r--docs/COPYING347
-rw-r--r--docs/README11
-rw-r--r--docs/inspircd.conf.example2196
-rw-r--r--docs/rfc/rfc1035.txt3078
-rw-r--r--docs/rfc/rfc1413.txt452
-rw-r--r--docs/rfc/rfc1459.txt3644
-rw-r--r--extras/m_sqllog.mysql.sql79
-rw-r--r--extras/m_sqllog.postgresql.sql52
-rw-r--r--extras/m_sqllog.sqlite3.sql35
-rw-r--r--extras/m_sqloper.mysql.sql25
-rw-r--r--extras/m_sqloper.postgresql.sql15
-rw-r--r--extras/m_sqloper.sqlite3.sql8
-rw-r--r--http/index.html13
-rw-r--r--include/base.h229
-rw-r--r--include/channels.h552
-rw-r--r--include/command_parse.h245
-rw-r--r--include/commands/cmd_admin.h45
-rw-r--r--include/commands/cmd_away.h46
-rw-r--r--include/commands/cmd_clearcache.h45
-rw-r--r--include/commands/cmd_commands.h46
-rw-r--r--include/commands/cmd_connect.h46
-rw-r--r--include/commands/cmd_die.h46
-rw-r--r--include/commands/cmd_eline.h46
-rw-r--r--include/commands/cmd_gline.h46
-rw-r--r--include/commands/cmd_info.h46
-rw-r--r--include/commands/cmd_invite.h46
-rw-r--r--include/commands/cmd_ison.h46
-rw-r--r--include/commands/cmd_join.h46
-rw-r--r--include/commands/cmd_kick.h46
-rw-r--r--include/commands/cmd_kill.h46
-rw-r--r--include/commands/cmd_kline.h46
-rw-r--r--include/commands/cmd_links.h46
-rw-r--r--include/commands/cmd_list.h46
-rw-r--r--include/commands/cmd_loadmodule.h46
-rw-r--r--include/commands/cmd_lusers.h46
-rw-r--r--include/commands/cmd_map.h46
-rw-r--r--include/commands/cmd_mode.h45
-rw-r--r--include/commands/cmd_modules.h46
-rw-r--r--include/commands/cmd_motd.h49
-rw-r--r--include/commands/cmd_names.h46
-rw-r--r--include/commands/cmd_nick.h46
-rw-r--r--include/commands/cmd_notice.h46
-rw-r--r--include/commands/cmd_oper.h48
-rw-r--r--include/commands/cmd_part.h46
-rw-r--r--include/commands/cmd_pass.h49
-rw-r--r--include/commands/cmd_ping.h46
-rw-r--r--include/commands/cmd_pong.h47
-rw-r--r--include/commands/cmd_privmsg.h46
-rw-r--r--include/commands/cmd_qline.h46
-rw-r--r--include/commands/cmd_quit.h46
-rw-r--r--include/commands/cmd_rehash.h46
-rw-r--r--include/commands/cmd_reloadmodule.h46
-rw-r--r--include/commands/cmd_restart.h49
-rw-r--r--include/commands/cmd_rules.h49
-rw-r--r--include/commands/cmd_server.h46
-rw-r--r--include/commands/cmd_squit.h49
-rw-r--r--include/commands/cmd_stats.h49
-rw-r--r--include/commands/cmd_summon.h49
-rw-r--r--include/commands/cmd_time.h46
-rw-r--r--include/commands/cmd_topic.h46
-rw-r--r--include/commands/cmd_trace.h46
-rw-r--r--include/commands/cmd_unloadmodule.h46
-rw-r--r--include/commands/cmd_user.h46
-rw-r--r--include/commands/cmd_userhost.h46
-rw-r--r--include/commands/cmd_users.h49
-rw-r--r--include/commands/cmd_version.h46
-rw-r--r--include/commands/cmd_wallops.h46
-rw-r--r--include/commands/cmd_who.h60
-rw-r--r--include/commands/cmd_whois.h49
-rw-r--r--include/commands/cmd_whowas.h145
-rw-r--r--include/commands/cmd_zline.h46
-rw-r--r--include/configreader.h791
-rw-r--r--include/connection.h80
-rw-r--r--include/ctables.h173
-rw-r--r--include/cull_list.h163
-rw-r--r--include/dns.h521
-rw-r--r--include/dynamic.h128
-rw-r--r--include/exitcodes.h46
-rw-r--r--include/globals.h40
-rw-r--r--include/hash_map.h34
-rw-r--r--include/hashcomp.h711
-rw-r--r--include/inspircd.h1280
-rw-r--r--include/inspsocket.h438
-rw-r--r--include/inspstring.h57
-rw-r--r--include/mode.h520
-rw-r--r--include/modes/cmode_b.h36
-rw-r--r--include/modes/cmode_h.h35
-rw-r--r--include/modes/cmode_i.h26
-rw-r--r--include/modes/cmode_k.h30
-rw-r--r--include/modes/cmode_l.h28
-rw-r--r--include/modes/cmode_m.h26
-rw-r--r--include/modes/cmode_n.h26
-rw-r--r--include/modes/cmode_o.h35
-rw-r--r--include/modes/cmode_p.h26
-rw-r--r--include/modes/cmode_s.h26
-rw-r--r--include/modes/cmode_t.h26
-rw-r--r--include/modes/cmode_v.h35
-rw-r--r--include/modes/umode_i.h27
-rw-r--r--include/modes/umode_n.h26
-rw-r--r--include/modes/umode_o.h27
-rw-r--r--include/modes/umode_s.h27
-rw-r--r--include/modes/umode_w.h27
-rw-r--r--include/modules.h1697
-rw-r--r--include/snomasks.h86
-rw-r--r--include/socket.h226
-rw-r--r--include/socketengine.h297
-rw-r--r--include/socketengine_epoll.h65
-rw-r--r--include/socketengine_iocp.h227
-rw-r--r--include/socketengine_kqueue.h69
-rw-r--r--include/socketengine_ports.h69
-rw-r--r--include/socketengine_select.h70
-rw-r--r--include/timer.h157
-rw-r--r--include/typedefs.h54
-rw-r--r--include/u_listmode.h475
-rw-r--r--include/users.h1032
-rw-r--r--include/wildcard.h46
-rw-r--r--include/xline.h531
-rw-r--r--make/configure.pm283
-rw-r--r--make/gnutlscert.pm116
-rw-r--r--make/opensslcert.pm37
-rw-r--r--make/utilities.pm384
-rw-r--r--src/base.cpp96
-rw-r--r--src/channels.cpp1068
-rw-r--r--src/cmd_admin.cpp36
-rw-r--r--src/cmd_away.cpp43
-rw-r--r--src/cmd_clearcache.cpp32
-rw-r--r--src/cmd_commands.cpp34
-rw-r--r--src/cmd_connect.cpp34
-rw-r--r--src/cmd_die.cpp48
-rw-r--r--src/cmd_eline.cpp78
-rw-r--r--src/cmd_gline.cpp90
-rw-r--r--src/cmd_info.cpp76
-rw-r--r--src/cmd_invite.cpp99
-rw-r--r--src/cmd_ison.cpp87
-rw-r--r--src/cmd_join.cpp53
-rw-r--r--src/cmd_kick.cpp59
-rw-r--r--src/cmd_kill.cpp118
-rw-r--r--src/cmd_kline.cpp89
-rw-r--r--src/cmd_links.cpp33
-rw-r--r--src/cmd_list.cpp86
-rw-r--r--src/cmd_loadmodule.cpp40
-rw-r--r--src/cmd_lusers.cpp43
-rw-r--r--src/cmd_map.cpp36
-rw-r--r--src/cmd_mode.cpp32
-rw-r--r--src/cmd_modules.cpp76
-rw-r--r--src/cmd_motd.cpp30
-rw-r--r--src/cmd_names.cpp55
-rw-r--r--src/cmd_nick.cpp190
-rw-r--r--src/cmd_notice.cpp159
-rw-r--r--src/cmd_oper.cpp154
-rw-r--r--src/cmd_part.cpp44
-rw-r--r--src/cmd_pass.cpp43
-rw-r--r--src/cmd_ping.cpp29
-rw-r--r--src/cmd_pong.cpp29
-rw-r--r--src/cmd_privmsg.cpp165
-rw-r--r--src/cmd_qline.cpp81
-rw-r--r--src/cmd_quit.cpp49
-rw-r--r--src/cmd_rehash.cpp57
-rw-r--r--src/cmd_reloadmodule.cpp40
-rw-r--r--src/cmd_restart.cpp50
-rw-r--r--src/cmd_rules.cpp28
-rw-r--r--src/cmd_server.cpp31
-rw-r--r--src/cmd_squit.cpp33
-rw-r--r--src/cmd_stats.cpp319
-rw-r--r--src/cmd_summon.cpp28
-rw-r--r--src/cmd_time.cpp41
-rw-r--r--src/cmd_topic.cpp119
-rw-r--r--src/cmd_trace.cpp47
-rw-r--r--src/cmd_unloadmodule.cpp40
-rw-r--r--src/cmd_user.cpp70
-rw-r--r--src/cmd_userhost.cpp64
-rw-r--r--src/cmd_users.cpp28
-rw-r--r--src/cmd_version.cpp32
-rw-r--r--src/cmd_wallops.cpp32
-rw-r--r--src/cmd_who.cpp329
-rw-r--r--src/cmd_whois.cpp145
-rw-r--r--src/cmd_whowas.cpp342
-rw-r--r--src/cmd_zline.cpp81
-rw-r--r--src/command_parse.cpp564
-rw-r--r--src/commands.cpp112
-rw-r--r--src/configreader.cpp1715
-rw-r--r--src/cull_list.cpp203
-rw-r--r--src/dns.cpp1170
-rw-r--r--src/dynamic.cpp90
-rw-r--r--src/hashcomp.cpp620
-rw-r--r--src/helperfuncs.cpp535
-rw-r--r--src/inspircd.cpp1308
-rw-r--r--src/inspsocket.cpp751
-rw-r--r--src/inspstring.cpp138
-rw-r--r--src/mode.cpp1067
-rw-r--r--src/modes/Makefile66
-rw-r--r--src/modes/cmode_b.cpp186
-rw-r--r--src/modes/cmode_h.cpp163
-rw-r--r--src/modes/cmode_i.cpp36
-rw-r--r--src/modes/cmode_k.cpp104
-rw-r--r--src/modes/cmode_l.cpp98
-rw-r--r--src/modes/cmode_m.cpp37
-rw-r--r--src/modes/cmode_n.cpp37
-rw-r--r--src/modes/cmode_o.cpp154
-rw-r--r--src/modes/cmode_p.cpp36
-rw-r--r--src/modes/cmode_s.cpp36
-rw-r--r--src/modes/cmode_t.cpp37
-rw-r--r--src/modes/cmode_v.cpp153
-rw-r--r--src/modes/umode_i.cpp46
-rw-r--r--src/modes/umode_n.cpp59
-rw-r--r--src/modes/umode_o.cpp50
-rw-r--r--src/modes/umode_s.cpp46
-rw-r--r--src/modes/umode_w.cpp47
-rw-r--r--src/modules.cpp733
-rw-r--r--src/modules/extra/README8
-rw-r--r--src/modules/extra/m_filter_pcre.cpp183
-rw-r--r--src/modules/extra/m_httpclienttest.cpp82
-rw-r--r--src/modules/extra/m_mysql.cpp890
-rw-r--r--src/modules/extra/m_pgsql.cpp985
-rw-r--r--src/modules/extra/m_sqlauth.cpp195
-rw-r--r--src/modules/extra/m_sqlite3.cpp661
-rw-r--r--src/modules/extra/m_sqllog.cpp311
-rw-r--r--src/modules/extra/m_sqloper.cpp284
-rw-r--r--src/modules/extra/m_sqlutils.cpp239
-rw-r--r--src/modules/extra/m_sqlutils.h144
-rw-r--r--src/modules/extra/m_sqlv2.h606
-rw-r--r--src/modules/extra/m_ssl_gnutls.cpp844
-rw-r--r--src/modules/extra/m_ssl_openssl.cpp902
-rw-r--r--src/modules/extra/m_ssl_oper_cert.cpp181
-rw-r--r--src/modules/extra/m_sslinfo.cpp95
-rw-r--r--src/modules/extra/m_testclient.cpp111
-rw-r--r--src/modules/extra/m_ziplink.cpp453
-rw-r--r--src/modules/httpclient.h128
-rw-r--r--src/modules/httpd.h167
-rw-r--r--src/modules/m_alias.cpp273
-rw-r--r--src/modules/m_alltime.cpp84
-rw-r--r--src/modules/m_antibear.cpp79
-rw-r--r--src/modules/m_antibottler.cpp100
-rw-r--r--src/modules/m_auditorium.cpp192
-rw-r--r--src/modules/m_banexception.cpp154
-rw-r--r--src/modules/m_banredirect.cpp344
-rw-r--r--src/modules/m_blockamsg.cpp192
-rw-r--r--src/modules/m_blockcaps.cpp144
-rw-r--r--src/modules/m_blockcolor.cpp119
-rw-r--r--src/modules/m_botmode.cpp96
-rw-r--r--src/modules/m_cban.cpp252
-rw-r--r--src/modules/m_censor.cpp197
-rw-r--r--src/modules/m_cgiirc.cpp512
-rw-r--r--src/modules/m_chancreate.cpp56
-rw-r--r--src/modules/m_chanfilter.cpp156
-rw-r--r--src/modules/m_chanprotect.cpp532
-rw-r--r--src/modules/m_check.cpp189
-rw-r--r--src/modules/m_chghost.cpp121
-rw-r--r--src/modules/m_chgident.cpp93
-rw-r--r--src/modules/m_chgname.cpp90
-rw-r--r--src/modules/m_cloaking.cpp316
-rw-r--r--src/modules/m_clones.cpp101
-rw-r--r--src/modules/m_conn_join.cpp97
-rw-r--r--src/modules/m_conn_umodes.cpp105
-rw-r--r--src/modules/m_conn_waitpong.cpp149
-rw-r--r--src/modules/m_connflood.cpp121
-rw-r--r--src/modules/m_cycle.cpp100
-rw-r--r--src/modules/m_dccallow.cpp490
-rw-r--r--src/modules/m_deaf.cpp136
-rw-r--r--src/modules/m_denychans.cpp81
-rw-r--r--src/modules/m_devoice.cpp82
-rw-r--r--src/modules/m_dnsbl.cpp354
-rw-r--r--src/modules/m_filter.cpp136
-rw-r--r--src/modules/m_filter.h454
-rw-r--r--src/modules/m_foobar.cpp99
-rw-r--r--src/modules/m_globalload.cpp142
-rw-r--r--src/modules/m_globops.cpp77
-rw-r--r--src/modules/m_hash.h197
-rw-r--r--src/modules/m_helpop.cpp192
-rw-r--r--src/modules/m_hidechans.cpp96
-rw-r--r--src/modules/m_hideoper.cpp95
-rw-r--r--src/modules/m_hostchange.cpp149
-rw-r--r--src/modules/m_http_client.cpp347
-rw-r--r--src/modules/m_httpd.cpp420
-rw-r--r--src/modules/m_httpd_stats.cpp242
-rw-r--r--src/modules/m_ident.cpp327
-rw-r--r--src/modules/m_invisible.cpp278
-rw-r--r--src/modules/m_inviteexception.cpp151
-rw-r--r--src/modules/m_joinflood.cpp286
-rw-r--r--src/modules/m_jumpserver.cpp165
-rw-r--r--src/modules/m_kicknorejoin.cpp225
-rw-r--r--src/modules/m_knock.cpp130
-rw-r--r--src/modules/m_lockserv.cpp132
-rw-r--r--src/modules/m_md5.cpp323
-rw-r--r--src/modules/m_messageflood.cpp305
-rw-r--r--src/modules/m_namesx.cpp128
-rw-r--r--src/modules/m_nicklock.cpp160
-rw-r--r--src/modules/m_noctcp.cpp108
-rw-r--r--src/modules/m_noinvite.cpp89
-rw-r--r--src/modules/m_nokicks.cpp106
-rw-r--r--src/modules/m_nonicks.cpp103
-rw-r--r--src/modules/m_nonotice.cpp104
-rw-r--r--src/modules/m_oper_hash.cpp164
-rw-r--r--src/modules/m_operchans.cpp98
-rw-r--r--src/modules/m_operjoin.cpp91
-rw-r--r--src/modules/m_operlevels.cpp123
-rw-r--r--src/modules/m_operlog.cpp76
-rw-r--r--src/modules/m_opermodes.cpp110
-rw-r--r--src/modules/m_opermotd.cpp117
-rw-r--r--src/modules/m_override.cpp295
-rw-r--r--src/modules/m_randquote.cpp139
-rw-r--r--src/modules/m_redirect.cpp161
-rw-r--r--src/modules/m_regonlycreate.cpp62
-rw-r--r--src/modules/m_remove.cpp289
-rw-r--r--src/modules/m_restrictbanned.cpp99
-rw-r--r--src/modules/m_restrictchans.cpp86
-rw-r--r--src/modules/m_restrictmsg.cpp76
-rw-r--r--src/modules/m_safelist.cpp269
-rw-r--r--src/modules/m_sajoin.cpp115
-rw-r--r--src/modules/m_samode.cpp99
-rw-r--r--src/modules/m_sanick.cpp98
-rw-r--r--src/modules/m_sapart.cpp114
-rw-r--r--src/modules/m_saquit.cpp83
-rw-r--r--src/modules/m_securelist.cpp98
-rw-r--r--src/modules/m_seenicks.cpp56
-rw-r--r--src/modules/m_services.cpp311
-rw-r--r--src/modules/m_services_account.cpp333
-rw-r--r--src/modules/m_sethost.cpp109
-rw-r--r--src/modules/m_setident.cpp84
-rw-r--r--src/modules/m_setidle.cpp75
-rw-r--r--src/modules/m_setname.cpp81
-rw-r--r--src/modules/m_sha256.cpp297
-rw-r--r--src/modules/m_showwhois.cpp110
-rw-r--r--src/modules/m_silence.cpp216
-rw-r--r--src/modules/m_silence_ext.cpp373
-rw-r--r--src/modules/m_spanningtree/README25
-rw-r--r--src/modules/m_spanningtree/handshaketimer.cpp63
-rw-r--r--src/modules/m_spanningtree/handshaketimer.h38
-rw-r--r--src/modules/m_spanningtree/link.h43
-rw-r--r--src/modules/m_spanningtree/main.cpp1393
-rw-r--r--src/modules/m_spanningtree/main.h199
-rw-r--r--src/modules/m_spanningtree/rconnect.cpp68
-rw-r--r--src/modules/m_spanningtree/rconnect.h29
-rw-r--r--src/modules/m_spanningtree/resolvers.cpp89
-rw-r--r--src/modules/m_spanningtree/resolvers.h91
-rw-r--r--src/modules/m_spanningtree/rsquit.cpp124
-rw-r--r--src/modules/m_spanningtree/rsquit.h30
-rw-r--r--src/modules/m_spanningtree/timesynctimer.cpp53
-rw-r--r--src/modules/m_spanningtree/timesynctimer.h48
-rw-r--r--src/modules/m_spanningtree/treeserver.cpp326
-rw-r--r--src/modules/m_spanningtree/treeserver.h187
-rw-r--r--src/modules/m_spanningtree/treesocket.h414
-rw-r--r--src/modules/m_spanningtree/treesocket1.cpp1274
-rw-r--r--src/modules/m_spanningtree/treesocket2.cpp1555
-rw-r--r--src/modules/m_spanningtree/utils.cpp650
-rw-r--r--src/modules/m_spanningtree/utils.h195
-rw-r--r--src/modules/m_spy.cpp164
-rw-r--r--src/modules/m_ssl_dummy.cpp85
-rw-r--r--src/modules/m_sslmodes.cpp146
-rw-r--r--src/modules/m_stripcolor.cpp186
-rw-r--r--src/modules/m_svshold.cpp283
-rw-r--r--src/modules/m_swhois.cpp268
-rw-r--r--src/modules/m_taxonomy.cpp100
-rw-r--r--src/modules/m_testcommand.cpp68
-rw-r--r--src/modules/m_timedbans.cpp205
-rw-r--r--src/modules/m_tline.cpp96
-rw-r--r--src/modules/m_uhnames.cpp99
-rw-r--r--src/modules/m_uninvite.cpp108
-rw-r--r--src/modules/m_userip.cpp87
-rw-r--r--src/modules/m_vhost.cpp96
-rw-r--r--src/modules/m_watch.cpp473
-rw-r--r--src/modules/m_xmlsocket.cpp171
-rw-r--r--src/modules/transport.h232
-rw-r--r--src/snomasks.cpp103
-rw-r--r--src/socket.cpp569
-rw-r--r--src/socketengine.cpp94
-rw-r--r--src/socketengine_epoll.cpp158
-rw-r--r--src/socketengine_iocp.cpp377
-rw-r--r--src/socketengine_kqueue.cpp159
-rw-r--r--src/socketengine_ports.cpp130
-rw-r--r--src/socketengine_select.cpp168
-rw-r--r--src/timer.cpp136
-rw-r--r--src/userprocess.cpp306
-rw-r--r--src/users.cpp2008
-rwxr-xr-xsrc/version.sh3
-rw-r--r--src/wildcard.cpp149
-rw-r--r--src/xline.cpp898
-rw-r--r--win/cert.pem19
-rw-r--r--win/colours.h110
-rw-r--r--win/configure.cpp510
-rw-r--r--win/inspircd.nsi273
-rw-r--r--win/inspircd_memory_functions.cpp45
-rw-r--r--win/inspircd_win32wrapper.cpp514
-rw-r--r--win/inspircd_win32wrapper.h190
-rw-r--r--win/key.pem16
-rw-r--r--win/makeinstaller.bat8
397 files changed, 84229 insertions, 397 deletions
diff --git a/conf/aliases/anope.conf.example b/conf/aliases/anope.conf.example
index 9b20fafcb..03e68d9d8 100644
--- a/conf/aliases/anope.conf.example
+++ b/conf/aliases/anope.conf.example
@@ -1 +1,25 @@
-# Aliases for nickserv, chanserv, operserv, memoserv <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> <alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> <alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> <alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes"> <alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="MemoServ" uline="yes"> <alias text="BOTSERV" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes"> # Note: We can't have a shorthand version of this, it conflicts with HS for helpserv <alias text="HELPSERV" replace="PRIVMSG HelpServ :$2-" requires="HelpServ" uline="yes"> # Shorthand aliases for nickserv, chanserv, operserv, memoserv <alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> <alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> <alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> <alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes"> <alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes"> <alias text="BS" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes"> # /id [channel] <password> # Identify for a channel or nickname <alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes"> <alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes"> \ No newline at end of file
+# Aliases for nickserv, chanserv, operserv, memoserv
+<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
+<alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="MemoServ" uline="yes">
+<alias text="BOTSERV" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
+
+# Note: We can't have a shorthand version of this, it conflicts with HS for helpserv
+<alias text="HELPSERV" replace="PRIVMSG HelpServ :$2-" requires="HelpServ" uline="yes">
+
+# Shorthand aliases for nickserv, chanserv, operserv, memoserv
+<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
+<alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
+<alias text="BS" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
+
+
+# /id [channel] <password>
+# Identify for a channel or nickname
+<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
+<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
+
diff --git a/conf/aliases/atheme.conf.example b/conf/aliases/atheme.conf.example
index 17f6d2907..d6771f965 100644
--- a/conf/aliases/atheme.conf.example
+++ b/conf/aliases/atheme.conf.example
@@ -1 +1,19 @@
-# Aliases for nickserv, chanserv, operserv, memoserv <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> <alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> <alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> <alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes"> <alias text="GAMESERV" replace="PRIVMSG GameServ :$2-" requires="GameServ" uline="yes"> # Shorthand aliases for nickserv, chanserv, operserv, memoserv <alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> <alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> <alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> <alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes"> <alias text="GS" replace="PRIVMSG GameServ :$2-" requires="GameServ" uline="yes"> # /id [channel] <password> # Identify for a channel or nickname <alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes"> <alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2-" requires="NickServ" uline="yes"> \ No newline at end of file
+# Aliases for nickserv, chanserv, operserv, memoserv
+<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
+<alias text="GAMESERV" replace="PRIVMSG GameServ :$2-" requires="GameServ" uline="yes">
+
+# Shorthand aliases for nickserv, chanserv, operserv, memoserv
+<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
+<alias text="GS" replace="PRIVMSG GameServ :$2-" requires="GameServ" uline="yes">
+
+# /id [channel] <password>
+# Identify for a channel or nickname
+<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
+<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2-" requires="NickServ" uline="yes">
+
diff --git a/conf/aliases/ircservices.conf.example b/conf/aliases/ircservices.conf.example
index 52512a9e5..edfe784a2 100644
--- a/conf/aliases/ircservices.conf.example
+++ b/conf/aliases/ircservices.conf.example
@@ -1 +1,19 @@
-# Aliases for nickserv, chanserv, operserv, memoserv <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> <alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> <alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> <alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes"> <alias text="STATSERV" replace="PRIVMSG StatServ :$2-" requires="StatServ" uline="yes" operonly="yes"> # Shorthand aliases for nickserv, chanserv, operserv, memoserv <alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> <alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> <alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> <alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes"> <alias text="SS" replace="PRIVMSG StatServ :$2-" requires="StatServ" uline="yes" operonly="yes"> # /id [channel] <password> # Identify for a channel or nickname <alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes"> <alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes"> \ No newline at end of file
+# Aliases for nickserv, chanserv, operserv, memoserv
+<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
+<alias text="STATSERV" replace="PRIVMSG StatServ :$2-" requires="StatServ" uline="yes" operonly="yes">
+
+# Shorthand aliases for nickserv, chanserv, operserv, memoserv
+<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
+<alias text="SS" replace="PRIVMSG StatServ :$2-" requires="StatServ" uline="yes" operonly="yes">
+
+# /id [channel] <password>
+# Identify for a channel or nickname
+<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
+<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
+
diff --git a/conf/inspircd.censor.example b/conf/inspircd.censor.example
index 108f88c12..05d9ce6fa 100644
--- a/conf/inspircd.censor.example
+++ b/conf/inspircd.censor.example
@@ -1 +1,15 @@
-# Configuration file for m_censor.so (1.0.0.0) # C.J.Edwards May 2004. # # The tags for this module are formatted as follows: # # <badword text="simple word" # replace="text to replace with"> # # You can specify <badword text="simple word" replace=""> # to block lines containing the word <badword text="shit" replace="poo"> <badword text="fuck" replace="(censored)"> \ No newline at end of file
+# Configuration file for m_censor.so (1.0.0.0)
+# C.J.Edwards May 2004.
+#
+
+# The tags for this module are formatted as follows:
+#
+# <badword text="simple word"
+# replace="text to replace with">
+#
+# You can specify <badword text="simple word" replace="">
+# to block lines containing the word
+
+<badword text="shit" replace="poo">
+<badword text="fuck" replace="(censored)">
+
diff --git a/conf/inspircd.filter.example b/conf/inspircd.filter.example
index f4487a719..7623a4472 100644
--- a/conf/inspircd.filter.example
+++ b/conf/inspircd.filter.example
@@ -1 +1,52 @@
-# Configuration file for m_filter.so and m_filter_pcre.so # The tags for this module are formatted as follows: # # <keyword pattern="any glob pattern here" # reason="reason for filtering" # action="action to take" # flags="filter flags" # duration="optional length of gline"> # # Valid actions for 'action' are: # # block This blocks the line, sends out a notice to all opers with # +s and informs the user that their message was blocked. # # silent This blocks the line only, and informs the user their message # was blocked, but does not notify opers. # # none This action causes nothing to be done except logging. This # is the default action if none is specified. # # kill This disconnects the user, with the 'reason' parameter as # the kill reason. # # gline G-LINE the user for 'duration' length of time. Durations may # be specified using the notation 1y2d3h4m6s in a similar way to # other glines, omitting the duration or setting it to 0 makes # any glines set by this filter be permanent. # # You can add filters from IRC using the /FILTER command. If you do this, they # will be set globally to your entire network. # # Valid characters for 'flags' are one or more of: # # p: Block private and channel messages # n: Block private and channel notices # P: Block part messages # q: Block quit messages # o: Don't match against opers # *: Represents all of the above flags # -: Does nothing, a non-op for when you do not want to specify any flags # Example filters for m_filter: # # <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: # # <keyword pattern="^blah.*?$" reason="Dont blah!" action="gline" duration="1d6h" flags="pnPq"> \ No newline at end of file
+# Configuration file for m_filter.so and m_filter_pcre.so
+
+# The tags for this module are formatted as follows:
+#
+# <keyword pattern="any glob pattern here"
+# reason="reason for filtering"
+# action="action to take"
+# flags="filter flags"
+# duration="optional length of gline">
+#
+# Valid actions for 'action' are:
+#
+# block This blocks the line, sends out a notice to all opers with
+# +s and informs the user that their message was blocked.
+#
+# silent This blocks the line only, and informs the user their message
+# was blocked, but does not notify opers.
+#
+# none This action causes nothing to be done except logging. This
+# is the default action if none is specified.
+#
+# kill This disconnects the user, with the 'reason' parameter as
+# the kill reason.
+#
+# gline G-LINE the user for 'duration' length of time. Durations may
+# be specified using the notation 1y2d3h4m6s in a similar way to
+# other glines, omitting the duration or setting it to 0 makes
+# any glines set by this filter be permanent.
+#
+# You can add filters from IRC using the /FILTER command. If you do this, they
+# will be set globally to your entire network.
+#
+# Valid characters for 'flags' are one or more of:
+#
+# p: Block private and channel messages
+# n: Block private and channel notices
+# P: Block part messages
+# q: Block quit messages
+# o: Don't match against opers
+# *: Represents all of the above flags
+# -: Does nothing, a non-op for when you do not want to specify any flags
+
+# Example filters for m_filter:
+#
+# <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:
+#
+# <keyword pattern="^blah.*?$" reason="Dont blah!" action="gline" duration="1d6h" flags="pnPq">
+
diff --git a/conf/inspircd.helpop-full.example b/conf/inspircd.helpop-full.example
index 64437a4d7..a4b4f1e11 100644
--- a/conf/inspircd.helpop-full.example
+++ b/conf/inspircd.helpop-full.example
@@ -1 +1,519 @@
-##################### # Helpop Standard # ##################### <helpop key="start" value="InspIRCd help system - This system provides help for commands and modes. Specify your question or a command name as the parameter for this command. If you are an oper you must prefix your query with a ? symbol. - /HELPOP COMMANDS - To see a list of user commands /HELPOP COPER - To see a list of oper commands /HELPOP UMODES - To see a list of user modes /HELPOP CHMODES - To see a list of channel modes"> <helpop key="nohelp" value="There is no help for the topic you searched for. Please try again."> ##################### # User Commands # ##################### <helpop key="commands" value="User Commands ------------- USER NICK QUIT VERSION PING PONG ADMIN PRIVMSG INFO TIME WHOIS NOTICE JOIN NAMES PART KICK MODE TOPIC WHO MOTD RULES OPER LIST LUSERS STATS USERHOST AWAY ISON SUMMON USERS INVITE PASS WHOWAS LINKS MAP COMMANDS MODULES KNOCK SILENCE DEVOICE REMOVE UNINVITE VHOST WATCH USERIP"> <helpop key="watch" value="/WATCH [C|S|+/-[NICK]] Adds or deletes a user from the watch list. C clears the list and S queries the status."> <helpop key="vhost" value="/VHOST [username] [password] Authenticate for a vhost."> <helpop key="kick" 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="devoice" value="/DEVOICE [channel] Devoices yourself from the specified channel."> <helpop key="silence" value="/SILENCE [+/-]<hostmask> [p|c|i|n|t|a|x] p Block private messages c Block channel messages i Block invites n Block private notices t Block channel notices a Block all of the above x Exception A serverside /ignore of the given hostmask. /SILENCE without a parameter will list the hostmasks that you have silenced."> <helpop key="knock" value="/KNOCK [channel] Sends a notice to a channel indicating you wish to join."> <helpop key="user" value="/USER [ident] [local host] [remote host] :[GECOS] This command is used by your client to register your irc session. You should not use it during an established connection."> <helpop key="nick" value="/NICK [new nick] Change your nickname to [new nick]."> <helpop key="quit" value="/QUIT [reason] Quit from IRC and end your current session."> <helpop key="version" value="/VERSION Returns the server's version number."> <helpop key="ping" value="/PING [server] Ping a server. Target server will answer with a PONG."> <helpop key="pong" value="/PONG [server] Your client should send this to answer server PINGs. You should not issue this command manually."> <helpop key="admin" value="/ADMIN [server] Fetches the administrative information on the given server."> <helpop key="privmsg" value="/MSG [target] [text] Sends a message to a user or channel specified in [target]."> <helpop key="notice" value="/NOTICE [target] [text] Sends a notice to a user or channel specified in [target]."> <helpop key="join" value="/JOIN [channel]{,[channel]} [key]{,[key]} Joins one or more channels you provide the names for."> <helpop key="names" value="/NAMES [channel]{,[channel]} Return a list of users on the channels you provide."> <helpop key="part" value="/PART [channel]{,[channel] [reason]} Leaves one or more channels you specify."> <helpop key="kick" value="/KICK [channel] [nick] {[reason]} Kicks a user from a channel you specify. You must be At least a channel halfoperator to kick a user."> <helpop key="mode" value="/MODE [target] [+|-][modes]{[+|-][modes]} {mode parameters} Sets the mode for a channel or a nickname specified in [target] A user may only set modes upon themselves, and may not set the +o usermode, and a user may only change channel modes of channels where they are at least a halfoperator."> <helpop key="topic" value="/TOPIC [channel] {topic} Sets or retrieves the channel topic. If a channel topic is given in the command and the channel is either not +t, or You are at least a halfoperator, the channel topic will be changed to the new one you provide."> <helpop key="who" value="/WHO [ [search-pattern] [ohurmaiMplf] ] Looks up the information of users matching the range you provide. You may only /WHO nicknames in channels or on servers where you share a common channel with them, or ones which are not +i (unless you are an IRC operator). The search-pattern may be a special sequence of characters determined by the flags given below, or it may be one of a nickname, a channel, a hostmask, an ip address mask or a server mask. - Valid WHO flags --------------- The following flags after the mask have the following affects: - o Show online IRC operators matching the mask u Unlimit the results past the maximum /who results value (IRC operators only) r Show all users whose realnames match the mask. When this flag is set it overrides the meaning of the search-pattern, which must contain a glob pattern intended to match GECOS (realname) fields. h Show real hostnames rather than masked hostnames (IRC operators only) m Search for all users with a given set of user modes. When this flag is set it overrides the meaning of the search-pattern, which must contain the mode sequence to search for, for example to find all users with +i and without +s, issue the command WHO +i-s m. a Show all users who have an away message matching the given mask p Show all users who are connected on the given port number i Show all users who have an ident (username) matching the given mask M Show all users who have metadata attached to them with the given key name l Show only local users f Show only remote (far) users - You may combine multiple flags in one WHO command except where stated in the table above."> <helpop key="motd" value="/MOTD [server] Show the message of the day for [server]. Messages of the day contain important server rules and notice and should be read before using a server in any way!"> <helpop key="rules" value="/RULES Show the rules file for the local server. This is similar in effect to /MOTD except that rules are optional. All users are sent the MOTD when they connect without having to request it."> <helpop key="oper" value="/OPER [login] [password] Attempts to authenticate a user as an IRC operator. Please be aware that both successful and unsuccessful oper attempts Are logged, and sent to online IRC operators."> <helpop key="list" value="/LIST [pattern] Creates a list of all existing channels matching the glob pattern [pattern], e.g. *chat* or bot*."> <helpop key="lusers" value="/LUSERS Shows a count of local and remote users, servers and channels."> <helpop key="userhost" value="/USERHOST [nickname] Returns the hostname and nickname of a user, and some other miscellaneous information."> <helpop key="userip" value="/USERIP [nickname] Returns the ip and nickname of a user."> <helpop key="away" value="/AWAY {message} If a message is given, marks you as being away, otherwise removes your away status and previous message."> <helpop key="ison" value="/ISON [nick] {[nick]...} Returns a subset of the nicks you give, showing only those that are currently online."> <helpop key="summon" value="/SUMMON [user] Summons a user from the shell where the ircd is running onto irc This command is deprecated in the current protocol."> <helpop key="users" value="/USERS Shows users logged into the shell where the ircd is running. This command is deprecated in the current protocol."> <helpop key="invite" value="/INVITE [nick] [channel] Invites a user to a channel. If the channel is NOT +i, any user, channel op or not, may invite any other user to the channel, so long as they are a member of that channel. Otherwise, if +i is set only channel halfoperators and above may invite users into the channel."> <helpop key="pass" value="/PASS [password] This command is used by your irc client when setting up your irc session, and should not be issued by a fully connected client."> <helpop key="whowas" value="/WHOWAS [nick] Returns a list of times the user was last seen on irc along with the time they were last seen and their server."> <helpop key="links" value="/LINKS Shows all servers linked to this one. Note that in this server implementation all links will be flattened as a tree based layout is not in use."> <helpop key="map" value="/MAP Shows a graphical representation of all users and servers on the network. The tree diagram is inaccurate in this implementation as a tree based network is not in place."> ##################### # Oper Commands # ##################### <helpop key="coper" value="Oper Commands ------------- DIE RESTART KILL REHASH TRACE CONNECT SQUIT MODULES MKPASSWD SHUN KLINE QLINE GLINE ELINE ZLINE SAJOIN SAPART SAMODE SAQUIT SANICK SETIDLE SETHOST SETNAME SETIDENT SWHOIS OPERMOTD CHGHOST CHGNAME CHGIDENT CBAN NICKLOCK NICKUNLOCK LOADMODULE UNLOADMODULE GLOBOPS SPYLIST SPYNAMES GLOADMODULE GUNLOADMODULE MKSHA256 FREEZE UNFREEZE OPERPERMS RCONNECT"> <helpop key="rconnect" value="/RCONNECT [source mask] [target mask] All servers matching [source mask] will try to connect to the first server in the config file matching [target mask]."> <helpop key="operperms" value="/OPERPERMS [nick] List all commands an oper has access to use."> <helpop key="freeze" value="/FREEZE [nick] Prevents the user from sending commands until they reconnect. User is also notified they have been frozen."> <helpop key="unfreeze" value="/UNFREEZE [nick] Unfreezes a user frozen by the /FREEZE command."> <helpop key="spylist" value="/SPYLIST Operates the same as /LIST but includes +s and +p channels."> <helpop key="spynames" value="/SPYNAMES [channel] Operates the same as /name but works on +s and +p channels."> <helpop key="globops" value="/GLOBOPS [message] Sends a message to all +g users."> <helpop key="cban " value="/CBAN [channel] {[duration] :[reason]} Sets or removes a channel ban. You must specify at least 3 parameters to add a ban, and one parameter to remove a ban. The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> <helpop key="sajoin" value="/SAJOIN [nick] [channel] Forces the user to join the channel."> <helpop key="sapart" value="/SAPART [nick] [channel] Forces the user to part the channel."> <helpop key="samode" value="/SAMODE [#chan/nick] +/-[modes] {[parameters for modes]} Gives the channel or nick the modes specified."> <helpop key="sanick" value="/SANICK [nick] [new nick] Changes the users nick to the new nick."> <helpop key="saquit" value="/SAQUIT [nick] [reason] Forces user to quit with the specified reason."> <helpop key="setidle" value="/SETIDLE [idle time] Sets your idle time (in seconds)."> <helpop key="sethost" value="/SETHOST [host] Sets your host to the specified host."> <helpop key="setident" value="/SETIDENT [ident] Sets your ident to the specified ident."> <helpop key="setname" value="/SETNAME [name] Sets your name to the specified name."> <helpop key="swhois" line="/SWHOIS [nick] [swhois] Sets the users swhois field to the given swhois."> <helpop key="mkpasswd" value="/MKPASSWD [hashtype] [plaintext] Encodes the plaintext to an MD5 hash and displays the result."> <helpop key="opermotd" value="/OPERMOTD Re-displays the Oper MOTD."> <helpop key="nicklock" value="/NICKLOCK [nick] [new nick] Changes user's nick to the new nick, and forces it to remain as such for the remainder of the session."> <helpop key="nickunlock" value="/NICKUNLOCK [nick] Allows the user to change nicks."> <helpop key="chghost" value="/CHGHOST [nickname] [new hostname] Changes the hostname of the user to the new hostname."> <helpop key="chgname" value="/CHGNAME [nickname] [new name] Changes the name of the user to the new name."> <helpop key="chgident" value="/CHGIDENT [nickname] [new ident] Changes the ident of the user to the new ident."> <helpop key="shun" value="/SHUN [user@host] {[duration] :[reason]} Sets or removes a shun (serverside ignore) on a host and ident mask. You must specify at least 3 parameters to add a shun, and one parameter to remove a shun (just the user@host section). The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> <helpop key="die" value="/DIE [password] If the correct password is provided, and you are an operator, This command will shut down the local server."> <helpop key="restart" value="/RESTART [password] If the correct password is provided, and you are an operator, This command will restart the local server."> <helpop key="commands" value="/COMMANDS Shows all currently available commands."> <helpop key="kill" value="/KILL [user] [reason] This command will disconnect a user from IRC with the given reason."> <helpop key="rehash" value="/REHASH This command will cause the server configuration file to be re-read and values re-initialized."> <helpop key="trace" value="/TRACE [nick|user@host|servermask] This command will provide a list of all users and servers which must be passed through or over to reach a given object (server or user)."> <helpop key="connect" value="/CONNECT [servermask] Create a mesh connection to the given servermask. You must have configured the server for linking in your configuration file, and provided a password."> <helpop key="squit" value="/SQUIT Disconnects the local server from the mesh network, causing every other server in the network to drop it."> <helpop key="modules" value="/MODULES Lists currently loaded modules, their memory offsets and version numbers and flags. If you are not an operator, you will see reduced detail."> <helpop key="loadmodule" value="/LOADMODULE [filename.so] Loads a module into the IRCd."> <helpop key="unloadmodule" value="/UNLOADMODULE [filename.so] Unloads a module from the IRCd. The module cannot have the static flag set (see the output of /MODULES)."> <helpop key="gloadmodule" value="/GLOADMODULE [filename.so] Globally loads a module into the network."> <helpop key="gunloadmodule" value="/GUNLOADMODULE [filename.so] Globally unloads a module from the network. The module cannot have the static flag set (see the output of /MODULES)."> <helpop key="kline" value="/KLINE [user@host] {[duration] :[reason]} Sets or removes a k-line (host based ban) on a host and ident mask. You must specify at least 3 parameters to add a ban, and one parameter to remove a ban (just the user@host section). The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> <helpop key="zline" value="/ZLINE [ipmask] {[duration] :[reason]} Sets or removes a z-line (ip based ban) on an ip range mask. You must specify at least 3 parameters to add a ban, and one parameter to remove a ban (just the user@host section). The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> <helpop key="qline" value="/QLINE [nickmask] {[duration] :[reason]} Sets or removes a q-line (nick based ban) on a nick mask. You must specify at least 3 parameters to add a ban, and one parameter to remove a ban (just the user@host section). The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> <helpop key="gline" value="/GLINE [user@host] {[duration] :[reason]} Sets or removes a g-line (global host based ban) on host mask. You must specify at least 3 parameters to add a ban, and one parameter to remove a ban (just the user@host section). The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> <helpop key="eline" value="/ELINE [user@host] {[duration] :[reason]} Sets or removes a e-line (local ban exception) on host mask. You must specify at least 3 parameters to add an exception, and one parameter to remove an exception (just the user@host section). The duration may be specified in seconds, or in the following format 1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours, 5 minutes and 6 seconds. All fields in this format are optional."> ###################### # User/Channel Modes # ###################### <helpop key="umodes" value="User Modes ---------- o Is an IRC operator i Is invisible to /WHO w Can receive wallops messages s Can receive server notices n [mask] Can receive server notices specified by [mask] x Cloaked hostname (requires cloaking module) g Can receive globops (requires globops module) h Will receive helpops notification (requires helpop module) r Nickname is registered R Only registered users can PRIVMSG this nickname B Is a bot G Messages are censored to user W Can see when a user uses WHOIS on them S mIRC colour codes are stripped to the user D User can not receive PRIVMSGs"> <helpop key="chmodes" value="Channel Modes ------------- v [nickname] Gives voice to [nickname] (can talk on +m channel) h [nickname] Gives halfops to [nickname] o [nickname] Gives ops to [nickname] b [hostmask] Bans [hostmask] on the channel a [nickname] Give protected status to [nickname] (+q only) q [nickname] Give founder status to [nickname] (ulines only) i Make the channel invite only, must /INVITE users k [key] Set the channel key (password) to [key] l [limit] Set the maximum possible users to [limit] m Enable moderation. Only +vo(h) can speak n Only users who are members of the channel may message it p Make channel private (hide from /LIST) s Make channel secret (can't be used at the same time as +p) O Channel is IRCops only (can only be set by IRCops) t Only halfops and above can change the topic Q Only U-Lined servers/nicks can kick T Only halfops/ops can send NOTICEs to the channel C No CTCPs allowed to the channel c mIRC colour codes blocked on the channel S mIRC colour codes are stripped from the channel e [hostmask] Ban exception on [hostmask] K No /KNOCK allowed to channel L [channel] If the channel is full, redirect users to [channel] N No nick changes while on the channel G Censors channel based on network censor config g [word] All messages containing the word are blocked I [hostmask] Invite exception allows user to join a channel with +i j [joins]:[sec] Prevents join flooding J [seconds] Prevents auto-rejoin on kick f [*][lines]:[sec] Kick on text flood. With * user is banned V No invites allowed r Channel is registered R Only registered users can join M Non-registered users can't chat z SSL clients only x Join exception mask. Avoids +ibkO ------------- NOTE: A large number of these modes are dependent upon server-side modules being loaded by a server/network administrator. The actual modes available on your network may be very different to this list. Please consult your help channel if you have any questions."> ###################### # Stats Symbols # ###################### <helpop key="stats" value="/STATS [symbol] Shows various server statistics. Depending on configuration this command may be reserved for oper-only use. - Valid symbols are: - m Show command statistics, number of times commands have been used z Show memory usage statistics o Show a list of all valid oper usernames and hostmasks l Show all inbound and outbound server and client connections u Show server uptime k Show k-lines (local bans) g Show g-lines (global bans) q Show q-lines (nick mask bans) Z Show z-lines (ip mask bans) Y Show connection classes C Show link blocks U Show u-lined servers P Show online opers and their idle times I Show connect class permissions e Show e-lines (local ban exemptions) C Show channel bans s Show filters - Note that all /STATS use is broadcast to online IRC operators."> \ No newline at end of file
+#####################
+# Helpop Standard #
+#####################
+
+<helpop key="start" value="InspIRCd help system
+-
+This system provides help for commands and modes.
+Specify your question or a command name as the
+parameter for this command. If you are an oper
+you must prefix your query with a ? symbol.
+-
+/HELPOP COMMANDS - To see a list of user commands
+/HELPOP COPER - To see a list of oper commands
+/HELPOP UMODES - To see a list of user modes
+/HELPOP CHMODES - To see a list of channel modes">
+
+<helpop key="nohelp" value="There is no help for the topic
+you searched for. Please try again.">
+
+#####################
+# User Commands #
+#####################
+
+<helpop key="commands" value="User Commands
+-------------
+USER NICK QUIT VERSION PING
+PONG ADMIN PRIVMSG INFO TIME
+WHOIS NOTICE JOIN NAMES PART
+KICK MODE TOPIC WHO MOTD
+RULES OPER LIST LUSERS STATS
+USERHOST AWAY ISON SUMMON USERS
+INVITE PASS WHOWAS LINKS MAP
+COMMANDS MODULES KNOCK SILENCE DEVOICE
+REMOVE UNINVITE VHOST WATCH USERIP">
+
+<helpop key="watch" value="/WATCH [C|S|+/-[NICK]]
+Adds or deletes a user from the watch list. C clears the list
+and S queries the status.">
+
+<helpop key="vhost" value="/VHOST [username] [password]
+Authenticate for a vhost.">
+
+<helpop key="kick" 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="devoice" value="/DEVOICE [channel]
+Devoices yourself from the specified channel.">
+
+<helpop key="silence" value="/SILENCE [+/-]<hostmask> [p|c|i|n|t|a|x]
+ p Block private messages
+ c Block channel messages
+ i Block invites
+ n Block private notices
+ t Block channel notices
+ a Block all of the above
+ x Exception
+A serverside /ignore of the given hostmask.
+/SILENCE without a parameter will list the hostmasks that you have silenced.">
+
+<helpop key="knock" value="/KNOCK [channel]
+Sends a notice to a channel indicating you wish to join.">
+
+<helpop key="user" value="/USER [ident] [local host] [remote host] :[GECOS]
+This command is used by your client to register your irc session.
+You should not use it during an established connection.">
+
+<helpop key="nick" value="/NICK [new nick]
+Change your nickname to [new nick].">
+
+<helpop key="quit" value="/QUIT [reason]
+Quit from IRC and end your current session.">
+
+<helpop key="version" value="/VERSION
+Returns the server's version number.">
+
+<helpop key="ping" value="/PING [server]
+Ping a server. Target server will answer with a PONG.">
+
+<helpop key="pong" value="/PONG [server]
+Your client should send this to answer server PINGs. You
+should not issue this command manually.">
+
+<helpop key="admin" value="/ADMIN [server]
+Fetches the administrative information on the given server.">
+
+<helpop key="privmsg" value="/MSG [target] [text]
+Sends a message to a user or channel specified in [target].">
+
+<helpop key="notice" value="/NOTICE [target] [text]
+Sends a notice to a user or channel specified in [target].">
+
+<helpop key="join" value="/JOIN [channel]{,[channel]} [key]{,[key]}
+Joins one or more channels you provide the names for.">
+
+<helpop key="names" value="/NAMES [channel]{,[channel]}
+Return a list of users on the channels you provide.">
+
+<helpop key="part" value="/PART [channel]{,[channel] [reason]}
+Leaves one or more channels you specify.">
+
+<helpop key="kick" value="/KICK [channel] [nick] {[reason]}
+Kicks a user from a channel you specify. You must be
+At least a channel halfoperator to kick a user.">
+
+<helpop key="mode" value="/MODE [target] [+|-][modes]{[+|-][modes]} {mode parameters}
+Sets the mode for a channel or a nickname specified in [target]
+A user may only set modes upon themselves, and may not set the
++o usermode, and a user may only change channel modes of
+channels where they are at least a halfoperator.">
+
+<helpop key="topic" value="/TOPIC [channel] {topic}
+Sets or retrieves the channel topic. If a channel topic is
+given in the command and the channel is either not +t, or
+You are at least a halfoperator, the channel topic will be
+changed to the new one you provide.">
+
+<helpop key="who" value="/WHO [ [search-pattern] [ohurmaiMplf] ]
+Looks up the information of users matching the range you provide.
+You may only /WHO nicknames in channels or on servers where you
+share a common channel with them, or ones which are not +i (unless
+you are an IRC operator). The search-pattern may be a special
+sequence of characters determined by the flags given below, or
+it may be one of a nickname, a channel, a hostmask, an ip address
+mask or a server mask.
+-
+Valid WHO flags
+---------------
+The following flags after the mask have the following affects:
+-
+ o Show online IRC operators matching the mask
+ u Unlimit the results past the maximum /who results value
+ (IRC operators only)
+ r Show all users whose realnames match the mask. When this
+ flag is set it overrides the meaning of the search-pattern,
+ which must contain a glob pattern intended to match GECOS
+ (realname) fields.
+ h Show real hostnames rather than masked hostnames (IRC
+ operators only)
+ m Search for all users with a given set of user modes. When
+ this flag is set it overrides the meaning of the
+ search-pattern, which must contain the mode sequence to
+ search for, for example to find all users with +i and
+ without +s, issue the command WHO +i-s m.
+ a Show all users who have an away message matching the given mask
+ p Show all users who are connected on the given port number
+ i Show all users who have an ident (username) matching the given mask
+ M Show all users who have metadata attached to them with
+ the given key name
+ l Show only local users
+ f Show only remote (far) users
+-
+You may combine multiple flags in one WHO command except where stated in the table above.">
+
+<helpop key="motd" value="/MOTD [server]
+Show the message of the day for [server]. Messages of the
+day contain important server rules and notice and should be
+read before using a server in any way!">
+
+<helpop key="rules" value="/RULES
+Show the rules file for the local server. This is similar in
+effect to /MOTD except that rules are optional. All users are
+sent the MOTD when they connect without having to request it.">
+
+<helpop key="oper" value="/OPER [login] [password]
+Attempts to authenticate a user as an IRC operator.
+Please be aware that both successful and unsuccessful oper attempts
+Are logged, and sent to online IRC operators.">
+
+<helpop key="list" value="/LIST [pattern]
+Creates a list of all existing channels matching the glob pattern
+[pattern], e.g. *chat* or bot*.">
+
+<helpop key="lusers" value="/LUSERS
+Shows a count of local and remote users, servers and channels.">
+
+<helpop key="userhost" value="/USERHOST [nickname]
+Returns the hostname and nickname of a user, and some other
+miscellaneous information.">
+
+<helpop key="userip" value="/USERIP [nickname]
+Returns the ip and nickname of a user.">
+
+<helpop key="away" value="/AWAY {message}
+If a message is given, marks you as being away, otherwise
+removes your away status and previous message.">
+
+<helpop key="ison" value="/ISON [nick] {[nick]...}
+Returns a subset of the nicks you give, showing only those
+that are currently online.">
+
+<helpop key="summon" value="/SUMMON [user]
+Summons a user from the shell where the ircd is running onto irc
+This command is deprecated in the current protocol.">
+
+<helpop key="users" value="/USERS
+Shows users logged into the shell where the ircd is running.
+This command is deprecated in the current protocol.">
+
+<helpop key="invite" value="/INVITE [nick] [channel]
+Invites a user to a channel. If the channel is NOT +i, any
+user, channel op or not, may invite any other user to the
+channel, so long as they are a member of that channel.
+Otherwise, if +i is set only channel halfoperators
+and above may invite users into the channel.">
+
+<helpop key="pass" value="/PASS [password]
+This command is used by your irc client when setting up
+your irc session, and should not be issued by a fully
+connected client.">
+
+<helpop key="whowas" value="/WHOWAS [nick]
+Returns a list of times the user was last seen on irc
+along with the time they were last seen and their server.">
+
+<helpop key="links" value="/LINKS
+Shows all servers linked to this one. Note that in this
+server implementation all links will be flattened as
+a tree based layout is not in use.">
+
+<helpop key="map" value="/MAP
+Shows a graphical representation of all users and servers
+on the network. The tree diagram is inaccurate in this
+implementation as a tree based network is not in place.">
+
+#####################
+# Oper Commands #
+#####################
+
+<helpop key="coper" value="Oper Commands
+-------------
+DIE RESTART KILL REHASH TRACE
+CONNECT SQUIT MODULES MKPASSWD SHUN
+KLINE QLINE GLINE ELINE ZLINE
+SAJOIN SAPART SAMODE SAQUIT SANICK
+SETIDLE SETHOST SETNAME SETIDENT SWHOIS
+OPERMOTD CHGHOST CHGNAME CHGIDENT CBAN
+NICKLOCK NICKUNLOCK LOADMODULE UNLOADMODULE GLOBOPS
+SPYLIST SPYNAMES GLOADMODULE GUNLOADMODULE MKSHA256
+FREEZE UNFREEZE OPERPERMS RCONNECT">
+
+<helpop key="rconnect" value="/RCONNECT [source mask] [target mask]
+All servers matching [source mask] will try to connect to
+the first server in the config file matching [target mask].">
+
+<helpop key="operperms" value="/OPERPERMS [nick]
+List all commands an oper has access to use.">
+
+<helpop key="freeze" value="/FREEZE [nick]
+Prevents the user from sending commands until they reconnect.
+User is also notified they have been frozen.">
+
+<helpop key="unfreeze" value="/UNFREEZE [nick]
+Unfreezes a user frozen by the /FREEZE command.">
+
+<helpop key="spylist" value="/SPYLIST
+Operates the same as /LIST but includes +s and +p channels.">
+
+<helpop key="spynames" value="/SPYNAMES [channel]
+Operates the same as /name but works on +s and +p channels.">
+
+<helpop key="globops" value="/GLOBOPS [message]
+Sends a message to all +g users.">
+
+<helpop key="cban " value="/CBAN [channel] {[duration] :[reason]}
+Sets or removes a channel ban. You must specify at least
+3 parameters to add a ban, and one parameter to remove a ban.
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+<helpop key="sajoin" value="/SAJOIN [nick] [channel]
+Forces the user to join the channel.">
+
+<helpop key="sapart" value="/SAPART [nick] [channel]
+Forces the user to part the channel.">
+
+<helpop key="samode" value="/SAMODE [#chan/nick] +/-[modes] {[parameters for modes]}
+Gives the channel or nick the modes specified.">
+
+<helpop key="sanick" value="/SANICK [nick] [new nick]
+Changes the users nick to the new nick.">
+
+<helpop key="saquit" value="/SAQUIT [nick] [reason]
+Forces user to quit with the specified reason.">
+
+<helpop key="setidle" value="/SETIDLE [idle time]
+Sets your idle time (in seconds).">
+
+<helpop key="sethost" value="/SETHOST [host]
+Sets your host to the specified host.">
+
+<helpop key="setident" value="/SETIDENT [ident]
+Sets your ident to the specified ident.">
+
+<helpop key="setname" value="/SETNAME [name]
+Sets your name to the specified name.">
+
+<helpop key="swhois" line="/SWHOIS [nick] [swhois]
+Sets the users swhois field to the given swhois.">
+
+<helpop key="mkpasswd" value="/MKPASSWD [hashtype] [plaintext]
+Encodes the plaintext to an MD5 hash and displays the result.">
+
+<helpop key="opermotd" value="/OPERMOTD
+Re-displays the Oper MOTD.">
+
+<helpop key="nicklock" value="/NICKLOCK [nick] [new nick]
+Changes user's nick to the new nick, and forces
+it to remain as such for the remainder of the session.">
+
+<helpop key="nickunlock" value="/NICKUNLOCK [nick]
+Allows the user to change nicks.">
+
+<helpop key="chghost" value="/CHGHOST [nickname] [new hostname]
+Changes the hostname of the user to the new hostname.">
+
+<helpop key="chgname" value="/CHGNAME [nickname] [new name]
+Changes the name of the user to the new name.">
+
+<helpop key="chgident" value="/CHGIDENT [nickname] [new ident]
+Changes the ident of the user to the new ident.">
+
+<helpop key="shun" value="/SHUN [user@host] {[duration] :[reason]}
+Sets or removes a shun (serverside ignore) on a host and ident mask.
+You must specify at least 3 parameters to add a shun, and one
+parameter to remove a shun (just the user@host section).
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+<helpop key="die" value="/DIE [password]
+If the correct password is provided, and you are an operator,
+This command will shut down the local server.">
+
+<helpop key="restart" value="/RESTART [password]
+If the correct password is provided, and you are an operator,
+This command will restart the local server.">
+
+<helpop key="commands" value="/COMMANDS
+Shows all currently available commands.">
+
+<helpop key="kill" value="/KILL [user] [reason]
+This command will disconnect a user from IRC with the given reason.">
+
+<helpop key="rehash" value="/REHASH
+This command will cause the server configuration file to be
+re-read and values re-initialized.">
+
+<helpop key="trace" value="/TRACE [nick|user@host|servermask]
+This command will provide a list of all users and servers which
+must be passed through or over to reach a given object (server or user).">
+
+<helpop key="connect" value="/CONNECT [servermask]
+Create a mesh connection to the given servermask. You must have
+configured the server for linking in your configuration file,
+and provided a password.">
+
+<helpop key="squit" value="/SQUIT
+Disconnects the local server from the mesh network, causing every
+other server in the network to drop it.">
+
+<helpop key="modules" value="/MODULES
+Lists currently loaded modules, their memory offsets and version
+numbers and flags. If you are not an operator, you will see reduced
+detail.">
+
+<helpop key="loadmodule" value="/LOADMODULE [filename.so]
+Loads a module into the IRCd.">
+
+<helpop key="unloadmodule" value="/UNLOADMODULE [filename.so]
+Unloads a module from the IRCd. The module cannot have the static
+flag set (see the output of /MODULES).">
+
+<helpop key="gloadmodule" value="/GLOADMODULE [filename.so]
+Globally loads a module into the network.">
+
+<helpop key="gunloadmodule" value="/GUNLOADMODULE [filename.so]
+Globally unloads a module from the network. The module cannot
+have the static flag set (see the output of /MODULES).">
+
+<helpop key="kline" value="/KLINE [user@host] {[duration] :[reason]}
+Sets or removes a k-line (host based ban) on a host and ident mask.
+You must specify at least 3 parameters to add a ban, and one
+parameter to remove a ban (just the user@host section).
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+<helpop key="zline" value="/ZLINE [ipmask] {[duration] :[reason]}
+Sets or removes a z-line (ip based ban) on an ip range mask.
+You must specify at least 3 parameters to add a ban, and one
+parameter to remove a ban (just the user@host section).
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+<helpop key="qline" value="/QLINE [nickmask] {[duration] :[reason]}
+Sets or removes a q-line (nick based ban) on a nick mask.
+You must specify at least 3 parameters to add a ban, and one
+parameter to remove a ban (just the user@host section).
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+<helpop key="gline" value="/GLINE [user@host] {[duration] :[reason]}
+Sets or removes a g-line (global host based ban) on host mask.
+You must specify at least 3 parameters to add a ban, and one
+parameter to remove a ban (just the user@host section).
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+<helpop key="eline" value="/ELINE [user@host] {[duration] :[reason]}
+Sets or removes a e-line (local ban exception) on host mask.
+You must specify at least 3 parameters to add an exception, and one
+parameter to remove an exception (just the user@host section).
+The duration may be specified in seconds, or in the following format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
+5 minutes and 6 seconds. All fields in this format are optional.">
+
+######################
+# User/Channel Modes #
+######################
+
+<helpop key="umodes" value="User Modes
+----------
+o Is an IRC operator
+i Is invisible to /WHO
+w Can receive wallops messages
+s Can receive server notices
+n [mask] Can receive server notices specified by [mask]
+x Cloaked hostname (requires cloaking module)
+g Can receive globops (requires globops module)
+h Will receive helpops notification (requires helpop module)
+r Nickname is registered
+R Only registered users can PRIVMSG this nickname
+B Is a bot
+G Messages are censored to user
+W Can see when a user uses WHOIS on them
+S mIRC colour codes are stripped to the user
+D User can not receive PRIVMSGs">
+
+<helpop key="chmodes" value="Channel Modes
+-------------
+v [nickname] Gives voice to [nickname] (can talk on +m channel)
+h [nickname] Gives halfops to [nickname]
+o [nickname] Gives ops to [nickname]
+b [hostmask] Bans [hostmask] on the channel
+a [nickname] Give protected status to [nickname] (+q only)
+q [nickname] Give founder status to [nickname] (ulines only)
+i Make the channel invite only, must /INVITE users
+k [key] Set the channel key (password) to [key]
+l [limit] Set the maximum possible users to [limit]
+m Enable moderation. Only +vo(h) can speak
+n Only users who are members of the channel may message it
+p Make channel private (hide from /LIST)
+s Make channel secret (can't be used at the same time as +p)
+O Channel is IRCops only (can only be set by IRCops)
+t Only halfops and above can change the topic
+Q Only U-Lined servers/nicks can kick
+T Only halfops/ops can send NOTICEs to the channel
+C No CTCPs allowed to the channel
+c mIRC colour codes blocked on the channel
+S mIRC colour codes are stripped from the channel
+e [hostmask] Ban exception on [hostmask]
+K No /KNOCK allowed to channel
+L [channel] If the channel is full, redirect users to [channel]
+N No nick changes while on the channel
+G Censors channel based on network censor config
+g [word] All messages containing the word are blocked
+I [hostmask] Invite exception allows user to join a channel with +i
+j [joins]:[sec] Prevents join flooding
+J [seconds] Prevents auto-rejoin on kick
+f [*][lines]:[sec] Kick on text flood. With * user is banned
+V No invites allowed
+r Channel is registered
+R Only registered users can join
+M Non-registered users can't chat
+z SSL clients only
+x Join exception mask. Avoids +ibkO
+-------------
+NOTE: A large number of these modes are dependent upon server-side modules
+being loaded by a server/network administrator. The actual modes available
+on your network may be very different to this list. Please consult your
+help channel if you have any questions.">
+
+######################
+# Stats Symbols #
+######################
+
+<helpop key="stats" value="/STATS [symbol]
+Shows various server statistics. Depending on configuration this
+command may be reserved for oper-only use.
+-
+Valid symbols are:
+-
+m Show command statistics, number of times commands have been used
+z Show memory usage statistics
+o Show a list of all valid oper usernames and hostmasks
+l Show all inbound and outbound server and client connections
+u Show server uptime
+k Show k-lines (local bans)
+g Show g-lines (global bans)
+q Show q-lines (nick mask bans)
+Z Show z-lines (ip mask bans)
+Y Show connection classes
+C Show link blocks
+U Show u-lined servers
+P Show online opers and their idle times
+I Show connect class permissions
+e Show e-lines (local ban exemptions)
+C Show channel bans
+s Show filters
+-
+Note that all /STATS use is broadcast to online IRC operators.">
+
diff --git a/conf/inspircd.helpop.example b/conf/inspircd.helpop.example
index 109a934a3..a02847ad0 100644
--- a/conf/inspircd.helpop.example
+++ b/conf/inspircd.helpop.example
@@ -1 +1,83 @@
-# Sample configuration file for m_helpop.so # You can either copy this into your conf folder and set up the module to use it, # or you can customize the responses for your network and/or add more. # # The way the new helpop system works is simple. You use one or more helpop tags. # <helpop key="moo" value="something here">. # key is what the user is looking for (i.e. /helpop moo), and value is what they get back # (note that it can span multiple lines!). # -- w00t 16/dec/2006 # <helpop key="start" value=" InspIRCd help system -------------------- - This system provides help for commands and modes. Specify your question or a command name as the parameter for this command. - /HELPOP COMMANDS - To see a list of commands /HELPOP UMODES - To see a list of user modes /HELPOP CHMODES - To see a list of channel modes"> <helpop key="nohelp" value="There is no help for the topic You searched for. Please try again."> <helpop key="commands" value="User Commands ------------- USER NICK QUIT VERSION PING PONG ADMIN PRIVMSG INFO TIME WHOIS NOTICE JOIN NAMES PART KICK MODE TOPIC WHO MOTD RULES OPER LIST LUSERS STATS USERHOST AWAY ISON SUMMON USERS INVITE PASS WHOWAS LINKS MAP COMMANDS MODULES Oper Commands ------------- DIE RESTART KILL REHASH TRACE CONNECT SQUIT MODULES KLINE ZLINE QLINE GLINE ELINE"> <helpop key="umodes" value="User Modes ---------- o Is an IRC operator i Is invisible to /WHO w Can receive wallops messages s Can receive server notices n [mask] Can receive server notices specified by [mask], or * for all. x Cloaked hostname (requires cloaking module) h Will receive helpops notification (requires helpop module)"> <helpop key="chmodes" value="Channel Modes ------------- v [nickname] Gives voice to [nickname] (can talk on +m channel) h [nickname] Gives halfops to [nickname] (requires halfop module) o [nickname] Gives ops to [nickname] b [hostmask] Bans [hostmask] on the channel a [nickname] Give protected status to [nickname] (+q only) q [nickname] Give founder status to [nickname] (ulines only) i Make the channel invite only, must /INVITE users k [key] Set the channel key (password) to [key] l [limit] Set the maximum possible users to [limit] m Enable moderation. Only +vo(h) can speak n Only users who are members of the channel may message it p Make channel private (hide from /LIST) s Make channel secret (can't be used at the same time as +p) O Channel is IRCops only (can only be set by IRCops) t Only halfops and above can change the topic Q Only U-Lined servers/nicks can kick T Only halfops/ops can send NOTICEs to the channel C No CTCPs allowed to the channel c mIRC colour codes blocked on the channel K No /KNOCK allowed to channel (if knock module is loaded) L [channel] If the channel is full, redirect users to [channel] N No nickchanges while on the channel ------------- NOTE: A large number of these modes are dependent upon server-side modules being loaded by a server/network administrator. The actual modes available on your network may be very different to this list. Please consult your help channel if you have any questions."> \ No newline at end of file
+# Sample configuration file for m_helpop.so
+# You can either copy this into your conf folder and set up the module to use it,
+# or you can customize the responses for your network and/or add more.
+#
+# The way the new helpop system works is simple. You use one or more helpop tags.
+# <helpop key="moo" value="something here">.
+# key is what the user is looking for (i.e. /helpop moo), and value is what they get back
+# (note that it can span multiple lines!).
+# -- w00t 16/dec/2006
+#
+
+<helpop key="start" value=" InspIRCd help system
+ --------------------
+-
+This system provides help for commands and modes.
+Specify your question or a command name as the
+parameter for this command.
+-
+/HELPOP COMMANDS - To see a list of commands
+/HELPOP UMODES - To see a list of user modes
+/HELPOP CHMODES - To see a list of channel modes">
+
+<helpop key="nohelp" value="There is no help for the topic
+You searched for. Please try again.">
+
+<helpop key="commands" value="User Commands
+-------------
+USER NICK QUIT VERSION PING
+PONG ADMIN PRIVMSG INFO TIME
+WHOIS NOTICE JOIN NAMES PART
+KICK MODE TOPIC WHO MOTD
+RULES OPER LIST LUSERS STATS
+USERHOST AWAY ISON SUMMON USERS
+INVITE PASS WHOWAS LINKS MAP
+COMMANDS MODULES
+
+Oper Commands
+-------------
+DIE RESTART KILL REHASH TRACE
+CONNECT SQUIT MODULES KLINE ZLINE
+QLINE GLINE ELINE">
+
+
+<helpop key="umodes" value="User Modes
+----------
+o Is an IRC operator
+i Is invisible to /WHO
+w Can receive wallops messages
+s Can receive server notices
+n [mask] Can receive server notices specified by [mask], or * for all.
+x Cloaked hostname (requires cloaking module)
+h Will receive helpops notification (requires helpop module)">
+
+<helpop key="chmodes" value="Channel Modes
+-------------
+v [nickname] Gives voice to [nickname] (can talk on +m channel)
+h [nickname] Gives halfops to [nickname] (requires halfop module)
+o [nickname] Gives ops to [nickname]
+b [hostmask] Bans [hostmask] on the channel
+a [nickname] Give protected status to [nickname] (+q only)
+q [nickname] Give founder status to [nickname] (ulines only)
+i Make the channel invite only, must /INVITE users
+k [key] Set the channel key (password) to [key]
+l [limit] Set the maximum possible users to [limit]
+m Enable moderation. Only +vo(h) can speak
+n Only users who are members of the channel may message it
+p Make channel private (hide from /LIST)
+s Make channel secret (can't be used at the same time as +p)
+O Channel is IRCops only (can only be set by IRCops)
+t Only halfops and above can change the topic
+Q Only U-Lined servers/nicks can kick
+T Only halfops/ops can send NOTICEs to the channel
+C No CTCPs allowed to the channel
+c mIRC colour codes blocked on the channel
+K No /KNOCK allowed to channel (if knock module is loaded)
+L [channel] If the channel is full, redirect users to [channel]
+N No nickchanges while on the channel
+-------------
+NOTE: A large number of these modes are dependent upon server-side modules
+being loaded by a server/network administrator. The actual modes available
+on your network may be very different to this list. Please consult your
+help channel if you have any questions.">
+
diff --git a/conf/inspircd.motd.example b/conf/inspircd.motd.example
index 06f1e5d66..6653792ac 100644
--- a/conf/inspircd.motd.example
+++ b/conf/inspircd.motd.example
@@ -1 +1,40 @@
- _____ _____ _____ _____ _ |_ _| |_ _| | __ \ / ____| | | | | _ __ ___ _ __ | | | |__) || | __| | | | | '_ \ / __| | '_ \ | | | _ / | | / _` | _| |_ | | | | \__ \ | |_) | _| |_ | | \ \ | |____ | (_| | |_____| |_| |_| |___/ | .__/ |_____| |_| \_\ \_____| \__,_| __________________| |_______________________________ |__________________|_|_______________________________| Putting the ricer in IRCer since 2007 //\ V \ WELCOME TO AN INSPIRCD NETWORK \ \_ If you see this, I am probably new \,'.`-. If I'm not new, my owner is lazy. |\ `. `. ( \ `. `-. _,.-:\ \ \ `. `-._ __..--' ,-';/ \ `. `-. `-..___..---' _.--' ,'/ `. `. `-._ __..--' ,' / `. `-_ ``--..'' _.-' ,' `-_ `-.___ __,--' ,' `-.__ `----""" __.-' `--..____..--' -- To change, see inspircd.motd.example -- / \ / * Web: http://www.inspircd.org \ | * Blog: http://www.inspircd.com | | * IRC: irc.inspircd.org #inspircd | | * Docs: http://www.inspircd.org/wiki | | | | We hope you like this software. Please do | | make sure you put some effort into | | your configuration though so you like it. | | Enjoy. | | | \ -- The InspIRCd Team / ------------------------------------------- \ No newline at end of file
+
+ _____ _____ _____ _____ _
+|_ _| |_ _| | __ \ / ____| | |
+ | | _ __ ___ _ __ | | | |__) || | __| |
+ | | | '_ \ / __| | '_ \ | | | _ / | | / _` |
+ _| |_ | | | | \__ \ | |_) | _| |_ | | \ \ | |____ | (_| |
+|_____| |_| |_| |___/ | .__/ |_____| |_| \_\ \_____| \__,_|
+ __________________| |_______________________________
+ |__________________|_|_______________________________|
+
+ Putting the ricer in IRCer since 2007
+
+ //\
+ V \ WELCOME TO AN INSPIRCD NETWORK
+ \ \_ If you see this, I am probably new
+ \,'.`-. If I'm not new, my owner is lazy.
+ |\ `. `.
+ ( \ `. `-. _,.-:\
+ \ \ `. `-._ __..--' ,-';/
+ \ `. `-. `-..___..---' _.--' ,'/
+ `. `. `-._ __..--' ,' /
+ `. `-_ ``--..'' _.-' ,'
+ `-_ `-.___ __,--' ,'
+ `-.__ `----""" __.-'
+ `--..____..--'
+
+ -- To change, see inspircd.motd.example --
+ / \
+ / * Web: http://www.inspircd.org \
+ | * Blog: http://www.inspircd.com |
+ | * IRC: irc.inspircd.org #inspircd |
+ | * Docs: http://www.inspircd.org/wiki |
+ | |
+ | We hope you like this software. Please do |
+ | make sure you put some effort into |
+ | your configuration though so you like it. |
+ | Enjoy. |
+ | |
+ \ -- The InspIRCd Team /
+ ------------------------------------------- \ No newline at end of file
diff --git a/conf/inspircd.quotes.example b/conf/inspircd.quotes.example
index 12ff74e4b..9b2bc0f6a 100644
--- a/conf/inspircd.quotes.example
+++ b/conf/inspircd.quotes.example
@@ -1 +1,175 @@
-Men are from Mars. Women are from Venus. Computers are from hell Computer /nm./: a device designed to speed and automate errors Hardware /nm./: the part of the computer that you can kick. Maniac /n./ An early computer built by nuts. RAM /abr./: Rarely Adequate Memory. Programmer /n./ A red-eyed, mumbling mammal capable of conversing with inanimate objects Multitasking /adj./ 3 PCs and a chair with wheels Plonk /excl./: The sound a newbie makes as he falls to the bottom of a kill file hURL /n./: a link to a web site that makes you want to puke SUPERCOMPUTER: what it sounded like before you bought it. If it's really a supercomputer, how come the bullets don't bounce off when I shoot it? . The Covert Comic. A computer is like an Old Testament god, with a lot of rules and no mercy. . Joseph Campbell I dropped my computer on my foot! That Megahurtz!! A computer's attention span is as long as it's power cord 586: The average IQ needed to understand a PC Memory is like an orgasm. It's a lot better if you don't have to fake it If it jams, force it. If it breaks, it needed replacing anyway. A bus station is where a bus stops. A train station is where a train stops. On my desk I have a workstation.. Want to come see my HARD DRIVE ? I promise it isn't 3.5 inches and it ain't floppy. . Geek pick-up line. If you torture the data enough, it will confess. . Ronald Coase If you give someone a program, you will frustrate them for a day; if you teach them how to program, you will frustrate them for a lifetime ASCII stupid question, get a stupid ANSI! Use the source, Luke... Programming is an art form that fights back MacOS, Windows, BeOS: they're all just Xerox copies Whenever you think you have a clever programming trick... forget it! Managing senior programmers is like herding cats. . Dave Platt Your program is sick ! Shoot it and put it out of its memory /* You are not expected to understand this */ To define recursion, we must first define recursion ERROR: Computer possessed; Load EXOR.SYS ? [Y/N] Linux is only free if your time is worthless Linux: find out what you've been missing while you've been rebooting Windows NT unzip; strip; touch; finger; mount; fsck; more; yes; unmount; sleep Profanity is the one language all programmers know best It's 5.50 a.m.... Do you know where your stack pointer is? #define QUESTION ((bb) || !(bb)) . Shakespeare The more I C, the less I see. Confucius say: He who play in root, eventually kill tree. Unix is the answer, but only if you phrase the question very carefully C++: Hard to learn and built to stay that way Java is, in many ways, C++-- . Michael Feldman. They don't make bugs like Bunny anymore . Olav Mjelde If debugging is the process of removing software bugs, then programming must be the process of putting them in When the only tool you own is a hammer, every problem you encounter resembles a nail System Error: press F13 to continue... To err is human, but for a real disaster you need a computer Computers make very fast, very accurate mistakes Life would be so much easier if we only had the source code Who is this 'General Failure' and why is he reading my disk? hAS aNYONE sEEN MY cAPSLOCK kEY? InspIRCd, now with excessive ammounts of Cheeze I'm in the computer business, I make Out-Of-Order signs Kevorkian Virus: helps your computer shut down whenever it wants to. [OUT OF QUOTES, PLEASE ORDER MORE] Error, no keyboard . press F1 to continue. Insert Something Funkeh.. err.. There! --> Cannot delete tmp150---3.tmp: There is not enough free disk space. Delete one or more files to free disk space, and then try again File not found. Should I fake it ? (Y/N) The definition of an upgrade: Take old bugs out, put new ones in If it's not on fire, it's a software problem My software never has bugs. It just develops random features It's a little-known fact that the Y1K problem caused the Dark Ages Artificial Intelligence usually beats natural stupidity Making fun of AOL users is like making fun of the kid in the wheel chair Daddy, why doesn't this magnet pick up this floppy disk? Daddy, what does FORMATTING DRIVE C mean? See daddy ? All the keys are in alphabetical order now. If you can't beat your computer at chess, do what I did . try kick-boxing. Enter any 11-digit prime number to continue... ASCII and ye shall receive. The web is a dominatrix. Every where I turn, I see little buttons ordering me to Submit. <FrostyCoolSlug> NO, You cannot dial 999, I'm downloading my mail ;/ 640K ought to be enough for anybody. . Bill Gates, 1981 Windows not found, [P]arty, [C]elebrate, [D]rink? English, the Microsoft of languages... It's been said that Bill Gates named his company after his dick... Ever notice how fast Windows runs ? -- Neither did I If at first you don't succeed, work for Microsoft We are Microsoft. Resistance Is Futile. You Will Be Assimilated "Microsoft Works." . Oxymoron Windows isn't a virus, viruses do something PANIC! buffer = :NickServ WRITE_DB(3). <-- JUST KIDDING! It just keeps going and going and going and going and goi <BANG> All that I know is that nukes are comming from 127.0.0.1 I know all about the irc and the mirc cops. M re ink n ed d, ple s r fil Please refrain from feeding the IRC Operators. Thank you. I know all about mirc stuff, hmm.. I think this channel is experiencing packet loss.. MacDonalds claims Macintosh stole their next idea of the iMac I can't hold her any longer, captain, she's gonna bl.. sorry, got caught up in the moment I recommend purchasing a Cyrix CPU for testing nuclear meltdowns Is it an international rule to have the worst picture possible on your driver license? Have you hugged your services coder, today? Ever wonder why they make the colon flash on alarm clocks? Whats this?.. blue screen with a VXD error?!.. I'VE BEEN NUKED! do-do-bop-doo-doo-do-do-doo.. For those of you who know that song, you have problems.. be wery wery quiet... hunting wabbit... I've been IRC Nuked"Great warrior? War does not make one great." - Yoda "I find your lack of faith.....disturbing." - Darth Vader "I have a bad feeling about this.."--All of the Star Wars characters. Can I upgrade my Hard Drive to a WARP drive? Canadian DOS prompt: EH?\> Canadian DOS: "Yer sure, eh?" [y/n] CONGRESS.SYS Corrupted: Re-boot Washington D.C (Y/n)? I don't have a solution but I admire the problem. Famous Last Words: Trust me. I know what I'm doing. Hey Captain, I just created a black ho-÷p!%$û NO CARRIER I like work ... I can sit and watch it for hours. Access denied--nah nah na nah nah! Bad command. Bad, bad command! Sit! Stay! Staaay.. Error: Keyboard not attached. Press F1 to continue. *grumble* "You're just supposed to sit here?" "Hey, what's this button d..<BOOM>" -W. Crusher "He has become One with Himself!" "He's passed out!" "That too."-B5 For a funny quote, call back later. Famous last words: 'You saw a WHAT around the corner?!' I like work ... I can sit and watch it for hours. If debugging is the process of removing bugs, then programming must be the process of putting them in. Copywight 1994 Elmer Fudd. All wights wesewved. Cannot find REALITY.SYS. Universe halted. BUFFERS=20 FILES=15 2nd down, 4th quarter, 5 yards to go! My software never has bugs. It just develops random features. Why doesn't DOS ever say 'EXCELLENT command or filename!? Who's General Failure & why's he reading my disk? Shell to DOS... Come in DOS, do you copy? Shell to DOS... Computing Definition - Network-Admin: Primary person who just got set up for the blame of the system crash. An expert is a person who has made all the mistakes which can be made in a very narrow field. Famous last words: This is the safe way to do it....... Famous Last Words: Trust me. I know what I'm doing. Clinton, "I didn't say that - er, well - yes, but I didn't mean..." CLINTON LEGACY??...even Pharaoh had only ten plagues... IBM I Bought McIntosh IBM I Bring Manuals IBM I've Been Moved IBM Idolized By Management IBM Impenetrable Brain Matter IBM Imperialism By Marketing IBM Incorrigible Boisterous Mammoth IBM Inertia Breeds Mediocrity IBM Ingenuity Becomes Mysterious IBM Ingrained Batch Mentality IBM Innovation By Management IBM Insipid Belligerent Mossbacks IBM Insipidly Bankrolling Millions IBM Inspect Before Multiusing IBM Install Bigger Memory IBM Institution By Machiavelli IBM Insultingly Boring Merchandisers IBM Intellectuals Being Moronized IBM Intelligence Belittling Meaning IBM Intimidated, Buffaloed Management IBM Into Building Money IBM Intolerant of Beards & Moustaches IBM Invest Before Multi-tasking IBM Investigate Baffling Malodor IBM Irresponsible Behave Multinational IBM It Beats Mattel IBM It's a Big Mess IBM It's Better Manually IBM Itty Bitty Machine IBM Institute for Black Magic 100,000 lemmings can't be wrong. Murphy's Eighth Law: If everything seems to be going well, you have obviously overlooked something. Rules of the game: Do not believe in miracles - rely on them. Rules of the game: Any given program, once running, is obsolete. Computing Definition - Error: What someone else has made when they disagree with your computer output. Backup not found: (A)bort (R)etry (P)anic WinErr 653: Multitasking attempted - system confused. Cannot join #real_life (invite only) "Unfortunatly, no one can be told what the Matrix is. You have to see it for yourself." - Matrix "Reality is a thing of the past" - Matrix "The future will not be user friendly" - Matrix "The general idea in chat is to make yourself understandable... ..." - Peer "heh i am talkin to someone...she not dead...yet anyways" - Stinky \ No newline at end of file
+Men are from Mars. Women are from Venus. Computers are from hell
+Computer /nm./: a device designed to speed and automate errors
+Hardware /nm./: the part of the computer that you can kick.
+Maniac /n./ An early computer built by nuts.
+RAM /abr./: Rarely Adequate Memory.
+Programmer /n./ A red-eyed, mumbling mammal capable of conversing with inanimate objects
+Multitasking /adj./ 3 PCs and a chair with wheels
+Plonk /excl./: The sound a newbie makes as he falls to the bottom of a kill file
+hURL /n./: a link to a web site that makes you want to puke
+SUPERCOMPUTER: what it sounded like before you bought it.
+If it's really a supercomputer, how come the bullets don't bounce off when I shoot it? . The Covert Comic.
+A computer is like an Old Testament god, with a lot of rules and no mercy. . Joseph Campbell
+I dropped my computer on my foot! That Megahurtz!!
+A computer's attention span is as long as it's power cord
+586: The average IQ needed to understand a PC
+Memory is like an orgasm. It's a lot better if you don't have to fake it
+If it jams, force it. If it breaks, it needed replacing anyway.
+A bus station is where a bus stops. A train station is where a train stops. On my desk I have a workstation..
+Want to come see my HARD DRIVE ? I promise it isn't 3.5 inches and it ain't floppy. . Geek pick-up line.
+If you torture the data enough, it will confess. . Ronald Coase
+If you give someone a program, you will frustrate them for a day; if you teach them how to program, you will frustrate them for a lifetime
+ASCII stupid question, get a stupid ANSI!
+Use the source, Luke...
+Programming is an art form that fights back
+MacOS, Windows, BeOS: they're all just Xerox copies
+Whenever you think you have a clever programming trick... forget it!
+Managing senior programmers is like herding cats. . Dave Platt
+Your program is sick ! Shoot it and put it out of its memory
+/* You are not expected to understand this */
+To define recursion, we must first define recursion
+ERROR: Computer possessed; Load EXOR.SYS ? [Y/N]
+Linux is only free if your time is worthless
+Linux: find out what you've been missing while you've been rebooting Windows NT
+unzip; strip; touch; finger; mount; fsck; more; yes; unmount; sleep
+Profanity is the one language all programmers know best
+It's 5.50 a.m.... Do you know where your stack pointer is?
+#define QUESTION ((bb) || !(bb)) . Shakespeare
+The more I C, the less I see.
+Confucius say: He who play in root, eventually kill tree.
+Unix is the answer, but only if you phrase the question very carefully
+C++: Hard to learn and built to stay that way
+Java is, in many ways, C++-- . Michael Feldman.
+They don't make bugs like Bunny anymore . Olav Mjelde
+If debugging is the process of removing software bugs, then programming must be the process of putting them in
+When the only tool you own is a hammer, every problem you encounter resembles a nail
+System Error: press F13 to continue...
+To err is human, but for a real disaster you need a computer
+Computers make very fast, very accurate mistakes
+Life would be so much easier if we only had the source code
+Who is this 'General Failure' and why is he reading my disk?
+hAS aNYONE sEEN MY cAPSLOCK kEY?
+InspIRCd, now with excessive ammounts of Cheeze
+I'm in the computer business, I make Out-Of-Order signs
+Kevorkian Virus: helps your computer shut down whenever it wants to.
+ [OUT OF QUOTES, PLEASE ORDER MORE]
+Error, no keyboard . press F1 to continue.
+Insert Something Funkeh.. err.. There! -->
+Cannot delete tmp150---3.tmp: There is not enough free disk space. Delete one or more files to free disk space, and then try again
+File not found. Should I fake it ? (Y/N)
+The definition of an upgrade: Take old bugs out, put new ones in
+If it's not on fire, it's a software problem
+My software never has bugs. It just develops random features
+It's a little-known fact that the Y1K problem caused the Dark Ages
+Artificial Intelligence usually beats natural stupidity
+Making fun of AOL users is like making fun of the kid in the wheel chair
+Daddy, why doesn't this magnet pick up this floppy disk?
+Daddy, what does FORMATTING DRIVE C mean?
+See daddy ? All the keys are in alphabetical order now.
+If you can't beat your computer at chess, do what I did . try kick-boxing.
+Enter any 11-digit prime number to continue...
+ASCII and ye shall receive.
+The web is a dominatrix. Every where I turn, I see little buttons ordering me to Submit.
+<FrostyCoolSlug> NO, You cannot dial 999, I'm downloading my mail ;/
+640K ought to be enough for anybody. . Bill Gates, 1981
+Windows not found, [P]arty, [C]elebrate, [D]rink?
+English, the Microsoft of languages...
+It's been said that Bill Gates named his company after his dick...
+Ever notice how fast Windows runs ? -- Neither did I
+If at first you don't succeed, work for Microsoft
+We are Microsoft. Resistance Is Futile. You Will Be Assimilated
+"Microsoft Works." . Oxymoron
+Windows isn't a virus, viruses do something
+PANIC! buffer = :NickServ WRITE_DB(3). <-- JUST KIDDING!
+It just keeps going and going and going and going and goi <BANG>
+All that I know is that nukes are comming from 127.0.0.1
+I know all about the irc and the mirc cops.
+M re ink n ed d, ple s r fil
+Please refrain from feeding the IRC Operators. Thank you.
+I know all about mirc stuff, hmm.. I think this channel is experiencing packet loss..
+MacDonalds claims Macintosh stole their next idea of the iMac
+I can't hold her any longer, captain, she's gonna bl.. sorry, got caught up in the moment
+I recommend purchasing a Cyrix CPU for testing nuclear meltdowns
+Is it an international rule to have the worst picture possible on your driver license?
+Have you hugged your services coder, today?
+Ever wonder why they make the colon flash on alarm clocks?
+Whats this?.. blue screen with a VXD error?!.. I'VE BEEN NUKED!
+do-do-bop-doo-doo-do-do-doo.. For those of you who know that song, you have problems..
+be wery wery quiet... hunting wabbit...
+I've been IRC Nuked"Great warrior? War does not make one great." - Yoda
+"I find your lack of faith.....disturbing." - Darth Vader
+"I have a bad feeling about this.."--All of the Star Wars characters.
+Can I upgrade my Hard Drive to a WARP drive?
+Canadian DOS prompt: EH?\>
+Canadian DOS: "Yer sure, eh?" [y/n]
+CONGRESS.SYS Corrupted: Re-boot Washington D.C (Y/n)?
+I don't have a solution but I admire the problem.
+Famous Last Words: Trust me. I know what I'm doing.
+Hey Captain, I just created a black ho-÷p!%$û NO CARRIER
+I like work ... I can sit and watch it for hours.
+Access denied--nah nah na nah nah!
+Bad command. Bad, bad command! Sit! Stay! Staaay..
+Error: Keyboard not attached. Press F1 to continue.
+*grumble* "You're just supposed to sit here?"
+"Hey, what's this button d..<BOOM>" -W. Crusher
+"He has become One with Himself!" "He's passed out!" "That too."-B5
+For a funny quote, call back later.
+Famous last words: 'You saw a WHAT around the corner?!'
+I like work ... I can sit and watch it for hours.
+If debugging is the process of removing bugs, then programming must be the process of putting them in.
+Copywight 1994 Elmer Fudd. All wights wesewved.
+Cannot find REALITY.SYS. Universe halted.
+BUFFERS=20 FILES=15 2nd down, 4th quarter, 5 yards to go!
+My software never has bugs. It just develops random features.
+Why doesn't DOS ever say 'EXCELLENT command or filename!?
+Who's General Failure & why's he reading my disk?
+Shell to DOS... Come in DOS, do you copy? Shell to DOS...
+Computing Definition - Network-Admin: Primary person who just got set up for the blame of the system crash.
+An expert is a person who has made all the mistakes which can be made in a very narrow field.
+Famous last words: This is the safe way to do it.......
+Famous Last Words: Trust me. I know what I'm doing.
+Clinton, "I didn't say that - er, well - yes, but I didn't mean..."
+CLINTON LEGACY??...even Pharaoh had only ten plagues...
+IBM I Bought McIntosh
+IBM I Bring Manuals
+IBM I've Been Moved
+IBM Idolized By Management
+IBM Impenetrable Brain Matter
+IBM Imperialism By Marketing
+IBM Incorrigible Boisterous Mammoth
+IBM Inertia Breeds Mediocrity
+IBM Ingenuity Becomes Mysterious
+IBM Ingrained Batch Mentality
+IBM Innovation By Management
+IBM Insipid Belligerent Mossbacks
+IBM Insipidly Bankrolling Millions
+IBM Inspect Before Multiusing
+IBM Install Bigger Memory
+IBM Institution By Machiavelli
+IBM Insultingly Boring Merchandisers
+IBM Intellectuals Being Moronized
+IBM Intelligence Belittling Meaning
+IBM Intimidated, Buffaloed Management
+IBM Into Building Money
+IBM Intolerant of Beards & Moustaches
+IBM Invest Before Multi-tasking
+IBM Investigate Baffling Malodor
+IBM Irresponsible Behave Multinational
+IBM It Beats Mattel
+IBM It's a Big Mess
+IBM It's Better Manually
+IBM Itty Bitty Machine
+IBM Institute for Black Magic
+100,000 lemmings can't be wrong.
+Murphy's Eighth Law: If everything seems to be going well, you have obviously overlooked something.
+Rules of the game: Do not believe in miracles - rely on them.
+Rules of the game: Any given program, once running, is obsolete.
+Computing Definition - Error: What someone else has made when they disagree with your computer output.
+Backup not found: (A)bort (R)etry (P)anic
+WinErr 653: Multitasking attempted - system confused.
+Cannot join #real_life (invite only)
+"Unfortunatly, no one can be told what the Matrix is. You have to see it for yourself." - Matrix
+"Reality is a thing of the past" - Matrix
+"The future will not be user friendly" - Matrix
+"The general idea in chat is to make yourself understandable... ..." - Peer
+"heh i am talkin to someone...she not dead...yet anyways" - Stinky
diff --git a/conf/inspircd.rules.example b/conf/inspircd.rules.example
index 100db84d0..e51f0afd9 100644
--- a/conf/inspircd.rules.example
+++ b/conf/inspircd.rules.example
@@ -1 +1,3 @@
-This is the InspIRCd rules file. Place any network or server rules here :) \ No newline at end of file
+This is the InspIRCd rules file.
+
+Place any network or server rules here :)
diff --git a/configure b/configure
index 28cf97967..0e9511b87 100755
--- a/configure
+++ b/configure
@@ -1 +1,1802 @@
-#!/usr/bin/perl ################################################### # InspIRCd Configuration Script # # Copyright 2002-2007 The InspIRCd Development Team # http://www.inspircd.org/wiki/index.php/Credits # # Licensed under GPL, please see the COPYING file # for more information # # $Id$ # ################################################### require 5.6.0; use Socket; use Cwd; use Getopt::Long; # Utility functions for our buildsystem use make::utilities; use make::configure; use make::gnutlscert; use make::opensslcert; GetOptions ( 'enable-gnutls' => \$opt_use_gnutls, 'rebuild' => \$opt_rebuild, 'enable-openssl' => \$opt_use_openssl, 'disable-interactive' => \$opt_nointeractive, 'with-nick-length=i' => \$opt_nick_length, 'with-channel-length=i' => \$opt_chan_length, 'with-max-clients=i' => \$opt_maxclients, 'enable-ports' => \$opt_ports, 'enable-epoll' => \$opt_epoll, 'enable-kqueue' => \$opt_kqueue, 'disable-ports' => \$opt_noports, 'disable-epoll' => \$opt_noepoll, 'disable-kqueue' => \$opt_nokqueue, 'enable-ipv6' => \$opt_ipv6, 'enable-remote-ipv6' => \$opt_ipv6links, 'disable-remote-ipv6' => \$opt_noipv6links, 'with-cc=s' => \$opt_cc, 'with-ident-length=i' => \$opt_ident, 'with-quit-length=i' => \$opt_quit, 'with-topic-length=i' => \$opt_topic, 'with-maxbuf=i' => \$opt_maxbuf, 'with-kick-length=i' => \$opt_kick, 'with-gecos-length=i' => \$opt_gecos, 'with-away-length=i' => \$opt_away, 'with-max-modes=i' => \$opt_modes, 'prefix=s' => \$opt_base_dir, 'config-dir=s' => \$opt_config_dir, 'module-dir=s' => \$opt_module_dir, 'binary-dir=s' => \$opt_binary_dir, 'library-dir=s' => \$opt_library_dir, 'disable-debuginfo' => sub { $opt_disable_debug = 1 }, 'help' => sub { showhelp(); }, 'modupdate' => sub { modupdate(); }, 'update' => sub { update(); }, 'svnupdate' => sub { svnupdate(); }, 'clean' => sub { clean(); }, ); my $non_interactive = ( (defined $opt_library_dir) || (defined $opt_base_dir) || (defined $opt_config_dir) || (defined $opt_module_dir) || (defined $opt_base_dir) || (defined $opt_binary_dir) || (defined $opt_nointeractive) || (defined $opt_away) || (defined $opt_gecos) || (defined $opt_kick) || (defined $opt_maxclients) || (defined $opt_modes) || (defined $opt_topic) || (defined $opt_quit) || (defined $opt_ident) || (defined $opt_cc) || (defined $opt_ipv6) || (defined $opt_ipv6links) || (defined $opt_noipv6links) || (defined $opt_kqueue) || (defined $opt_epoll) || (defined $opt_ports) || (defined $opt_maxchans) || (defined $opt_opermaxchans) || (defined $opt_chan_length) || (defined $opt_nick_length) || (defined $opt_use_openssl) || (defined $opt_nokqueue) || (defined $opt_noepoll) || (defined $opt_noports) || (defined $opt_maxbuf) || (defined $opt_use_gnutls) ); my $interactive = !$non_interactive; chomp($topdir = getcwd()); $this = resolve_directory($topdir); # PWD, Regardless. @modlist = (); # Declare for Module List.. %config = (); # Initiate Configuration Hash.. $config{ME} = resolve_directory($topdir); # Present Working Directory $config{BASE_DIR} = $config{ME}; if (defined $opt_base_dir) { $config{BASE_DIR} = $opt_base_dir; } $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{LIBRARY_DIR} = resolve_directory($config{BASE_DIR}."/lib"); # Library 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_library_dir) { $config{LIBRARY_DIR} = $opt_library_dir; } chomp($config{HAS_GNUTLS} = `libgnutls-config --version 2>/dev/null | cut -c 1,2,3`); # GNUTLS Version. chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`); # Openssl version chomp($gnutls_ver = $config{HAS_GNUTLS}); chomp($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"; } # no, let's not change these. $config{OPTIMITEMP} = "0"; # Default Optimisation Value if (!defined $opt_disable_debug) { $config{OPTIMISATI} = "-g1"; # Optimisation Flag } else { $config{OPTIMISATI} = "-O2"; # DEBUGGING OFF! } $config{NICK_LENGT} = "31"; # Default Nick Length if (defined $opt_nick_length) { $config{NICK_LENGT} = $opt_nick_length; } $config{CHAN_LENGT} = "64"; # Default Channel Name Length if (defined $opt_chan_length) { $config{CHAN_LENGT} = $opt_chan_length; } $config{MAXI_MODES} = "20"; # Default Max. Number of Modes set at once. if (defined $opt_modes) { $config{MAXI_MODES} = $opt_modes; } $config{HAS_STRLCPY} = "false"; # strlcpy Check. $config{HAS_STDINT} = "false"; # stdint.h check $config{USE_KQUEUE} = "y"; # kqueue enabled if (defined $opt_kqueue) { $config{USE_KQUEUE} = "y"; } if (defined $opt_nokqueue) { $config{USE_KQUEUE} = "n"; } $config{USE_EPOLL} = "y"; # epoll enabled if (defined $opt_epoll) { $config{USE_EPOLL} = "y"; } if (defined $opt_noepoll) { $config{USE_EPOLL} = "n"; } $config{USE_PORTS} = "y"; # epoll enabled if (defined $opt_ports) { $config{USE_PORTS} = "y"; } if (defined $opt_noports) { $config{USE_PORTS} = "n"; } $config{IPV6} = "n"; # IPv6 support (experimental) if (defined $opt_ipv6) { $config{IPV6} = "y"; } $config{SUPPORT_IP6LINKS} = "y"; # IPv4 supporting IPv6 links (experimental) if (defined $opt_ipv6links) { $config{SUPPORT_IP6LINKS} = "y"; } if (defined $opt_noipv6links) { $config{SUPPORT_IP6LINKS} = "n"; } $config{STATIC_LINK} = "no"; # are doing static modules? chomp($config{MAX_CLIENT_T} = `sh -c \"ulimit -n\"`); # FD Limit chomp($config{MAX_DESCRIPTORS} = `sh -c \"ulimit -n\"`); # Hard FD Limit chomp($config{GCCVER} = `g++ -dumpversion | cut -c 1`); # Major GCC Version $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. $config{EXTRA_DIR} = ""; # Is empty. if ($config{OSNAME} =~ /darwin/i) { $config{IS_DARWIN} = "YES"; $config{STARTSCRIPT} = "org.inspircd.plist"; # start script for OSX. $config{DESTINATION} = "LAUNCHDPATH"; # Is OSX target. $config{EXTRA_DIR} = " launchd_dir"; # Is OSX specific path. } $config{CC} = "g++"; # C++ compiler if (defined $opt_cc) { $config{CC} = $opt_cc; } $exec = $config{CC} . " -dumpversion | cut -c 1"; chomp($config{GCCVER} = `$exec`); # Major GCC Version $config{MAKEORDER} = "ircd mods"; # build order $config{STATICLIBS} = ""; # library archive path $config{MAX_IDENT} = "12"; # max ident size $config{MAX_QUIT} = "255"; # max quit message size $config{MAX_TOPIC} = "307"; # max topic size $config{MAX_KICK} = "255"; # max kick message size $config{MAX_GECOS} = "128"; # max GECOS size $config{MAX_AWAY} = "200"; # max AWAY size $config{MAXBUF} = "512"; # Max buffer size if (defined $opt_ident) { $config{MAX_IDENT} = $opt_ident; } if (defined $opt_quit) { $config{MAX_QUIT} = $opt_quit; } if (defined $opt_topic) { $config{MAX_TOPIC} = $opt_topic; } if (defined $opt_kick) { $config{MAX_KICK} = $opt_kick; } if (defined $opt_gecos) { $config{MAX_GECOS} = $opt_gecos; } if (defined $opt_away) { $config{MAX_AWAY} = $opt_away; } $config{HAS_OPENSSL} =~ /^([-[:digit:].]+)([a-z])?(\-[a-z][0-9])?$/; $config{HAS_OPENSSL} = $1; if ($config{GCCVER} eq "") { print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n"; exit; } # Minihack! Convert Cygwin to 'Cyg-Static' so i can # Keep my dynamic module experiments here for later # consideration! if ($config{OSNAME} =~ /CYGWIN/i) { $config{OSNAME} = "CYG-STATIC"; } if (!$config{MAX_CLIENT_T}) { $config{MAX_CLIENT_T} = 1024; # Set a reasonable 'Default' $fd_scan_fail = "true"; # Used Later } # Get and Set some important vars.. getmodules(); sub clean { system("rm -rf .config.cache"); } 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"; getosflags(); if ($opt_disable_debug == 1) { print "Disabling debug information (-g).\n"; $config{OPTIMISATI} = ""; getosflags(); } $has_epoll = $config{HAS_EPOLL}; $has_ports = $config{HAS_PORTS}; $has_kqueue = $config{HAS_KQUEUE}; writefiles(1); makecache(); print "Complete.\n"; exit; } }; if ($@) { print "Configure update failed: $@\n"; } exit; } sub modupdate { 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"; getosflags(); $has_epoll = $config{HAS_EPOLL}; $has_ports = $config{HAS_PORTS}; $has_kqueue = $config{HAS_KQUEUE}; writefiles(0); makecache(); print "Complete.\n"; exit; } }; if ($@) { print "Module update failed: $@\n"; } exit; } sub svnupdate { my $fail = 0; open(FH,"<.svn/entries") or $fail = 1; if ($fail) { print "This is not an SVN copy of InspIRCd.\n"; exit; } else { close(FH); } system("svn update"); system("perl configure -update"); if (defined $opt_rebuild) { system("make install"); } exit; } print "Running non-interactive configure...\n" unless $interactive; print "Checking for cache from previous configure... "; print ((getcache() eq "true") ? "found\n" : "not found\n"); print "Checking operating system version... "; print getosflags() . "\n"; if (defined $opt_maxclients) { $config{MAX_CLIENT} = $opt_maxclients; } if (!$config{MAX_CLIENT}) { # If the cache hasn't set the max clients, copy the variable of MAX_CLIENT_T, this # allows us to keep _T for testing purposes. (ie. "Are you sure you want to go # higher than the found value" :)) $config{MAX_CLIENT} = $config{MAX_CLIENT_T}; } printf "Checking if stdint.h exists... "; $config{HAS_STDINT} = "true"; my $fail = 0; open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false"; if ($config{HAS_STDINT} eq "true") { close(STDINT); } print "yes\n" if $config{HAS_STDINT} eq "true"; print "no\n" if $config{HAS_STDINT} eq "false"; printf "Checking if strlcpy exists... "; # Perform the strlcpy() test.. $config{HAS_STRLCPY} = "false"; my $fail = 0; open(STRLCPY, "</usr/include/string.h") or $fail = 1; if (!$fail) { while (chomp($line = <STRLCPY>)) { # try and find the delcaration of: # size_t strlcpy(...) if ($line =~ /size_t(\0x9|\s)+strlcpy/) { $config{HAS_STRLCPY} = "true"; } } close(STRLCPY); } print "yes\n" if $config{HAS_STRLCPY} eq "true"; print "no\n" if $config{HAS_STRLCPY} eq "false"; printf "Checking if kqueue exists... "; $has_kqueue = 0; $fail = 0; open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1; if (!$fail) { while (chomp($line = <KQUEUE>)) { # try and find the delcaration of: # int kqueue(void); if ($line =~ /int(\0x9|\s)+kqueue/) { $has_kqueue = 1; } } close(KQUEUE); } print "yes\n" if $has_kqueue == 1; print "no\n" if $has_kqueue == 0; printf "Checking if epoll exists... "; $has_epoll = 0; $fail = 0; open(EPOLL, "</usr/include/sys/epoll.h") or $fail = 1; if (!$fail) { $has_epoll = 1; close(EPOLL); } if ($has_epoll) { my $kernel = `uname -r`; chomp($kernel); if (($kernel =~ /^2\.0\./) || ($kernel =~ /^2\.2\./) || ($kernel =~ /^2\.4\./)) { $has_epoll = 0; } } print "yes\n" if $has_epoll == 1; print "no\n" if $has_epoll == 0; printf "Checking if Solaris I/O completion ports are available... "; $has_ports = 0; my $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; } } print "yes\n" if $has_ports == 1; print "no\n" if $has_ports == 0; if (($config{OSNAME} =~ /CYGWIN/) || ($config{OSNAME} eq "CYG-STATIC")) { $config{HAS_STRLCPY} = "true"; } $config{HAS_EPOLL} = $has_epoll; $config{HAS_KQUEUE} = $has_kqueue; printf "Checking for libgnutls... "; if (($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS} >= 1.2) || ($config{HAS_GNUTLS} eq "y"))) { print "yes\n"; $config{HAS_GNUTLS} = "y"; } else { print "no\n"; $config{HAS_GNUTLS} = "n"; } printf "Checking for openssl... "; if (($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL} >= 0.8) || ($config{HAS_OPENSSL} eq "y"))) { print "yes\n"; $config{HAS_OPENSSL} = "y"; } else { print "no\n"; $config{HAS_OPENSSL} = "n"; } ################################################################################ # BEGIN INTERACTIVE PART # ################################################################################ # Clear the Screen.. if ($interactive) { system("clear"); $wholeos = $^O; my $rev = getrevision(); # Display Introduction Message.. print " Welcome to the \033[1mInspIRCd\033[0m Configuration program! (\033[1minteractive mode\033[0m) \033[1mPackage maintainers: Type ./configure --help for non-interactive help\033[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 \033[1m<RETURN>\033[0m to accept the default for any option, or enter a new value. Please note: You will \033[1mHAVE\033[0m to read the docs dir, otherwise you won't have a config file! Your operating system is: \033[1;32m$config{OSNAME}\033[0m ($wholeos) Maximum file descriptors: \033[1;32m$config{MAX_CLIENT_T}\033[0m Your InspIRCd revision ID is \033[1;32mr$rev\033[0m"; if ($rev eq "r0") { print " (Non-SVN build)"; } print ".\n\n"; $config{CHANGE_COMPILER} = "n"; print "I have detected the following compiler: \033[1;32m$config{CC}\033[0m (version \033[1;32m$config{GCCVER}.x\033[0m)\n"; while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) { print "\033[1;32mIMPORTANT!\033[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 "[\033[1;32m$config{CC}\033[0m] -> "; chomp($config{CC} = <STDIN>); if ($config{CC} eq "") { $config{CC} = "g++"; } chomp($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 print "Queried compiler: \033[1;32m$config{CC}\033[0m (version \033[1;32m$config{GCCVER}.x\033[0m)\n"; if ($config{GCCVER} < 3) { print "\033[1;32mGCC 2.x WILL NOT WORK!\033[0m. Let's try that again, shall we?\n"; } } else { print "\033[1;32mWARNING!\033[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{BINARY_DIR} = resolve_directory($config{BASE_DIR}."/bin"); # Binary Directory $config{LIBRARY_DIR} = resolve_directory($config{BASE_DIR}."/lib"); # Library 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 the IRCd libraries to be placed", "LIBRARY_DIR"); 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 ($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 ($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"; } $chose_hiperf = (($config{USE_EPOLL} eq "y") || ($config{USE_KQUEUE} eq "y") || ($config{USE_PORTS} eq "y")); if (!$chose_hiperf) { print "No high-performance socket engines are available, or you chose\n"; print "not to enable one. Defaulting to select() engine.\n\n"; } yesno(IPV6,"Would you like to build InspIRCd with IPv6 support?"); print "\n"; if ($config{IPV6} eq "y") { print "You have chosen to build an \033[1;32mIPV6-enabled\033[0m server.\nTo accept IPV4 users, you can still use IPV4 addresses\nin your port bindings..\n\n"; $config{SUPPORT_IP6LINKS} = "y"; } else { yesno(SUPPORT_IP6LINKS,"You have chosen to build an \033[1;32mIPV4-only\033[0m server.\nWould you like to enable support for linking to IPV6-enabled\nInspIRCd servers?\nIf you are using a recent operating\nsystem and are unsure, answer yes.\nIf you answer 'no' here, your InspIRCd server will be unable\nto parse IPV6 addresses (e.g. for CIDR bans)"); print "\n"; } if (($config{HAS_GNUTLS} eq "y") && ($config{HAS_OPENSSL} eq "y")) { print "I have detected both \033[1;32mGnuTLS\033[0m and \033[1;32mOpenSSL\033[0m on your system.\n"; print "I will default to GnuTLS. If you wish to use OpenSSL\n"; print "instead, you should enable the OpenSSL module yourself\n"; print "by copying it from src/modules/extra to src/modules.\n\n"; print "Detected GnuTLS version: \033[1;32m" . $gnutls_ver . "\033[0m\n"; print "Detected OpenSSL version: \033[1;32m" . $openssl_ver . "\033[0m\n\n"; } if ($config{HAS_GNUTLS} eq "y") { yesno(USE_GNUTLS, "Would you like to enable SSL Support?"); if ($config{USE_GNUTLS} eq "y") { print "\nUsing GnuTLS SSL module.\n"; } } elsif ($config{HAS_OPENSSL} eq "y") { yesno(USE_OPENSSL, "Would you like to enable SSL Support?"); 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 if\nyou intend to use OpenSSL, or that GnuTLS is in your path if you intend\nto use GnuTLS.\n\n"; } print "\nThe following questions will ask you for various figures relating\n"; print "To your IRCd install. Please note that these should usually be left\n"; print "as defaults unless you have a real reason to change them. If they\n"; print "changed, then the values must be identical on all servers on your\n"; print "network, or malfunctions and/or crashes may occur, with the exception\n"; print "of the 'maximum number of clients' setting which may be different on\n"; print "different servers on the network.\n\n"; # File Descriptor Settings.. promptnumeric("number of clients at any one time", "MAX_CLIENT_T"); $config{MAX_CLIENT} = $config{MAX_CLIENT_T}; $config{MAX_DESCRIPTORS} = $config{MAX_CLIENT_T}; promptnumeric("length of nicknames", "NICK_LENGT"); promptnumeric("length of channel names", "CHAN_LENGT"); promptnumeric("number of mode changes in one line", "MAXI_MODES"); promptnumeric("length of an ident (username)", "MAX_IDENT"); promptnumeric("length of a quit message", "MAX_QUIT"); promptnumeric("length of a channel topic", "MAX_TOPIC"); promptnumeric("length of a kick message", "MAX_KICK"); promptnumeric("length of a GECOS (real name)", "MAX_GECOS"); promptnumeric("length of an away message", "MAX_AWAY"); } dumphash(); if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y")) { print "Sorry, but i couldn't detect gnutls. Make sure gnutls-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 openssl is in your path.\n"; exit(0); } if ($config{USE_GNUTLS} eq "y") { $failed = 0; open(TMP, "<src/modules/m_ssl_gnutls.cpp") or $failed = 1; close(TMP); if ($failed) { print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n"; chdir("src/modules"); system("ln -s extra/m_ssl_gnutls.cpp"); chdir("../.."); } getmodules(); if ($interactive) { $failed = 0; open(TMP, "<$config{CONFIG_DIR}/key.pem") or $failed = 1; close(TMP); open(TMP, "<$config{CONFIG_DIR}/cert.pem") or $failed = 1; close(TMP); if ($failed) { print "SSL Certificates Not found, Generating.. \n\n ************************************************************* * Generating the Private Key may take some time, go grab a * * Coffee. Even better, to generate some more entropy if it * * is taking a while, open another console and type du / a * * few times and get that HD going :) Then answer the * * Questions which follow. If you are unsure, just hit enter * *************************************************************\n\n"; make_gnutls_cert() or $failed = 1; if (!$failed) { print "\nCertificate generation complete, copying to config directory... "; system("mv key.pem $config{CONFIG_DIR}/key.pem"); system("mv cert.pem $config{CONFIG_DIR}/cert.pem"); print "Done.\n\n"; } else { print "\n\033[1;32mCertificate generation failed!\033[0m\n\n"; } } else { print "SSL Certificates found, skipping.\n\n"; } } else { print "Skipping SSL certificate generation\nin non-interactive mode.\n\n"; } } elsif ($config{USE_OPENSSL} eq "y") { $failed = 0; open(TMP, "<src/modules/m_ssl_openssl.cpp") or $failed = 1; close(TMP); if ($failed) { print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n"; chdir("src/modules"); system("ln -s extra/m_ssl_openssl.cpp"); chdir("../.."); } getmodules(); $failed = 0; if ($interactive) { open(TMP, "<$config{CONFIG_DIR}/key.pem") or $failed = 1; close(TMP); open(TMP, "<$config{CONFIG_DIR}/cert.pem") or $failed = 1; close(TMP); if ($failed) { 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... "; system("mv key.pem $config{CONFIG_DIR}/key.pem"); system("mv cert.pem $config{CONFIG_DIR}/cert.pem"); system("mv dhparams.pem $config{CONFIG_DIR}/dhparams.pem"); print "Done.\n\n"; } else { print "SSL Certificates found, skipping.\n\n" } } else { print "Skipping SSL certificate generation\nin non-interactive mode.\n\n"; } } if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) { print "Skipping SSL Certificate generation, SSL support is not available.\n\n"; } getosflags(); writefiles(1); makecache(); print "\n\n"; print "To build your server with these settings, please type '\033[1;32m$config{MAKEPROG}\033[0m' now.\n"; if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) { print "Please remember that to enable \033[1;32mSSL support\033[0m you must\n"; print "load the required modules in your config. This configure process\n"; print "has just prepared these modules to be compiled for you, and has not\n"; print "configured them to be compiled into the core of the ircd.\n"; } print "*** \033[1;32mRemember to edit your configuration files!!!\033[0m ***\n\n\n"; if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) { print "\033[1;32mWARNING!\033[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"; } if ($config{GCCVER} < "3") { print <<FOO2; \033[1;32mWARNING!\033[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 } ################################################################################ # HELPER FUNCTIONS # ################################################################################ sub getcache { # Retrieves the .config.cache file, and loads values into the main config hash. open(CACHE, ".config.cache") or return undef; 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(CONFIG); return "true"; } sub makecache { # Dump the contents of %config print "Writing \033[1;32mcache file\033[0m for future ./configures ...\n"; open(FILEHANDLE, ">.config.cache"); foreach $key (keys %config) { print FILEHANDLE "$key=\"$config{$key}\"\n"; } close(FILEHANDLE); } sub dir_check { my ($desc, $hash_key) = @_; my $complete = 0; while (!$complete) { print "In what directory $desc?\n"; print "[\033[1;32m$config{$hash_key}\033[0m] -> "; chomp($var = <STDIN>); if ($var eq "") { $var = $config{$hash_key}; } if ($var =~ /^\~\/(.+)$/) { # Convert it to a full path.. $var = resolve_directory($ENV{HOME} . "/" . $1); } elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/")) { # Assume relative Path was given.. fill in the rest. $var = $this . "/$var"; } $var = resolve_directory($var); if (! -e $var) { print "$var does not exist. Create it?\n[\033[1;32my\033[0m] "; chomp($tmp = <STDIN>); if (($tmp eq "") || ($tmp =~ /^y/i)) { # Attempt to Create the Dir.. system("mkdir -p \"$var\" >> /dev/null 2>&1"); $chk = system("mkdir -p \"$var\" >> /dev/null 2>&1") / 256; if ($chk != 0) { 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"; } } sub getosflags { $config{LDLIBS} = "-lstdc++"; $config{FLAGS} = "-fno-strict-aliasing -fPIC -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}"; $config{DEVELOPER} = "-fno-strict-aliasing -fPIC -Wall -Woverloaded-virtual -Wno-deprecated -g"; $SHARED = "-Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared"; $config{MAKEPROG} = "make"; if ($config{OSNAME} =~ /darwin/i) { $config{FLAGS} = "-DDARWIN -frtti -fPIC -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}"; $SHARED = "-bundle -twolevel_namespace -undefined dynamic_lookup"; $config{LDLIBS} = "-ldl -lstdc++"; } if ($config{OSNAME} =~ /OpenBSD/i) { $config{MAKEPROG} = "gmake"; chomp($foo = `eg++ -dumpversion | cut -c 1`); # theyre running the package version of gcc (eg++)... detect it and set up its version numbers. # if theyre not running this, configure lets the build continue but they probably wont manage to # compile as this standard version is 2.95.3! if ($foo ne "") { $config{CC} = "eg++"; chomp($config{GCCVER} = `eg++ -dumpversion | cut -c 1`); # we must redo these if we change the compiler path } return "OpenBSD"; } if ($config{OSNAME} =~ /Linux/i) { $config{LDLIBS} = "-ldl -lstdc++"; $config{FLAGS} = "-fno-strict-aliasing -fPIC -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}"; $config{FLAGS} .= " " . $ENV{CXXFLAGS} if exists($ENV{CXXFLAGS}); $config{LDLIBS} .= " " . $ENV{LDLIBS} if exists($ENV{LDLIBS}); $config{MAKEPROG} = "make"; if ($config{OSNAME} =~ /CYGWIN/) { $config{FLAGS} = "-fno-strict-aliasing -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}"; $config{LDLIBS} = ""; $config{MAKEPROG} = "/usr/bin/make"; $config{MAKEORDER} = "ircd mods"; return "Cygwin"; } elsif ($config{OSNAME} eq "CYG-STATIC") { $config{FLAGS} = "-fno-strict-aliasing -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}"; $config{LDLIBS} = ""; $config{MAKEPROG} = "/usr/bin/make"; $config{MAKEORDER} = "mods ircd"; $config{STATICLIBS} = "modules/mods.a"; $config{STATIC_LINK} = "yes"; return "Cygwin-Static"; } } if ($config{OSNAME} =~ /FreeBSD/i) { $config{FLAGS} .= " " . $ENV{CXXFLAGS} if exists($ENV{CXXFLAGS}); $config{LDLIBS} .= " " . $ENV{LDLIBS} if exists($ENV{LDLIBS}); } if ($config{OSNAME} =~ /SunOS/i or $config{OSNAME} =~ /solaris/i) { # solaris/sunos needs these # socket = bsd sockets api # nsl = dns stuff # rt = POSIX realtime extensions # resolv = inet_aton only (why isnt this in nsl?!) $config{MAKEPROG} = "gmake"; $config{LDLIBS} .= " -lsocket -lnsl -lrt -lresolv"; return "Solaris"; } if($config{OSNAME} =~ /MINGW32/i) { # All code is position-independent on windows $config{FLAGS} =~ s/-fPIC //; return "MinGW"; } return $config{OSNAME}; } sub writefiles { my($writeheader) = @_; # First File.. inspircd_config.h chomp(my $incos = `uname -n -s -r`); chomp($version = `sh src/version.sh`); chomp(my $revision2 = getrevision()); if ($writeheader == 1) { print "Writing \033[1;32minspircd_config.h\033[0m\n"; open(FILEHANDLE, ">include/inspircd_config.h"); my $NL = $config{NICK_LENGT}+1; my $CL = $config{CHAN_LENGT}+1; 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_FILE "$config{CONFIG_DIR}/inspircd.conf" #define MOD_PATH "$config{MODULE_DIR}" #define VERSION "$version" #define REVISION "$revision2" #define MAXCLIENTS $config{MAX_CLIENT} #define MAXCLIENTS_S "$config{MAX_CLIENT}" #define SOMAXCONN_S "$config{_SOMAXCONN}" #define MAX_DESCRIPTORS $config{MAX_DESCRIPTORS} #define NICKMAX $NL #define CHANMAX $CL #define MAXMODES $config{MAXI_MODES} #define IDENTMAX $config{MAX_IDENT} #define MAXQUIT $config{MAX_QUIT} #define MAXTOPIC $config{MAX_TOPIC} #define MAXKICK $config{MAX_KICK} #define MAXGECOS $config{MAX_GECOS} #define MAXAWAY $config{MAX_AWAY} #define OPTIMISATION $config{OPTIMITEMP} #define LIBRARYDIR "$config{LIBRARY_DIR}" #define SYSTEM "$incos" EOF print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n"; if ($config{OSNAME} =~ /SunOS/i) { print FILEHANDLE "#define IS_SOLARIS\n"; } if ($config{OSNAME} =~ /CYGWIN/i) { print FILEHANDLE "#define IS_CYGWIN\n"; print FILEHANDLE "#ifndef FD_SETSIZE\n#define FD_SETSIZE 1024\n#endif\n"; } if ($config{OSNAME} =~ /MINGW32/i) { print FILEHANDLE "#define IS_MINGW\n"; } if ($config{OSNAME} =~ /CYG-STATIC/i) { print FILEHANDLE "#ifndef FD_SETSIZE\n#define FD_SETSIZE 1024\n#endif\n"; } if ($config{STATIC_LINK} eq "yes") { print FILEHANDLE "#define STATIC_LINK\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{IPV6} =~ /y/i) { print FILEHANDLE "#define IPV6\n"; } if ($config{SUPPORT_IP6LINKS} =~ /y/i) { print FILEHANDLE "#define SUPPORT_IP6LINKS\n"; } my $use_hiperf = 0; if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) { print FILEHANDLE "#define USE_KQUEUE\n"; $se = "socketengine_kqueue"; $use_hiperf = 1; } if (($has_epoll) && ($config{USE_EPOLL} eq "y")) { print FILEHANDLE "#define USE_EPOLL\n"; $se = "socketengine_epoll"; $use_hiperf = 1; } if (($has_ports) && ($config{USE_PORTS} eq "y")) { print FILEHANDLE "#define USE_PORTS\n"; $se = "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 FILEHANDLE "#define USE_SELECT\n"; $se = "socketengine_select"; } print FILEHANDLE "\n#endif\n"; close(FILEHANDLE); } if ($writeheader) { open(FILEHANDLE, ">include/inspircd_se_config.h"); print FILEHANDLE <<EOF; /* Auto generated by configure, do not modify or commit to svn! */ #ifndef __CONFIGURATION_SOCKETENGINE__ #define __CONFIGURATION_SOCKETENGINE__ #include "$se.h" #endif EOF close(FILEHANDLE); } # Create a Modules List.. my $modules = ""; foreach $i (@modlist) { if ($config{STATIC_LINK} eq "yes") { $modules .= "m_".$i.".o "; } else { $modules .= "m_".$i.".so "; } } chomp($modules); # Remove Redundant whitespace.. opendir(DIRHANDLE, "src/modules"); foreach $name (sort readdir(DIRHANDLE)) { if ($name =~ /^m_(.+?)$/) { if (opendir(MDIRHANDLE, "src/modules/$name") != 0) { closedir(MDIRHANDLE); $modules .= "$name.so "; } } } closedir(DIRHANDLE); # Write all .in files. my $tmp = ""; my $file = ""; my $exe = "inspircd"; if ($config{OSNAME} =~ /CYGWIN/i) { $exe = "inspircd.exe"; } opendir(DIRHANDLE, $this); # 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(my $version = `sh ./src/version.sh`); chomp(my $revision = getrevision()); $version = "$version(r$revision)"; my $LIBEXT = "so"; if ($config{IS_DARWIN} eq "YES") { $LIBEXT = "dylib"; } # We can actually parse any file starting with . and ending with .inc, # but right now we only parse .inspircd.inc to form './inspircd' foreach $name (sort readdir(DIRHANDLE)) { if ($name =~ /^\.(.+)\.inc$/) { $file = $1; # All .name.inc files need parsing! $tmp = ""; open(FILEHANDLE, ".$file.inc"); while (<FILEHANDLE>) { $tmp .= $_; } close(FILEHANDLE); $tmp =~ s/\@CC\@/$config{CC}/; $tmp =~ s/\@MAKEPROG\@/$config{MAKEPROG}/; $tmp =~ s/\@FLAGS\@/$config{FLAGS}/; $tmp =~ s/\@DEVELOPER\@/$config{DEVELOPER}/; $tmp =~ s/\@LDLIBS\@/$config{LDLIBS}/; $tmp =~ s/\@BASE_DIR\@/$config{BASE_DIR}/; $tmp =~ s/\@CONFIG_DIR\@/$config{CONFIG_DIR}/; $tmp =~ s/\@MODULE_DIR\@/$config{MODULE_DIR}/; $tmp =~ s/\@BINARY_DIR\@/$config{BINARY_DIR}/; $tmp =~ s/\@LIBRARY_DIR\@/$config{LIBRARY_DIR}/; $tmp =~ s/\@LIBRARY_EXT\@/$LIBEXT/; $tmp =~ s/\@MODULES\@/$modules/; $tmp =~ s/\@STARTSCRIPT\@/$config{STARTSCRIPT}/; $tmp =~ s/\@DESTINATION\@/$config{DESTINATION}/; $tmp =~ s/\@EXTRA_DIR\@/$config{EXTRA_DIR}/; $tmp =~ s/\@EXECUTABLE\@/$exe/; $tmp =~ s/\@MAKEORDER\@/$config{MAKEORDER}/; $tmp =~ s/\@STATICLIBS\@/$config{STATICLIBS}/; $tmp =~ s/\@VERSION\@/$version/; print "Writing \033[1;32m$file\033[0m\n"; open(FILEHANDLE, ">$file"); print FILEHANDLE $tmp; } } closedir(DIRHANDLE); # Make inspircd executable! chmod 0744, 'inspircd'; if ($config{STATIC_LINK} eq "yes") { print "Writing static-build \033[1;32msrc/Makefile\033[0m\n"; write_static_makefile(); write_static_modules_makefile(); } elsif ($config{OSNAME} =~ /CYGWIN/i) { print "Writing cygwin-build \033[1;32msrc/Makefile\033[0m\n"; write_static_makefile(); write_dynamic_modules_makefile(); } else { print "Writing dynamic-build \033[1;32msrc/Makefile\033[0m\n"; write_dynamic_makefile(); write_dynamic_modules_makefile(); } } sub write_static_modules_makefile { # Modules Makefile.. print "Writing \033[1;32msrc/modules/Makefile\033[0m\n"; open(FILEHANDLE, ">src/modules/Makefile"); ### # Module Makefile Header ### print FILEHANDLE <<EOF; # (C) ChatSpike development team # Makefile by <Craig\@ChatSpike.net> # Many Thanks to Andrew Church <achurch\@achurch.org> # for assisting with making this work right. # # Automatically Generated by ./configure to add a modules # please run ./configure --update all: \$(MODULES) EOF ### # End Module Makefile Header ### # Create a Modules List.. my $modules = ""; my $cmflags = ""; my $liflags = ""; open(MODLIST,">include/modlist.h"); ### # Include File Header ### print MODLIST <<HEADER; // Generated automatically by configure. DO NOT EDIT! #ifndef __SYMBOLS__H_CONFIGURED__ #define __SYMBOLS__H_CONFIGURED__ HEADER ### # End Include File Header ### # Place Module List into Include foreach $i (@modlist) { if ($i !~ /_static$/) { print MODLIST "extern \"C\" void * $i\_init (void);\n"; } } print MODLIST "\nstruct {const char *name; initfunc *value; } modsyms[] = {\n"; ### # Build Module Crap ### foreach $i (@modlist) { if ($i !~ /_static$/) { $cmflags = getcompilerflags("src/modules/m_".$i.".cpp"); $liflags = getlinkerflags("src/modules/m_".$i.".cpp"); $deps = getdependencies("src/modules/m_".$i.".cpp"); #print "file: $i: cmflags=$cmflags; liflags=$liflags; deps=$deps\n"; ### # Write Entry to the Makefile ### print FILEHANDLE <<EOCHEESE; m_$i.o: .m_$i\_static.cpp ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h $deps \$(CC) -pipe -I../../include \$(FLAGS) $flags -export-dynamic -c .m_$i\_static.cpp mv .m_$i\_static.o ../m_$i.o EOCHEESE ### # End Write Entry to the MakeFile ### print "Configuring module [\033[1;32mm_$i.so\033[0m] for static linking... "; open(MODULE,"<src/modules/m_".$i.".cpp") or die("Could not open m_".$i.".cpp"); open(MUNGED,">src/modules/.m_".$i."_static.cpp") or die("Could not create .m_".$i."_static.cpp"); while (chomp($a = <MODULE>)) { $a =~ s/init_module/$i\_init/g; print MUNGED "$a\n"; } close(MODULE); close(MUNGED); print MODLIST <<EOENT; {"m_$i.so",\&$i\_init}, EOENT print "done\n"; } } print MODLIST "{0}};\n\n#endif\n"; close(MODLIST); } sub write_dynamic_modules_makefile { # Modules Makefile.. print "Writing \033[1;32msrc/modules/Makefile\033[0m\n"; open(FILEHANDLE, ">src/modules/Makefile"); my $extra = ""; if ($config{OSNAME} =~ /CYGWIN/i) { $extra = "../inspircd.dll.a"; } ### # Module Makefile Header ### print FILEHANDLE <<EOF; # (C) ChatSpike development team # Makefile by <Craig\@ChatSpike.net> # Many Thanks to Andrew Church <achurch\@achurch.org> # for assisting with making this work right. # # Automatically Generated by ./configure to add a modules # please run ./configure -update or ./configure -modupdate all: \$(MODULES) EOF ### # End Module Makefile Header ### # Create a Modules List.. my $modules = ""; my $cmflags = ""; my $liflags = ""; my $crud = ""; foreach $i (@modlist) { ### # Write Entry to the MakeFile ### $cmflags = getcompilerflags("src/modules/m_".$i.".cpp"); $liflags = getlinkerflags("src/modules/m_".$i.".cpp"); $deps = getdependencies("src/modules/m_".$i.".cpp"); #print "file: $i: cmflags=$cmflags; liflags=$liflags; deps=$deps\n"; print FILEHANDLE <<EOCHEESE; m_$i.so: m_$i.cpp ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h $deps \$(CC) -pipe -I../../include \$(FLAGS) $cmflags -export-dynamic -c m_$i.cpp EOCHEESE if ($config{OSNAME} =~ /darwin/) { print FILEHANDLE <<EOCHEESE; \$(CC) -pipe -twolevel_namespace -undefined dynamic_lookup \$(FLAGS) -bundle $liflags -o m_$i.so m_$i.o $extra EOCHEESE } else { print FILEHANDLE <<EOCHEESE; \$(CC) -pipe \$(FLAGS) -shared $liflags -o m_$i.so m_$i.o $extra EOCHEESE } $crud = $crud . " install -m \$(INSTMODE) m_$i.so \$(MODPATH)\n"; ### # End Write Entry to the MakeFile ### } opendir(DIRHANDLE, "src/modules"); foreach $name (sort readdir(DIRHANDLE)) { if ($name =~ /^m_(.+?)$/) { $crapola = ""; $crap3 = ""; # A module made of multiple files, in a dir, e.g. src/modules/m_spanningtree/ if (opendir(MDIRHANDLE, "src/modules/$name") != 0) { my $i = 0; print FILEHANDLE "$name.so: ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h"; foreach $fname (sort readdir(MDIRHANDLE)) { if ($fname =~ /\.cpp$/) { $cmflags = getcompilerflags("src/modules/$name/$fname"); $liflags = getlinkerflags("src/modules/$name/$fname"); $deps = getdependencies("src/modules/$name/$fname"); $oname = $fname; $oname =~ s/\.cpp$/.o/g; print FILEHANDLE " $name/$oname"; $crapola = $crapola . "$name/$oname: $name/$fname ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h $deps\n"; $crapola = $crapola . " \$(CC) -pipe -I../../include -I. \$(FLAGS) $cmflags -export-dynamic -o $name/$oname -c $name/$fname\n\n"; $crap3 = $crap3 . " $name/$oname"; $i++; } } print "Composing Makefile rules for directory \033[1;32m$name\033[0m... (\033[1;32m$i files found\033[0m)\n"; if ($config{IS_DARWIN} eq "YES") { print FILEHANDLE "\n \$(CC) -pipe -twolevel_namespace -undefined dynamic_lookup \$(FLAGS) -bundle -o $name.so $crap3\n"; } else { print FILEHANDLE "\n \$(CC) -pipe \$(FLAGS) -shared $liflags -o $name.so $crap3\n"; } print FILEHANDLE "\n$crapola\n"; closedir(MDIRHANDLE); $crud = $crud . " install -m \$(INSTMODE) $name.so \$(MODPATH)\n"; } } } closedir(DIRHANDLE); print FILEHANDLE "modinst:\n \@echo \"Installing modules...\"\n" . $crud; } sub write_static_makefile { open(FH,">src/Makefile") or die("Could not write src/Makefile!"); my $i = 0; my @cmdlist = (); opendir(DIRHANDLE, "src"); foreach $name (sort readdir(DIRHANDLE)) { if ($name =~ /^cmd_(.+)\.cpp$/) { $cmdlist[$i++] = $1; } } closedir(DIRHANDLE); my $cmdobjs = ""; my $srcobjs = ""; foreach my $cmd (@cmdlist) { $cmdobjs = $cmdobjs . "cmd_$cmd.o "; $srcobjs = $srcobjs . "cmd_$cmd.cpp "; } print FH <<EOM; # Insp Makefile :p # # (C) ChatSpike development team # Makefile by <Craig\@ChatSpike.net> # Makefile version 2 (statically linked core) by <brain\@inspircd.org> # CC = im a cheezeball CXXFLAGS = -I../include \${FLAGS} CPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* //' | grep -v svn) RELCPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* /modes\\//' | grep -v svn) EOM $se = "socketengine_select"; if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) { $se = "socketengine_kqueue"; } elsif (($has_epoll) && ($config{USE_EPOLL} eq "y")) { $se = "socketengine_epoll"; } elsif (($has_ports) && ($config{USE_PORTS} eq "y")) { $se = "socketengine_ports"; } ### # This next section is for cygwin dynamic module builds. # Basically, what we do, is build the inspircd core as a library # then the main executable uses that. the library is capable of # loading / unloading the modules dynamically :) # Massive thanks to the guys on #cygwin @ irc.freenode.net for helping # make this work :) ### if ($config{OSNAME} =~ /CYGWIN/i) { print FH <<EOM; all: timer.o command_parse.o cull_list.o userprocess.o socketengine.o socket.o hashcomp.o channels.o mode.o xline.o inspstring.o dns.o base.o configreader.o inspsocket.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o snomasks.o inspircd.exe inspircd.exe: inspircd.dll.a \$(CC) -o \$@ \$^ inspircd.dll inspircd.dll.a: inspircd.o channels.o mode.o xline.o inspstring.o dns.o base.o configreader.o inspsocket.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o hashcomp.o socket.o socketengine.o userprocess.o cull_list.o command_parse.o timer.o snomasks.o \$(CC) -shared -Wl,--out-implib=inspircd.dll.a -o inspircd.dll \$^ EOM } else { print FH <<EOM; all: timer.o command_parse.o cull_list.o userprocess.o socketengine.o socket.o hashcomp.o channels.o mode.o xline.o inspstring.o dns.o base.o configreader.o inspsocket.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o snomasks.o \$(MODULES) inspircd.exe inspircd.exe: inspircd.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/channels.h ../include/globals.h ../include/inspircd_config.h ../include/base.h \$(CC) -I../include \$(FLAGS) inspircd.cpp -o inspircd.exe \$(LDLIBS) channels.o mode.o xline.o inspstring.o dns.o base.o inspsocket.o configreader.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o hashcomp.o socket.o socketengine.o userprocess.o cull_list.o command_parse.o timer.o snomasks.o modes/modeclasses.a \$(MODULES) EOM } print FH <<EOM; cull_list.o: cull_list.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/users.h ../include/channels.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cull_list.cpp snomasks.o: snomasks.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/channels.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c snomasks.cpp command_parse.o: command_parse.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c command_parse.cpp userprocess.o: userprocess.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c userprocess.cpp socketengine.o: $se.cpp socketengine.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/$se.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socketengine.cpp $se.cpp hashcomp.o: hashcomp.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c hashcomp.cpp helperfuncs.o: helperfuncs.cpp ../include/base.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c helperfuncs.cpp channels.o: channels.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c channels.cpp mode.o: mode.cpp ../include/base.h ../include/mode.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(RELCPPFILES) modes/modeclasses.a \${MAKE} -C "modes" DIRNAME="src/modes" CC="\$(CC)" \$(MAKEARGS) \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c mode.cpp xline.o: xline.cpp ../include/base.h ../include/xline.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c xline.cpp inspstring.o: inspstring.cpp ../include/base.h ../include/inspstring.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspstring.cpp dns.o: dns.cpp ../include/base.h ../include/dns.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dns.cpp base.o: base.cpp ../include/base.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c base.cpp configreader.o: configreader.cpp ../include/base.h ../include/configreader.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c configreader.cpp commands.o: commands.cpp ../include/base.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h $srcobjs \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c commands.cpp $cmdobjs dynamic.o: dynamic.cpp ../include/base.h ../include/dynamic.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dynamic.cpp users.o: users.cpp ../include/base.h ../include/users.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c users.cpp modules.o: modules.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c modules.cpp wildcard.o: wildcard.cpp ../include/base.h ../include/wildcard.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c wildcard.cpp socket.o: socket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socket.cpp inspsocket.o: inspsocket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspsocket.cpp timer.o: timer.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c timer.cpp EOM foreach my $cmd (@cmdlist) { print FH <<ITEM; cmd_$cmd.o: cmd_$cmd.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/commands/cmd_$cmd.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cmd_$cmd.cpp ITEM } close(FH); } sub write_dynamic_makefile { my $i = 0; my @cmdlist = (); opendir(DIRHANDLE, "src"); foreach $name (sort readdir(DIRHANDLE)) { if ($name =~ /^cmd_(.+)\.cpp$/) { $cmdlist[$i++] = $1; } } closedir(DIRHANDLE); my $cmdobjs = ""; my $srcobjs = ""; foreach my $cmd (@cmdlist) { $cmdobjs = $cmdobjs . "cmd_$cmd.so "; $srcobjs = $srcobjs . "cmd_$cmd.cpp "; } $se = "socketengine_select"; if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) { $se = "socketengine_kqueue"; } elsif (($has_epoll) && ($config{USE_EPOLL} eq "y")) { $se = "socketengine_epoll"; } elsif (($has_ports) && ($config{USE_PORTS} eq "y")) { $se = "socketengine_ports"; } open(FH,">src/Makefile") or die("Could not write src/Makefile"); print FH <<EOM; # Insp Makefile :p # # (C) ChatSpike development team # Makefile by <Craig\@ChatSpike.net> # Makefile version 2 (dynamically linked core) by <brain\@inspircd.org> # CC = im a cheezeball CXXFLAGS = -I../include \${FLAGS} CPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* //' | grep -v svn) RELCPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* /modes\\//' | grep -v svn) EOM if ($config{IS_DARWIN} eq "YES") { print FH <<EOM; all: libIRCDtimer.dylib libIRCDcull_list.dylib libIRCDuserprocess.dylib libIRCDsocketengine.dylib libIRCDsocket.dylib libIRCDhash.dylib libIRCDchannels.dylib libIRCDmode.dylib libIRCDxline.dylib libIRCDstring.dylib libIRCDasyncdns.dylib libIRCDbase.dylib libIRCDconfigreader.dylib libIRCDinspsocket.dylib libIRCDcommands.dylib libIRCDdynamic.dylib libIRCDusers.dylib libIRCDmodules.dylib libIRCDwildcard.dylib libIRCDhelper.dylib libIRCDcommand_parse.dylib libIRCDsnomasks.dylib inspircd inspircd: inspircd.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/channels.h ../include/globals.h ../include/inspircd_config.h ../include/socket.h $cmdobjs libIRCDtimer.dylib libIRCDcull_list.dylib libIRCDuserprocess.dylib libIRCDsocketengine.dylib libIRCDsocket.dylib libIRCDhash.dylib libIRCDchannels.dylib libIRCDmode.dylib libIRCDxline.dylib libIRCDstring.dylib libIRCDasyncdns.dylib libIRCDbase.dylib libIRCDconfigreader.dylib libIRCDinspsocket.dylib libIRCDsnomasks.dylib libIRCDcommands.dylib libIRCDdynamic.dylib libIRCDusers.dylib libIRCDmodules.dylib libIRCDwildcard.dylib libIRCDhelper.dylib libIRCDcommand_parse.dylib \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspircd.cpp \$(CC) -pipe -dynamic -bind_at_load -L. -o inspircd \$(LDLIBS) inspircd.o libIRCDchannels.dylib libIRCDmode.dylib libIRCDxline.dylib libIRCDstring.dylib libIRCDasyncdns.dylib libIRCDbase.dylib libIRCDconfigreader.dylib libIRCDinspsocket.dylib libIRCDcommands.dylib libIRCDdynamic.dylib libIRCDusers.dylib libIRCDmodules.dylib libIRCDwildcard.dylib libIRCDhelper.dylib libIRCDhash.dylib libIRCDsocket.dylib libIRCDsocketengine.dylib libIRCDuserprocess.dylib libIRCDcull_list.dylib libIRCDcommand_parse.dylib libIRCDtimer.dylib libIRCDsnomasks.dylib libIRCDsocketengine.dylib: $se.cpp socketengine.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/$se.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socketengine.cpp \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c $se.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDsocketengine.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDsocketengine.dylib socketengine.o $se.o libIRCDsnomasks.dylib: snomasks.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/channels.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c snomasks.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDsnomasks.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDsnomasks.dylib snomasks.o libIRCDcommand_parse.dylib: command_parse.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c command_parse.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDcommand_parse.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDcommand_parse.dylib command_parse.o libIRCDcull_list.dylib: cull_list.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/users.h ../include/channels.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cull_list.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDcull_list.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDcull_list.dylib cull_list.o libIRCDuserprocess.dylib: userprocess.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c userprocess.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDuserprocess.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDuserprocess.dylib userprocess.o libIRCDhash.dylib: hashcomp.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c hashcomp.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDhash.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDhash.dylib hashcomp.o libIRCDhelper.dylib: helperfuncs.cpp ../include/base.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c helperfuncs.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDhelper.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDhelper.dylib helperfuncs.o libIRCDchannels.dylib: channels.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c channels.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDchannels.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDchannels.dylib channels.o libIRCDmode.dylib: mode.cpp ../include/base.h ../include/mode.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(RELCPPFILES) \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c mode.cpp \${MAKE} -C "modes" DIRNAME="src/modes" CC="\$(CC)" \$(MAKEARGS) CPPFILES="\$(CPPFILES)" \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDmode.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDmode.dylib mode.o modes/modeclasses.a libIRCDxline.dylib: xline.cpp ../include/base.h ../include/xline.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c xline.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDxline.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDxline.dylib xline.o libIRCDstring.dylib: inspstring.cpp ../include/base.h ../include/inspstring.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspstring.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDstring.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDstring.dylib inspstring.o libIRCDasyncdns.dylib: dns.cpp ../include/base.h ../include/dns.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dns.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDasyncdns.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDasyncdns.dylib dns.o libIRCDbase.dylib: base.cpp ../include/base.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c base.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDbase.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDbase.dylib base.o libIRCDconfigreader.dylib: configreader.cpp ../include/base.h ../include/configreader.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c configreader.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDconfigreader.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDconfigreader.dylib configreader.o libIRCDcommands.dylib: commands.cpp ../include/base.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c commands.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDcommands.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDcommands.dylib commands.o libIRCDdynamic.dylib: dynamic.cpp ../include/base.h ../include/dynamic.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dynamic.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDdynamic.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDdynamic.dylib dynamic.o libIRCDusers.dylib: users.cpp ../include/base.h ../include/users.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c users.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDusers.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDusers.dylib users.o libIRCDmodules.dylib: modules.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c modules.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDmodules.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDmodules.dylib modules.o libIRCDwildcard.dylib: wildcard.cpp ../include/base.h ../include/wildcard.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c wildcard.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDwildcard.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDwildcard.dylib wildcard.o libIRCDsocket.dylib: socket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socket.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDsocket.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDsocket.dylib socket.o libIRCDinspsocket.dylib: inspsocket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspsocket.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDinspsocket.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDinspsocket.dylib inspsocket.o libIRCDtimer.dylib: timer.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c timer.cpp \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDtimer.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDtimer.dylib timer.o EOM } else { print FH <<EOM; all: libIRCDtimer.so libIRCDcull_list.so libIRCDuserprocess.so libIRCDsocketengine.so libIRCDsocket.so libIRCDhash.so libIRCDchannels.so libIRCDmode.so libIRCDxline.so libIRCDstring.so libIRCDasyncdns.so libIRCDbase.so libIRCDconfigreader.so libIRCDinspsocket.so $cmdobjs libIRCDcommands.so libIRCDdynamic.so libIRCDusers.so libIRCDmodules.so libIRCDwildcard.so libIRCDhelper.so libIRCDcommand_parse.so libIRCDsnomasks.so inspircd inspircd: inspircd.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/channels.h ../include/globals.h ../include/inspircd_config.h ../include/socket.h libIRCDtimer.so libIRCDcull_list.so libIRCDuserprocess.so libIRCDsocketengine.so libIRCDsocket.so libIRCDhash.so libIRCDchannels.so libIRCDmode.so libIRCDxline.so libIRCDstring.so libIRCDasyncdns.so libIRCDbase.so libIRCDconfigreader.so libIRCDinspsocket.so $cmdobjs libIRCDsnomasks.so libIRCDcommands.so libIRCDdynamic.so libIRCDusers.so libIRCDmodules.so libIRCDwildcard.so libIRCDhelper.so libIRCDcommand_parse.so \$(CC) -pipe -I../include $extra -Wl,--rpath -Wl,$config{LIBRARY_DIR} \$(FLAGS) -rdynamic -L. inspircd.cpp -o inspircd \$(LDLIBS) libIRCDchannels.so libIRCDmode.so libIRCDxline.so libIRCDstring.so libIRCDasyncdns.so libIRCDbase.so libIRCDconfigreader.so libIRCDinspsocket.so libIRCDcommands.so libIRCDdynamic.so libIRCDusers.so libIRCDmodules.so libIRCDwildcard.so libIRCDhelper.so libIRCDhash.so libIRCDsocket.so libIRCDsocketengine.so libIRCDuserprocess.so libIRCDcull_list.so libIRCDcommand_parse.so libIRCDtimer.so libIRCDsnomasks.so libIRCDsocketengine.so: $se.cpp socketengine.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/$se.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socketengine.cpp $se.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDsocketengine.so socketengine.o $se.o libIRCDsnomasks.so: snomasks.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/channels.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c snomasks.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDsnomasks.so snomasks.o libIRCDcommand_parse.so: command_parse.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c command_parse.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDcommand_parse.so command_parse.o libIRCDcull_list.so: cull_list.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/users.h ../include/channels.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cull_list.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDcull_list.so cull_list.o libIRCDuserprocess.so: userprocess.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c userprocess.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDuserprocess.so userprocess.o libIRCDhash.so: hashcomp.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c hashcomp.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDhash.so hashcomp.o libIRCDhelper.so: helperfuncs.cpp ../include/base.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c helperfuncs.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDhelper.so helperfuncs.o libIRCDchannels.so: channels.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c channels.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDchannels.so channels.o libIRCDmode.so: mode.cpp ../include/base.h ../include/mode.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(RELCPPFILES) \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c mode.cpp \${MAKE} -C "modes" DIRNAME="src/modes" CC="\$(CC)" \$(MAKEARGS) CPPFILES="\$(CPPFILES)" \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDmode.so mode.o modes/modeclasses.a libIRCDxline.so: xline.cpp ../include/base.h ../include/xline.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c xline.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDxline.so xline.o libIRCDstring.so: inspstring.cpp ../include/base.h ../include/inspstring.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspstring.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDstring.so inspstring.o libIRCDasyncdns.so: dns.cpp ../include/base.h ../include/dns.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dns.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDasyncdns.so dns.o libIRCDbase.so: base.cpp ../include/base.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c base.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDbase.so base.o libIRCDconfigreader.so: configreader.cpp ../include/base.h ../include/configreader.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c configreader.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDconfigreader.so configreader.o libIRCDcommands.so: commands.cpp ../include/base.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c commands.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDcommands.so commands.o libIRCDdynamic.so: dynamic.cpp ../include/base.h ../include/dynamic.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dynamic.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDdynamic.so dynamic.o libIRCDusers.so: users.cpp ../include/base.h ../include/users.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c users.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDusers.so users.o libIRCDmodules.so: modules.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c modules.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDmodules.so modules.o libIRCDwildcard.so: wildcard.cpp ../include/base.h ../include/wildcard.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c wildcard.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDwildcard.so wildcard.o libIRCDsocket.so: socket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socket.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDsocket.so socket.o libIRCDinspsocket.so: inspsocket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspsocket.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDinspsocket.so inspsocket.o libIRCDtimer.so: timer.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c timer.cpp \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDtimer.so timer.o EOM } foreach my $cmd (@cmdlist) { print FH <<ITEM; cmd_$cmd.so: cmd_$cmd.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/commands/cmd_$cmd.h \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cmd_$cmd.cpp \$(CC) -pipe $SHARED -o cmd_$cmd.so cmd_$cmd.o ITEM } close(FH); } \ No newline at end of file
+#!/usr/bin/perl
+###################################################
+# InspIRCd Configuration Script
+#
+# Copyright 2002-2007 The InspIRCd Development Team
+# http://www.inspircd.org/wiki/index.php/Credits
+#
+# Licensed under GPL, please see the COPYING file
+# for more information
+#
+# $Id$
+#
+###################################################
+
+require 5.6.0;
+use Socket;
+use Cwd;
+use Getopt::Long;
+
+# Utility functions for our buildsystem
+use make::utilities;
+use make::configure;
+use make::gnutlscert;
+use make::opensslcert;
+
+GetOptions (
+ 'enable-gnutls' => \$opt_use_gnutls,
+ 'rebuild' => \$opt_rebuild,
+ 'enable-openssl' => \$opt_use_openssl,
+ 'disable-interactive' => \$opt_nointeractive,
+ 'with-nick-length=i' => \$opt_nick_length,
+ 'with-channel-length=i' => \$opt_chan_length,
+ 'with-max-clients=i' => \$opt_maxclients,
+ 'enable-ports' => \$opt_ports,
+ 'enable-epoll' => \$opt_epoll,
+ 'enable-kqueue' => \$opt_kqueue,
+ 'disable-ports' => \$opt_noports,
+ 'disable-epoll' => \$opt_noepoll,
+ 'disable-kqueue' => \$opt_nokqueue,
+ 'enable-ipv6' => \$opt_ipv6,
+ 'enable-remote-ipv6' => \$opt_ipv6links,
+ 'disable-remote-ipv6' => \$opt_noipv6links,
+ 'with-cc=s' => \$opt_cc,
+ 'with-ident-length=i' => \$opt_ident,
+ 'with-quit-length=i' => \$opt_quit,
+ 'with-topic-length=i' => \$opt_topic,
+ 'with-maxbuf=i' => \$opt_maxbuf,
+ 'with-kick-length=i' => \$opt_kick,
+ 'with-gecos-length=i' => \$opt_gecos,
+ 'with-away-length=i' => \$opt_away,
+ 'with-max-modes=i' => \$opt_modes,
+ 'prefix=s' => \$opt_base_dir,
+ 'config-dir=s' => \$opt_config_dir,
+ 'module-dir=s' => \$opt_module_dir,
+ 'binary-dir=s' => \$opt_binary_dir,
+ 'library-dir=s' => \$opt_library_dir,
+ 'disable-debuginfo' => sub { $opt_disable_debug = 1 },
+ 'help' => sub { showhelp(); },
+ 'modupdate' => sub { modupdate(); },
+ 'update' => sub { update(); },
+ 'svnupdate' => sub { svnupdate(); },
+ 'clean' => sub { clean(); },
+);
+
+my $non_interactive = (
+ (defined $opt_library_dir) ||
+ (defined $opt_base_dir) ||
+ (defined $opt_config_dir) ||
+ (defined $opt_module_dir) ||
+ (defined $opt_base_dir) ||
+ (defined $opt_binary_dir) ||
+ (defined $opt_nointeractive) ||
+ (defined $opt_away) ||
+ (defined $opt_gecos) ||
+ (defined $opt_kick) ||
+ (defined $opt_maxclients) ||
+ (defined $opt_modes) ||
+ (defined $opt_topic) ||
+ (defined $opt_quit) ||
+ (defined $opt_ident) ||
+ (defined $opt_cc) ||
+ (defined $opt_ipv6) ||
+ (defined $opt_ipv6links) ||
+ (defined $opt_noipv6links) ||
+ (defined $opt_kqueue) ||
+ (defined $opt_epoll) ||
+ (defined $opt_ports) ||
+ (defined $opt_maxchans) ||
+ (defined $opt_opermaxchans) ||
+ (defined $opt_chan_length) ||
+ (defined $opt_nick_length) ||
+ (defined $opt_use_openssl) ||
+ (defined $opt_nokqueue) ||
+ (defined $opt_noepoll) ||
+ (defined $opt_noports) ||
+ (defined $opt_maxbuf) ||
+ (defined $opt_use_gnutls)
+);
+my $interactive = !$non_interactive;
+
+
+chomp($topdir = getcwd());
+$this = resolve_directory($topdir); # PWD, Regardless.
+@modlist = (); # Declare for Module List..
+%config = (); # Initiate Configuration Hash..
+$config{ME} = resolve_directory($topdir); # Present Working Directory
+
+$config{BASE_DIR} = $config{ME};
+
+if (defined $opt_base_dir)
+{
+ $config{BASE_DIR} = $opt_base_dir;
+}
+
+$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{LIBRARY_DIR} = resolve_directory($config{BASE_DIR}."/lib"); # Library 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_library_dir)
+{
+ $config{LIBRARY_DIR} = $opt_library_dir;
+}
+chomp($config{HAS_GNUTLS} = `libgnutls-config --version 2>/dev/null | cut -c 1,2,3`); # GNUTLS Version.
+chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`); # Openssl version
+chomp($gnutls_ver = $config{HAS_GNUTLS});
+chomp($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";
+}
+
+# no, let's not change these.
+$config{OPTIMITEMP} = "0"; # Default Optimisation Value
+if (!defined $opt_disable_debug)
+{
+ $config{OPTIMISATI} = "-g1"; # Optimisation Flag
+}
+else
+{
+ $config{OPTIMISATI} = "-O2"; # DEBUGGING OFF!
+}
+
+$config{NICK_LENGT} = "31"; # Default Nick Length
+if (defined $opt_nick_length)
+{
+ $config{NICK_LENGT} = $opt_nick_length;
+}
+$config{CHAN_LENGT} = "64"; # Default Channel Name Length
+if (defined $opt_chan_length)
+{
+ $config{CHAN_LENGT} = $opt_chan_length;
+}
+$config{MAXI_MODES} = "20"; # Default Max. Number of Modes set at once.
+if (defined $opt_modes)
+{
+ $config{MAXI_MODES} = $opt_modes;
+}
+$config{HAS_STRLCPY} = "false"; # strlcpy Check.
+$config{HAS_STDINT} = "false"; # stdint.h check
+$config{USE_KQUEUE} = "y"; # kqueue enabled
+if (defined $opt_kqueue)
+{
+ $config{USE_KQUEUE} = "y";
+}
+if (defined $opt_nokqueue)
+{
+ $config{USE_KQUEUE} = "n";
+}
+$config{USE_EPOLL} = "y"; # epoll enabled
+if (defined $opt_epoll)
+{
+ $config{USE_EPOLL} = "y";
+}
+if (defined $opt_noepoll)
+{
+ $config{USE_EPOLL} = "n";
+}
+$config{USE_PORTS} = "y"; # epoll enabled
+if (defined $opt_ports)
+{
+ $config{USE_PORTS} = "y";
+}
+if (defined $opt_noports)
+{
+ $config{USE_PORTS} = "n";
+}
+$config{IPV6} = "n"; # IPv6 support (experimental)
+if (defined $opt_ipv6)
+{
+ $config{IPV6} = "y";
+}
+$config{SUPPORT_IP6LINKS} = "y"; # IPv4 supporting IPv6 links (experimental)
+if (defined $opt_ipv6links)
+{
+ $config{SUPPORT_IP6LINKS} = "y";
+}
+if (defined $opt_noipv6links)
+{
+ $config{SUPPORT_IP6LINKS} = "n";
+}
+$config{STATIC_LINK} = "no"; # are doing static modules?
+chomp($config{MAX_CLIENT_T} = `sh -c \"ulimit -n\"`); # FD Limit
+chomp($config{MAX_DESCRIPTORS} = `sh -c \"ulimit -n\"`); # Hard FD Limit
+chomp($config{GCCVER} = `g++ -dumpversion | cut -c 1`); # Major GCC Version
+$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.
+$config{EXTRA_DIR} = ""; # Is empty.
+if ($config{OSNAME} =~ /darwin/i)
+{
+ $config{IS_DARWIN} = "YES";
+ $config{STARTSCRIPT} = "org.inspircd.plist"; # start script for OSX.
+ $config{DESTINATION} = "LAUNCHDPATH"; # Is OSX target.
+ $config{EXTRA_DIR} = " launchd_dir"; # Is OSX specific path.
+}
+$config{CC} = "g++"; # C++ compiler
+if (defined $opt_cc)
+{
+ $config{CC} = $opt_cc;
+}
+$exec = $config{CC} . " -dumpversion | cut -c 1";
+chomp($config{GCCVER} = `$exec`); # Major GCC Version
+$config{MAKEORDER} = "ircd mods"; # build order
+$config{STATICLIBS} = ""; # library archive path
+$config{MAX_IDENT} = "12"; # max ident size
+$config{MAX_QUIT} = "255"; # max quit message size
+$config{MAX_TOPIC} = "307"; # max topic size
+$config{MAX_KICK} = "255"; # max kick message size
+$config{MAX_GECOS} = "128"; # max GECOS size
+$config{MAX_AWAY} = "200"; # max AWAY size
+$config{MAXBUF} = "512"; # Max buffer size
+if (defined $opt_ident)
+{
+ $config{MAX_IDENT} = $opt_ident;
+}
+if (defined $opt_quit)
+{
+ $config{MAX_QUIT} = $opt_quit;
+}
+if (defined $opt_topic)
+{
+ $config{MAX_TOPIC} = $opt_topic;
+}
+if (defined $opt_kick)
+{
+ $config{MAX_KICK} = $opt_kick;
+}
+if (defined $opt_gecos)
+{
+ $config{MAX_GECOS} = $opt_gecos;
+}
+if (defined $opt_away)
+{
+ $config{MAX_AWAY} = $opt_away;
+}
+
+$config{HAS_OPENSSL} =~ /^([-[:digit:].]+)([a-z])?(\-[a-z][0-9])?$/;
+$config{HAS_OPENSSL} = $1;
+
+if ($config{GCCVER} eq "") {
+ print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n";
+ exit;
+}
+
+# Minihack! Convert Cygwin to 'Cyg-Static' so i can
+# Keep my dynamic module experiments here for later
+# consideration!
+
+if ($config{OSNAME} =~ /CYGWIN/i)
+{
+ $config{OSNAME} = "CYG-STATIC";
+}
+
+if (!$config{MAX_CLIENT_T}) {
+ $config{MAX_CLIENT_T} = 1024; # Set a reasonable 'Default'
+ $fd_scan_fail = "true"; # Used Later
+}
+
+# Get and Set some important vars..
+getmodules();
+
+sub clean
+{
+ system("rm -rf .config.cache");
+}
+
+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";
+ getosflags();
+ if ($opt_disable_debug == 1)
+ {
+ print "Disabling debug information (-g).\n";
+ $config{OPTIMISATI} = "";
+ getosflags();
+ }
+ $has_epoll = $config{HAS_EPOLL};
+ $has_ports = $config{HAS_PORTS};
+ $has_kqueue = $config{HAS_KQUEUE};
+ writefiles(1);
+ makecache();
+ print "Complete.\n";
+ exit;
+ }
+ };
+ if ($@)
+ {
+ print "Configure update failed: $@\n";
+ }
+ exit;
+}
+
+sub modupdate
+{
+ 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";
+ getosflags();
+ $has_epoll = $config{HAS_EPOLL};
+ $has_ports = $config{HAS_PORTS};
+ $has_kqueue = $config{HAS_KQUEUE};
+ writefiles(0);
+ makecache();
+ print "Complete.\n";
+ exit;
+ }
+ };
+ if ($@)
+ {
+ print "Module update failed: $@\n";
+ }
+ exit;
+}
+
+
+
+sub svnupdate
+{
+ my $fail = 0;
+ open(FH,"<.svn/entries") or $fail = 1;
+ if ($fail) {
+ print "This is not an SVN copy of InspIRCd.\n";
+ exit;
+ }
+ else
+ {
+ close(FH);
+ }
+ system("svn update");
+ system("perl configure -update");
+ if (defined $opt_rebuild) {
+ system("make install");
+ }
+ exit;
+}
+
+print "Running non-interactive configure...\n" unless $interactive;
+print "Checking for cache from previous configure... ";
+print ((getcache() eq "true") ? "found\n" : "not found\n");
+print "Checking operating system version... ";
+print getosflags() . "\n";
+
+if (defined $opt_maxclients)
+{
+ $config{MAX_CLIENT} = $opt_maxclients;
+}
+
+if (!$config{MAX_CLIENT}) {
+ # If the cache hasn't set the max clients, copy the variable of MAX_CLIENT_T, this
+ # allows us to keep _T for testing purposes. (ie. "Are you sure you want to go
+ # higher than the found value" :))
+ $config{MAX_CLIENT} = $config{MAX_CLIENT_T};
+}
+
+printf "Checking if stdint.h exists... ";
+$config{HAS_STDINT} = "true";
+my $fail = 0;
+open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
+if ($config{HAS_STDINT} eq "true") {
+ close(STDINT);
+}
+print "yes\n" if $config{HAS_STDINT} eq "true";
+print "no\n" if $config{HAS_STDINT} eq "false";
+
+
+printf "Checking if strlcpy exists... ";
+# Perform the strlcpy() test..
+$config{HAS_STRLCPY} = "false";
+my $fail = 0;
+open(STRLCPY, "</usr/include/string.h") or $fail = 1;
+if (!$fail) {
+ while (chomp($line = <STRLCPY>)) {
+ # try and find the delcaration of:
+ # size_t strlcpy(...)
+ if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
+ $config{HAS_STRLCPY} = "true";
+ }
+ }
+ close(STRLCPY);
+}
+print "yes\n" if $config{HAS_STRLCPY} eq "true";
+print "no\n" if $config{HAS_STRLCPY} eq "false";
+
+
+printf "Checking if kqueue exists... ";
+$has_kqueue = 0;
+$fail = 0;
+open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1;
+if (!$fail) {
+ while (chomp($line = <KQUEUE>)) {
+ # try and find the delcaration of:
+ # int kqueue(void);
+ if ($line =~ /int(\0x9|\s)+kqueue/) {
+ $has_kqueue = 1;
+ }
+ }
+ close(KQUEUE);
+}
+print "yes\n" if $has_kqueue == 1;
+print "no\n" if $has_kqueue == 0;
+
+printf "Checking if epoll exists... ";
+$has_epoll = 0;
+$fail = 0;
+open(EPOLL, "</usr/include/sys/epoll.h") or $fail = 1;
+if (!$fail) {
+ $has_epoll = 1;
+ close(EPOLL);
+}
+if ($has_epoll) {
+ my $kernel = `uname -r`;
+ chomp($kernel);
+ if (($kernel =~ /^2\.0\./) || ($kernel =~ /^2\.2\./) || ($kernel =~ /^2\.4\./)) {
+ $has_epoll = 0;
+ }
+}
+print "yes\n" if $has_epoll == 1;
+print "no\n" if $has_epoll == 0;
+
+printf "Checking if Solaris I/O completion ports are available... ";
+$has_ports = 0;
+my $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;
+ }
+}
+print "yes\n" if $has_ports == 1;
+print "no\n" if $has_ports == 0;
+
+if (($config{OSNAME} =~ /CYGWIN/) || ($config{OSNAME} eq "CYG-STATIC")) {
+ $config{HAS_STRLCPY} = "true";
+}
+
+$config{HAS_EPOLL} = $has_epoll;
+$config{HAS_KQUEUE} = $has_kqueue;
+
+printf "Checking for libgnutls... ";
+if (($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS} >= 1.2) || ($config{HAS_GNUTLS} eq "y"))) {
+ print "yes\n";
+ $config{HAS_GNUTLS} = "y";
+} else {
+ print "no\n";
+ $config{HAS_GNUTLS} = "n";
+}
+
+printf "Checking for openssl... ";
+if (($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL} >= 0.8) || ($config{HAS_OPENSSL} eq "y"))) {
+ print "yes\n";
+ $config{HAS_OPENSSL} = "y";
+} else {
+ print "no\n";
+ $config{HAS_OPENSSL} = "n";
+}
+
+################################################################################
+# BEGIN INTERACTIVE PART #
+################################################################################
+
+# Clear the Screen..
+if ($interactive)
+{
+ system("clear");
+ $wholeos = $^O;
+
+ my $rev = getrevision();
+ # Display Introduction Message..
+ print "
+Welcome to the \033[1mInspIRCd\033[0m Configuration program! (\033[1minteractive mode\033[0m)
+\033[1mPackage maintainers: Type ./configure --help for non-interactive help\033[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 \033[1m<RETURN>\033[0m to accept the default for any option, or enter
+a new value. Please note: You will \033[1mHAVE\033[0m to read the docs
+dir, otherwise you won't have a config file!
+
+Your operating system is: \033[1;32m$config{OSNAME}\033[0m ($wholeos)
+Maximum file descriptors: \033[1;32m$config{MAX_CLIENT_T}\033[0m
+Your InspIRCd revision ID is \033[1;32mr$rev\033[0m";
+ if ($rev eq "r0") {
+ print " (Non-SVN build)";
+ }
+ print ".\n\n";
+
+ $config{CHANGE_COMPILER} = "n";
+ print "I have detected the following compiler: \033[1;32m$config{CC}\033[0m (version \033[1;32m$config{GCCVER}.x\033[0m)\n";
+
+ while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
+ print "\033[1;32mIMPORTANT!\033[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 "[\033[1;32m$config{CC}\033[0m] -> ";
+ chomp($config{CC} = <STDIN>);
+ if ($config{CC} eq "") {
+ $config{CC} = "g++";
+ }
+ chomp($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
+ print "Queried compiler: \033[1;32m$config{CC}\033[0m (version \033[1;32m$config{GCCVER}.x\033[0m)\n";
+ if ($config{GCCVER} < 3) {
+ print "\033[1;32mGCC 2.x WILL NOT WORK!\033[0m. Let's try that again, shall we?\n";
+ }
+ }
+ else {
+ print "\033[1;32mWARNING!\033[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{BINARY_DIR} = resolve_directory($config{BASE_DIR}."/bin"); # Binary Directory
+ $config{LIBRARY_DIR} = resolve_directory($config{BASE_DIR}."/lib"); # Library 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 the IRCd libraries to be placed", "LIBRARY_DIR");
+
+ 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 ($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 ($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";
+ }
+ $chose_hiperf = (($config{USE_EPOLL} eq "y") || ($config{USE_KQUEUE} eq "y") || ($config{USE_PORTS} eq "y"));
+ if (!$chose_hiperf) {
+ print "No high-performance socket engines are available, or you chose\n";
+ print "not to enable one. Defaulting to select() engine.\n\n";
+ }
+
+ yesno(IPV6,"Would you like to build InspIRCd with IPv6 support?");
+ print "\n";
+
+ if ($config{IPV6} eq "y") {
+ print "You have chosen to build an \033[1;32mIPV6-enabled\033[0m server.\nTo accept IPV4 users, you can still use IPV4 addresses\nin your port bindings..\n\n";
+ $config{SUPPORT_IP6LINKS} = "y";
+ } else {
+ yesno(SUPPORT_IP6LINKS,"You have chosen to build an \033[1;32mIPV4-only\033[0m server.\nWould you like to enable support for linking to IPV6-enabled\nInspIRCd servers?\nIf you are using a recent operating\nsystem and are unsure, answer yes.\nIf you answer 'no' here, your InspIRCd server will be unable\nto parse IPV6 addresses (e.g. for CIDR bans)");
+ print "\n";
+ }
+
+ if (($config{HAS_GNUTLS} eq "y") && ($config{HAS_OPENSSL} eq "y")) {
+ print "I have detected both \033[1;32mGnuTLS\033[0m and \033[1;32mOpenSSL\033[0m on your system.\n";
+ print "I will default to GnuTLS. If you wish to use OpenSSL\n";
+ print "instead, you should enable the OpenSSL module yourself\n";
+ print "by copying it from src/modules/extra to src/modules.\n\n";
+ print "Detected GnuTLS version: \033[1;32m" . $gnutls_ver . "\033[0m\n";
+ print "Detected OpenSSL version: \033[1;32m" . $openssl_ver . "\033[0m\n\n";
+ }
+
+ if ($config{HAS_GNUTLS} eq "y") {
+ yesno(USE_GNUTLS, "Would you like to enable SSL Support?");
+ if ($config{USE_GNUTLS} eq "y") {
+ print "\nUsing GnuTLS SSL module.\n";
+ }
+ } elsif ($config{HAS_OPENSSL} eq "y") {
+ yesno(USE_OPENSSL, "Would you like to enable SSL Support?");
+ 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 if\nyou intend to use OpenSSL, or that GnuTLS is in your path if you intend\nto use GnuTLS.\n\n";
+ }
+
+ print "\nThe following questions will ask you for various figures relating\n";
+ print "To your IRCd install. Please note that these should usually be left\n";
+ print "as defaults unless you have a real reason to change them. If they\n";
+ print "changed, then the values must be identical on all servers on your\n";
+ print "network, or malfunctions and/or crashes may occur, with the exception\n";
+ print "of the 'maximum number of clients' setting which may be different on\n";
+ print "different servers on the network.\n\n";
+
+ # File Descriptor Settings..
+ promptnumeric("number of clients at any one time", "MAX_CLIENT_T");
+ $config{MAX_CLIENT} = $config{MAX_CLIENT_T};
+ $config{MAX_DESCRIPTORS} = $config{MAX_CLIENT_T};
+
+ promptnumeric("length of nicknames", "NICK_LENGT");
+ promptnumeric("length of channel names", "CHAN_LENGT");
+ promptnumeric("number of mode changes in one line", "MAXI_MODES");
+ promptnumeric("length of an ident (username)", "MAX_IDENT");
+ promptnumeric("length of a quit message", "MAX_QUIT");
+ promptnumeric("length of a channel topic", "MAX_TOPIC");
+ promptnumeric("length of a kick message", "MAX_KICK");
+ promptnumeric("length of a GECOS (real name)", "MAX_GECOS");
+ promptnumeric("length of an away message", "MAX_AWAY");
+}
+
+dumphash();
+
+if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
+{
+ print "Sorry, but i couldn't detect gnutls. Make sure gnutls-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 openssl is in your path.\n";
+ exit(0);
+}
+
+if ($config{USE_GNUTLS} eq "y") {
+ $failed = 0;
+ open(TMP, "<src/modules/m_ssl_gnutls.cpp") or $failed = 1;
+ close(TMP);
+ if ($failed) {
+ print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
+ chdir("src/modules");
+ system("ln -s extra/m_ssl_gnutls.cpp");
+ chdir("../..");
+ }
+ getmodules();
+ if ($interactive)
+ {
+ $failed = 0;
+ open(TMP, "<$config{CONFIG_DIR}/key.pem") or $failed = 1;
+ close(TMP);
+ open(TMP, "<$config{CONFIG_DIR}/cert.pem") or $failed = 1;
+ close(TMP);
+ if ($failed) {
+ print "SSL Certificates Not found, Generating.. \n\n
+*************************************************************
+* Generating the Private Key may take some time, go grab a *
+* Coffee. Even better, to generate some more entropy if it *
+* is taking a while, open another console and type du / a *
+* few times and get that HD going :) Then answer the *
+* Questions which follow. If you are unsure, just hit enter *
+*************************************************************\n\n";
+ make_gnutls_cert() or $failed = 1;
+ if (!$failed) {
+ print "\nCertificate generation complete, copying to config directory... ";
+ system("mv key.pem $config{CONFIG_DIR}/key.pem");
+ system("mv cert.pem $config{CONFIG_DIR}/cert.pem");
+ print "Done.\n\n";
+ } else {
+ print "\n\033[1;32mCertificate generation failed!\033[0m\n\n";
+ }
+ }
+ else {
+ print "SSL Certificates found, skipping.\n\n";
+ }
+ }
+ else
+ {
+ print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
+ }
+} elsif ($config{USE_OPENSSL} eq "y") {
+ $failed = 0;
+ open(TMP, "<src/modules/m_ssl_openssl.cpp") or $failed = 1;
+ close(TMP);
+ if ($failed) {
+ print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
+ chdir("src/modules");
+ system("ln -s extra/m_ssl_openssl.cpp");
+ chdir("../..");
+ }
+ getmodules();
+ $failed = 0;
+ if ($interactive)
+ {
+ open(TMP, "<$config{CONFIG_DIR}/key.pem") or $failed = 1;
+ close(TMP);
+ open(TMP, "<$config{CONFIG_DIR}/cert.pem") or $failed = 1;
+ close(TMP);
+ if ($failed) {
+ 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... ";
+ system("mv key.pem $config{CONFIG_DIR}/key.pem");
+ system("mv cert.pem $config{CONFIG_DIR}/cert.pem");
+ system("mv dhparams.pem $config{CONFIG_DIR}/dhparams.pem");
+ print "Done.\n\n";
+ } else {
+ print "SSL Certificates found, skipping.\n\n"
+ }
+ }
+ else
+ {
+ print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
+ }
+}
+if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
+ print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
+}
+
+getosflags();
+writefiles(1);
+makecache();
+
+print "\n\n";
+print "To build your server with these settings, please type '\033[1;32m$config{MAKEPROG}\033[0m' now.\n";
+if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
+ print "Please remember that to enable \033[1;32mSSL support\033[0m you must\n";
+ print "load the required modules in your config. This configure process\n";
+ print "has just prepared these modules to be compiled for you, and has not\n";
+ print "configured them to be compiled into the core of the ircd.\n";
+}
+print "*** \033[1;32mRemember to edit your configuration files!!!\033[0m ***\n\n\n";
+if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
+ print "\033[1;32mWARNING!\033[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";
+}
+
+if ($config{GCCVER} < "3") {
+ print <<FOO2;
+\033[1;32mWARNING!\033[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
+}
+
+################################################################################
+# HELPER FUNCTIONS #
+################################################################################
+sub getcache {
+ # Retrieves the .config.cache file, and loads values into the main config hash.
+ open(CACHE, ".config.cache") or return undef;
+ 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(CONFIG);
+ return "true";
+}
+
+sub makecache {
+ # Dump the contents of %config
+ print "Writing \033[1;32mcache file\033[0m for future ./configures ...\n";
+ open(FILEHANDLE, ">.config.cache");
+ foreach $key (keys %config) {
+ print FILEHANDLE "$key=\"$config{$key}\"\n";
+ }
+ close(FILEHANDLE);
+}
+
+sub dir_check {
+ my ($desc, $hash_key) = @_;
+ my $complete = 0;
+ while (!$complete) {
+ print "In what directory $desc?\n";
+ print "[\033[1;32m$config{$hash_key}\033[0m] -> ";
+ chomp($var = <STDIN>);
+ if ($var eq "") {
+ $var = $config{$hash_key};
+ }
+ if ($var =~ /^\~\/(.+)$/) {
+ # Convert it to a full path..
+ $var = resolve_directory($ENV{HOME} . "/" . $1);
+ }
+ elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
+ {
+ # Assume relative Path was given.. fill in the rest.
+ $var = $this . "/$var";
+ }
+
+ $var = resolve_directory($var);
+ if (! -e $var) {
+ print "$var does not exist. Create it?\n[\033[1;32my\033[0m] ";
+ chomp($tmp = <STDIN>);
+ if (($tmp eq "") || ($tmp =~ /^y/i)) {
+ # Attempt to Create the Dir..
+
+ system("mkdir -p \"$var\" >> /dev/null 2>&1");
+ $chk = system("mkdir -p \"$var\" >> /dev/null 2>&1") / 256;
+ if ($chk != 0) {
+ 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";
+ }
+}
+
+sub getosflags {
+
+ $config{LDLIBS} = "-lstdc++";
+ $config{FLAGS} = "-fno-strict-aliasing -fPIC -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}";
+ $config{DEVELOPER} = "-fno-strict-aliasing -fPIC -Wall -Woverloaded-virtual -Wno-deprecated -g";
+ $SHARED = "-Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared";
+ $config{MAKEPROG} = "make";
+
+ if ($config{OSNAME} =~ /darwin/i) {
+ $config{FLAGS} = "-DDARWIN -frtti -fPIC -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}";
+ $SHARED = "-bundle -twolevel_namespace -undefined dynamic_lookup";
+ $config{LDLIBS} = "-ldl -lstdc++";
+ }
+
+ if ($config{OSNAME} =~ /OpenBSD/i) {
+ $config{MAKEPROG} = "gmake";
+ chomp($foo = `eg++ -dumpversion | cut -c 1`);
+ # theyre running the package version of gcc (eg++)... detect it and set up its version numbers.
+ # if theyre not running this, configure lets the build continue but they probably wont manage to
+ # compile as this standard version is 2.95.3!
+ if ($foo ne "") {
+ $config{CC} = "eg++";
+ chomp($config{GCCVER} = `eg++ -dumpversion | cut -c 1`); # we must redo these if we change the compiler path
+ }
+ return "OpenBSD";
+ }
+
+ if ($config{OSNAME} =~ /Linux/i) {
+ $config{LDLIBS} = "-ldl -lstdc++";
+ $config{FLAGS} = "-fno-strict-aliasing -fPIC -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}";
+ $config{FLAGS} .= " " . $ENV{CXXFLAGS} if exists($ENV{CXXFLAGS});
+ $config{LDLIBS} .= " " . $ENV{LDLIBS} if exists($ENV{LDLIBS});
+ $config{MAKEPROG} = "make";
+ if ($config{OSNAME} =~ /CYGWIN/) {
+ $config{FLAGS} = "-fno-strict-aliasing -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}";
+ $config{LDLIBS} = "";
+ $config{MAKEPROG} = "/usr/bin/make";
+ $config{MAKEORDER} = "ircd mods";
+ return "Cygwin";
+ } elsif ($config{OSNAME} eq "CYG-STATIC") {
+ $config{FLAGS} = "-fno-strict-aliasing -Wall -Woverloaded-virtual -Wno-deprecated $config{OPTIMISATI}";
+ $config{LDLIBS} = "";
+ $config{MAKEPROG} = "/usr/bin/make";
+ $config{MAKEORDER} = "mods ircd";
+ $config{STATICLIBS} = "modules/mods.a";
+ $config{STATIC_LINK} = "yes";
+ return "Cygwin-Static";
+ }
+ }
+
+ if ($config{OSNAME} =~ /FreeBSD/i) {
+ $config{FLAGS} .= " " . $ENV{CXXFLAGS} if exists($ENV{CXXFLAGS});
+ $config{LDLIBS} .= " " . $ENV{LDLIBS} if exists($ENV{LDLIBS});
+ }
+
+ if ($config{OSNAME} =~ /SunOS/i or $config{OSNAME} =~ /solaris/i)
+ {
+ # solaris/sunos needs these
+ # socket = bsd sockets api
+ # nsl = dns stuff
+ # rt = POSIX realtime extensions
+ # resolv = inet_aton only (why isnt this in nsl?!)
+ $config{MAKEPROG} = "gmake";
+ $config{LDLIBS} .= " -lsocket -lnsl -lrt -lresolv";
+ return "Solaris";
+ }
+
+ if($config{OSNAME} =~ /MINGW32/i)
+ {
+ # All code is position-independent on windows
+ $config{FLAGS} =~ s/-fPIC //;
+ return "MinGW";
+ }
+
+ return $config{OSNAME};
+}
+
+sub writefiles {
+ my($writeheader) = @_;
+ # First File.. inspircd_config.h
+ chomp(my $incos = `uname -n -s -r`);
+ chomp($version = `sh src/version.sh`);
+ chomp(my $revision2 = getrevision());
+ if ($writeheader == 1)
+ {
+ print "Writing \033[1;32minspircd_config.h\033[0m\n";
+ open(FILEHANDLE, ">include/inspircd_config.h");
+ my $NL = $config{NICK_LENGT}+1;
+ my $CL = $config{CHAN_LENGT}+1;
+ 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_FILE "$config{CONFIG_DIR}/inspircd.conf"
+#define MOD_PATH "$config{MODULE_DIR}"
+#define VERSION "$version"
+#define REVISION "$revision2"
+#define MAXCLIENTS $config{MAX_CLIENT}
+#define MAXCLIENTS_S "$config{MAX_CLIENT}"
+#define SOMAXCONN_S "$config{_SOMAXCONN}"
+#define MAX_DESCRIPTORS $config{MAX_DESCRIPTORS}
+#define NICKMAX $NL
+#define CHANMAX $CL
+#define MAXMODES $config{MAXI_MODES}
+#define IDENTMAX $config{MAX_IDENT}
+#define MAXQUIT $config{MAX_QUIT}
+#define MAXTOPIC $config{MAX_TOPIC}
+#define MAXKICK $config{MAX_KICK}
+#define MAXGECOS $config{MAX_GECOS}
+#define MAXAWAY $config{MAX_AWAY}
+#define OPTIMISATION $config{OPTIMITEMP}
+#define LIBRARYDIR "$config{LIBRARY_DIR}"
+#define SYSTEM "$incos"
+EOF
+print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
+
+ if ($config{OSNAME} =~ /SunOS/i) {
+ print FILEHANDLE "#define IS_SOLARIS\n";
+ }
+ if ($config{OSNAME} =~ /CYGWIN/i) {
+ print FILEHANDLE "#define IS_CYGWIN\n";
+ print FILEHANDLE "#ifndef FD_SETSIZE\n#define FD_SETSIZE 1024\n#endif\n";
+ }
+ if ($config{OSNAME} =~ /MINGW32/i) {
+ print FILEHANDLE "#define IS_MINGW\n";
+ }
+ if ($config{OSNAME} =~ /CYG-STATIC/i) {
+ print FILEHANDLE "#ifndef FD_SETSIZE\n#define FD_SETSIZE 1024\n#endif\n";
+ }
+ if ($config{STATIC_LINK} eq "yes") {
+ print FILEHANDLE "#define STATIC_LINK\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{IPV6} =~ /y/i) {
+ print FILEHANDLE "#define IPV6\n";
+ }
+ if ($config{SUPPORT_IP6LINKS} =~ /y/i) {
+ print FILEHANDLE "#define SUPPORT_IP6LINKS\n";
+ }
+ my $use_hiperf = 0;
+ if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
+ print FILEHANDLE "#define USE_KQUEUE\n";
+ $se = "socketengine_kqueue";
+ $use_hiperf = 1;
+ }
+ if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
+ print FILEHANDLE "#define USE_EPOLL\n";
+ $se = "socketengine_epoll";
+ $use_hiperf = 1;
+ }
+ if (($has_ports) && ($config{USE_PORTS} eq "y")) {
+ print FILEHANDLE "#define USE_PORTS\n";
+ $se = "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 FILEHANDLE "#define USE_SELECT\n";
+ $se = "socketengine_select";
+ }
+ print FILEHANDLE "\n#endif\n";
+ close(FILEHANDLE);
+ }
+
+ if ($writeheader)
+ {
+ open(FILEHANDLE, ">include/inspircd_se_config.h");
+ print FILEHANDLE <<EOF;
+/* Auto generated by configure, do not modify or commit to svn! */
+#ifndef __CONFIGURATION_SOCKETENGINE__
+#define __CONFIGURATION_SOCKETENGINE__
+
+#include "$se.h"
+
+#endif
+EOF
+ close(FILEHANDLE);
+ }
+
+
+ # Create a Modules List..
+ my $modules = "";
+ foreach $i (@modlist)
+ {
+ if ($config{STATIC_LINK} eq "yes") {
+ $modules .= "m_".$i.".o ";
+ }
+ else {
+ $modules .= "m_".$i.".so ";
+ }
+ }
+ chomp($modules); # Remove Redundant whitespace..
+
+ opendir(DIRHANDLE, "src/modules");
+ foreach $name (sort readdir(DIRHANDLE)) {
+ if ($name =~ /^m_(.+?)$/) {
+ if (opendir(MDIRHANDLE, "src/modules/$name") != 0) {
+ closedir(MDIRHANDLE);
+ $modules .= "$name.so ";
+ }
+ }
+ }
+ closedir(DIRHANDLE);
+
+
+ # Write all .in files.
+ my $tmp = "";
+ my $file = "";
+ my $exe = "inspircd";
+
+ if ($config{OSNAME} =~ /CYGWIN/i) {
+ $exe = "inspircd.exe";
+ }
+
+ opendir(DIRHANDLE, $this);
+
+ # 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(my $version = `sh ./src/version.sh`);
+ chomp(my $revision = getrevision());
+ $version = "$version(r$revision)";
+
+ my $LIBEXT = "so";
+ if ($config{IS_DARWIN} eq "YES")
+ {
+ $LIBEXT = "dylib";
+ }
+ # We can actually parse any file starting with . and ending with .inc,
+ # but right now we only parse .inspircd.inc to form './inspircd'
+
+ foreach $name (sort readdir(DIRHANDLE)) {
+ if ($name =~ /^\.(.+)\.inc$/) {
+ $file = $1;
+ # All .name.inc files need parsing!
+ $tmp = "";
+ open(FILEHANDLE, ".$file.inc");
+ while (<FILEHANDLE>) {
+ $tmp .= $_;
+ }
+ close(FILEHANDLE);
+
+ $tmp =~ s/\@CC\@/$config{CC}/;
+ $tmp =~ s/\@MAKEPROG\@/$config{MAKEPROG}/;
+ $tmp =~ s/\@FLAGS\@/$config{FLAGS}/;
+ $tmp =~ s/\@DEVELOPER\@/$config{DEVELOPER}/;
+ $tmp =~ s/\@LDLIBS\@/$config{LDLIBS}/;
+ $tmp =~ s/\@BASE_DIR\@/$config{BASE_DIR}/;
+ $tmp =~ s/\@CONFIG_DIR\@/$config{CONFIG_DIR}/;
+ $tmp =~ s/\@MODULE_DIR\@/$config{MODULE_DIR}/;
+ $tmp =~ s/\@BINARY_DIR\@/$config{BINARY_DIR}/;
+ $tmp =~ s/\@LIBRARY_DIR\@/$config{LIBRARY_DIR}/;
+ $tmp =~ s/\@LIBRARY_EXT\@/$LIBEXT/;
+ $tmp =~ s/\@MODULES\@/$modules/;
+ $tmp =~ s/\@STARTSCRIPT\@/$config{STARTSCRIPT}/;
+ $tmp =~ s/\@DESTINATION\@/$config{DESTINATION}/;
+ $tmp =~ s/\@EXTRA_DIR\@/$config{EXTRA_DIR}/;
+ $tmp =~ s/\@EXECUTABLE\@/$exe/;
+ $tmp =~ s/\@MAKEORDER\@/$config{MAKEORDER}/;
+ $tmp =~ s/\@STATICLIBS\@/$config{STATICLIBS}/;
+ $tmp =~ s/\@VERSION\@/$version/;
+
+ print "Writing \033[1;32m$file\033[0m\n";
+ open(FILEHANDLE, ">$file");
+ print FILEHANDLE $tmp;
+ }
+ }
+ closedir(DIRHANDLE);
+
+ # Make inspircd executable!
+ chmod 0744, 'inspircd';
+
+ if ($config{STATIC_LINK} eq "yes") {
+ print "Writing static-build \033[1;32msrc/Makefile\033[0m\n";
+ write_static_makefile();
+ write_static_modules_makefile();
+ } elsif ($config{OSNAME} =~ /CYGWIN/i) {
+ print "Writing cygwin-build \033[1;32msrc/Makefile\033[0m\n";
+ write_static_makefile();
+ write_dynamic_modules_makefile();
+ } else {
+ print "Writing dynamic-build \033[1;32msrc/Makefile\033[0m\n";
+ write_dynamic_makefile();
+ write_dynamic_modules_makefile();
+ }
+}
+
+sub write_static_modules_makefile {
+ # Modules Makefile..
+ print "Writing \033[1;32msrc/modules/Makefile\033[0m\n";
+ open(FILEHANDLE, ">src/modules/Makefile");
+
+ ###
+ # Module Makefile Header
+ ###
+ print FILEHANDLE <<EOF;
+# (C) ChatSpike development team
+# Makefile by <Craig\@ChatSpike.net>
+# Many Thanks to Andrew Church <achurch\@achurch.org>
+# for assisting with making this work right.
+#
+# Automatically Generated by ./configure to add a modules
+# please run ./configure --update
+
+all: \$(MODULES)
+
+EOF
+ ###
+ # End Module Makefile Header
+ ###
+
+ # Create a Modules List..
+ my $modules = "";
+ my $cmflags = "";
+ my $liflags = "";
+
+ open(MODLIST,">include/modlist.h");
+
+ ###
+ # Include File Header
+ ###
+ print MODLIST <<HEADER;
+// Generated automatically by configure. DO NOT EDIT!
+
+#ifndef __SYMBOLS__H_CONFIGURED__
+#define __SYMBOLS__H_CONFIGURED__
+
+HEADER
+ ###
+ # End Include File Header
+ ###
+
+ # Place Module List into Include
+ foreach $i (@modlist) {
+ if ($i !~ /_static$/) {
+ print MODLIST "extern \"C\" void * $i\_init (void);\n";
+ }
+ }
+ print MODLIST "\nstruct {const char *name; initfunc *value; } modsyms[] = {\n";
+
+ ###
+ # Build Module Crap
+ ###
+ foreach $i (@modlist)
+ {
+ if ($i !~ /_static$/) {
+ $cmflags = getcompilerflags("src/modules/m_".$i.".cpp");
+ $liflags = getlinkerflags("src/modules/m_".$i.".cpp");
+ $deps = getdependencies("src/modules/m_".$i.".cpp");
+
+ #print "file: $i: cmflags=$cmflags; liflags=$liflags; deps=$deps\n";
+
+ ###
+ # Write Entry to the Makefile
+ ###
+ print FILEHANDLE <<EOCHEESE;
+m_$i.o: .m_$i\_static.cpp ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h $deps
+ \$(CC) -pipe -I../../include \$(FLAGS) $flags -export-dynamic -c .m_$i\_static.cpp
+ mv .m_$i\_static.o ../m_$i.o
+
+EOCHEESE
+ ###
+ # End Write Entry to the MakeFile
+ ###
+ print "Configuring module [\033[1;32mm_$i.so\033[0m] for static linking... ";
+ open(MODULE,"<src/modules/m_".$i.".cpp") or die("Could not open m_".$i.".cpp");
+ open(MUNGED,">src/modules/.m_".$i."_static.cpp") or die("Could not create .m_".$i."_static.cpp");
+ while (chomp($a = <MODULE>)) {
+ $a =~ s/init_module/$i\_init/g;
+ print MUNGED "$a\n";
+ }
+ close(MODULE);
+ close(MUNGED);
+ print MODLIST <<EOENT;
+{"m_$i.so",\&$i\_init},
+EOENT
+ print "done\n";
+ }
+ }
+
+ print MODLIST "{0}};\n\n#endif\n";
+ close(MODLIST);
+}
+
+sub write_dynamic_modules_makefile {
+ # Modules Makefile..
+ print "Writing \033[1;32msrc/modules/Makefile\033[0m\n";
+ open(FILEHANDLE, ">src/modules/Makefile");
+ my $extra = "";
+
+ if ($config{OSNAME} =~ /CYGWIN/i) {
+ $extra = "../inspircd.dll.a";
+ }
+
+###
+# Module Makefile Header
+###
+ print FILEHANDLE <<EOF;
+# (C) ChatSpike development team
+# Makefile by <Craig\@ChatSpike.net>
+# Many Thanks to Andrew Church <achurch\@achurch.org>
+# for assisting with making this work right.
+#
+# Automatically Generated by ./configure to add a modules
+# please run ./configure -update or ./configure -modupdate
+
+all: \$(MODULES)
+
+EOF
+ ###
+ # End Module Makefile Header
+ ###
+
+ # Create a Modules List..
+ my $modules = "";
+ my $cmflags = "";
+ my $liflags = "";
+ my $crud = "";
+
+ foreach $i (@modlist) {
+ ###
+ # Write Entry to the MakeFile
+ ###
+ $cmflags = getcompilerflags("src/modules/m_".$i.".cpp");
+ $liflags = getlinkerflags("src/modules/m_".$i.".cpp");
+ $deps = getdependencies("src/modules/m_".$i.".cpp");
+
+ #print "file: $i: cmflags=$cmflags; liflags=$liflags; deps=$deps\n";
+
+ print FILEHANDLE <<EOCHEESE;
+m_$i.so: m_$i.cpp ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h $deps
+ \$(CC) -pipe -I../../include \$(FLAGS) $cmflags -export-dynamic -c m_$i.cpp
+EOCHEESE
+
+if ($config{OSNAME} =~ /darwin/) {
+ print FILEHANDLE <<EOCHEESE;
+ \$(CC) -pipe -twolevel_namespace -undefined dynamic_lookup \$(FLAGS) -bundle $liflags -o m_$i.so m_$i.o $extra
+
+EOCHEESE
+} else {
+ print FILEHANDLE <<EOCHEESE;
+ \$(CC) -pipe \$(FLAGS) -shared $liflags -o m_$i.so m_$i.o $extra
+
+EOCHEESE
+}
+ $crud = $crud . " install -m \$(INSTMODE) m_$i.so \$(MODPATH)\n";
+###
+ # End Write Entry to the MakeFile
+ ###
+ }
+
+ opendir(DIRHANDLE, "src/modules");
+ foreach $name (sort readdir(DIRHANDLE)) {
+ if ($name =~ /^m_(.+?)$/) {
+ $crapola = "";
+ $crap3 = "";
+ # A module made of multiple files, in a dir, e.g. src/modules/m_spanningtree/
+ if (opendir(MDIRHANDLE, "src/modules/$name") != 0) {
+ my $i = 0;
+ print FILEHANDLE "$name.so: ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h";
+ foreach $fname (sort readdir(MDIRHANDLE)) {
+ if ($fname =~ /\.cpp$/) {
+ $cmflags = getcompilerflags("src/modules/$name/$fname");
+ $liflags = getlinkerflags("src/modules/$name/$fname");
+ $deps = getdependencies("src/modules/$name/$fname");
+ $oname = $fname;
+ $oname =~ s/\.cpp$/.o/g;
+ print FILEHANDLE " $name/$oname";
+ $crapola = $crapola . "$name/$oname: $name/$fname ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h $deps\n";
+ $crapola = $crapola . " \$(CC) -pipe -I../../include -I. \$(FLAGS) $cmflags -export-dynamic -o $name/$oname -c $name/$fname\n\n";
+ $crap3 = $crap3 . " $name/$oname";
+ $i++;
+ }
+ }
+ print "Composing Makefile rules for directory \033[1;32m$name\033[0m... (\033[1;32m$i files found\033[0m)\n";
+ if ($config{IS_DARWIN} eq "YES") {
+ print FILEHANDLE "\n \$(CC) -pipe -twolevel_namespace -undefined dynamic_lookup \$(FLAGS) -bundle -o $name.so $crap3\n";
+ } else {
+ print FILEHANDLE "\n \$(CC) -pipe \$(FLAGS) -shared $liflags -o $name.so $crap3\n";
+ }
+ print FILEHANDLE "\n$crapola\n";
+ closedir(MDIRHANDLE);
+ $crud = $crud . " install -m \$(INSTMODE) $name.so \$(MODPATH)\n";
+ }
+ }
+ }
+ closedir(DIRHANDLE);
+
+ print FILEHANDLE "modinst:\n \@echo \"Installing modules...\"\n" . $crud;
+}
+
+
+sub write_static_makefile {
+ open(FH,">src/Makefile") or die("Could not write src/Makefile!");
+ my $i = 0;
+ my @cmdlist = ();
+ opendir(DIRHANDLE, "src");
+ foreach $name (sort readdir(DIRHANDLE)) {
+ if ($name =~ /^cmd_(.+)\.cpp$/) {
+ $cmdlist[$i++] = $1;
+ }
+ }
+ closedir(DIRHANDLE);
+ my $cmdobjs = "";
+ my $srcobjs = "";
+ foreach my $cmd (@cmdlist) {
+ $cmdobjs = $cmdobjs . "cmd_$cmd.o ";
+ $srcobjs = $srcobjs . "cmd_$cmd.cpp ";
+ }
+ print FH <<EOM;
+# Insp Makefile :p
+#
+# (C) ChatSpike development team
+# Makefile by <Craig\@ChatSpike.net>
+# Makefile version 2 (statically linked core) by <brain\@inspircd.org>
+#
+
+CC = im a cheezeball
+
+CXXFLAGS = -I../include \${FLAGS}
+CPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* //' | grep -v svn)
+RELCPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* /modes\\//' | grep -v svn)
+
+EOM
+
+$se = "socketengine_select";
+if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
+ $se = "socketengine_kqueue";
+}
+elsif (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
+ $se = "socketengine_epoll";
+}
+elsif (($has_ports) && ($config{USE_PORTS} eq "y")) {
+ $se = "socketengine_ports";
+}
+
+ ###
+ # This next section is for cygwin dynamic module builds.
+ # Basically, what we do, is build the inspircd core as a library
+ # then the main executable uses that. the library is capable of
+ # loading / unloading the modules dynamically :)
+ # Massive thanks to the guys on #cygwin @ irc.freenode.net for helping
+ # make this work :)
+ ###
+
+ if ($config{OSNAME} =~ /CYGWIN/i) {
+ print FH <<EOM;
+all: timer.o command_parse.o cull_list.o userprocess.o socketengine.o socket.o hashcomp.o channels.o mode.o xline.o inspstring.o dns.o base.o configreader.o inspsocket.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o snomasks.o inspircd.exe
+
+inspircd.exe: inspircd.dll.a
+ \$(CC) -o \$@ \$^
+
+inspircd.dll inspircd.dll.a: inspircd.o channels.o mode.o xline.o inspstring.o dns.o base.o configreader.o inspsocket.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o hashcomp.o socket.o socketengine.o userprocess.o cull_list.o command_parse.o timer.o snomasks.o
+ \$(CC) -shared -Wl,--out-implib=inspircd.dll.a -o inspircd.dll \$^
+EOM
+ } else {
+ print FH <<EOM;
+all: timer.o command_parse.o cull_list.o userprocess.o socketengine.o socket.o hashcomp.o channels.o mode.o xline.o inspstring.o dns.o base.o configreader.o inspsocket.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o snomasks.o \$(MODULES) inspircd.exe
+
+inspircd.exe: inspircd.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/channels.h ../include/globals.h ../include/inspircd_config.h ../include/base.h
+ \$(CC) -I../include \$(FLAGS) inspircd.cpp -o inspircd.exe \$(LDLIBS) channels.o mode.o xline.o inspstring.o dns.o base.o inspsocket.o configreader.o $cmdobjs commands.o dynamic.o users.o modules.o wildcard.o helperfuncs.o hashcomp.o socket.o socketengine.o userprocess.o cull_list.o command_parse.o timer.o snomasks.o modes/modeclasses.a \$(MODULES)
+EOM
+ }
+
+ print FH <<EOM;
+
+cull_list.o: cull_list.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/users.h ../include/channels.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cull_list.cpp
+
+snomasks.o: snomasks.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/channels.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c snomasks.cpp
+
+command_parse.o: command_parse.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c command_parse.cpp
+
+userprocess.o: userprocess.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c userprocess.cpp
+
+socketengine.o: $se.cpp socketengine.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/$se.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socketengine.cpp $se.cpp
+
+hashcomp.o: hashcomp.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c hashcomp.cpp
+
+helperfuncs.o: helperfuncs.cpp ../include/base.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c helperfuncs.cpp
+
+channels.o: channels.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c channels.cpp
+
+mode.o: mode.cpp ../include/base.h ../include/mode.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(RELCPPFILES) modes/modeclasses.a
+ \${MAKE} -C "modes" DIRNAME="src/modes" CC="\$(CC)" \$(MAKEARGS)
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c mode.cpp
+
+xline.o: xline.cpp ../include/base.h ../include/xline.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c xline.cpp
+
+inspstring.o: inspstring.cpp ../include/base.h ../include/inspstring.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspstring.cpp
+
+dns.o: dns.cpp ../include/base.h ../include/dns.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dns.cpp
+
+base.o: base.cpp ../include/base.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c base.cpp
+
+configreader.o: configreader.cpp ../include/base.h ../include/configreader.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c configreader.cpp
+
+commands.o: commands.cpp ../include/base.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h $srcobjs
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c commands.cpp $cmdobjs
+
+dynamic.o: dynamic.cpp ../include/base.h ../include/dynamic.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dynamic.cpp
+
+users.o: users.cpp ../include/base.h ../include/users.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c users.cpp
+
+modules.o: modules.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c modules.cpp
+
+wildcard.o: wildcard.cpp ../include/base.h ../include/wildcard.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c wildcard.cpp
+
+socket.o: socket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socket.cpp
+
+inspsocket.o: inspsocket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspsocket.cpp
+
+timer.o: timer.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c timer.cpp
+
+EOM
+ foreach my $cmd (@cmdlist) {
+ print FH <<ITEM;
+cmd_$cmd.o: cmd_$cmd.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/commands/cmd_$cmd.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cmd_$cmd.cpp
+ITEM
+ }
+ close(FH);
+}
+
+sub write_dynamic_makefile {
+
+ my $i = 0;
+ my @cmdlist = ();
+ opendir(DIRHANDLE, "src");
+ foreach $name (sort readdir(DIRHANDLE)) {
+ if ($name =~ /^cmd_(.+)\.cpp$/) {
+ $cmdlist[$i++] = $1;
+ }
+ }
+ closedir(DIRHANDLE);
+
+ my $cmdobjs = "";
+ my $srcobjs = "";
+ foreach my $cmd (@cmdlist) {
+ $cmdobjs = $cmdobjs . "cmd_$cmd.so ";
+ $srcobjs = $srcobjs . "cmd_$cmd.cpp ";
+ }
+
+ $se = "socketengine_select";
+ if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
+ $se = "socketengine_kqueue";
+ }
+ elsif (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
+ $se = "socketengine_epoll";
+ }
+ elsif (($has_ports) && ($config{USE_PORTS} eq "y")) {
+ $se = "socketengine_ports";
+ }
+
+ open(FH,">src/Makefile") or die("Could not write src/Makefile");
+ print FH <<EOM;
+# Insp Makefile :p
+#
+# (C) ChatSpike development team
+# Makefile by <Craig\@ChatSpike.net>
+# Makefile version 2 (dynamically linked core) by <brain\@inspircd.org>
+#
+
+CC = im a cheezeball
+
+CXXFLAGS = -I../include \${FLAGS}
+CPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* //' | grep -v svn)
+RELCPPFILES = \$(shell /bin/ls -l modes/ | grep '\\.cpp' | sed 's/^.* /modes\\//' | grep -v svn)
+
+EOM
+
+if ($config{IS_DARWIN} eq "YES") {
+ print FH <<EOM;
+all: libIRCDtimer.dylib libIRCDcull_list.dylib libIRCDuserprocess.dylib libIRCDsocketengine.dylib libIRCDsocket.dylib libIRCDhash.dylib libIRCDchannels.dylib libIRCDmode.dylib libIRCDxline.dylib libIRCDstring.dylib libIRCDasyncdns.dylib libIRCDbase.dylib libIRCDconfigreader.dylib libIRCDinspsocket.dylib libIRCDcommands.dylib libIRCDdynamic.dylib libIRCDusers.dylib libIRCDmodules.dylib libIRCDwildcard.dylib libIRCDhelper.dylib libIRCDcommand_parse.dylib libIRCDsnomasks.dylib inspircd
+
+inspircd: inspircd.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/channels.h ../include/globals.h ../include/inspircd_config.h ../include/socket.h $cmdobjs libIRCDtimer.dylib libIRCDcull_list.dylib libIRCDuserprocess.dylib libIRCDsocketengine.dylib libIRCDsocket.dylib libIRCDhash.dylib libIRCDchannels.dylib libIRCDmode.dylib libIRCDxline.dylib libIRCDstring.dylib libIRCDasyncdns.dylib libIRCDbase.dylib libIRCDconfigreader.dylib libIRCDinspsocket.dylib libIRCDsnomasks.dylib libIRCDcommands.dylib libIRCDdynamic.dylib libIRCDusers.dylib libIRCDmodules.dylib libIRCDwildcard.dylib libIRCDhelper.dylib libIRCDcommand_parse.dylib
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspircd.cpp
+ \$(CC) -pipe -dynamic -bind_at_load -L. -o inspircd \$(LDLIBS) inspircd.o libIRCDchannels.dylib libIRCDmode.dylib libIRCDxline.dylib libIRCDstring.dylib libIRCDasyncdns.dylib libIRCDbase.dylib libIRCDconfigreader.dylib libIRCDinspsocket.dylib libIRCDcommands.dylib libIRCDdynamic.dylib libIRCDusers.dylib libIRCDmodules.dylib libIRCDwildcard.dylib libIRCDhelper.dylib libIRCDhash.dylib libIRCDsocket.dylib libIRCDsocketengine.dylib libIRCDuserprocess.dylib libIRCDcull_list.dylib libIRCDcommand_parse.dylib libIRCDtimer.dylib libIRCDsnomasks.dylib
+
+libIRCDsocketengine.dylib: $se.cpp socketengine.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/$se.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socketengine.cpp
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c $se.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDsocketengine.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDsocketengine.dylib socketengine.o $se.o
+
+libIRCDsnomasks.dylib: snomasks.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/channels.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c snomasks.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDsnomasks.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDsnomasks.dylib snomasks.o
+
+libIRCDcommand_parse.dylib: command_parse.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c command_parse.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDcommand_parse.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDcommand_parse.dylib command_parse.o
+
+libIRCDcull_list.dylib: cull_list.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/users.h ../include/channels.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cull_list.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDcull_list.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDcull_list.dylib cull_list.o
+
+libIRCDuserprocess.dylib: userprocess.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c userprocess.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDuserprocess.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDuserprocess.dylib userprocess.o
+
+libIRCDhash.dylib: hashcomp.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c hashcomp.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDhash.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDhash.dylib hashcomp.o
+
+libIRCDhelper.dylib: helperfuncs.cpp ../include/base.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c helperfuncs.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDhelper.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDhelper.dylib helperfuncs.o
+
+libIRCDchannels.dylib: channels.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c channels.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDchannels.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDchannels.dylib channels.o
+
+libIRCDmode.dylib: mode.cpp ../include/base.h ../include/mode.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(RELCPPFILES)
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c mode.cpp
+ \${MAKE} -C "modes" DIRNAME="src/modes" CC="\$(CC)" \$(MAKEARGS) CPPFILES="\$(CPPFILES)"
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDmode.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDmode.dylib mode.o modes/modeclasses.a
+
+libIRCDxline.dylib: xline.cpp ../include/base.h ../include/xline.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c xline.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDxline.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDxline.dylib xline.o
+
+libIRCDstring.dylib: inspstring.cpp ../include/base.h ../include/inspstring.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspstring.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDstring.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDstring.dylib inspstring.o
+
+libIRCDasyncdns.dylib: dns.cpp ../include/base.h ../include/dns.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dns.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDasyncdns.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDasyncdns.dylib dns.o
+
+libIRCDbase.dylib: base.cpp ../include/base.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c base.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDbase.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDbase.dylib base.o
+
+libIRCDconfigreader.dylib: configreader.cpp ../include/base.h ../include/configreader.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c configreader.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDconfigreader.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDconfigreader.dylib configreader.o
+
+libIRCDcommands.dylib: commands.cpp ../include/base.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c commands.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDcommands.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDcommands.dylib commands.o
+
+libIRCDdynamic.dylib: dynamic.cpp ../include/base.h ../include/dynamic.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dynamic.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDdynamic.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDdynamic.dylib dynamic.o
+
+libIRCDusers.dylib: users.cpp ../include/base.h ../include/users.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c users.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDusers.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDusers.dylib users.o
+
+libIRCDmodules.dylib: modules.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c modules.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDmodules.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDmodules.dylib modules.o
+
+libIRCDwildcard.dylib: wildcard.cpp ../include/base.h ../include/wildcard.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c wildcard.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDwildcard.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDwildcard.dylib wildcard.o
+
+libIRCDsocket.dylib: socket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socket.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDsocket.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDsocket.dylib socket.o
+
+libIRCDinspsocket.dylib: inspsocket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspsocket.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDinspsocket.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDinspsocket.dylib inspsocket.o
+
+libIRCDtimer.dylib: timer.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c timer.cpp
+ \$(CC) -pipe -install_name $config{LIBRARY_DIR}/libIRCDtimer.dylib -dynamiclib -twolevel_namespace -undefined dynamic_lookup -o libIRCDtimer.dylib timer.o
+
+EOM
+
+} else {
+
+ print FH <<EOM;
+all: libIRCDtimer.so libIRCDcull_list.so libIRCDuserprocess.so libIRCDsocketengine.so libIRCDsocket.so libIRCDhash.so libIRCDchannels.so libIRCDmode.so libIRCDxline.so libIRCDstring.so libIRCDasyncdns.so libIRCDbase.so libIRCDconfigreader.so libIRCDinspsocket.so $cmdobjs libIRCDcommands.so libIRCDdynamic.so libIRCDusers.so libIRCDmodules.so libIRCDwildcard.so libIRCDhelper.so libIRCDcommand_parse.so libIRCDsnomasks.so inspircd
+
+inspircd: inspircd.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/channels.h ../include/globals.h ../include/inspircd_config.h ../include/socket.h libIRCDtimer.so libIRCDcull_list.so libIRCDuserprocess.so libIRCDsocketengine.so libIRCDsocket.so libIRCDhash.so libIRCDchannels.so libIRCDmode.so libIRCDxline.so libIRCDstring.so libIRCDasyncdns.so libIRCDbase.so libIRCDconfigreader.so libIRCDinspsocket.so $cmdobjs libIRCDsnomasks.so libIRCDcommands.so libIRCDdynamic.so libIRCDusers.so libIRCDmodules.so libIRCDwildcard.so libIRCDhelper.so libIRCDcommand_parse.so
+ \$(CC) -pipe -I../include $extra -Wl,--rpath -Wl,$config{LIBRARY_DIR} \$(FLAGS) -rdynamic -L. inspircd.cpp -o inspircd \$(LDLIBS) libIRCDchannels.so libIRCDmode.so libIRCDxline.so libIRCDstring.so libIRCDasyncdns.so libIRCDbase.so libIRCDconfigreader.so libIRCDinspsocket.so libIRCDcommands.so libIRCDdynamic.so libIRCDusers.so libIRCDmodules.so libIRCDwildcard.so libIRCDhelper.so libIRCDhash.so libIRCDsocket.so libIRCDsocketengine.so libIRCDuserprocess.so libIRCDcull_list.so libIRCDcommand_parse.so libIRCDtimer.so libIRCDsnomasks.so
+
+libIRCDsocketengine.so: $se.cpp socketengine.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/$se.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socketengine.cpp $se.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDsocketengine.so socketengine.o $se.o
+
+libIRCDsnomasks.so: snomasks.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/channels.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c snomasks.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDsnomasks.so snomasks.o
+
+libIRCDcommand_parse.so: command_parse.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c command_parse.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDcommand_parse.so command_parse.o
+
+libIRCDcull_list.so: cull_list.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h ../include/users.h ../include/channels.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cull_list.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDcull_list.so cull_list.o
+
+libIRCDuserprocess.so: userprocess.cpp ../include/base.h ../include/hashcomp.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c userprocess.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDuserprocess.so userprocess.o
+
+libIRCDhash.so: hashcomp.cpp ../include/base.h ../include/hashcomp.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c hashcomp.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDhash.so hashcomp.o
+
+libIRCDhelper.so: helperfuncs.cpp ../include/base.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c helperfuncs.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDhelper.so helperfuncs.o
+
+libIRCDchannels.so: channels.cpp ../include/base.h ../include/channels.h ../include/inspircd.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c channels.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDchannels.so channels.o
+
+libIRCDmode.so: mode.cpp ../include/base.h ../include/mode.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h \$(RELCPPFILES)
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c mode.cpp
+ \${MAKE} -C "modes" DIRNAME="src/modes" CC="\$(CC)" \$(MAKEARGS) CPPFILES="\$(CPPFILES)"
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDmode.so mode.o modes/modeclasses.a
+
+libIRCDxline.so: xline.cpp ../include/base.h ../include/xline.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c xline.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDxline.so xline.o
+
+libIRCDstring.so: inspstring.cpp ../include/base.h ../include/inspstring.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspstring.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDstring.so inspstring.o
+
+libIRCDasyncdns.so: dns.cpp ../include/base.h ../include/dns.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dns.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDasyncdns.so dns.o
+
+libIRCDbase.so: base.cpp ../include/base.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c base.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDbase.so base.o
+
+libIRCDconfigreader.so: configreader.cpp ../include/base.h ../include/configreader.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c configreader.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDconfigreader.so configreader.o
+
+libIRCDcommands.so: commands.cpp ../include/base.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c commands.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDcommands.so commands.o
+
+libIRCDdynamic.so: dynamic.cpp ../include/base.h ../include/dynamic.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c dynamic.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDdynamic.so dynamic.o
+
+libIRCDusers.so: users.cpp ../include/base.h ../include/users.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c users.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDusers.so users.o
+
+libIRCDmodules.so: modules.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c modules.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDmodules.so modules.o
+
+libIRCDwildcard.so: wildcard.cpp ../include/base.h ../include/wildcard.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c wildcard.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDwildcard.so wildcard.o
+
+libIRCDsocket.so: socket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c socket.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDsocket.so socket.o
+
+libIRCDinspsocket.so: inspsocket.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c inspsocket.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDinspsocket.so inspsocket.o
+
+libIRCDtimer.so: timer.cpp ../include/base.h ../include/inspircd.h ../include/globals.h ../include/inspircd_config.h ../include/timer.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c timer.cpp
+ \$(CC) -pipe -Wl,--rpath -Wl,$config{LIBRARY_DIR} -shared -o libIRCDtimer.so timer.o
+
+EOM
+}
+ foreach my $cmd (@cmdlist) {
+ print FH <<ITEM;
+cmd_$cmd.so: cmd_$cmd.cpp ../include/base.h ../include/modules.h ../include/inspircd.h ../include/channels.h ../include/users.h ../include/globals.h ../include/inspircd_config.h ../include/commands/cmd_$cmd.h
+ \$(CC) -pipe -I../include \$(FLAGS) -export-dynamic -c cmd_$cmd.cpp
+ \$(CC) -pipe $SHARED -o cmd_$cmd.so cmd_$cmd.o
+
+ITEM
+ }
+ close(FH);
+}
+
diff --git a/docs/COPYING b/docs/COPYING
index de02fcca0..2ef0e3171 100644
--- a/docs/COPYING
+++ b/docs/COPYING
@@ -1 +1,346 @@
-NOTE: InspIRCd is licensed under GPL version 2 only. "upgrading" to a later version of the GENERAL PUBLIC LICENSE is not permitted. For further information on this, please contact us at irc.inspircd.org on #inspircd. ---------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) 19yy <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. \ No newline at end of file
+NOTE: InspIRCd is licensed under GPL version 2 only.
+ "upgrading" to a later version of the GENERAL PUBLIC
+ LICENSE is not permitted. For further information on
+ this, please contact us at irc.inspircd.org on #inspircd.
+
+----------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/docs/README b/docs/README
index 25423247a..fdc6ac3b2 100644
--- a/docs/README
+++ b/docs/README
@@ -1 +1,10 @@
-Because of the dynamic nature of InspIRCd, we do not have traditional documentation in our tarball. The documentation for InspIRCd can be found on our wiki, at http://www.inspircd.org/wiki Our bugtracker can be found at http://www.inspircd.org/bugtrack Our forums can be found at http://www.inspircd.org/forum Our development blog can be found at http://www.inspircd.com For online support, please connect to irc.inspircd.org, and join #inspircd. -- The InspIRCd Team \ No newline at end of file
+Because of the dynamic nature of InspIRCd, we do not have traditional documentation in our tarball.
+
+The documentation for InspIRCd can be found on our wiki, at http://www.inspircd.org/wiki
+Our bugtracker can be found at http://www.inspircd.org/bugtrack
+Our forums can be found at http://www.inspircd.org/forum
+Our development blog can be found at http://www.inspircd.com
+
+For online support, please connect to irc.inspircd.org, and join #inspircd.
+
+ -- The InspIRCd Team
diff --git a/docs/inspircd.conf.example b/docs/inspircd.conf.example
index 973f22301..1fef8f707 100644
--- a/docs/inspircd.conf.example
+++ b/docs/inspircd.conf.example
@@ -1 +1,2195 @@
-######################################################################## # # # ___ ___ ____ ____ _ # # |_ _|_ __ ___ _ __|_ _| _ \ / ___|__| | # # | || '_ \/ __| '_ \| || |_) | | / _` | # # | || | | \__ \ |_) | || _ <| |__| (_| | # # |___|_| |_|___/ .__/___|_| \_\\____\__,_| # # |_| # # ____ __ _ _ _ # # / ___|___ _ __ / _(_) __ _ _ _ _ __ __ _| |_(_) ___ _ __ # # | | / _ \| '_ \| |_| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ # # | |__| (_) | | | | _| | (_| | |_| | | | (_| | |_| | (_) | | | | # # \____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| # # |___/ # # # ##################################||#################################### #||# ##################################||#################################### # # # This is an example of the config file for InspIRCd. # # Change the options to suit your network # # # # $Id$ # # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # Lines prefixed with READ THIS BIT, as shown above, are IMPORTANT # # lines, and you REALLY SHOULD READ THEM. Yes, THIS MEANS YOU. Even # # if you've configured InspIRCd before, these probably indicate # # something new or different to this version and you SHOULD READ IT. # # # ######################################################################## # # # Unalphabeticalise the modules list at your own risk # # # ######################################################################## #-#-#-#-#-#-#-#-#-#-#-#- SERVER DESCRIPTION -#-#-#-#-#-#-#-#-#-#-#-#- # # # Here is where you enter the information about your server. # # # # Syntax is as follows: # # # # <server name="server.name" # # description="Server Description" # # networkemail="Email address shown on g/k/z/q lines" # # network="MyNetwork"> # # # <server name="penguin.omega.org.za" description="Waddle World" network="Omega"> #-#-#-#-#-#-#-#-#-#-#-#- ADMIN INFORMATION -#-#-#-#-#-#-#-#-#-#-#-# # # # Describes the Server Administrator's real name (optionally), # # nick, and email address. # # # # Syntax is as follows: # # <admin name="real name" # # nick="nick name" # # email="email@address.com"> # # # <admin name="Johnny English" nick="MI5" email="MI5@the.best.secret.agent"> #-#-#-#-#-#-#-#-#-#-#-#- PORT CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#- # # # Enter the port and address bindings here. # # # # bind address - specifies which address ports bind to. Leaving this # # field blank binds the port to all IPs available. # # # # port - The port number to bind to. You may specify a port # # range here, e.g. "6667-6669,7000,7001". If you do # # this, the server will count each port within your # # range as a seperate binding, making the above # # example equivalent to five seperate bind tags. # # A failure on one port in the range does not prevent # # the entire range from being bound, just that one # # port number. # # # # type - can be 'clients' or 'servers'. The clients type is # # a standard tcp based socket, the servers type is a # # also a TCP based connection but of a different # # format. SSL support is provided by modules, to # # enable SSL support, please read the module section # # of this configuration file. # # # # ssl - When using m_ssl_gnutls.so or m_ssl_openssl.so # # modules, you must define this value to use ssl on # # that port. valid values are 'gnutls' or 'openssl' # # respectively. If the module is not loaded, this # # setting is ignored. # # # # transport - If you have m_spanningtree.so loaded, along with # # either of the SSL modules (m_ssl_gnutls or # # m_ssl_openssl) or m_ziplinks.so, then you may make # # use of this value. # # setting it to 'openssl' or 'gnutls' or 'zip' # # indicates that the port should accept connections # # using the given transport name. Transports are # # layers which sit on top of a socket and change the # # way data is sent and received, e.g. encryption, # # compression, and other such things. Because this # # may not be limited in use to just encryption, # # the 'ssl' value used for client ports does not # # exist for servers, and this value is used instead. # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # If you want to link servers to InspIRCd you must load the # # m_spanningtree module! Please see the modules list below for # # information on how to load this module! If you do not load this # # module, server ports will NOT be bound! # # # # Leaving address empty binds to all available interfaces # # # # Syntax is as follows: # # # # <bind address="ip address" port="port" type="clients"> # # <bind address="ip address" port="port" type="servers"> # # # # If InspIRCd is built for IPV6, and you wish to accept IPV4 clients, # # then you can specify IPV4 ip addresses here to bind. You may also # # use the 4in6 notation, ::ffff:1.2.3.4, where 1.2.3.4 is the IPV4 # # address to bind the port, but as of InspIRCd 1.1.1, this is not # # required. # # # # ------------------------------------------------------------------- # # # # PLEASE NOTE: If you have build InspIRCd as an ipv6 server, and you # # specify an empty bind address, the binding will be bound to ALL THE # # IPV6 IP ADDRESSES, and not the ipv4 addresses. If you are using an # # ipv6 enabled InspIRCd and want to bind to multiple IPV4 addresses # # in this way, you must specify them by hand. If you have built the # # server for ipv4 connections only, then specifying an empty bind # # address binds the port to all ipv4 IP addresses, as expected. # # # <bind address="" port="6000" type="clients"> <bind address="" port="6660-6669" type="clients" ssl="gnutls"> # When linking servers, the openssl and gnutls transports are largely # link-compatible and can be used alongside each other or either/or # on each end of the link without any significant issues. <bind address="" port="7000,7001" type="servers"> <bind address="1.2.3.4" port="7005" type="servers" transport="openssl"> #-#-#-#-#-#-#-#-#-#- DIE/RESTART CONFIGURATION -#-#-#-#-#-#-#-#-#-#- # # # You can configure the passwords here which you wish to use for # # the die and restart commands. Only trusted ircops who will # # need this ability should know the die and restart password. # # # # Syntax is as follows: # # <power diepass="die password" restartpass="restart password" # # pause="secs before dying"> # # # <power diepass="" restartpass="" pause="2"> #-#-#-#-#-#-#-#-#-# INCLUDE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#-# # # # This optional tag allows you to include another config file # # allowing you to keep your configuration tidy. The configuration # # file you include will be treated as part of the configuration file # # which includes it, in simple terms the inclusion is transparent. # # # # All paths to config files are relative to the directory of the main # # config file inspircd.conf, unless the filename starts with a forward# # slash (/) in which case it is treated as an absolute path. # # # # Syntax is as follows: # #<include file="file.conf"> # # # #-#-#-#-#-#-#-#-#-#- CONNECTIONS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # This is where you can configure which connections are allowed # # and denied access onto your server. The password is optional. # # You may have as many of these as you require. To allow/deny all # # connections, use a '*' or 0.0.0.0/0. # # # # Syntax is as follows: # # # # <connect allow="1.2.3.0/24" password="blahblah" # # timeout="10" timeout="blah" flood="5" # # threshold="8" pingfreq="120" sendq="99999" # # revcq="696969" localmax="3" globalmax="3" # # port="6660"> # # # # <connect deny="127.0.0.1" port="6667"> # # # # IP masks may be specified in CIDR format or wildcard format, # # for IPV4 and IPV6. You *cannot* use hostnames in the allow or # # deny field, as the state is applied before the user's DNS has # # been resolved. # # # # You may optionally include timeout="x" on any allow line, which # # specifies the amount of time given before an unknown connection # # is closed if USER/NICK/PASS are not given. This value is in secs # # # # You should also include a flood="x" line which indicates # # the number of lines a user may place into their buffer at once # # before they are disconnected for excess flood. This feature can # # not be disabled, however it can be set to extremely high values, # # rendering it effectively disabled. A recommended value is 10. # # A counter is maintained for each user which is reset every # # 'threshold' seconds and specifying this threshold value with # # threshold="X" indicates how often the counter is reset. For # # example, with flood="5" and threshold="8", the user may not send # # more than 5 lines in 8 secs. # # # # You may optionally specify the sendq size and ping frequency of # # each connect:allow line using the pingfreq="X" and sendq="X" # # settings as shown in the full example below. # # The ping frequency is specified in seconds, and the sendq size # # in bytes. It is recommended, although not enforced, that you # # should never set your sendq size to less than 8k. Send Queues are # # dynamically allocated and can grow as needed up to the maximum # # size specified. # # # # The optional recvq value is the maximum size which users in this # # group may grow their receive queue to. This is recommended to be # # kept pretty low compared to the sendq, as users will always # # receive more than they send in normal circumstances. The default # # if not specified is 4096. # # # # The sendq is the data waiting to be sent TO THE USER. # # The recvq is the data being received FROM THE USER. # # The names sendq and recvq are from the SERVER'S PERSPECTIVE not # # that of the user... Just to clear up any confusion or complaints # # that these are backwards :p # # # # The localmax and globalmax values can be used to enforce local # # and global session limits on connections. The session limits are # # counted against all users, but applied only to users within the # # class. For example, if you had a class 'A' which has a session # # limit of 3, and a class 'B' which has a session limit of 5, and # # somehow, two users managed to get into class B which also match # # class A, there is only one connection left for this IP now in A, # # but if they can connect again to B, there are three. You get the # # idea (i hope). # # # # The optional port value determines which port the connect tag is # # handling. If left out the connect tag covers all bound ports else # # only incoming connections on the specified port will match. Port # # tags may be used on connect allow and connect deny tags. # # # <connect allow="196.12.*" password="secret" port="6667"> <connect allow="*" timeout="60" flood="20" threshold="1" pingfreq="120" sendq="262144" recvq="8192" localmax="3" globalmax="3"> <connect deny="69.254.*"> <connect deny="3ffe::0/32"> #-#-#-#-#-#-#-#-#-#-#-#- CLASS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#- # # # Classes are a group of commands which are grouped together # # and given a unique name. They used to define which commands # # are available to certain types of Operators. # # # # Syntax is as follows: # # # # <class name="name" commands="oper commands"> # # # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # You are not forced to give these classes the names given below. # # You can create your own named classes, if you want, in fact that # # is the whole idea of this system! # # # # Note: It is possible to make a class which covers all available # # commands. To do this, specify commands="*". This is not really # # recommended, as it negates the whole purpose of the class system, # # however it is provided for fast configuration (e.g. in test nets) # # # <class name="Shutdown" commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOAD"> <class name="ServerLink" commands="CONNECT SQUIT RCONNECT MKPASSWD MKSHA256"> <class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE"> <class name="OperChat" commands="WALLOPS GLOBOPS SETIDLE SPYLIST SPYNAMES"> <class name="HostCloak" commands="SETHOST SETIDENT SETNAME CHGHOST CHGIDENT"> #-#-#-#-#-#-#-#-#-#-#-#- OPERATOR COMPOSITION -#-#-#-#-#-#-#-#-#-#-# # # # This is where you specify which types of operators you have on # # your server, as well as the commands they are allowed to use. # # This works alongside with the classes specified above. # # # # type name - a name for the combined class types # # a type name cannot contain spaces, however if you # # put an _ symbol in the name, it will be translated # # to a space when displayed in a WHOIS. # # # # classes - specified above, used for flexibility for the # # server admin to decide on which operators get # # what commands. Class names are case sensitive, # # seperate multiple class names with spaces. # # # # host - optional hostmask operators will receive on oper-up. # # # # Syntax is as follows: # # # # <type name="name" classes="class names" host="oper hostmask"> # # # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # You are not forced to give these types the names given below. # # You can create your own named types, if you want, in fact that # # is the whole idea of this system! # # # <type name="NetAdmin" classes="OperChat BanControl HostCloak Shutdown ServerLink" host="netadmin.omega.org.za"> <type name="GlobalOp" classes="OperChat BanControl HostCloak ServerLink" host="ircop.omega.org.za"> <type name="Helper" classes="HostCloak" host="helper.omega.org.za"> #-#-#-#-#-#-#-#-#-#-#- OPERATOR CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # Opers are defined here. This is a very important section. # # Remember to only make operators out of truthworthy people. # # # # name - oper name, This is case sensitive, so it is best to # # use lower-case. # # # # password - password to oper-up, also case sensitive. # # encryption is supported via modules. You may load # # modules for MD5 or SHA256 encryption, and if you do, # # this value will be a hash value, otherwise put a # # plaintext password in this value. # # # # host - hosts of client allowed to oper-up. # # wildcards accepted, seperate multiple hosts with a # # space. You may also specify CIDR ip addresses. # # # # fingerprint - When using the m_ssl_oper_cert.so module, you may # # specify a key fingerprint here. This can be obtained # # using the /fingerprint command whilst the module is # # loaded, or from the notice given to you when you # # connect to the ircd using a client certificate, # # and will lock this oper block to only the user who # # has that specific key/certificate pair. # # This enhances security a great deal, however it # # requires that opers use clients which can send ssl # # client certificates, if this is configured for that # # oper. Note that if the m_ssl_oper.so module is not # # loaded, and/or one of m_ssl_openssl or m_ssl_gnutls # # is not loaded, this configuration option has no # # effect and will be ignored. # # # # type - Defines the kind of operator. This must match a type # # tag you defined above, and is case sensitive. # # # # Syntax is as follows: # # <oper name="login" # # password="pass" # # host="hostmask@of.oper" # # fingerprint="hexsequence" # # type="oper type"> # # # <oper name="Brain" password="s3cret" host="ident@dialup15.isp.com *@localhost *@server.com *@3ffe::0/16" type="NetAdmin"> #-#-#-#-#-#-#-#-#-#-#- SERVER LINK CONFIGURATION -#-#-#-#-#-#-#-#-#-# # # # Defines which servers can link to this one, and which servers this # # server may create outbound links to. # # # # name - The name is the canocial name of the server, does # # not have to resolve - but it is expected to be set # # in the remote servers connection info. # # # # ipaddr - Valid host or ip address for remote server. These # # hosts are resolved on rehash, and cached, if you # # specify a hostname, so if you find that your server # # is still trying to connect to an old IP after you # # have updated your dns, try rehashing and then # # attempting the connect again. # # # # port - The TCP port for the remote server. # # # # sendpass - Password to send to create an outbound connection # # to this server. # # # # recvpass - Password to receive to accept an inbound connection # # from this server. # # # # autoconnect - Sets the server to autoconnect. Where x is the num. # # (optional) of seconds between attempts. e.g. 300 = 5 minutes. # # # # transport - If defined, this is a transport name implemented by # # another module. Transports are layers on top of # # plaintext connections, which alter them in certain # # ways. Currently the three supported transports are # # 'openssl' and 'gnutls' which are types of SSL # # encryption, and 'zip' which is for compression. # # If you define a transport, both ends of the # # connection must use a compatible transport for the # # link to succeed. OpenSSL and GnuTLS are link- # # compatible with each other. # # # # statshidden - When using m_spanningtree.so for linking. you may # # set this to 'yes', and if you do, the IP address/ # # hostname of this connection will NEVER be shown to # # any opers on the network. In /STATS c its address # # will show as *@<hidden>, and during CONNECT and # # inbound connections, its IP will show as <hidden> # # UNLESS the connection fails (e.g. due to a bad # # password or servername) # # # # allowmask - When this is defined, it indicates a range of IP # # addresses to allow for this link (You may use CIDR # # or wildcard form for this address). # # e.g. if your server is going to connect to you from # # the range 1.2.3.1 through 1.2.3.255, put 1.2.3.0/24 # # into this value. If it is not defined, then only # # the ipaddr field of the server shall be allowed. # # # # failover - If you define this option, it must be the name of a # # different link tag in your configuration. This # # option causes the ircd to attempt a connection to # # the failover link in the event that the connection # # to this server fails. For example, you could define # # two hub uplinks to a leaf server, and set an # # american server to autoconnect, with a european # # hub as its failover. In this situation, your ircd # # will only try the link to the european hub if the # # american hub is unreachable. NOTE that for the # # intents and purposes of this option, an unreachable # # server is one which DOES NOT ANSWER THE CONNECTION. # # If the server answers the connection with accept(), # # EVEN IF THE CREDENTIALS ARE INVALID, the failover # # link will not be tried! Failover settings will also # # apply to autoconnected servers as well as manually # # connected ones. # # # # timeout - If this is defined, then outbound connections will # # time out if they are not connected within this many # # seconds. If this is not defined, the default of ten # # seconds is used. # # # # bind - If you specify this value, then when creating an # # outbound connection to the given server, the IP you # # place here will be bound to. This is for multi- # # homed servers which may have multiple IP addresses. # # If you do not define this value, the first IP that # # is not empty or localhost from your <bind> tags # # will be bound to. This is usually acceptable, # # however if your server has multiple network cards # # then you may have to manually specify the bind # # value instead of leaving it to automatic binding. # # You can usually tell if you need to set this by # # looking for the error 'Could not assign requested # # address' in your log when connecting to servers. # # # # hidden - If this is set to true, yes, or 1, then the server # # is completely hidden from non-opers. It does not # # show in LINKS and it does not show in MAP. Also, # # any servers which are child servers of this one # # in the network will *also* be hidden. Use with # # care! You can use this to 'mask off' sections of # # the network so that users only see a small portion # # of a much larger net. It should NOT be relied upon # # as a security tool, unless it is being used for # # example to hide a non-client hub, for which clients # # do not have an IP address or resolvable hostname. # # # # to u:line a server (give it extra privilages required for running # # services, Q, etc) you must include the <uline server> tag as shown # # in the example below. You can have as many of these as you like. # # # # WARNING: Unlike other ircds, u:lining a server allows ALL users on # # that server to operoverride modes. This should only be used for # # services and protected oper servers! # # # # ------------------------------------------------------------------- # # # # NOTE: If you have built your server as an ipv6 server, then when a # # DNS lookup of a server's host occurs, AAAA records (ipv6) are # # priorotized over A records (ipv4). Therefore, if the server you are # # connecting to has both an IPV6 ip address and an IPV4 ip address in # # its DNS entry, the IPV6 address will *always* be selected. To # # change this behaviour simply specify the IPV4 IP address rather # # than the hostname of the server. # # # # ------------------------------------------------------------------- # # # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # If you want to link servers to InspIRCd you must load the # # m_spanningtree module! Please see the modules list below for # # information on how to load this module! If you do not load this # # module, server links will NOT work! # # # # Also, if you define any transports, you must load the modules for # # these transports BEFORE you load m_spanningtree, e.g. place them # # above it in the configuration file. Currently this means the three # # modules m_ssl_gnutls, m_ziplinks and m_ssl_openssl, depending on # # which you choose to use. # # # <link name="hub.penguin.org" ipaddr="penguin.box.com" port="7000" allowmask="69.58.44.0/24" autoconnect="300" failover="hub.other.net" timeout="15" transport="gnutls" bind="1.2.3.4" statshidden="no" hidden="no" sendpass="outgoing!password" recvpass="incoming!password"> <link name="services.antarctic.com" ipaddr="localhost" port="7000" allowmask="127.0.0.0/8" sendpass="penguins" recvpass="polarbears"> #-#-#-#-#-#-#-#-#-#-#-#- ULINES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-# # This tag defines a ulined server. A U-Lined server has special # # permissions, and should be used with caution. Services servers are # # usually u-lined in this manner. # # # # The 'silent' value if set to yes indicates that this server should # # not generate quit and connect notices, which can cut down on noise # # to opers on the network. # # # <uline server="services.antarctic.com" silent="yes"> #-#-#-#-#-#-#-#-#-#- MISCELLANEOUS CONFIGURATION -#-#-#-#-#-#-#-#-#-# # # # These options let you define the path to your motd and rules # # files. If these are relative paths, they are relative to the # # configurtion directory. # # # <files motd="inspircd.motd.example" rules="inspircd.rules.example"> #-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # This optional configuration tag lets you define the maximum number # # of channels that both opers and users may be on at any one time. # # the default is 20 for user and 60 for opers if this tag is not # # defined. Remote users are not restricted in any manner. # # # <channels users="20" opers="60"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-# DNS SERVER -#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # Define your DNS server address here. InspIRCd has its own resolver. # # If you do not define this value, then then InspIRCd will attempt to # # determine your DNS server from your operating system. On POSIX # # platforms, InspIRCd will read /etc/resolv.conf, and populate this # # value with the first DNS server address found. On Windows platforms # # InspIRCd will check the registry, and use the DNS server of the # # first active network interface, if one exists. # # If a DNS server cannot be determined from these checks, the default # # value '127.0.0.1' is used instead. The timeout value is in seconds. # # # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # When choosing a server, be sure to choose one which will do a # # RECURSIVE LOOKUP. InspIRCd's resolver does not currently do these # # recursive lookups itself, to save time and resources. The dns # # server recommended by the InspIRCd team is bind, available from the # # ISC website. If your DNS server does not do a recursive lookup, you # # will be able to notice this by the fact that none of your users are # # resolving even though the DNS server appears to be up! Most ISP and # # hosting provider DNS servers support recursive lookups. # # # # ------------------------------------------------------------------- # # # # NOTE: if you have built InspIRCd with IPV6 support, then both # # ipv6 and ipv4 addresses are allowed here, and also in the system # # resolv.conf file. Remember that an ipv4 dns server can still # # resolve ipv6 addresses, and vice versa. # # # <dns server="127.0.0.1" timeout="5"> # An example of using an IPV6 nameserver #<dns server="::1" timeout="5"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-# PID FILE -#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # Define the path to the PID file here. The PID file can be used to # # rehash the ircd from the shell or to terminate the ircd from the # # shell using shell scripts, perl scripts etc, and to monitor the # # ircd's state via cron jobs. If this is a relative path, it will be # # relative to the configuration directory, and if it is not defined, # # the default of 'inspircd.pid' is used. # # # #<pid file="/path/to/inspircd.pid"> #-#-#-#-#-#-#-#-#-#-#-#-#- BANLIST LIMITS #-#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # Use these tags to customise the ban limits on a per channel basis. # # the tags are read from top to bottom, and any tag found which # # matches the channels name applies the banlimit to that channel. # # It is advisable to put an entry with the channel as '*' at the # # bottom of the list. If none are specified or no maxbans tag is # # matched, the banlist size defaults to 64 entries. # # # <banlist chan="#morons" limit="128"> <banlist chan="*" limit="69"> #-#-#-#-#-#-#-#-#-#-#- DISABLED COMMANDS -#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # This tag is optional, and specifies one or more commands which are # # not available to non-operators. For example you may wish to disable # # NICK and prevent non-opers from changing their nicknames. # # Note that any disabled commands take effect only after the user has # # 'registered' (e.g. after the initial USER/NICK/PASS on connection) # # so for example disabling NICK will not cripple your network. # # # #<disabled commands="TOPIC MODE"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#- RTFM LINE -#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # Just remove this... Its here to make you read ALL of the config # # file options ;) # <die value="You should probably edit your config *PROPERLY* and try again."> #-#-#-#-#-#-#-#-#-#-#-#-#- SERVER OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-# # # # Settings to define which features are useable on your server. # # # # prefixquit - A prefix to be placed on the start of a client's # # quit message # # # # suffixquit - A suffix to be placed on the end of a client's # # quit message. # # # # fixedquit - A fixed quit message to display for all client # # QUITS. If specified, overrides both prefixquit # # and suffixquit options. # # # # loglevel - specifies what detail of messages to log in the # # log file. You may select from debug, verbose, # # default, sparse and none. # # # # allowhalfop - allows the +h channel mode # # # # noservices - If noservices is true, yes, or 1, then the first # # user into a channel gets founder status. This is # # only useful on networks running the m_chanprotect # # module without services. # # # # qaprefixes - If qaprefixes is true, yes, or 1, then users # # with +q or +a will get the ~ or & prefixes # # used in unreal. This is only useful on networks # # running the m_chanprotect module # # # # deprotectself - If this value is set to yes, true, or 1, then any # # user with +q or +a may remove the +q or +a from # # themselves. The default setting is to not enable # # this feature, which stops even the founder taking # # away their founder status without using services. # # # # 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. The default setting is to not enable # # this feature, so that only +q may remove +a, and # # nothing but services may remove +q. # # # # cyclehosts - If this is set to true, yes or 1, then when a # # user's hostname changes, they will appear to quit # # and then rejoin with their new host. This prevents # # clients from being confused by host changes, # # especially in the case of bots, and it is # # recommended that this option is enabled. # # # # netbuffersize - size of the buffer used to receive data from # # clients. The ircd may only read() this amount # # of text in one go at any time. (OPTIONAL) # # # # maxwho - The maximum number of results returned by a /WHO # # query. This is to prevent /WHO being used as a # # spam vector or means of flooding an ircd. The # # default is 128, it is not recommended to raise it # # above 1024. Values up to 65535 are permitted. If # # this value is omitted, any size WHO is allowed by # # anyone. # # # # somaxconn - The maximum number of sockets that may be waiting # # in the accept queue. This usually allows the ircd # # to soak up more connections in a shorter space of # # time when increased but please be aware there is a # # system defined maximum value to this, the same way # # there is a system defined maximum number of file # # descriptors. Some systems may only allow this to # # be up to 5 (ugh) while others such as FreeBSD will # # default to a much nicer 128. # # # # moduledir - This optional value indicates a runtime change of # # the location where modules are to be found. This # # does not add a supplementary directory. There can # # only be one module path. # # # # softlimit - This optional feature allows a defined softlimit. # # if defined sets a soft maxconnections value, has # # to be less than the ./configure maxclients # # # # userstats - The userstats field is optional and specifies # # which stats characters in /STATS may be requested # # by non-operators. Stats characters in this field # # are case sensitive and are allowed to users # # independent of if they are in a module or the core # # # # operspywhois - If this is set then when an IRC operator uses # # /WHOIS on a user they will see all channels, even # # ones if channels are secret (+s), private (+p) or # # if the target user is invisible +i. # # # # customversion - If you specify this configuration item, and it is # # not set to an empty value, then when a user does # # a /VERSION command on the ircd, this string will # # be displayed as the second portion of the output, # # replacing the system 'uname', compile flags and # # socket engine/dns engine names. You may use this # # to enhance security, or simply for vanity. # # # # maxtargets - The maxtargets field is optional, and if not # # defined, defaults to 20. It indicates the maximum # # number of targets which may be given to commands # # such as PRIVMSG, KICK etc. # # # # hidesplits - When set to 'yes', will hide split server names # # from non-opers. Non-opers will see '*.net *.split' # # instead of the server names in the quit message, # # identical to the way IRCu displays them. # # # # hidebans - When set to 'yes', will hide gline, kline, zline # # and qline quit messages from non-opers. For # # example, user A who is not an oper will just see # # (G-Lined) while user B who is an oper will see the # # text (G-Lined: Reason here) instead. # # # # hidewhois - When defined with a non-empty value, the given # # text will be used in place of the user's server # # in WHOIS, when a user is WHOISed by a non-oper. # # For example, most nets will want to set this to # # something like '*.netname.net' to conceal the # # actual server the user is on. # # # # flatlinks - When you are using m_spanningtree.so, and this # # value is set to true, yes or 1, /MAP and /LINKS # # will be flattened when shown to a non-oper. # # # # hideulines - When you are using m_spanningtree.so, and this # # value is set to true, yes or 1, then U-lined # # servers will be hidden in /LINKS and /MAP. For non # # opers. Please be aware that this will also hide # # any leaf servers of a U-lined server, e.g. jupes. # # # # nouserdns - If set to 'yes', 'true' or '1', no user dns # # lookups will be performed for connecting users. # # this can save a lot of resources on very busy irc # # servers. # # # # syntaxhints - If set to 'yes', 'true' or '1', when a user does # # not give enough parameters for a command, a syntax # # hint will be given (using the RPL_TEXT numeric) # # as well as the standard ERR_NEEDMOREPARAMS. # # # # announcets - If this value is defined to 'yes', 'true' or '1', # # then if a channel's timestamp is updated the users # # on the channel will be informed of the change via # # a server notice to the channel with the old and # # new TS values in the timestamp. If you think this # # is just pointless noise, define the value to 0. # # # # ircumsgprefix - Use undernet style message prefix for channel # # NOTICE and PRIVMSG adding the prefix to the line # # of text sent out. Eg. NOTICE @#test :@ testing # # vs. the off setting: NOTICE @#test :testing # # # # hostintopic - If this is set to yes (the default) then the full # # nick!user@host is shown for who set a TOPIC last. # # if set to no, then only the nickname is shown. # # # # announceinvites # # - If this option is set to yes (the default), then # # invites are announced to the channel when a user # # invites annother user. If you consider this to be # # unnecessary noise, explicitly set this to no. # # # # disablehmac - If you are linking your InspIRCd to older versions # # then you can specify this option and set it to # # yes. 1.1.6 and above support HMAC and challenge- # # response for password authentication. These can # # greatly enhance security of your server to server # # connections when you are not using SSL (as is the # # case with a lot of larger networks). Linking to # # older versions of InspIRCd should not *usually* be # # a problem, but if you have problems with HMAC # # authentication, this option can be used to turn it # # off. # # # # hidemodes - If this option is enabled, then the listmodes # # given (e.g. +eI), will be hidden from users below # # halfop. This is not recommended to be set on mode # # +b, as it may break some features in popular # # clients such as mIRC. # # # # quietbursts - When synching or splitting from the network, a # # server can generate a lot of connect and quit # # snotices to the +C and +Q snomasks. Setting this # # value to yes squelches those messages, which can # # make them more useful for opers, however it will # # degrade their use by certain third party programs # # such as BOPM which rely on them to scan users when # # a split heals in certain configurations. # # # # pingwarning - This should be set to a number between 1 and 59 if # # defined, and if it is defined will cause the server# # to send out a warning via snomask +l if a server # # does not answer to PING after this many seconds. # # This can be useful for finding servers which are # # at risk of pinging out due to network issues. # # # # exemptchanops - This option allows channel operators to be exempted# # from certain channel modes. # # Supported modes are +SfgNc. Defaults to off. # # # # defaultmodes - The default modes to be given to each channel on # # creation. Defaults to 'nt'. There should be no + # # or - symbols in this sequence, if you add them # # they will be ignored. You may add parameters for # # parameterised modes. # # # # moronbanner - The NOTICE to show to users who are glined, zlined # # klined or qlined when they are disconnected. This # # is totally freeform, you may place any text here # # you wish. # # # <options prefixquit="Quit: " loglevel="default" netbuffersize="10240" maxwho="128" noservices="no" qaprefixes="no" deprotectself="no" deprotectothers="no" somaxconn="128" softlimit="12800" userstats="Pu" operspywhois="no" customversion="" maxtargets="20" hidesplits="no" hidebans="no" hidewhois="" flatlinks="no" hideulines="no" nouserdns="no" syntaxhints="no" cyclehosts="yes" ircumsgprefix="no" announcets="yes" disablehmac="no" hostintopic="yes" hidemodes="eI" quietbursts="yes" pingwarning="15" allowhalfop="yes" defaultmodes="nt" moronbanner="You're banned! Email haha@abuse.com with the ERROR line below for help." exemptchanops=""> #-#-#-#-#-#-#-#-#-#-#-#-#-#- TIME SYNC OPTIONS -#-#-#-#-#-#-#-#-#-#-#-# # Time sychronization options for m_spanningtree linking. # # # # Because IRC is very time and clock dependent, InspIRCd provides its # # own methods for syncronization of time between servers as shown # # in the example below, for servers that don't have ntpd running. # # # # enable - If this value is 'yes', 'true', or '1', time # # synchronization is enabled on this server. This # # means any servers you are linked to will # # automatically synchronize time, however you should # # use ntpd instead where possible, NOT this option. # # # # master - If this value is set to yes, then this server will # # act as the authoritative time source for the whole # # network. All other servers will respect its time # # without question, and match their times to it. # # only one server should have the master value set # # to 'yes'. # # # <timesync enable="no" master="no"> #-#-#-#-#-#-#-#-#-#-#-#-#- WHOWAS OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-# # # # This tag lets you define the behaviour of the /whowas command of # # your server. # # # # groupsize - Controls the maximum entries per nick shown when # # performing a /whowas nick. Setting this to 0 dis- # # ables whowas completely. # # # # maxgroups - The maximum number of nickgroups that can be added # # to the list. If max is reached, oldest group will # # be deleted first like a FIFO. A groupsize of 3 and # # a maxgroups of 5000 will allow for 5000 nicks to # # be stored with a history of 3, thus giving a total # # of 3 * 5000 = 15000 entries. A setting of 0 dis- # # ables whowas completely. # # # # maxkeep - The maximum time a nick is kept in the whowas list # # before being pruned. Time may be specified in # # seconds, or in the following format: 1y2w3d4h5m6s # # meaning one year, two weeks, three days, 4 hours, # # 5 minutes and 6 seconds. All fields in this format # # are optional. Minimum is 1 hour, if less InspIRCd # # will default back to 1 hour. # # # #<whowas groupsize="10" # # maxgroups="100000" # # maxkeep="3d"> # #-#-#-#-#-#-#-#-#-#-#-#-#- MODULE OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-# # # # These tags define which modules will be loaded on startup by your # # server. Add modules without any paths. When you make your ircd # # using the 'make' command, all compiled modules will be moved into # # the folder you specified when you ran ./configure. The module tag # # automatically looks for modules in this location. # # If you attempt to load a module outside of this location, either # # in the config, or via /LOADMODULE, you will receive an error. # # # # By default, ALL modules are commented out. You must uncomment them # # or add lines to your config to load modules. Please refer to # # http://www.inspircd.org/wiki/Modules_List for a list of modules and# # each modules link for any additional conf tags they require. # # # # You may use wildcards in a <module> tag to load all modules which # # match a glob pattern (e.g. m_sa????.so would load m_sajoin, # # m_sapart, m_saquit and m_sanick) # # # # ____ _ _____ _ _ ____ _ _ _ # # | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | # # | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | # # | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| # # |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) # # # # To link servers to InspIRCd, you MUST load the m_spanningtree # # module, as shown below. If you DO NOT do this, server links will # # NOT work at all. ie. The ports will NOT bind, and /connect will not # # work properly. This is by design, to allow for the implementation # # of other linking protocols in modules in the future. # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Spanning Tree module - allows linking of servers using the spanning # tree protocol (see the READ THIS BIT section above). # #<module name="m_spanningtree.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # MD5 Module - Allows other modules to generate MD5 hashes, usually for # cryptographic uses and security. # # IMPORTANT: # Other modules such as m_cloaking.so and m_opermd5.so may rely on # this module being loaded to function. # #<module name="m_md5.so"> # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SHA256 Module - Allows other modules to generate SHA256 hashes, # usually for cryptographic uses and security. # # IMPORTANT: # Other modules such as m_opermd5.so may rely on this module being # loaded to function. # #<module name="m_sha256.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Alias module: Allows you to define server-side command aliases #<module name="m_alias.so"> # #-#-#-#-#-#-#-#-#-#-#- 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.# # An alias tag requires the following values to be defined in it: # # # # text - The text to detect as the actual command line, # # Cant contain spaces, but case insensitive. # # You may have multiple aliases with the same # # command name (text="" value), however the first # # found will be executed if its format value is # # matched, or it has no format value. Aliases are # # read from the top of the file to the bottom. # # # # format - If this is defined, the parameters of the alias # # must match this glob pattern. For example if you # # want the first parameter to start with a # for # # the alias to be executed, set format="#*" in the # # alias definition. Note that the :'s which are # # part of IRC formatted lines will be preserved # # for matching of this text. This value is # # optional. # # # # replace - The text to replace 'text' with. Usually this # # will be "PRIVMSG ServiceName :$2-" or similar. # # You may use the variables $1 through $9 in the # # replace string, which refer to the first through # # ninth word in the original string typed by the # # user. You may also use $1- through $9- which # # refer to the first word onwards, through to the # # ninth word onwards, e.g. if the user types the # # command "foo bar baz qux quz" then $3- will hold # # "baz qux quz" and $2 will contain "bar". You may # # also use the special variables: $nick, $ident, # # $host and $vhost, and you may seperate multiple # # commands with \n. If you wish to use the ACTUAL # # characters \ and n together in a line, you must # # use the sequence "\\n". # # # # requires - If you provide a value for 'requires' this means # # the given nickname MUST be online for the alias # # to successfully trigger. If they are not, then # # the user receives a 'no such nick' 401 numeric. # # # # uline - Defining this value with 'yes', 'true' or '1' # # will ensure that the user given in 'requires' # # must also be on a u-lined server, as well as # # actually being on the network. If the user is # # online, but not on a u-lined server, then an # # oper-alert is sent out as this is possibly signs # # of a user trying to impersonate a service. # # # # operonly - Defining this value, with a value of 'yes', '1' # # or true will make the alias oper only. If a non- # # oper attempts to use the alias, it will appear # # to not exist. # # # #<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> #<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> #<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> #<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes"> #<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes"> #<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes"> # # An example of using the format value to create an alias with two # different behaviours depending on the format of the parameters. # #<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" # requires="ChanServ" uline="yes"> # #<alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2" # requires="NickServ" uline="yes"> # # This alias fixes a glitch in xchat 2.6.x and above and the way it # assumes IDENTIFY must be prefixed by a colon (:) character. It should # be placed ABOVE the default NICKSERV alias (the first example) listed # above. # #<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-" # requires="NickServ" uline="yes"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Alltime module: Shows time on all connected servers at once #<module name="m_alltime.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Antibear security module: Prevents 'bear.txt' based trojans from # connecting to your network by sending them a numeric they can't handle. #<module name="m_antibear.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Antibottler module: Labels bottler leech bots #<module name="m_antibottler.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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"> # # Auditorium settings: # #<auditorium showops="no"> # # Setting this value to yes makes m_auditorium behave like unrealircd # +u channel mode, e.g. ops see users joining, parting, etc, and users # joining the channel see the ops. Without this flag, the mode acts # like ircnet's +a (anonymous channels), showing only the user in the # names list, and not even showing the ops in the list, or showing the # ops that the user has joined. #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Ban except module: Adds support for channel ban exceptions (+e) #<module name="m_banexception.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Ban redirection module: Allows bans which redirect to a specified # channel. e.g. +b nick!ident@host#channelbanneduserissentto #<module name="m_banredirect.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Block amsg module: Attempt to block all usage of /amsg and /ame #<module name="m_blockamsg.so"> # #-#-#-#-#-#-#-#-#-#-#- BLOCKAMSG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # If you have the m_blockamsg.so 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. # # action - Any of 'notice', 'noticeopers', 'silent', 'kill' # # or 'killopers'. Define how to take action when # # a user uses /amsg or /ame. # # #<blockamsg delay="3" action="killopers"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Block CAPS module: Blocking all-CAPS messages with cmode +P #<module name="m_blockcaps.so"> # # #-#-#-#-#-#-#-#-#-#-#- BLOCKCAPS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # percent - How many percent of text must be caps before text # # will be blocked. # # # # minlen - The minimum length a line must be for the block # # percent to have any effect. # # # # capsmap - A list of chars to be considered CAPS, this was # # you can add CAPS for your language. Also you can # # add things like ! and space to further lock down # # on caps usage. # #<blockcaps percent="50" # minlen="5" # capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! "> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Block colour module: Blocking colour-coded messages with cmode +c #<module name="m_blockcolor.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Botmode module: Adds the user mode +B #<module name="m_botmode.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # CBAN module: Lets you disallow channels from being used at runtime. #<module name="m_cban.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Censor module: Adds the channel mode +G #<module name="m_censor.so"> # #-#-#-#-#-#-#-#-#-#-#- CENSOR CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # Optional - If you specify to use the m_censor module, then you must # # specify some censor tags. See also: # # http://www.inspircd.org/wiki/Censor_Module # # #<include file="censor.conf"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # CGI:IRC module: Adds support for automatic host changing in CGI:IRC # (http://cgiirc.sourceforge.net). #<module name="m_cgiirc.so"> # #-#-#-#-#-#-#-#-#-#-#-# CGIIRC CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-# # # Optional - If you specify to use m_cgiirc, then you must specify one # or more cgihost tags which indicate authorized CGI:IRC servers which # will be connecting to your network, and an optional cgiirc tag. # For more information see: http://www.inspircd.org/wiki/CGI-IRC_Module # # Set to yes if you want to notice opers when CGI clients connect # <cgiirc opernotice="no"> # # The type field indicates where the module should get the real # client's IP address from, for further information, please see the # CGI:IRC documentation. # # <cgihost type="pass" mask="www.mysite.com"> # Get IP from PASS # <cgihost type="webirc" mask="somebox.mysite.com"> # Get IP from WEBIRC # <cgihost type="ident" mask="otherbox.mysite.com"> # Get IP from ident # <cgihost type="passfirst" mask="www.mysite.com"> # See the docs # # IMPORTANT NOTE: # --------------- # # When you connect CGI:IRC clients, there are two connect classes which # apply to these clients. When the client initially connects, the connect # class which matches the cgi:irc site's host is checked. Therefore you # must raise the maximum local/global clients for this ip as high as you # want to allow cgi clients. After the client has connected and is # determined to be a cgi:irc client, the class which matches the client's # real IP is then checked. You may set this class to a lower value, so that # the real IP of the client can still be restricted to, for example, 3 # sessions maximum. # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Channel create module: Adds snomask +j, which will notify opers of # any new channels that are created #<module name="m_chancreate.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Channel filter module: Allows channel-op defined message # filtering using simple string matches (channel mode +g) #<module name="m_chanfilter.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Chanprotect module: gives +q and +a channel modes #<module name="m_chanprotect.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Check module: gives /check # Check is useful for looking up information on channels, # users, IP addresses and hosts. #<module name="m_check.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # CHGHOST module: Adds the /CHGHOST command #<module name="m_chghost.so"> # #-#-#-#-#-#-#-#-# /CHGHOST - /SETHOST CONFIGURATION #-#-#-#-#-#-#-#-# # Optional - If you want to use special chars for hostnames you can # # specify your own custom list of chars with the <hostname> tag: # # # # charmap - A list of chars accepted as valid by the /CHGHOST # # and /SETHOST commands. Also note that the list is # # case-sensitive. # #<hostname charmap="abcdefghijklmnopqrstuvwxyz.-_/0123456789"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # CHGIDENT module: Adds the /CHGIDENT command #<module name="m_chgident.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # CHGNAME module: Adds the /CHGNAME command #<module name="m_chgname.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Cloaking module: Adds usermode +x and cloaking support. # Relies on the module m_md5.so being loaded before m_cloaking.so in # the configuration file. #<module name="m_cloaking.so"> # #-#-#-#-#-#-#-#-#-#-#- CLOAKING CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # Optional - If you specify the m_cloaking.so module as above, you # # must define cloak keys, and optionally a cloak prefix as shown # # below. When using cloaking, the cloak keys are MANDITORY and must # # be included. However, if prefix is not included, it will default # # to your networks name from the <server> tag. # # # # <cloak key1="0x2AF39F40" # # key2="0x78E10B32" # # key3="0x4F2D2E82" # # key4="0x043A4C81" # # prefix="mynet"> # # # # Please note that the key values will accept any number, and should # # be large numbers. Using small numbers such as "7" or "1924" will # # seriously weaken the security of your cloak. It is recommended you # # use hexdecimal numbers prefixed by "0x", as shown in this example, # # with each key eight hex digits long. # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Clones module: Adds an oper command /CLONES for detecting cloned # users. Warning: This module may be resource intensive when its # command is issued, use with care. #<module name="m_clones.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Conn-Join: Allows you to force users to join one or more channels # automatically upon connecting to the server. #<module name="m_conn_join.so"> # #-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # If you have m_conn_join.so loaded, you can configure it using the # follow values: # #<autojoin channel="#one,#two,#three"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Conn-Usermodes: Set modes on users when they connect # When this module is loaded <connect:allow> tags 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"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Conn-Wait-for-Pong: Don't let a user connect until they PONG #<module name="m_conn_waitpong.so"> # #-#-#-#-#-#-#-#-#-#-#- WAITPONG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # If you have the m_conn_waitpong.so module loaded, configure it with # # the <waitpong> tag: # # # # sendsnotice - Whether to send a snotice on connect, like other # # older ircds # # # # killonbadreply - Whether to kill the user if they send the wrong # # PONG reply. # # # #<waitpong sendsnotice="yes" killonbadreply="yes"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Channel cycle module. Server side /hop, with +ilk etc bypass. #<module name="m_cycle.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Connection throttle module. Configuration: #<module name="m_connflood.so"> # #-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # seconds, maxconns - Amount of connections per <seconds>. # # timeout - Time to wait after the throttle was activated # before deactivating it. Be aware that the time # is seconds + timeout. # # quitmsg - The message that users get if they attempt to # connect while the throttle is active. # # bootwait - Amount of time to wait before enforcing the # throttling when the server just booted. # #<connflood seconds="30" maxconns="3" timeout="30" # quitmsg="Throttled" bootwait="10"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # DCCALLOW module: Adds the /DCCALLOW command #<module name="m_dccallow.so"> # #-#-#-#-#-#-#-#-#-#-#- DCCALLOW CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # blockchat - Whether to block DCC CHAT as well as DCC SEND # length - Default duration of entries in DCCALLOW list # action - Default action to take if no action is specified # can be 'block' or 'allow' # # File configuration: # pattern - The glob pattern to match against # action - Action to take if a user attempts to send a file # that matches this pattern, can be 'block' or 'allow' # #<dccallow blockchat="yes" length="5m" action="block"> #<banfile pattern="*.exe" action="block"> #<banfile pattern="*.txt" action="allow"> # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Deaf module: adds support for ircu style usermode +d - deaf to # channel messages and channel notices. #<module name="m_deaf.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Deny Channels: Deny Channels from being used by users #<module name="m_denychans.so"> # #-#-#-#-#-#-#-#-#-#-#- DENYCHAN DEFINITIONS -#-#-#-#-#-#-#-#-#-#-#-# # # # If you have the m_denychans.so module loaded, you need to specify # # the channels to deny: # # # # name - The channel name to deny. # # # # allowopers - If operators are allowed to override the deny. # # # # reason - Reason given for the deny. # # # #<badchan name="#gods" allowopers="yes" reason="Tortoises!"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Devoice Module: Let users devoice themselves. #<module name="m_devoice.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # DNS Blacklist Module: Provides support for looking up IPs on one or # # more blacklists. # #<module name="m_dnsbl.so"> # # # # For configuration options please see the wiki page for m_dnsbl at # # http://inspircd.org/wiki/DNS_Blacklist_Module # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Filter module: Provides glob-based message filtering #<module name="m_filter.so"> # OR # PCRE filter module: Filters messages using regular expressions #<module name="m_filter_pcre.so"> # # You may only use one or the other with these modules, network-wide. # #-#-#-#-#-#-#-#-#-#-#- FILTER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # Optional - If you specify to use the m_filter or m_filter_pcre # # modules, then specfiy below the path to the filter.conf file, # # or define some <filter> tags. # # # #<include file="filter.conf"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Foobar module: does nothing - historical relic #<module name="m_foobar.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Globops module: gives /GLOBOPS and usermode +g #<module name="m_globops.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Global load module: Allows loading and unloading of modules network- # wide (USE WITH EXTREME CAUTION!) #<module name="m_globalload.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # HELPOP module: Provides the /HELPOP command #<module name="m_helpop.so"> # #-#-#-#-#-#-#-#-#-#-#-#- HELPOP CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # Optional - If you specify to use the m_helpop.so module, then # # specify below the path to the helpop.conf file, or if you like to # # make a mess, define your helpop tags in this conf. # # # #<include file="helpop.conf"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # HIDECHANS module: Allows opers to hide their channels list from non- # opers by setting user mode +I on themselves. # <module name="m_hidechans.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # HIDEOPER module: Allows opers to hide their oper status from non- # opers by setting user mode +H on themselves. # <module name="m_hideoper.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Hostchange module: Allows a different style of cloaking #<module name="m_hostchange.so"> # #-#-#-#-#-#-#-#-#-#-#- HOSTCHANGE CONFIGURATION -#-#-#-#-#-#-#-#-#-# # # # Optional - If you choose to use the m_hostchange.so module. # # Config Help - See http://www.inspircd.org/wiki/Host_Changer_Module # # # #<host suffix="polarbears.org" separator="." prefix=""> #<hostchange mask="*@fbi.gov" action="addnick"> #<hostchange mask="*r00t@*" action="suffix"> #<hostchange mask="a@b.com" action="set" value="blah.blah.blah"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # httpd module: Provides http server support for InspIRCd #<module name="m_httpd.so"> # #-#-#-#-#-#-#-#-#-#-#-#- HTTPD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # Optional - If you choose to use the m_httpd.so module, then you must # specify the port number and other details of your http server: # #<http ip="192.168.1.10" host="brainwave" port="32006" # index="/home/brain/inspircd/http/index.html"> # # You may have as many of these tags as you wish, each with a different # IP, port, host or index file. Each one will act as an independent # HTTP server. # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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"> # #-#-#-#-#-#-#-#-#-#-#-#- HTTPD STATS CONFIGURATION -#-#-#-#-#-#-#-#-#-# # #<httpstats stylesheet="http://remote.style/sheet.css"> # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Ident: Provides RFC 1413 ident lookup support #<module name="m_ident.so"> # #-#-#-#-#-#-#-#-#-#-#-#- 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 one second. 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. # # # #<ident timeout="5" bind=""> # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Invite except module: Adds support for channel invite exceptions (+I) #<module name="m_inviteexception.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Invisible module - Adds support for usermode +Q (quiet) which lets an # oper go 'invisible' similar to unrealircd 3.1's +I mode. Note that # opers are still able to see invisible users, and if an oper with +Q # deopers, they will become visible. # # IMPORTANT NOTE: To allow this mode to be used by a type of oper, you # must first add the value canquiet="yes" to that oper's type tag. # #<module name="m_invisible.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Join flood module: Adds support for join flood protection (+j) #<module name="m_joinflood.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Jump Server module: Adds support for the RPL_REDIR numeric #<module name="m_jumpserver.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Anti-Auto-Rejoin: Adds support for prevention of auto-rejoin (+J) #<module name="m_kicknorejoin.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Knock module: adds the /KNOCK command and +K channel mode #<module name="m_knock.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Lock server module: Adds /LOCKSERV and /UNLOCKSERV commands that is # # used to temporarily close/open for new connections to the server. # # These commands require OPER status and that the LOCKSERV UNLOCKSERV # # are specified in a <class> tag that the oper is part of. This is so # # you can control who has access to this possible dangerous command. # # If your server is locked and you got disconnected, do a REHASH from # # shell to open up again. #<module name="m_lockserv.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Msg flood module: Adds message/notice flood protection (+f) #<module name="m_messageflood.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # MySQL module: Allows other SQL modules to access MySQL databases # through a unified API. You must copy the source for this module # from the directory src/modules/extra, plus the file m_sqlv2.h #<module name="m_mysql.so"> # #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-# # # # m_mysql.so is more complex than described here, see the wiki for # # more: http://www.inspircd.org/wiki/SQL_Service_Provider_Module # # #<database name="mydb" username="myuser" password="mypass" hostname="localhost" id="my_database2"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Nicklock module: Let opers change a user's nick and then stop that # user from changing their nick again. #<module name="m_nicklock.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # No ctcp module: Adds the channel mode +C to block CTCPs #<module name="m_noctcp.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Noinvite module: Gives channel mode +V #<module name="m_noinvite.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # No kicks module: Adds the +Q channel mode #<module name="m_nokicks.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # No nicks module: Adds the +N channel mode #<module name="m_nonicks.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # No Notice module: adds the channel mode +T #<module name="m_nonotice.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Oper channels mode: Adds the +O channel mode #<module name="m_operchans.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Oper hash module: Allows hashed oper passwords # Relies on the module m_md5.so and/or m_sha256.so being loaded before # m_oper_hash.so in the configuration file. #<module name="m_oper_hash.so"> # #-#-#-#-#-#-#-#-#-#-# OPER HASH CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-# # # To use this module, you must define a hash type for each oper's # password you want to hash. For example: # # <oper name="Brain" # host="ident@dialup15.isp.com" # hash="sha256" # password="a41d730937a53b79f788c0ab13e9e1d5" # type="NetAdmin"> # # The types of hashing available vary depending on which hashing modules # you load, but usually if you load m_sha256.so and m_md5.so, both md5 # and sha256 type hashing will be available (the most secure of which # is SHA256). #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Oper Join module: Forces opers to join a channel on oper-up #<module name="m_operjoin.so"> # #-#-#-#-#-#-#-#-#-#-# OPERJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # If you are using the m_operjoin.so module, specify the channel here # # # #<operjoin channel="#channel"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Oper MOTD module: Provides support for seperate message of the day # on oper-up #<module name="m_opermotd.so"> # #-#-#-#-#-#-#-#-#-#-# OPERMOTD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # If you are using the m_opermotd.so module, specify the motd here # # # #<opermotd file="oper.motd"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Override module: Adds support for oper override #<module name="m_override.so"> # #-#-#-#-#-#-#-#-#-#-# OVERRIDE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # m_override.so is too complex it describe here, see the wiki: # # http://www.inspircd.org/wiki/Oper_Override_Module # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Oper levels module: Gives each oper a level and prevents # actions being taken against higher level opers # Specify the level as the 'level' parameter of the <type> tag #<module name="m_operlevels.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Oper modes module: Allows you to specify modes to add/remove on oper # Specify the modes as the 'modes' parameter of the <type> tag #<module name="m_opermodes.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # PostgreSQL module: Allows other SQL modules to access PgSQL databases # through a unified API. You must copy the source for this module # from the directory src/modules/extra, plus the file m_sqlv2.h #<module name="m_pgsql.so"> # #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-# # # # m_pgsql.so is more complex than described here, see the wiki for # # more: http://www.inspircd.org/wiki/SQL_Service_Provider_Module # # #<database name="mydb" username="myuser" password="mypass" hostname="localhost" id="my_database" ssl="no"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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"> # #-#-#-#-#-#-#-#-#-#- RANDOMQUOTES CONFIGURATION -#-#-#-#-#-#-#-#-#-#-# # # # Optional - If you specify to use the m_randquote.so module, then # # specify below the path to the randquotes.conf file. # # # #<randquote file="randquotes.conf"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Redirect module: Adds channel redirection (mode +L) #<module name="m_redirect.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Registered users only channel creation # Allows only registered users and opers to create new channels. #<module name="m_regonlycreate.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Remove module: Adds the /REMOVE command which is a peaceful # alternative to /KICK #<module name="m_remove.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Restrict banned users module: # Disallows banned users on a channel from messaging the channel, # changing nick, or changing the topic, if loaded. #<module name="m_restrictbanned.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Restricted channels module: Allows only opers to create channels #<module name="m_restrictchans.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Restrict message module: Allows users to only message opers #<module name="m_restrictmsg.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Provide /LIST throttling (to prevent flooding) and /LIST safety to # prevent excess flood when the list is large. #<module name="m_safelist.so"> # #-#-#-#-#-#-#-#-#-#-# SAFELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-# # # When using Safelist, you may set the following values; # # The first value, 'throttle', sets the amount of time in seconds a user # must wait between LIST commands. For example, if this is set to 60 # (the default) then the user may not /LIST more than once a minute. # If not defined, the default value is 60 seconds. # # The second value, 'maxlisters', indicates the maximum number of users # which may be retrieving a LIST at once. It is not recommended you raise # this value, as increasing it too high can make your network vulnerable # to floodbots which waste your bandwidth and CPU time with LIST requests. # #<safelist throttle="60" maxlisters="50"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SAJOIN module: Adds the /SAJOIN command #<module name="m_sajoin.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SAMODE module: Adds the oper /SAMODE command #<module name="m_samode.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SANICK module: Allows opers to change user's nicks #<module name="m_sanick.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SAPART module: Adds the oper /SAPART command #<module name="m_sapart.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SAQUIT module: Adds the oper /SAQUIT command (abusable!!!) #<module name="m_saquit.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Secure list module: Prevent /LIST in the first minute of connection, # crippling most spambots and trojan spreader bots. #<module name="m_securelist.so"> # #-#-#-#-#-#-#-#-#-# SECURELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-# # # # Securelist can be harmful to some irc search engines such as # # netsplit.de and searchirc.com. To prevent securelist blocking these # # sites from listing, define exception tags as shown below: # <securehost exception="*@*.searchirc.org"> <securehost exception="*@*.netsplit.de"> <securehost exception="*@echo940.server4you.de"> # # # Define the following variable to change how long a user must wait # # before issuing a LIST. If not defined, defaults to 60 seconds. # # # #<securelist waittime="60"> # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # See nicks module: Allow for SNOMASK +N which shows nick changes. #<module name="m_seenicks.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Set Idle module: Adds a command for opers to change their # idle time (mainly a toy) #<module name="m_setidle.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Services support module: Adds several usermodes such as +R and +M # this module implements the 'identified' state via user mode +r, which # is similar to the DALnet and dreamforge systems. #<module name="m_services.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Services support module: Adds several usermodes such as +R and +M # this module implements the 'identified' state via account names (AC) # and is similar in operation to the way asuka and ircu handle services. # it cannot be used at the same time as m_services, above. #<module name="m_services_account.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Sethost module: Adds the /SETHOST command # See m_chghost for how to customise valid chars for hostnames #<module name="m_sethost.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Setident module: Adds the /SETIDENT command #<module name="m_setident.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SETNAME module: Adds the /SETNAME command #<module name="m_setname.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Show Whois module: Adds the +W usermode which allows opers # to see when they are whois'ed (can be annoying). #<module name="m_showwhois.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Spy module: Adds the commands SPYLIST and SPYNAMES that let opers # see who is in a +s channel, and list +s channels, show keys of keyed # channels the oper is not a member of etc. (standard 'abusive' features # of many other ircds, modulized here in InspIRCd). #<module name="m_spy.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SSL channel mode module: Adds support for SSL-only channels (+z). # does not do anything useful without a working SSL module (see below) #<module name="m_sslmodes.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Dummy ssl module: If you have other servers on your network which # have SSL, but your server does not have ssl enabled, you should load # this module, which will handle SSL metadata (e.g. the "Is using ssl" # field in the WHOIS information). #<module name="m_ssl_dummy.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # GnuTLS ssl module: Adds support for client-server SSL using GnuTLS, # if enabled. You must copy the source for this module from the directory # src/modules/extra, or answer 'yes' in ./configure when asked if you # want to enable this, or it will not load. #<module name="m_ssl_gnutls.so"> # #-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # m_ssl_gnutls.so is too complex it describe here, see the wiki: # # http://www.inspircd.org/wiki/GnuTLS_SSL_Module # # # # NOTE: If you want to use this module to encrypt and sign your # # server to server traffic, you MUST load it before m_spanningtree in # # your configuration file! # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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. You must symlink the source for # this module from the directory src/modules/extra. #<module name="m_sslinfo.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # OpenSSL ssl module: Adds support for client-server SSL using OpenSSL, # if enabled. You must copy the source for this module from the directory # src/modules/extra, or answer 'yes' in ./configure when asked if you # want to enable this, or it will not load. #<module name="m_ssl_openssl.so"> # #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # m_ssl_openssl.so is too complex it describe here, see the wiki: # # http://www.inspircd.org/wiki/OpenSSL_SSL_Module # # # # NOTE: If you want to use this module to encrypt and sign your # # server to server traffic, you MUST load it before m_spanningtree in # # your configuration file! # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SSL Cert Oper module: Allows opers to oper up using the key fingerprint # stored within their SSL certificate and key pair. # When using this module, one of m_ssl_gnutls.so or m_ssl_openssl.so must # be loaded. An extra value should be added to enabled opers, which # is in the following format: fingerprint="<hash>". For more information, # see the example in the oper blocks. #<module name="m_ssl_oper_cert.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Strip colour module: Adds the channel mode +S #<module name="m_stripcolor.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SILENCE module: Adds support for /SILENCE #<module name="m_silence.so"> # # Configuration tags: # #<silence maxentries="32"> # # Sets the maximum number of entries on a users silence list. #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Extended SILENCE module: Adds support for /SILENCE with additional # features to silence based on invites, channel messages, etc. #<module name="m_silence_ext.so"> # # The configuration tags for this module are identical to those of # m_silence, shown above. #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SQLite3 module: Allows other SQL modules to access SQLite3 # # databases through a unified API. You must link the source for this # # module from the directory src/modules/extra to src/modules, plus # # the file m_sqlv2.h # #<module name="m_sqlite3.so"> # #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-# # # # m_sqlite.so is more complex than described here, see the wiki for # # more: http://www.inspircd.org/wiki/SQLite3_Service_Provider_Module # # #<database hostname="/full/path/to/database.db" id="anytext"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SQLutils module: Provides some utilities to SQL client modules, such # as mapping queries to users and channels. You must copy the source # for this module from the directory src/modules/extra/m_sqlutils.cpp # and src/modules/extra/m_sqlutils.h into /src/modules # Needed for, and loaded before: SQLauth and SQLoper #<module name="m_sqlutils.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SQL authentication module: Allows IRCd connections to be tied into # a database table (for example a forum). You must copy the source for # this module from the directory src/modules/extra # Depends on the SQLutils module being loaded first. #<module name="m_sqlauth.so"> # #-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # m_sqlauth.so is too complex it describe here, see the wiki: # # http://www.inspircd.org/wiki/SQL_Authentication_Module # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SQL logging module: Allows you to log network-wide data for your # network in a fully normalized set of SQL tables. You must copy the # source for this module from the directory src/modules/extra #<module name="m_sqllog.so"> # #-#-#-#-#-#-#-#-#-#-#- SQLLOG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # dbid - Database ID to use (see m_sql) # # # # See also: http://www.inspircd.org/wiki/SQL_Logging_Module # # # #<sqllog dbid="1"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SQL oper module: Allows you to store oper credentials in an SQL table # You must copy the source for this module from the directory src/modules/extra # Depends on the SQLutils module being loaded first. #<module name="m_sqloper.so"> # #-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-# # # # dbid - Database ID to use (see m_sql) # # # # See also: http://www.inspircd.org/wiki/SQL_Oper_Storage_Module # # # #<sqloper dbid="1"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be # # added/removed by Services. # #<module name="m_svshold.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # SWHOIS module: Allows you to add arbitary lines to user WHOIS. #<module name="m_swhois.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Test command module: Does nothing significant. Read: pointless. #<module name="m_testcommand.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Timed bans module: Adds timed bans and the /TBAN command #<module name="m_timedbans.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Test line module: Adds the /TLINE command, used to test how many # users a /GLINE or /ZLINE etc would match. #<module name="m_tline.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # UHNAMES support module: Adds support for the IRCX style UHNAMES # extension, which displays ident and hostname in the names list for # each user, saving clients from doing a WHO on the channel. Note that # this module is not widely supported yet. If a client does not support # UHNAMES it will not enable it, this will not break incompatible # clients. #<module name="m_uhnames.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Userip module: Adds the /USERIP command #<module name="m_userip.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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"> # #-#-#-#-#-#-#-#-#-#-#- VHOST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-# # # # user - Username for the vhost. # # # # pass - Password for the vhost. # # # # host - Vhost to set. # # #<vhost user="some_username" pass="some_password" host="some.host"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Watch module: Adds the WATCH command, which is used by clients to # maintain notify lists. #<module name="m_watch.so"> # # Configuration tags: # #<watch maxentries="32"> # # Sets the maximum number of entries on a user's watch list. #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # XMLSocket module: Adds support for connections using the shockwave # flash XMLSocket. Note that this does not work if the client you are # using has retarded ideas of the IRC protocol. Your client must still # send RFC-correct lines to the server, this module only changes the # line ending from newlines to null terminators. # #<module name="m_xmlsocket.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # ZipLinks module: Adds support for zlib deflate on server to server # connections. Both ends of the connection must load this module. # #<module name="m_ziplink.so"> # # To use this module, you must enable it as a transport type in your # <link> tags or <bind> tags using the transport name 'zip'. # See the documentation of <link> and <bind>, respectively. # #-#-#-#-#-#-#-#-#-#-#-#-#-#- BAN OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # The ban tags define nick masks, host masks and ip ranges which are # # banned from your server. All details in these tags are local to # # Your server. # # # # # # badip lines ban an ip range (same as a zline) # # # # ipmask - The ip range to ban (wildcards possible) # # CIDR is supported in the IP mask. # # reason - Reason to display when disconnected # # # # badnick lines ban a nick mask (same as a qline) # # # # nick - Nick mask to ban (wildcards possible) # # reason - Reason to display on /NICK # # # # badhost lines ban a user@host mask (same as a kline) # # # # host - ident@hostname (wildcards possible) # # If you specify an IP, CIDR is supported. # # reason - Reason to display on disconnection # # # # exception lines define a hostmask that is excempt from [kzg]lines # # # # host - ident@hostname (wildcards possible) # # If you specify an IP, CIDR is supported. # # reason - Reason, shown only in /stats e # # # <badip ipmask="69.69.69.69" reason="No porn here thanks."> <badnick nick="ChanServ" reason="Reserved For Services"> <badnick nick="NickServ" reason="Reserved For Services"> <badnick nick="OperServ" reason="Reserved For Services"> <badnick nick="MemoServ" reason="Reserved For Services"> <badhost host="*@hundredz.n.hundredz.o.1337.kiddies.com" reason="Too many 1337 kiddiots"> <badhost host="*@localhost" reason="No irc from localhost!"> <badhost host="*@172.32.0.0/16" reason="This subnet is bad."> <exception host="*@ircop.host.com" reason="Opers hostname"> #-#-#-#-#-#-#-#-#-#-#- INSANE BAN OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # This optional tag allows you to specify how wide a gline, eline, # # kline, zline or qline can be before it is forbidden from being # # set. By setting hostmasks="yes", you can allow all G, K, E lines, # # no matter how many users the ban would cover. This is not # # recommended! By setting ipmasks="yes", you can allow all Z lines, # # no matter how many users these cover too. Needless to say we # # don't recommend you do this, or, set nickmasks="yes", which will # # allow any qline. # # # # The trigger value indicates how wide any mask will be before it is # # prevented from being set. The default value is 95.5% if this tag is # # not defined in your configuration file, meaning that if your # # network has 1000 users, a gline matching over 955 of them will be # # prevented from being added. # # # # Please note that remote servers (and services) are exempt from # # these restrictions and expected to enforce their own policies # # locally! # # # <insane hostmasks="no" ipmasks="no" nickmasks="no" trigger="95.5"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- YAWN -#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # # # You should already know what to do here :) # <die value="User error. Insert new user and press any key. (you didn't edit your config properly.)"> ######################################################################### # # # - InspIRCd Development Team - # # http://www.inspircd.org # # # ######################################################################### \ No newline at end of file
+########################################################################
+# #
+# ___ ___ ____ ____ _ #
+# |_ _|_ __ ___ _ __|_ _| _ \ / ___|__| | #
+# | || '_ \/ __| '_ \| || |_) | | / _` | #
+# | || | | \__ \ |_) | || _ <| |__| (_| | #
+# |___|_| |_|___/ .__/___|_| \_\\____\__,_| #
+# |_| #
+# ____ __ _ _ _ #
+# / ___|___ _ __ / _(_) __ _ _ _ _ __ __ _| |_(_) ___ _ __ #
+# | | / _ \| '_ \| |_| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ #
+# | |__| (_) | | | | _| | (_| | |_| | | | (_| | |_| | (_) | | | | #
+# \____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| #
+# |___/ #
+# #
+##################################||####################################
+ #||#
+##################################||####################################
+# #
+# This is an example of the config file for InspIRCd. #
+# Change the options to suit your network #
+# #
+# $Id$
+# #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# Lines prefixed with READ THIS BIT, as shown above, are IMPORTANT #
+# lines, and you REALLY SHOULD READ THEM. Yes, THIS MEANS YOU. Even #
+# if you've configured InspIRCd before, these probably indicate #
+# something new or different to this version and you SHOULD READ IT. #
+# #
+########################################################################
+# #
+# Unalphabeticalise the modules list at your own risk #
+# #
+########################################################################
+
+
+#-#-#-#-#-#-#-#-#-#-#-#- SERVER DESCRIPTION -#-#-#-#-#-#-#-#-#-#-#-#-
+# #
+# Here is where you enter the information about your server. #
+# #
+# Syntax is as follows: #
+# #
+# <server name="server.name" #
+# description="Server Description" #
+# networkemail="Email address shown on g/k/z/q lines" #
+# network="MyNetwork"> #
+# #
+
+<server name="penguin.omega.org.za"
+ description="Waddle World"
+ network="Omega">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#- ADMIN INFORMATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Describes the Server Administrator's real name (optionally), #
+# nick, and email address. #
+# #
+# Syntax is as follows: #
+# <admin name="real name" #
+# nick="nick name" #
+# email="email@address.com"> #
+# #
+
+<admin name="Johnny English"
+ nick="MI5"
+ email="MI5@the.best.secret.agent">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#- PORT CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-
+# #
+# Enter the port and address bindings here. #
+# #
+# bind address - specifies which address ports bind to. Leaving this #
+# field blank binds the port to all IPs available. #
+# #
+# port - The port number to bind to. You may specify a port #
+# range here, e.g. "6667-6669,7000,7001". If you do #
+# this, the server will count each port within your #
+# range as a seperate binding, making the above #
+# example equivalent to five seperate bind tags. #
+# A failure on one port in the range does not prevent #
+# the entire range from being bound, just that one #
+# port number. #
+# #
+# type - can be 'clients' or 'servers'. The clients type is #
+# a standard tcp based socket, the servers type is a #
+# also a TCP based connection but of a different #
+# format. SSL support is provided by modules, to #
+# enable SSL support, please read the module section #
+# of this configuration file. #
+# #
+# ssl - When using m_ssl_gnutls.so or m_ssl_openssl.so #
+# modules, you must define this value to use ssl on #
+# that port. valid values are 'gnutls' or 'openssl' #
+# respectively. If the module is not loaded, this #
+# setting is ignored. #
+# #
+# transport - If you have m_spanningtree.so loaded, along with #
+# either of the SSL modules (m_ssl_gnutls or #
+# m_ssl_openssl) or m_ziplinks.so, then you may make #
+# use of this value. #
+# setting it to 'openssl' or 'gnutls' or 'zip' #
+# indicates that the port should accept connections #
+# using the given transport name. Transports are #
+# layers which sit on top of a socket and change the #
+# way data is sent and received, e.g. encryption, #
+# compression, and other such things. Because this #
+# may not be limited in use to just encryption, #
+# the 'ssl' value used for client ports does not #
+# exist for servers, and this value is used instead. #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# If you want to link servers to InspIRCd you must load the #
+# m_spanningtree module! Please see the modules list below for #
+# information on how to load this module! If you do not load this #
+# module, server ports will NOT be bound! #
+# #
+# Leaving address empty binds to all available interfaces #
+# #
+# Syntax is as follows: #
+# #
+# <bind address="ip address" port="port" type="clients"> #
+# <bind address="ip address" port="port" type="servers"> #
+# #
+# If InspIRCd is built for IPV6, and you wish to accept IPV4 clients, #
+# then you can specify IPV4 ip addresses here to bind. You may also #
+# use the 4in6 notation, ::ffff:1.2.3.4, where 1.2.3.4 is the IPV4 #
+# address to bind the port, but as of InspIRCd 1.1.1, this is not #
+# required. #
+# #
+# ------------------------------------------------------------------- #
+# #
+# PLEASE NOTE: If you have build InspIRCd as an ipv6 server, and you #
+# specify an empty bind address, the binding will be bound to ALL THE #
+# IPV6 IP ADDRESSES, and not the ipv4 addresses. If you are using an #
+# ipv6 enabled InspIRCd and want to bind to multiple IPV4 addresses #
+# in this way, you must specify them by hand. If you have built the #
+# server for ipv4 connections only, then specifying an empty bind #
+# address binds the port to all ipv4 IP addresses, as expected. #
+# #
+
+<bind address="" port="6000" type="clients">
+<bind address="" port="6660-6669" type="clients" ssl="gnutls">
+
+# When linking servers, the openssl and gnutls transports are largely
+# link-compatible and can be used alongside each other or either/or
+# on each end of the link without any significant issues.
+
+<bind address="" port="7000,7001" type="servers">
+<bind address="1.2.3.4" port="7005" type="servers" transport="openssl">
+
+
+#-#-#-#-#-#-#-#-#-#- DIE/RESTART CONFIGURATION -#-#-#-#-#-#-#-#-#-#-
+# #
+# You can configure the passwords here which you wish to use for #
+# the die and restart commands. Only trusted ircops who will #
+# need this ability should know the die and restart password. #
+# #
+# Syntax is as follows: #
+# <power diepass="die password" restartpass="restart password" #
+# pause="secs before dying"> #
+# #
+
+<power diepass="" restartpass="" pause="2">
+
+
+#-#-#-#-#-#-#-#-#-# INCLUDE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This optional tag allows you to include another config file #
+# allowing you to keep your configuration tidy. The configuration #
+# file you include will be treated as part of the configuration file #
+# which includes it, in simple terms the inclusion is transparent. #
+# #
+# All paths to config files are relative to the directory of the main #
+# config file inspircd.conf, unless the filename starts with a forward#
+# slash (/) in which case it is treated as an absolute path. #
+# #
+# Syntax is as follows: #
+#<include file="file.conf"> #
+# #
+
+#-#-#-#-#-#-#-#-#-#- CONNECTIONS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# This is where you can configure which connections are allowed #
+# and denied access onto your server. The password is optional. #
+# You may have as many of these as you require. To allow/deny all #
+# connections, use a '*' or 0.0.0.0/0. #
+# #
+# Syntax is as follows: #
+# #
+# <connect allow="1.2.3.0/24" password="blahblah" #
+# timeout="10" timeout="blah" flood="5" #
+# threshold="8" pingfreq="120" sendq="99999" #
+# revcq="696969" localmax="3" globalmax="3" #
+# port="6660"> #
+# #
+# <connect deny="127.0.0.1" port="6667"> #
+# #
+# IP masks may be specified in CIDR format or wildcard format, #
+# for IPV4 and IPV6. You *cannot* use hostnames in the allow or #
+# deny field, as the state is applied before the user's DNS has #
+# been resolved. #
+# #
+# You may optionally include timeout="x" on any allow line, which #
+# specifies the amount of time given before an unknown connection #
+# is closed if USER/NICK/PASS are not given. This value is in secs #
+# #
+# You should also include a flood="x" line which indicates #
+# the number of lines a user may place into their buffer at once #
+# before they are disconnected for excess flood. This feature can #
+# not be disabled, however it can be set to extremely high values, #
+# rendering it effectively disabled. A recommended value is 10. #
+# A counter is maintained for each user which is reset every #
+# 'threshold' seconds and specifying this threshold value with #
+# threshold="X" indicates how often the counter is reset. For #
+# example, with flood="5" and threshold="8", the user may not send #
+# more than 5 lines in 8 secs. #
+# #
+# You may optionally specify the sendq size and ping frequency of #
+# each connect:allow line using the pingfreq="X" and sendq="X" #
+# settings as shown in the full example below. #
+# The ping frequency is specified in seconds, and the sendq size #
+# in bytes. It is recommended, although not enforced, that you #
+# should never set your sendq size to less than 8k. Send Queues are #
+# dynamically allocated and can grow as needed up to the maximum #
+# size specified. #
+# #
+# The optional recvq value is the maximum size which users in this #
+# group may grow their receive queue to. This is recommended to be #
+# kept pretty low compared to the sendq, as users will always #
+# receive more than they send in normal circumstances. The default #
+# if not specified is 4096. #
+# #
+# The sendq is the data waiting to be sent TO THE USER. #
+# The recvq is the data being received FROM THE USER. #
+# The names sendq and recvq are from the SERVER'S PERSPECTIVE not #
+# that of the user... Just to clear up any confusion or complaints #
+# that these are backwards :p #
+# #
+# The localmax and globalmax values can be used to enforce local #
+# and global session limits on connections. The session limits are #
+# counted against all users, but applied only to users within the #
+# class. For example, if you had a class 'A' which has a session #
+# limit of 3, and a class 'B' which has a session limit of 5, and #
+# somehow, two users managed to get into class B which also match #
+# class A, there is only one connection left for this IP now in A, #
+# but if they can connect again to B, there are three. You get the #
+# idea (i hope). #
+# #
+# The optional port value determines which port the connect tag is #
+# handling. If left out the connect tag covers all bound ports else #
+# only incoming connections on the specified port will match. Port #
+# tags may be used on connect allow and connect deny tags. #
+# #
+
+<connect allow="196.12.*" password="secret" port="6667">
+
+<connect allow="*"
+ timeout="60"
+ flood="20"
+ threshold="1"
+ pingfreq="120"
+ sendq="262144"
+ recvq="8192"
+ localmax="3"
+ globalmax="3">
+
+<connect deny="69.254.*">
+<connect deny="3ffe::0/32">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#- CLASS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-
+# #
+# Classes are a group of commands which are grouped together #
+# and given a unique name. They used to define which commands #
+# are available to certain types of Operators. #
+# #
+# Syntax is as follows: #
+# #
+# <class name="name" commands="oper commands"> #
+# #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# You are not forced to give these classes the names given below. #
+# You can create your own named classes, if you want, in fact that #
+# is the whole idea of this system! #
+# #
+# Note: It is possible to make a class which covers all available #
+# commands. To do this, specify commands="*". This is not really #
+# recommended, as it negates the whole purpose of the class system, #
+# however it is provided for fast configuration (e.g. in test nets) #
+# #
+
+<class name="Shutdown" commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOAD">
+<class name="ServerLink" commands="CONNECT SQUIT RCONNECT MKPASSWD MKSHA256">
+<class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE">
+<class name="OperChat" commands="WALLOPS GLOBOPS SETIDLE SPYLIST SPYNAMES">
+<class name="HostCloak" commands="SETHOST SETIDENT SETNAME CHGHOST CHGIDENT">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#- OPERATOR COMPOSITION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# This is where you specify which types of operators you have on #
+# your server, as well as the commands they are allowed to use. #
+# This works alongside with the classes specified above. #
+# #
+# type name - a name for the combined class types #
+# a type name cannot contain spaces, however if you #
+# put an _ symbol in the name, it will be translated #
+# to a space when displayed in a WHOIS. #
+# #
+# classes - specified above, used for flexibility for the #
+# server admin to decide on which operators get #
+# what commands. Class names are case sensitive, #
+# seperate multiple class names with spaces. #
+# #
+# host - optional hostmask operators will receive on oper-up. #
+# #
+# Syntax is as follows: #
+# #
+# <type name="name" classes="class names" host="oper hostmask"> #
+# #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# You are not forced to give these types the names given below. #
+# You can create your own named types, if you want, in fact that #
+# is the whole idea of this system! #
+# #
+
+<type name="NetAdmin" classes="OperChat BanControl HostCloak Shutdown ServerLink" host="netadmin.omega.org.za">
+<type name="GlobalOp" classes="OperChat BanControl HostCloak ServerLink" host="ircop.omega.org.za">
+<type name="Helper" classes="HostCloak" host="helper.omega.org.za">
+
+
+#-#-#-#-#-#-#-#-#-#-#- OPERATOR CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# Opers are defined here. This is a very important section. #
+# Remember to only make operators out of truthworthy people. #
+# #
+# name - oper name, This is case sensitive, so it is best to #
+# use lower-case. #
+# #
+# password - password to oper-up, also case sensitive. #
+# encryption is supported via modules. You may load #
+# modules for MD5 or SHA256 encryption, and if you do, #
+# this value will be a hash value, otherwise put a #
+# plaintext password in this value. #
+# #
+# host - hosts of client allowed to oper-up. #
+# wildcards accepted, seperate multiple hosts with a #
+# space. You may also specify CIDR ip addresses. #
+# #
+# fingerprint - When using the m_ssl_oper_cert.so module, you may #
+# specify a key fingerprint here. This can be obtained #
+# using the /fingerprint command whilst the module is #
+# loaded, or from the notice given to you when you #
+# connect to the ircd using a client certificate, #
+# and will lock this oper block to only the user who #
+# has that specific key/certificate pair. #
+# This enhances security a great deal, however it #
+# requires that opers use clients which can send ssl #
+# client certificates, if this is configured for that #
+# oper. Note that if the m_ssl_oper.so module is not #
+# loaded, and/or one of m_ssl_openssl or m_ssl_gnutls #
+# is not loaded, this configuration option has no #
+# effect and will be ignored. #
+# #
+# type - Defines the kind of operator. This must match a type #
+# tag you defined above, and is case sensitive. #
+# #
+# Syntax is as follows: #
+# <oper name="login" #
+# password="pass" #
+# host="hostmask@of.oper" #
+# fingerprint="hexsequence" #
+# type="oper type"> #
+# #
+
+<oper name="Brain"
+ password="s3cret"
+ host="ident@dialup15.isp.com *@localhost *@server.com *@3ffe::0/16"
+ type="NetAdmin">
+
+
+#-#-#-#-#-#-#-#-#-#-#- SERVER LINK CONFIGURATION -#-#-#-#-#-#-#-#-#-#
+# #
+# Defines which servers can link to this one, and which servers this #
+# server may create outbound links to. #
+# #
+# name - The name is the canocial name of the server, does #
+# not have to resolve - but it is expected to be set #
+# in the remote servers connection info. #
+# #
+# ipaddr - Valid host or ip address for remote server. These #
+# hosts are resolved on rehash, and cached, if you #
+# specify a hostname, so if you find that your server #
+# is still trying to connect to an old IP after you #
+# have updated your dns, try rehashing and then #
+# attempting the connect again. #
+# #
+# port - The TCP port for the remote server. #
+# #
+# sendpass - Password to send to create an outbound connection #
+# to this server. #
+# #
+# recvpass - Password to receive to accept an inbound connection #
+# from this server. #
+# #
+# autoconnect - Sets the server to autoconnect. Where x is the num. #
+# (optional) of seconds between attempts. e.g. 300 = 5 minutes. #
+# #
+# transport - If defined, this is a transport name implemented by #
+# another module. Transports are layers on top of #
+# plaintext connections, which alter them in certain #
+# ways. Currently the three supported transports are #
+# 'openssl' and 'gnutls' which are types of SSL #
+# encryption, and 'zip' which is for compression. #
+# If you define a transport, both ends of the #
+# connection must use a compatible transport for the #
+# link to succeed. OpenSSL and GnuTLS are link- #
+# compatible with each other. #
+# #
+# statshidden - When using m_spanningtree.so for linking. you may #
+# set this to 'yes', and if you do, the IP address/ #
+# hostname of this connection will NEVER be shown to #
+# any opers on the network. In /STATS c its address #
+# will show as *@<hidden>, and during CONNECT and #
+# inbound connections, its IP will show as <hidden> #
+# UNLESS the connection fails (e.g. due to a bad #
+# password or servername) #
+# #
+# allowmask - When this is defined, it indicates a range of IP #
+# addresses to allow for this link (You may use CIDR #
+# or wildcard form for this address). #
+# e.g. if your server is going to connect to you from #
+# the range 1.2.3.1 through 1.2.3.255, put 1.2.3.0/24 #
+# into this value. If it is not defined, then only #
+# the ipaddr field of the server shall be allowed. #
+# #
+# failover - If you define this option, it must be the name of a #
+# different link tag in your configuration. This #
+# option causes the ircd to attempt a connection to #
+# the failover link in the event that the connection #
+# to this server fails. For example, you could define #
+# two hub uplinks to a leaf server, and set an #
+# american server to autoconnect, with a european #
+# hub as its failover. In this situation, your ircd #
+# will only try the link to the european hub if the #
+# american hub is unreachable. NOTE that for the #
+# intents and purposes of this option, an unreachable #
+# server is one which DOES NOT ANSWER THE CONNECTION. #
+# If the server answers the connection with accept(), #
+# EVEN IF THE CREDENTIALS ARE INVALID, the failover #
+# link will not be tried! Failover settings will also #
+# apply to autoconnected servers as well as manually #
+# connected ones. #
+# #
+# timeout - If this is defined, then outbound connections will #
+# time out if they are not connected within this many #
+# seconds. If this is not defined, the default of ten #
+# seconds is used. #
+# #
+# bind - If you specify this value, then when creating an #
+# outbound connection to the given server, the IP you #
+# place here will be bound to. This is for multi- #
+# homed servers which may have multiple IP addresses. #
+# If you do not define this value, the first IP that #
+# is not empty or localhost from your <bind> tags #
+# will be bound to. This is usually acceptable, #
+# however if your server has multiple network cards #
+# then you may have to manually specify the bind #
+# value instead of leaving it to automatic binding. #
+# You can usually tell if you need to set this by #
+# looking for the error 'Could not assign requested #
+# address' in your log when connecting to servers. #
+# #
+# hidden - If this is set to true, yes, or 1, then the server #
+# is completely hidden from non-opers. It does not #
+# show in LINKS and it does not show in MAP. Also, #
+# any servers which are child servers of this one #
+# in the network will *also* be hidden. Use with #
+# care! You can use this to 'mask off' sections of #
+# the network so that users only see a small portion #
+# of a much larger net. It should NOT be relied upon #
+# as a security tool, unless it is being used for #
+# example to hide a non-client hub, for which clients #
+# do not have an IP address or resolvable hostname. #
+# #
+# to u:line a server (give it extra privilages required for running #
+# services, Q, etc) you must include the <uline server> tag as shown #
+# in the example below. You can have as many of these as you like. #
+# #
+# WARNING: Unlike other ircds, u:lining a server allows ALL users on #
+# that server to operoverride modes. This should only be used for #
+# services and protected oper servers! #
+# #
+# ------------------------------------------------------------------- #
+# #
+# NOTE: If you have built your server as an ipv6 server, then when a #
+# DNS lookup of a server's host occurs, AAAA records (ipv6) are #
+# priorotized over A records (ipv4). Therefore, if the server you are #
+# connecting to has both an IPV6 ip address and an IPV4 ip address in #
+# its DNS entry, the IPV6 address will *always* be selected. To #
+# change this behaviour simply specify the IPV4 IP address rather #
+# than the hostname of the server. #
+# #
+# ------------------------------------------------------------------- #
+# #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# If you want to link servers to InspIRCd you must load the #
+# m_spanningtree module! Please see the modules list below for #
+# information on how to load this module! If you do not load this #
+# module, server links will NOT work! #
+# #
+# Also, if you define any transports, you must load the modules for #
+# these transports BEFORE you load m_spanningtree, e.g. place them #
+# above it in the configuration file. Currently this means the three #
+# modules m_ssl_gnutls, m_ziplinks and m_ssl_openssl, depending on #
+# which you choose to use. #
+# #
+
+<link name="hub.penguin.org"
+ ipaddr="penguin.box.com"
+ port="7000"
+ allowmask="69.58.44.0/24"
+ autoconnect="300"
+ failover="hub.other.net"
+ timeout="15"
+ transport="gnutls"
+ bind="1.2.3.4"
+ statshidden="no"
+ hidden="no"
+ sendpass="outgoing!password"
+ recvpass="incoming!password">
+
+<link name="services.antarctic.com"
+ ipaddr="localhost"
+ port="7000"
+ allowmask="127.0.0.0/8"
+ sendpass="penguins"
+ recvpass="polarbears">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#- ULINES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+# This tag defines a ulined server. A U-Lined server has special #
+# permissions, and should be used with caution. Services servers are #
+# usually u-lined in this manner. #
+# #
+# The 'silent' value if set to yes indicates that this server should #
+# not generate quit and connect notices, which can cut down on noise #
+# to opers on the network. #
+# #
+<uline server="services.antarctic.com" silent="yes">
+
+
+#-#-#-#-#-#-#-#-#-#- MISCELLANEOUS CONFIGURATION -#-#-#-#-#-#-#-#-#-#
+# #
+# These options let you define the path to your motd and rules #
+# files. If these are relative paths, they are relative to the #
+# configurtion directory. #
+# #
+
+<files motd="inspircd.motd.example"
+ rules="inspircd.rules.example">
+
+#-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This optional configuration tag lets you define the maximum number #
+# of channels that both opers and users may be on at any one time. #
+# the default is 20 for user and 60 for opers if this tag is not #
+# defined. Remote users are not restricted in any manner. #
+# #
+
+<channels users="20"
+ opers="60">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-# DNS SERVER -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Define your DNS server address here. InspIRCd has its own resolver. #
+# If you do not define this value, then then InspIRCd will attempt to #
+# determine your DNS server from your operating system. On POSIX #
+# platforms, InspIRCd will read /etc/resolv.conf, and populate this #
+# value with the first DNS server address found. On Windows platforms #
+# InspIRCd will check the registry, and use the DNS server of the #
+# first active network interface, if one exists. #
+# If a DNS server cannot be determined from these checks, the default #
+# value '127.0.0.1' is used instead. The timeout value is in seconds. #
+# #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# When choosing a server, be sure to choose one which will do a #
+# RECURSIVE LOOKUP. InspIRCd's resolver does not currently do these #
+# recursive lookups itself, to save time and resources. The dns #
+# server recommended by the InspIRCd team is bind, available from the #
+# ISC website. If your DNS server does not do a recursive lookup, you #
+# will be able to notice this by the fact that none of your users are #
+# resolving even though the DNS server appears to be up! Most ISP and #
+# hosting provider DNS servers support recursive lookups. #
+# #
+# ------------------------------------------------------------------- #
+# #
+# NOTE: if you have built InspIRCd with IPV6 support, then both #
+# ipv6 and ipv4 addresses are allowed here, and also in the system #
+# resolv.conf file. Remember that an ipv4 dns server can still #
+# resolve ipv6 addresses, and vice versa. #
+# #
+
+<dns server="127.0.0.1" timeout="5">
+
+# An example of using an IPV6 nameserver
+#<dns server="::1" timeout="5">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-# PID FILE -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Define the path to the PID file here. The PID file can be used to #
+# rehash the ircd from the shell or to terminate the ircd from the #
+# shell using shell scripts, perl scripts etc, and to monitor the #
+# ircd's state via cron jobs. If this is a relative path, it will be #
+# relative to the configuration directory, and if it is not defined, #
+# the default of 'inspircd.pid' is used. #
+# #
+
+#<pid file="/path/to/inspircd.pid">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#- BANLIST LIMITS #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Use these tags to customise the ban limits on a per channel basis. #
+# the tags are read from top to bottom, and any tag found which #
+# matches the channels name applies the banlimit to that channel. #
+# It is advisable to put an entry with the channel as '*' at the #
+# bottom of the list. If none are specified or no maxbans tag is #
+# matched, the banlist size defaults to 64 entries. #
+# #
+
+<banlist chan="#morons" limit="128">
+<banlist chan="*" limit="69">
+
+#-#-#-#-#-#-#-#-#-#-#- DISABLED COMMANDS -#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This tag is optional, and specifies one or more commands which are #
+# not available to non-operators. For example you may wish to disable #
+# NICK and prevent non-opers from changing their nicknames. #
+# Note that any disabled commands take effect only after the user has #
+# 'registered' (e.g. after the initial USER/NICK/PASS on connection) #
+# so for example disabling NICK will not cripple your network. #
+# #
+
+#<disabled commands="TOPIC MODE">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- RTFM LINE -#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Just remove this... Its here to make you read ALL of the config #
+# file options ;) #
+
+<die value="You should probably edit your config *PROPERLY* and try again.">
+
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#- SERVER OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Settings to define which features are useable on your server. #
+# #
+# prefixquit - A prefix to be placed on the start of a client's #
+# quit message #
+# #
+# suffixquit - A suffix to be placed on the end of a client's #
+# quit message. #
+# #
+# fixedquit - A fixed quit message to display for all client #
+# QUITS. If specified, overrides both prefixquit #
+# and suffixquit options. #
+# #
+# loglevel - specifies what detail of messages to log in the #
+# log file. You may select from debug, verbose, #
+# default, sparse and none. #
+# #
+# allowhalfop - allows the +h channel mode #
+# #
+# noservices - If noservices is true, yes, or 1, then the first #
+# user into a channel gets founder status. This is #
+# only useful on networks running the m_chanprotect #
+# module without services. #
+# #
+# qaprefixes - If qaprefixes is true, yes, or 1, then users #
+# with +q or +a will get the ~ or & prefixes #
+# used in unreal. This is only useful on networks #
+# running the m_chanprotect module #
+# #
+# deprotectself - If this value is set to yes, true, or 1, then any #
+# user with +q or +a may remove the +q or +a from #
+# themselves. The default setting is to not enable #
+# this feature, which stops even the founder taking #
+# away their founder status without using services. #
+# #
+# 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. The default setting is to not enable #
+# this feature, so that only +q may remove +a, and #
+# nothing but services may remove +q. #
+# #
+# cyclehosts - If this is set to true, yes or 1, then when a #
+# user's hostname changes, they will appear to quit #
+# and then rejoin with their new host. This prevents #
+# clients from being confused by host changes, #
+# especially in the case of bots, and it is #
+# recommended that this option is enabled. #
+# #
+# netbuffersize - size of the buffer used to receive data from #
+# clients. The ircd may only read() this amount #
+# of text in one go at any time. (OPTIONAL) #
+# #
+# maxwho - The maximum number of results returned by a /WHO #
+# query. This is to prevent /WHO being used as a #
+# spam vector or means of flooding an ircd. The #
+# default is 128, it is not recommended to raise it #
+# above 1024. Values up to 65535 are permitted. If #
+# this value is omitted, any size WHO is allowed by #
+# anyone. #
+# #
+# somaxconn - The maximum number of sockets that may be waiting #
+# in the accept queue. This usually allows the ircd #
+# to soak up more connections in a shorter space of #
+# time when increased but please be aware there is a #
+# system defined maximum value to this, the same way #
+# there is a system defined maximum number of file #
+# descriptors. Some systems may only allow this to #
+# be up to 5 (ugh) while others such as FreeBSD will #
+# default to a much nicer 128. #
+# #
+# moduledir - This optional value indicates a runtime change of #
+# the location where modules are to be found. This #
+# does not add a supplementary directory. There can #
+# only be one module path. #
+# #
+# softlimit - This optional feature allows a defined softlimit. #
+# if defined sets a soft maxconnections value, has #
+# to be less than the ./configure maxclients #
+# #
+# userstats - The userstats field is optional and specifies #
+# which stats characters in /STATS may be requested #
+# by non-operators. Stats characters in this field #
+# are case sensitive and are allowed to users #
+# independent of if they are in a module or the core #
+# #
+# operspywhois - If this is set then when an IRC operator uses #
+# /WHOIS on a user they will see all channels, even #
+# ones if channels are secret (+s), private (+p) or #
+# if the target user is invisible +i. #
+# #
+# customversion - If you specify this configuration item, and it is #
+# not set to an empty value, then when a user does #
+# a /VERSION command on the ircd, this string will #
+# be displayed as the second portion of the output, #
+# replacing the system 'uname', compile flags and #
+# socket engine/dns engine names. You may use this #
+# to enhance security, or simply for vanity. #
+# #
+# maxtargets - The maxtargets field is optional, and if not #
+# defined, defaults to 20. It indicates the maximum #
+# number of targets which may be given to commands #
+# such as PRIVMSG, KICK etc. #
+# #
+# hidesplits - When set to 'yes', will hide split server names #
+# from non-opers. Non-opers will see '*.net *.split' #
+# instead of the server names in the quit message, #
+# identical to the way IRCu displays them. #
+# #
+# hidebans - When set to 'yes', will hide gline, kline, zline #
+# and qline quit messages from non-opers. For #
+# example, user A who is not an oper will just see #
+# (G-Lined) while user B who is an oper will see the #
+# text (G-Lined: Reason here) instead. #
+# #
+# hidewhois - When defined with a non-empty value, the given #
+# text will be used in place of the user's server #
+# in WHOIS, when a user is WHOISed by a non-oper. #
+# For example, most nets will want to set this to #
+# something like '*.netname.net' to conceal the #
+# actual server the user is on. #
+# #
+# flatlinks - When you are using m_spanningtree.so, and this #
+# value is set to true, yes or 1, /MAP and /LINKS #
+# will be flattened when shown to a non-oper. #
+# #
+# hideulines - When you are using m_spanningtree.so, and this #
+# value is set to true, yes or 1, then U-lined #
+# servers will be hidden in /LINKS and /MAP. For non #
+# opers. Please be aware that this will also hide #
+# any leaf servers of a U-lined server, e.g. jupes. #
+# #
+# nouserdns - If set to 'yes', 'true' or '1', no user dns #
+# lookups will be performed for connecting users. #
+# this can save a lot of resources on very busy irc #
+# servers. #
+# #
+# syntaxhints - If set to 'yes', 'true' or '1', when a user does #
+# not give enough parameters for a command, a syntax #
+# hint will be given (using the RPL_TEXT numeric) #
+# as well as the standard ERR_NEEDMOREPARAMS. #
+# #
+# announcets - If this value is defined to 'yes', 'true' or '1', #
+# then if a channel's timestamp is updated the users #
+# on the channel will be informed of the change via #
+# a server notice to the channel with the old and #
+# new TS values in the timestamp. If you think this #
+# is just pointless noise, define the value to 0. #
+# #
+# ircumsgprefix - Use undernet style message prefix for channel #
+# NOTICE and PRIVMSG adding the prefix to the line #
+# of text sent out. Eg. NOTICE @#test :@ testing #
+# vs. the off setting: NOTICE @#test :testing #
+# #
+# hostintopic - If this is set to yes (the default) then the full #
+# nick!user@host is shown for who set a TOPIC last. #
+# if set to no, then only the nickname is shown. #
+# #
+# announceinvites #
+# - If this option is set to yes (the default), then #
+# invites are announced to the channel when a user #
+# invites annother user. If you consider this to be #
+# unnecessary noise, explicitly set this to no. #
+# #
+# disablehmac - If you are linking your InspIRCd to older versions #
+# then you can specify this option and set it to #
+# yes. 1.1.6 and above support HMAC and challenge- #
+# response for password authentication. These can #
+# greatly enhance security of your server to server #
+# connections when you are not using SSL (as is the #
+# case with a lot of larger networks). Linking to #
+# older versions of InspIRCd should not *usually* be #
+# a problem, but if you have problems with HMAC #
+# authentication, this option can be used to turn it #
+# off. #
+# #
+# hidemodes - If this option is enabled, then the listmodes #
+# given (e.g. +eI), will be hidden from users below #
+# halfop. This is not recommended to be set on mode #
+# +b, as it may break some features in popular #
+# clients such as mIRC. #
+# #
+# quietbursts - When synching or splitting from the network, a #
+# server can generate a lot of connect and quit #
+# snotices to the +C and +Q snomasks. Setting this #
+# value to yes squelches those messages, which can #
+# make them more useful for opers, however it will #
+# degrade their use by certain third party programs #
+# such as BOPM which rely on them to scan users when #
+# a split heals in certain configurations. #
+# #
+# pingwarning - This should be set to a number between 1 and 59 if #
+# defined, and if it is defined will cause the server#
+# to send out a warning via snomask +l if a server #
+# does not answer to PING after this many seconds. #
+# This can be useful for finding servers which are #
+# at risk of pinging out due to network issues. #
+# #
+# exemptchanops - This option allows channel operators to be exempted#
+# from certain channel modes. #
+# Supported modes are +SfgNc. Defaults to off. #
+# #
+# defaultmodes - The default modes to be given to each channel on #
+# creation. Defaults to 'nt'. There should be no + #
+# or - symbols in this sequence, if you add them #
+# they will be ignored. You may add parameters for #
+# parameterised modes. #
+# #
+# moronbanner - The NOTICE to show to users who are glined, zlined #
+# klined or qlined when they are disconnected. This #
+# is totally freeform, you may place any text here #
+# you wish. #
+# #
+
+<options prefixquit="Quit: "
+ loglevel="default"
+ netbuffersize="10240"
+ maxwho="128"
+ noservices="no"
+ qaprefixes="no"
+ deprotectself="no"
+ deprotectothers="no"
+ somaxconn="128"
+ softlimit="12800"
+ userstats="Pu"
+ operspywhois="no"
+ customversion=""
+ maxtargets="20"
+ hidesplits="no"
+ hidebans="no"
+ hidewhois=""
+ flatlinks="no"
+ hideulines="no"
+ nouserdns="no"
+ syntaxhints="no"
+ cyclehosts="yes"
+ ircumsgprefix="no"
+ announcets="yes"
+ disablehmac="no"
+ hostintopic="yes"
+ hidemodes="eI"
+ quietbursts="yes"
+ pingwarning="15"
+ allowhalfop="yes"
+ defaultmodes="nt"
+ moronbanner="You're banned! Email haha@abuse.com with the ERROR line below for help."
+ exemptchanops="">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#- TIME SYNC OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#
+# Time sychronization options for m_spanningtree linking. #
+# #
+# Because IRC is very time and clock dependent, InspIRCd provides its #
+# own methods for syncronization of time between servers as shown #
+# in the example below, for servers that don't have ntpd running. #
+# #
+# enable - If this value is 'yes', 'true', or '1', time #
+# synchronization is enabled on this server. This #
+# means any servers you are linked to will #
+# automatically synchronize time, however you should #
+# use ntpd instead where possible, NOT this option. #
+# #
+# master - If this value is set to yes, then this server will #
+# act as the authoritative time source for the whole #
+# network. All other servers will respect its time #
+# without question, and match their times to it. #
+# only one server should have the master value set #
+# to 'yes'. #
+# #
+<timesync enable="no" master="no">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#- WHOWAS OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This tag lets you define the behaviour of the /whowas command of #
+# your server. #
+# #
+# groupsize - Controls the maximum entries per nick shown when #
+# performing a /whowas nick. Setting this to 0 dis- #
+# ables whowas completely. #
+# #
+# maxgroups - The maximum number of nickgroups that can be added #
+# to the list. If max is reached, oldest group will #
+# be deleted first like a FIFO. A groupsize of 3 and #
+# a maxgroups of 5000 will allow for 5000 nicks to #
+# be stored with a history of 3, thus giving a total #
+# of 3 * 5000 = 15000 entries. A setting of 0 dis- #
+# ables whowas completely. #
+# #
+# maxkeep - The maximum time a nick is kept in the whowas list #
+# before being pruned. Time may be specified in #
+# seconds, or in the following format: 1y2w3d4h5m6s #
+# meaning one year, two weeks, three days, 4 hours, #
+# 5 minutes and 6 seconds. All fields in this format #
+# are optional. Minimum is 1 hour, if less InspIRCd #
+# will default back to 1 hour. #
+# #
+#<whowas groupsize="10" #
+# maxgroups="100000" #
+# maxkeep="3d"> #
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#- MODULE OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# These tags define which modules will be loaded on startup by your #
+# server. Add modules without any paths. When you make your ircd #
+# using the 'make' command, all compiled modules will be moved into #
+# the folder you specified when you ran ./configure. The module tag #
+# automatically looks for modules in this location. #
+# If you attempt to load a module outside of this location, either #
+# in the config, or via /LOADMODULE, you will receive an error. #
+# #
+# By default, ALL modules are commented out. You must uncomment them #
+# or add lines to your config to load modules. Please refer to #
+# http://www.inspircd.org/wiki/Modules_List for a list of modules and#
+# each modules link for any additional conf tags they require. #
+# #
+# You may use wildcards in a <module> tag to load all modules which #
+# match a glob pattern (e.g. m_sa????.so would load m_sajoin, #
+# m_sapart, m_saquit and m_sanick) #
+# #
+# ____ _ _____ _ _ ____ _ _ _ #
+# | _ \ ___ __ _ __| | |_ _| |__ (_)___ | __ )(_) |_| | #
+# | |_) / _ \/ _` |/ _` | | | | '_ \| / __| | _ \| | __| | #
+# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
+# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
+# #
+# To link servers to InspIRCd, you MUST load the m_spanningtree #
+# module, as shown below. If you DO NOT do this, server links will #
+# NOT work at all. ie. The ports will NOT bind, and /connect will not #
+# work properly. This is by design, to allow for the implementation #
+# of other linking protocols in modules in the future. #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Spanning Tree module - allows linking of servers using the spanning
+# tree protocol (see the READ THIS BIT section above).
+#
+#<module name="m_spanningtree.so">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# MD5 Module - Allows other modules to generate MD5 hashes, usually for
+# cryptographic uses and security.
+#
+# IMPORTANT:
+# Other modules such as m_cloaking.so and m_opermd5.so may rely on
+# this module being loaded to function.
+#
+#<module name="m_md5.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SHA256 Module - Allows other modules to generate SHA256 hashes,
+# usually for cryptographic uses and security.
+#
+# IMPORTANT:
+# Other modules such as m_opermd5.so may rely on this module being
+# loaded to function.
+#
+#<module name="m_sha256.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Alias module: Allows you to define server-side command aliases
+#<module name="m_alias.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- 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.#
+# An alias tag requires the following values to be defined in it: #
+# #
+# text - The text to detect as the actual command line, #
+# Cant contain spaces, but case insensitive. #
+# You may have multiple aliases with the same #
+# command name (text="" value), however the first #
+# found will be executed if its format value is #
+# matched, or it has no format value. Aliases are #
+# read from the top of the file to the bottom. #
+# #
+# format - If this is defined, the parameters of the alias #
+# must match this glob pattern. For example if you #
+# want the first parameter to start with a # for #
+# the alias to be executed, set format="#*" in the #
+# alias definition. Note that the :'s which are #
+# part of IRC formatted lines will be preserved #
+# for matching of this text. This value is #
+# optional. #
+# #
+# replace - The text to replace 'text' with. Usually this #
+# will be "PRIVMSG ServiceName :$2-" or similar. #
+# You may use the variables $1 through $9 in the #
+# replace string, which refer to the first through #
+# ninth word in the original string typed by the #
+# user. You may also use $1- through $9- which #
+# refer to the first word onwards, through to the #
+# ninth word onwards, e.g. if the user types the #
+# command "foo bar baz qux quz" then $3- will hold #
+# "baz qux quz" and $2 will contain "bar". You may #
+# also use the special variables: $nick, $ident, #
+# $host and $vhost, and you may seperate multiple #
+# commands with \n. If you wish to use the ACTUAL #
+# characters \ and n together in a line, you must #
+# use the sequence "\\n". #
+# #
+# requires - If you provide a value for 'requires' this means #
+# the given nickname MUST be online for the alias #
+# to successfully trigger. If they are not, then #
+# the user receives a 'no such nick' 401 numeric. #
+# #
+# uline - Defining this value with 'yes', 'true' or '1' #
+# will ensure that the user given in 'requires' #
+# must also be on a u-lined server, as well as #
+# actually being on the network. If the user is #
+# online, but not on a u-lined server, then an #
+# oper-alert is sent out as this is possibly signs #
+# of a user trying to impersonate a service. #
+# #
+# operonly - Defining this value, with a value of 'yes', '1' #
+# or true will make the alias oper only. If a non- #
+# oper attempts to use the alias, it will appear #
+# to not exist. #
+# #
+#<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+#<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+#<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+#<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
+#<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
+#<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
+#
+# An example of using the format value to create an alias with two
+# different behaviours depending on the format of the parameters.
+#
+#<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3"
+# requires="ChanServ" uline="yes">
+#
+#<alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2"
+# requires="NickServ" uline="yes">
+#
+# This alias fixes a glitch in xchat 2.6.x and above and the way it
+# assumes IDENTIFY must be prefixed by a colon (:) character. It should
+# be placed ABOVE the default NICKSERV alias (the first example) listed
+# above.
+#
+#<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
+# requires="NickServ" uline="yes">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Alltime module: Shows time on all connected servers at once
+#<module name="m_alltime.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Antibear security module: Prevents 'bear.txt' based trojans from
+# connecting to your network by sending them a numeric they can't handle.
+#<module name="m_antibear.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Antibottler module: Labels bottler leech bots
+#<module name="m_antibottler.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+#
+# Auditorium settings:
+#
+#<auditorium showops="no">
+#
+# Setting this value to yes makes m_auditorium behave like unrealircd
+# +u channel mode, e.g. ops see users joining, parting, etc, and users
+# joining the channel see the ops. Without this flag, the mode acts
+# like ircnet's +a (anonymous channels), showing only the user in the
+# names list, and not even showing the ops in the list, or showing the
+# ops that the user has joined.
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Ban except module: Adds support for channel ban exceptions (+e)
+#<module name="m_banexception.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Ban redirection module: Allows bans which redirect to a specified
+# channel. e.g. +b nick!ident@host#channelbanneduserissentto
+#<module name="m_banredirect.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Block amsg module: Attempt to block all usage of /amsg and /ame
+#<module name="m_blockamsg.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- BLOCKAMSG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# If you have the m_blockamsg.so 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. #
+# action - Any of 'notice', 'noticeopers', 'silent', 'kill' #
+# or 'killopers'. Define how to take action when #
+# a user uses /amsg or /ame. #
+#
+#<blockamsg delay="3" action="killopers">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Block CAPS module: Blocking all-CAPS messages with cmode +P
+#<module name="m_blockcaps.so">
+# #
+#-#-#-#-#-#-#-#-#-#-#- BLOCKCAPS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# percent - How many percent of text must be caps before text #
+# will be blocked. #
+# #
+# minlen - The minimum length a line must be for the block #
+# percent to have any effect. #
+# #
+# capsmap - A list of chars to be considered CAPS, this was #
+# you can add CAPS for your language. Also you can #
+# add things like ! and space to further lock down #
+# on caps usage. #
+#<blockcaps percent="50"
+# minlen="5"
+# capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Block colour module: Blocking colour-coded messages with cmode +c
+#<module name="m_blockcolor.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Botmode module: Adds the user mode +B
+#<module name="m_botmode.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# CBAN module: Lets you disallow channels from being used at runtime.
+#<module name="m_cban.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Censor module: Adds the channel mode +G
+#<module name="m_censor.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- CENSOR CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Optional - If you specify to use the m_censor module, then you must #
+# specify some censor tags. See also: #
+# http://www.inspircd.org/wiki/Censor_Module #
+#
+#<include file="censor.conf">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
+# (http://cgiirc.sourceforge.net).
+#<module name="m_cgiirc.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-# CGIIRC CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+#
+# Optional - If you specify to use m_cgiirc, then you must specify one
+# or more cgihost tags which indicate authorized CGI:IRC servers which
+# will be connecting to your network, and an optional cgiirc tag.
+# For more information see: http://www.inspircd.org/wiki/CGI-IRC_Module
+#
+# Set to yes if you want to notice opers when CGI clients connect
+# <cgiirc opernotice="no">
+#
+# The type field indicates where the module should get the real
+# client's IP address from, for further information, please see the
+# CGI:IRC documentation.
+#
+# <cgihost type="pass" mask="www.mysite.com"> # Get IP from PASS
+# <cgihost type="webirc" mask="somebox.mysite.com"> # Get IP from WEBIRC
+# <cgihost type="ident" mask="otherbox.mysite.com"> # Get IP from ident
+# <cgihost type="passfirst" mask="www.mysite.com"> # See the docs
+#
+# IMPORTANT NOTE:
+# ---------------
+#
+# When you connect CGI:IRC clients, there are two connect classes which
+# apply to these clients. When the client initially connects, the connect
+# class which matches the cgi:irc site's host is checked. Therefore you
+# must raise the maximum local/global clients for this ip as high as you
+# want to allow cgi clients. After the client has connected and is
+# determined to be a cgi:irc client, the class which matches the client's
+# real IP is then checked. You may set this class to a lower value, so that
+# the real IP of the client can still be restricted to, for example, 3
+# sessions maximum.
+#
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Channel create module: Adds snomask +j, which will notify opers of
+# any new channels that are created
+#<module name="m_chancreate.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Channel filter module: Allows channel-op defined message
+# filtering using simple string matches (channel mode +g)
+#<module name="m_chanfilter.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Chanprotect module: gives +q and +a channel modes
+#<module name="m_chanprotect.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Check module: gives /check
+# Check is useful for looking up information on channels,
+# users, IP addresses and hosts.
+#<module name="m_check.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# CHGHOST module: Adds the /CHGHOST command
+#<module name="m_chghost.so">
+#
+#-#-#-#-#-#-#-#-# /CHGHOST - /SETHOST CONFIGURATION #-#-#-#-#-#-#-#-#
+# Optional - If you want to use special chars for hostnames you can #
+# specify your own custom list of chars with the <hostname> tag: #
+# #
+# charmap - A list of chars accepted as valid by the /CHGHOST #
+# and /SETHOST commands. Also note that the list is #
+# case-sensitive. #
+#<hostname charmap="abcdefghijklmnopqrstuvwxyz.-_/0123456789">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# CHGIDENT module: Adds the /CHGIDENT command
+#<module name="m_chgident.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# CHGNAME module: Adds the /CHGNAME command
+#<module name="m_chgname.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Cloaking module: Adds usermode +x and cloaking support.
+# Relies on the module m_md5.so being loaded before m_cloaking.so in
+# the configuration file.
+#<module name="m_cloaking.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- CLOAKING CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Optional - If you specify the m_cloaking.so module as above, you #
+# must define cloak keys, and optionally a cloak prefix as shown #
+# below. When using cloaking, the cloak keys are MANDITORY and must #
+# be included. However, if prefix is not included, it will default #
+# to your networks name from the <server> tag. #
+# #
+# <cloak key1="0x2AF39F40" #
+# key2="0x78E10B32" #
+# key3="0x4F2D2E82" #
+# key4="0x043A4C81" #
+# prefix="mynet"> #
+# #
+# Please note that the key values will accept any number, and should #
+# be large numbers. Using small numbers such as "7" or "1924" will #
+# seriously weaken the security of your cloak. It is recommended you #
+# use hexdecimal numbers prefixed by "0x", as shown in this example, #
+# with each key eight hex digits long. #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Clones module: Adds an oper command /CLONES for detecting cloned
+# users. Warning: This module may be resource intensive when its
+# command is issued, use with care.
+#<module name="m_clones.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Conn-Join: Allows you to force users to join one or more channels
+# automatically upon connecting to the server.
+#<module name="m_conn_join.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+#
+# If you have m_conn_join.so loaded, you can configure it using the
+# follow values:
+#
+#<autojoin channel="#one,#two,#three">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Conn-Usermodes: Set modes on users when they connect
+# When this module is loaded <connect:allow> tags 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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Conn-Wait-for-Pong: Don't let a user connect until they PONG
+#<module name="m_conn_waitpong.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- WAITPONG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# If you have the m_conn_waitpong.so module loaded, configure it with #
+# the <waitpong> tag: #
+# #
+# sendsnotice - Whether to send a snotice on connect, like other #
+# older ircds #
+# #
+# killonbadreply - Whether to kill the user if they send the wrong #
+# PONG reply. #
+# #
+#<waitpong sendsnotice="yes" killonbadreply="yes">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Channel cycle module. Server side /hop, with +ilk etc bypass.
+#<module name="m_cycle.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Connection throttle module. Configuration:
+#<module name="m_connflood.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# seconds, maxconns - Amount of connections per <seconds>.
+#
+# timeout - Time to wait after the throttle was activated
+# before deactivating it. Be aware that the time
+# is seconds + timeout.
+#
+# quitmsg - The message that users get if they attempt to
+# connect while the throttle is active.
+#
+# bootwait - Amount of time to wait before enforcing the
+# throttling when the server just booted.
+#
+#<connflood seconds="30" maxconns="3" timeout="30"
+# quitmsg="Throttled" bootwait="10">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# DCCALLOW module: Adds the /DCCALLOW command
+#<module name="m_dccallow.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- DCCALLOW CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# blockchat - Whether to block DCC CHAT as well as DCC SEND
+# length - Default duration of entries in DCCALLOW list
+# action - Default action to take if no action is specified
+# can be 'block' or 'allow'
+#
+# File configuration:
+# pattern - The glob pattern to match against
+# action - Action to take if a user attempts to send a file
+# that matches this pattern, can be 'block' or 'allow'
+#
+#<dccallow blockchat="yes" length="5m" action="block">
+#<banfile pattern="*.exe" action="block">
+#<banfile pattern="*.txt" action="allow">
+#
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Deaf module: adds support for ircu style usermode +d - deaf to
+# channel messages and channel notices.
+#<module name="m_deaf.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Deny Channels: Deny Channels from being used by users
+#<module name="m_denychans.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- DENYCHAN DEFINITIONS -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# If you have the m_denychans.so module loaded, you need to specify #
+# the channels to deny: #
+# #
+# name - The channel name to deny. #
+# #
+# allowopers - If operators are allowed to override the deny. #
+# #
+# reason - Reason given for the deny. #
+# #
+#<badchan name="#gods" allowopers="yes" reason="Tortoises!">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Devoice Module: Let users devoice themselves.
+#<module name="m_devoice.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# DNS Blacklist Module: Provides support for looking up IPs on one or #
+# more blacklists. #
+#<module name="m_dnsbl.so"> #
+# #
+# For configuration options please see the wiki page for m_dnsbl at #
+# http://inspircd.org/wiki/DNS_Blacklist_Module #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Filter module: Provides glob-based message filtering
+#<module name="m_filter.so">
+# OR
+# PCRE filter module: Filters messages using regular expressions
+#<module name="m_filter_pcre.so">
+#
+# You may only use one or the other with these modules, network-wide.
+#
+#-#-#-#-#-#-#-#-#-#-#- FILTER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Optional - If you specify to use the m_filter or m_filter_pcre #
+# modules, then specfiy below the path to the filter.conf file, #
+# or define some <filter> tags. #
+# #
+#<include file="filter.conf">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Foobar module: does nothing - historical relic
+#<module name="m_foobar.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Globops module: gives /GLOBOPS and usermode +g
+#<module name="m_globops.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Global load module: Allows loading and unloading of modules network-
+# wide (USE WITH EXTREME CAUTION!)
+#<module name="m_globalload.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# HELPOP module: Provides the /HELPOP command
+#<module name="m_helpop.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- HELPOP CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# Optional - If you specify to use the m_helpop.so module, then #
+# specify below the path to the helpop.conf file, or if you like to #
+# make a mess, define your helpop tags in this conf. #
+# #
+#<include file="helpop.conf">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# HIDECHANS module: Allows opers to hide their channels list from non-
+# opers by setting user mode +I on themselves.
+# <module name="m_hidechans.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# HIDEOPER module: Allows opers to hide their oper status from non-
+# opers by setting user mode +H on themselves.
+# <module name="m_hideoper.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Hostchange module: Allows a different style of cloaking
+#<module name="m_hostchange.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- HOSTCHANGE CONFIGURATION -#-#-#-#-#-#-#-#-#-#
+# #
+# Optional - If you choose to use the m_hostchange.so module. #
+# Config Help - See http://www.inspircd.org/wiki/Host_Changer_Module #
+# #
+#<host suffix="polarbears.org" separator="." prefix="">
+#<hostchange mask="*@fbi.gov" action="addnick">
+#<hostchange mask="*r00t@*" action="suffix">
+#<hostchange mask="a@b.com" action="set" value="blah.blah.blah">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# httpd module: Provides http server support for InspIRCd
+#<module name="m_httpd.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- HTTPD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+#
+# Optional - If you choose to use the m_httpd.so module, then you must
+# specify the port number and other details of your http server:
+#
+#<http ip="192.168.1.10" host="brainwave" port="32006"
+# index="/home/brain/inspircd/http/index.html">
+#
+# You may have as many of these tags as you wish, each with a different
+# IP, port, host or index file. Each one will act as an independent
+# HTTP server.
+#
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- HTTPD STATS CONFIGURATION -#-#-#-#-#-#-#-#-#-#
+#
+#<httpstats stylesheet="http://remote.style/sheet.css">
+#
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Ident: Provides RFC 1413 ident lookup support
+#<module name="m_ident.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- 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 one second. 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. #
+# #
+#<ident timeout="5" bind=""> #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Invite except module: Adds support for channel invite exceptions (+I)
+#<module name="m_inviteexception.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Invisible module - Adds support for usermode +Q (quiet) which lets an
+# oper go 'invisible' similar to unrealircd 3.1's +I mode. Note that
+# opers are still able to see invisible users, and if an oper with +Q
+# deopers, they will become visible.
+#
+# IMPORTANT NOTE: To allow this mode to be used by a type of oper, you
+# must first add the value canquiet="yes" to that oper's type tag.
+#
+#<module name="m_invisible.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Join flood module: Adds support for join flood protection (+j)
+#<module name="m_joinflood.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Jump Server module: Adds support for the RPL_REDIR numeric
+#<module name="m_jumpserver.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Anti-Auto-Rejoin: Adds support for prevention of auto-rejoin (+J)
+#<module name="m_kicknorejoin.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Knock module: adds the /KNOCK command and +K channel mode
+#<module name="m_knock.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Lock server module: Adds /LOCKSERV and /UNLOCKSERV commands that is #
+# used to temporarily close/open for new connections to the server. #
+# These commands require OPER status and that the LOCKSERV UNLOCKSERV #
+# are specified in a <class> tag that the oper is part of. This is so #
+# you can control who has access to this possible dangerous command. #
+# If your server is locked and you got disconnected, do a REHASH from #
+# shell to open up again.
+#<module name="m_lockserv.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Msg flood module: Adds message/notice flood protection (+f)
+#<module name="m_messageflood.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# MySQL module: Allows other SQL modules to access MySQL databases
+# through a unified API. You must copy the source for this module
+# from the directory src/modules/extra, plus the file m_sqlv2.h
+#<module name="m_mysql.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_mysql.so is more complex than described here, see the wiki for #
+# more: http://www.inspircd.org/wiki/SQL_Service_Provider_Module #
+#
+#<database name="mydb" username="myuser" password="mypass" hostname="localhost" id="my_database2">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Nicklock module: Let opers change a user's nick and then stop that
+# user from changing their nick again.
+#<module name="m_nicklock.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# No ctcp module: Adds the channel mode +C to block CTCPs
+#<module name="m_noctcp.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Noinvite module: Gives channel mode +V
+#<module name="m_noinvite.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# No kicks module: Adds the +Q channel mode
+#<module name="m_nokicks.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# No nicks module: Adds the +N channel mode
+#<module name="m_nonicks.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# No Notice module: adds the channel mode +T
+#<module name="m_nonotice.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Oper channels mode: Adds the +O channel mode
+#<module name="m_operchans.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Oper hash module: Allows hashed oper passwords
+# Relies on the module m_md5.so and/or m_sha256.so being loaded before
+# m_oper_hash.so in the configuration file.
+#<module name="m_oper_hash.so">
+#
+#-#-#-#-#-#-#-#-#-#-# OPER HASH CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+#
+# To use this module, you must define a hash type for each oper's
+# password you want to hash. For example:
+#
+# <oper name="Brain"
+# host="ident@dialup15.isp.com"
+# hash="sha256"
+# password="a41d730937a53b79f788c0ab13e9e1d5"
+# type="NetAdmin">
+#
+# The types of hashing available vary depending on which hashing modules
+# you load, but usually if you load m_sha256.so and m_md5.so, both md5
+# and sha256 type hashing will be available (the most secure of which
+# is SHA256).
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Oper Join module: Forces opers to join a channel on oper-up
+#<module name="m_operjoin.so">
+#
+#-#-#-#-#-#-#-#-#-#-# OPERJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# If you are using the m_operjoin.so module, specify the channel here #
+# #
+#<operjoin channel="#channel">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Oper MOTD module: Provides support for seperate message of the day
+# on oper-up
+#<module name="m_opermotd.so">
+#
+#-#-#-#-#-#-#-#-#-#-# OPERMOTD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# If you are using the m_opermotd.so module, specify the motd here #
+# #
+#<opermotd file="oper.motd">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Override module: Adds support for oper override
+#<module name="m_override.so">
+#
+#-#-#-#-#-#-#-#-#-#-# OVERRIDE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_override.so is too complex it describe here, see the wiki: #
+# http://www.inspircd.org/wiki/Oper_Override_Module #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Oper levels module: Gives each oper a level and prevents
+# actions being taken against higher level opers
+# Specify the level as the 'level' parameter of the <type> tag
+#<module name="m_operlevels.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Oper modes module: Allows you to specify modes to add/remove on oper
+# Specify the modes as the 'modes' parameter of the <type> tag
+#<module name="m_opermodes.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# PostgreSQL module: Allows other SQL modules to access PgSQL databases
+# through a unified API. You must copy the source for this module
+# from the directory src/modules/extra, plus the file m_sqlv2.h
+#<module name="m_pgsql.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_pgsql.so is more complex than described here, see the wiki for #
+# more: http://www.inspircd.org/wiki/SQL_Service_Provider_Module #
+#
+#<database name="mydb" username="myuser" password="mypass" hostname="localhost" id="my_database" ssl="no">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+#
+#-#-#-#-#-#-#-#-#-#- RANDOMQUOTES CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
+# #
+# Optional - If you specify to use the m_randquote.so module, then #
+# specify below the path to the randquotes.conf file. #
+# #
+#<randquote file="randquotes.conf">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Redirect module: Adds channel redirection (mode +L)
+#<module name="m_redirect.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Registered users only channel creation
+# Allows only registered users and opers to create new channels.
+#<module name="m_regonlycreate.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Remove module: Adds the /REMOVE command which is a peaceful
+# alternative to /KICK
+#<module name="m_remove.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Restrict banned users module:
+# Disallows banned users on a channel from messaging the channel,
+# changing nick, or changing the topic, if loaded.
+#<module name="m_restrictbanned.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Restricted channels module: Allows only opers to create channels
+#<module name="m_restrictchans.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Restrict message module: Allows users to only message opers
+#<module name="m_restrictmsg.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Provide /LIST throttling (to prevent flooding) and /LIST safety to
+# prevent excess flood when the list is large.
+#<module name="m_safelist.so">
+#
+#-#-#-#-#-#-#-#-#-#-# SAFELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+#
+# When using Safelist, you may set the following values;
+#
+# The first value, 'throttle', sets the amount of time in seconds a user
+# must wait between LIST commands. For example, if this is set to 60
+# (the default) then the user may not /LIST more than once a minute.
+# If not defined, the default value is 60 seconds.
+#
+# The second value, 'maxlisters', indicates the maximum number of users
+# which may be retrieving a LIST at once. It is not recommended you raise
+# this value, as increasing it too high can make your network vulnerable
+# to floodbots which waste your bandwidth and CPU time with LIST requests.
+#
+#<safelist throttle="60" maxlisters="50">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SAJOIN module: Adds the /SAJOIN command
+#<module name="m_sajoin.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SAMODE module: Adds the oper /SAMODE command
+#<module name="m_samode.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SANICK module: Allows opers to change user's nicks
+#<module name="m_sanick.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SAPART module: Adds the oper /SAPART command
+#<module name="m_sapart.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SAQUIT module: Adds the oper /SAQUIT command (abusable!!!)
+#<module name="m_saquit.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Secure list module: Prevent /LIST in the first minute of connection,
+# crippling most spambots and trojan spreader bots.
+#<module name="m_securelist.so">
+#
+#-#-#-#-#-#-#-#-#-# SECURELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# Securelist can be harmful to some irc search engines such as #
+# netsplit.de and searchirc.com. To prevent securelist blocking these #
+# sites from listing, define exception tags as shown below: #
+<securehost exception="*@*.searchirc.org">
+<securehost exception="*@*.netsplit.de">
+<securehost exception="*@echo940.server4you.de">
+# #
+# Define the following variable to change how long a user must wait #
+# before issuing a LIST. If not defined, defaults to 60 seconds. #
+# #
+#<securelist waittime="60"> #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# See nicks module: Allow for SNOMASK +N which shows nick changes.
+#<module name="m_seenicks.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Set Idle module: Adds a command for opers to change their
+# idle time (mainly a toy)
+#<module name="m_setidle.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Services support module: Adds several usermodes such as +R and +M
+# this module implements the 'identified' state via user mode +r, which
+# is similar to the DALnet and dreamforge systems.
+#<module name="m_services.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Services support module: Adds several usermodes such as +R and +M
+# this module implements the 'identified' state via account names (AC)
+# and is similar in operation to the way asuka and ircu handle services.
+# it cannot be used at the same time as m_services, above.
+#<module name="m_services_account.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Sethost module: Adds the /SETHOST command
+# See m_chghost for how to customise valid chars for hostnames
+#<module name="m_sethost.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Setident module: Adds the /SETIDENT command
+#<module name="m_setident.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SETNAME module: Adds the /SETNAME command
+#<module name="m_setname.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Show Whois module: Adds the +W usermode which allows opers
+# to see when they are whois'ed (can be annoying).
+#<module name="m_showwhois.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Spy module: Adds the commands SPYLIST and SPYNAMES that let opers
+# see who is in a +s channel, and list +s channels, show keys of keyed
+# channels the oper is not a member of etc. (standard 'abusive' features
+# of many other ircds, modulized here in InspIRCd).
+#<module name="m_spy.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SSL channel mode module: Adds support for SSL-only channels (+z).
+# does not do anything useful without a working SSL module (see below)
+#<module name="m_sslmodes.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Dummy ssl module: If you have other servers on your network which
+# have SSL, but your server does not have ssl enabled, you should load
+# this module, which will handle SSL metadata (e.g. the "Is using ssl"
+# field in the WHOIS information).
+#<module name="m_ssl_dummy.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# GnuTLS ssl module: Adds support for client-server SSL using GnuTLS,
+# if enabled. You must copy the source for this module from the directory
+# src/modules/extra, or answer 'yes' in ./configure when asked if you
+# want to enable this, or it will not load.
+#<module name="m_ssl_gnutls.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_ssl_gnutls.so is too complex it describe here, see the wiki: #
+# http://www.inspircd.org/wiki/GnuTLS_SSL_Module #
+# #
+# NOTE: If you want to use this module to encrypt and sign your #
+# server to server traffic, you MUST load it before m_spanningtree in #
+# your configuration file! #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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. You must symlink the source for
+# this module from the directory src/modules/extra.
+#<module name="m_sslinfo.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# OpenSSL ssl module: Adds support for client-server SSL using OpenSSL,
+# if enabled. You must copy the source for this module from the directory
+# src/modules/extra, or answer 'yes' in ./configure when asked if you
+# want to enable this, or it will not load.
+#<module name="m_ssl_openssl.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_ssl_openssl.so is too complex it describe here, see the wiki: #
+# http://www.inspircd.org/wiki/OpenSSL_SSL_Module #
+# #
+# NOTE: If you want to use this module to encrypt and sign your #
+# server to server traffic, you MUST load it before m_spanningtree in #
+# your configuration file! #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SSL Cert Oper module: Allows opers to oper up using the key fingerprint
+# stored within their SSL certificate and key pair.
+# When using this module, one of m_ssl_gnutls.so or m_ssl_openssl.so must
+# be loaded. An extra value should be added to enabled opers, which
+# is in the following format: fingerprint="<hash>". For more information,
+# see the example in the oper blocks.
+#<module name="m_ssl_oper_cert.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Strip colour module: Adds the channel mode +S
+#<module name="m_stripcolor.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SILENCE module: Adds support for /SILENCE
+#<module name="m_silence.so">
+#
+# Configuration tags:
+#
+#<silence maxentries="32">
+#
+# Sets the maximum number of entries on a users silence list.
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Extended SILENCE module: Adds support for /SILENCE with additional
+# features to silence based on invites, channel messages, etc.
+#<module name="m_silence_ext.so">
+#
+# The configuration tags for this module are identical to those of
+# m_silence, shown above.
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SQLite3 module: Allows other SQL modules to access SQLite3 #
+# databases through a unified API. You must link the source for this #
+# module from the directory src/modules/extra to src/modules, plus #
+# the file m_sqlv2.h #
+#<module name="m_sqlite3.so">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_sqlite.so is more complex than described here, see the wiki for #
+# more: http://www.inspircd.org/wiki/SQLite3_Service_Provider_Module #
+#
+#<database hostname="/full/path/to/database.db" id="anytext">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SQLutils module: Provides some utilities to SQL client modules, such
+# as mapping queries to users and channels. You must copy the source
+# for this module from the directory src/modules/extra/m_sqlutils.cpp
+# and src/modules/extra/m_sqlutils.h into /src/modules
+# Needed for, and loaded before: SQLauth and SQLoper
+#<module name="m_sqlutils.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SQL authentication module: Allows IRCd connections to be tied into
+# a database table (for example a forum). You must copy the source for
+# this module from the directory src/modules/extra
+# Depends on the SQLutils module being loaded first.
+#<module name="m_sqlauth.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# m_sqlauth.so is too complex it describe here, see the wiki: #
+# http://www.inspircd.org/wiki/SQL_Authentication_Module #
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SQL logging module: Allows you to log network-wide data for your
+# network in a fully normalized set of SQL tables. You must copy the
+# source for this module from the directory src/modules/extra
+#<module name="m_sqllog.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- SQLLOG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# dbid - Database ID to use (see m_sql) #
+# #
+# See also: http://www.inspircd.org/wiki/SQL_Logging_Module #
+# #
+#<sqllog dbid="1">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SQL oper module: Allows you to store oper credentials in an SQL table
+# You must copy the source for this module from the directory src/modules/extra
+# Depends on the SQLutils module being loaded first.
+#<module name="m_sqloper.so">
+#
+#-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# dbid - Database ID to use (see m_sql) #
+# #
+# See also: http://www.inspircd.org/wiki/SQL_Oper_Storage_Module #
+# #
+#<sqloper dbid="1">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be #
+# added/removed by Services. #
+#<module name="m_svshold.so">
+
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SWHOIS module: Allows you to add arbitary lines to user WHOIS.
+#<module name="m_swhois.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Test command module: Does nothing significant. Read: pointless.
+#<module name="m_testcommand.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Timed bans module: Adds timed bans and the /TBAN command
+#<module name="m_timedbans.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Test line module: Adds the /TLINE command, used to test how many
+# users a /GLINE or /ZLINE etc would match.
+#<module name="m_tline.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# UHNAMES support module: Adds support for the IRCX style UHNAMES
+# extension, which displays ident and hostname in the names list for
+# each user, saving clients from doing a WHO on the channel. Note that
+# this module is not widely supported yet. If a client does not support
+# UHNAMES it will not enable it, this will not break incompatible
+# clients.
+#<module name="m_uhnames.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Userip module: Adds the /USERIP command
+#<module name="m_userip.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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">
+#
+#-#-#-#-#-#-#-#-#-#-#- VHOST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# user - Username for the vhost. #
+# #
+# pass - Password for the vhost. #
+# #
+# host - Vhost to set. #
+#
+#<vhost user="some_username" pass="some_password" host="some.host">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Watch module: Adds the WATCH command, which is used by clients to
+# maintain notify lists.
+#<module name="m_watch.so">
+#
+# Configuration tags:
+#
+#<watch maxentries="32">
+#
+# Sets the maximum number of entries on a user's watch list.
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# XMLSocket module: Adds support for connections using the shockwave
+# flash XMLSocket. Note that this does not work if the client you are
+# using has retarded ideas of the IRC protocol. Your client must still
+# send RFC-correct lines to the server, this module only changes the
+# line ending from newlines to null terminators.
+#
+#<module name="m_xmlsocket.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# ZipLinks module: Adds support for zlib deflate on server to server
+# connections. Both ends of the connection must load this module.
+#
+#<module name="m_ziplink.so">
+#
+# To use this module, you must enable it as a transport type in your
+# <link> tags or <bind> tags using the transport name 'zip'.
+# See the documentation of <link> and <bind>, respectively.
+#
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#- BAN OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# The ban tags define nick masks, host masks and ip ranges which are #
+# banned from your server. All details in these tags are local to #
+# Your server. #
+# #
+# #
+# badip lines ban an ip range (same as a zline) #
+# #
+# ipmask - The ip range to ban (wildcards possible) #
+# CIDR is supported in the IP mask. #
+# reason - Reason to display when disconnected #
+# #
+# badnick lines ban a nick mask (same as a qline) #
+# #
+# nick - Nick mask to ban (wildcards possible) #
+# reason - Reason to display on /NICK #
+# #
+# badhost lines ban a user@host mask (same as a kline) #
+# #
+# host - ident@hostname (wildcards possible) #
+# If you specify an IP, CIDR is supported. #
+# reason - Reason to display on disconnection #
+# #
+# exception lines define a hostmask that is excempt from [kzg]lines #
+# #
+# host - ident@hostname (wildcards possible) #
+# If you specify an IP, CIDR is supported. #
+# reason - Reason, shown only in /stats e #
+# #
+
+<badip ipmask="69.69.69.69" reason="No porn here thanks.">
+
+<badnick nick="ChanServ" reason="Reserved For Services">
+<badnick nick="NickServ" reason="Reserved For Services">
+<badnick nick="OperServ" reason="Reserved For Services">
+<badnick nick="MemoServ" reason="Reserved For Services">
+
+<badhost host="*@hundredz.n.hundredz.o.1337.kiddies.com" reason="Too many 1337 kiddiots">
+<badhost host="*@localhost" reason="No irc from localhost!">
+<badhost host="*@172.32.0.0/16" reason="This subnet is bad.">
+
+<exception host="*@ircop.host.com" reason="Opers hostname">
+
+#-#-#-#-#-#-#-#-#-#-#- INSANE BAN OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This optional tag allows you to specify how wide a gline, eline, #
+# kline, zline or qline can be before it is forbidden from being #
+# set. By setting hostmasks="yes", you can allow all G, K, E lines, #
+# no matter how many users the ban would cover. This is not #
+# recommended! By setting ipmasks="yes", you can allow all Z lines, #
+# no matter how many users these cover too. Needless to say we #
+# don't recommend you do this, or, set nickmasks="yes", which will #
+# allow any qline. #
+# #
+# The trigger value indicates how wide any mask will be before it is #
+# prevented from being set. The default value is 95.5% if this tag is #
+# not defined in your configuration file, meaning that if your #
+# network has 1000 users, a gline matching over 955 of them will be #
+# prevented from being added. #
+# #
+# Please note that remote servers (and services) are exempt from #
+# these restrictions and expected to enforce their own policies #
+# locally! #
+# #
+
+<insane hostmasks="no" ipmasks="no" nickmasks="no" trigger="95.5">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- YAWN -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# You should already know what to do here :) #
+
+<die value="User error. Insert new user and press any key. (you didn't edit your config properly.)">
+
+
+#########################################################################
+# #
+# - InspIRCd Development Team - #
+# http://www.inspircd.org #
+# #
+#########################################################################
diff --git a/docs/rfc/rfc1035.txt b/docs/rfc/rfc1035.txt
index 007e3c028..b1a9bf5a9 100644
--- a/docs/rfc/rfc1035.txt
+++ b/docs/rfc/rfc1035.txt
@@ -1 +1,3077 @@
-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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] \ No newline at end of file
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
diff --git a/docs/rfc/rfc1413.txt b/docs/rfc/rfc1413.txt
index e175dcece..17ede58a2 100644
--- a/docs/rfc/rfc1413.txt
+++ b/docs/rfc/rfc1413.txt
@@ -1 +1,451 @@
- 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] 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] 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] 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] 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] 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] 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] 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] \ No newline at end of file
+
+
+
+
+
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+ \ No newline at end of file
diff --git a/docs/rfc/rfc1459.txt b/docs/rfc/rfc1459.txt
index c93005377..09fbf34f7 100644
--- a/docs/rfc/rfc1459.txt
+++ b/docs/rfc/rfc1459.txt
@@ -1 +1,3643 @@
- 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] \ No newline at end of file
+
+
+
+
+
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+ \ No newline at end of file
diff --git a/extras/m_sqllog.mysql.sql b/extras/m_sqllog.mysql.sql
index ce413bf82..0793b0543 100644
--- a/extras/m_sqllog.mysql.sql
+++ b/extras/m_sqllog.mysql.sql
@@ -1 +1,78 @@
--- MySQL dump 9.11 -- -- Host: localhost Database: brain -- ------------------------------------------------------ -- Server version 4.0.20 -- -- Table structure for table `ircd_log` -- CREATE TABLE ircd_log ( id bigint(20) NOT NULL auto_increment, category_id bigint(20) NOT NULL default '0', nick bigint(20) default NULL, host bigint(20) default NULL, source bigint(20) default NULL, dtime bigint(20) NOT NULL default '0', PRIMARY KEY (id) ) TYPE=MyISAM; -- -- Dumping data for table `ircd_log` -- -- -- Table structure for table `ircd_log_categories` -- CREATE TABLE ircd_log_categories ( category_id bigint(20) NOT NULL default '0', category text NOT NULL, PRIMARY KEY (category_id) ) TYPE=MyISAM; -- -- Dumping data for table `ircd_log_categories` -- INSERT INTO ircd_log_categories VALUES (1,'Oper'); INSERT INTO ircd_log_categories VALUES (2,'Kill'); INSERT INTO ircd_log_categories VALUES (3,'Server Link'); INSERT INTO ircd_log_categories VALUES (4,'G/Z/K/E Line'); INSERT INTO ircd_log_categories VALUES (5,'Connect'); INSERT INTO ircd_log_categories VALUES (6,'Disconnect'); INSERT INTO ircd_log_categories VALUES (7,'Flooding'); INSERT INTO ircd_log_categories VALUES (8,'Load Module'); -- -- Table structure for table `ircd_log_actors` -- CREATE TABLE ircd_log_actors ( id bigint(20) NOT NULL auto_increment, actor text, PRIMARY KEY (id) ) TYPE=MyISAM; -- -- Dumping data for table `ircd_log_actors` -- -- -- Table structure for table `ircd_log_hosts` -- CREATE TABLE ircd_log_hosts ( id bigint(20) NOT NULL auto_increment, hostname text, PRIMARY KEY (id) ) TYPE=MyISAM; -- -- Dumping data for table `ircd_log_hosts` -- \ No newline at end of file
+-- MySQL dump 9.11
+--
+-- Host: localhost Database: brain
+-- ------------------------------------------------------
+-- Server version 4.0.20
+
+--
+-- Table structure for table `ircd_log`
+--
+
+CREATE TABLE ircd_log (
+ id bigint(20) NOT NULL auto_increment,
+ category_id bigint(20) NOT NULL default '0',
+ nick bigint(20) default NULL,
+ host bigint(20) default NULL,
+ source bigint(20) default NULL,
+ dtime bigint(20) NOT NULL default '0',
+ PRIMARY KEY (id)
+) TYPE=MyISAM;
+
+--
+-- Dumping data for table `ircd_log`
+--
+
+
+--
+-- Table structure for table `ircd_log_categories`
+--
+
+CREATE TABLE ircd_log_categories (
+ category_id bigint(20) NOT NULL default '0',
+ category text NOT NULL,
+ PRIMARY KEY (category_id)
+) TYPE=MyISAM;
+
+--
+-- Dumping data for table `ircd_log_categories`
+--
+
+INSERT INTO ircd_log_categories VALUES (1,'Oper');
+INSERT INTO ircd_log_categories VALUES (2,'Kill');
+INSERT INTO ircd_log_categories VALUES (3,'Server Link');
+INSERT INTO ircd_log_categories VALUES (4,'G/Z/K/E Line');
+INSERT INTO ircd_log_categories VALUES (5,'Connect');
+INSERT INTO ircd_log_categories VALUES (6,'Disconnect');
+INSERT INTO ircd_log_categories VALUES (7,'Flooding');
+INSERT INTO ircd_log_categories VALUES (8,'Load Module');
+
+--
+-- Table structure for table `ircd_log_actors`
+--
+
+CREATE TABLE ircd_log_actors (
+ id bigint(20) NOT NULL auto_increment,
+ actor text,
+ PRIMARY KEY (id)
+) TYPE=MyISAM;
+
+--
+-- Dumping data for table `ircd_log_actors`
+--
+
+
+--
+-- Table structure for table `ircd_log_hosts`
+--
+
+CREATE TABLE ircd_log_hosts (
+ id bigint(20) NOT NULL auto_increment,
+ hostname text,
+ PRIMARY KEY (id)
+) TYPE=MyISAM;
+
+--
+-- Dumping data for table `ircd_log_hosts`
+--
+
+
diff --git a/extras/m_sqllog.postgresql.sql b/extras/m_sqllog.postgresql.sql
index 561dba16d..2e40dd90d 100644
--- a/extras/m_sqllog.postgresql.sql
+++ b/extras/m_sqllog.postgresql.sql
@@ -1 +1,51 @@
--- -- PostgreSQL database dump -- CREATE TABLE ircd_log ( id serial NOT NULL, category_id bigint, nick bigint, host bigint, source bigint, dtime bigint DEFAULT 0 NOT NULL ); ALTER TABLE ONLY ircd_log ADD CONSTRAINT ircd_log_pkey PRIMARY KEY (id); CREATE TABLE ircd_log_actors ( id serial NOT NULL, actor text ); ALTER TABLE ONLY ircd_log_actors ADD CONSTRAINT ircd_log_actors_pkey PRIMARY KEY (id); CREATE TABLE ircd_log_categories ( category_id serial NOT NULL, category text NOT NULL ); INSERT INTO ircd_log_categories VALUES (1, 'Oper'); INSERT INTO ircd_log_categories VALUES (2, 'Kill'); INSERT INTO ircd_log_categories VALUES (3, 'Server Link'); INSERT INTO ircd_log_categories VALUES (4, 'G/Z/K/E Line'); INSERT INTO ircd_log_categories VALUES (5, 'Connect'); INSERT INTO ircd_log_categories VALUES (6, 'Disconnect'); INSERT INTO ircd_log_categories VALUES (7, 'Flooding'); INSERT INTO ircd_log_categories VALUES (8, 'Load Module'); ALTER TABLE ONLY ircd_log_categories ADD CONSTRAINT ircd_log_categories_pkey PRIMARY KEY (category_id); CREATE TABLE ircd_log_hosts ( id serial NOT NULL, hostname text ); ALTER TABLE ONLY ircd_log_hosts ADD CONSTRAINT ircd_log_hosts_pkey PRIMARY KEY (id); \ No newline at end of file
+--
+-- PostgreSQL database dump
+--
+
+CREATE TABLE ircd_log (
+ id serial NOT NULL,
+ category_id bigint,
+ nick bigint,
+ host bigint,
+ source bigint,
+ dtime bigint DEFAULT 0 NOT NULL
+);
+ALTER TABLE ONLY ircd_log
+ ADD CONSTRAINT ircd_log_pkey PRIMARY KEY (id);
+
+
+
+CREATE TABLE ircd_log_actors (
+ id serial NOT NULL,
+ actor text
+);
+ALTER TABLE ONLY ircd_log_actors
+ ADD CONSTRAINT ircd_log_actors_pkey PRIMARY KEY (id);
+
+
+
+CREATE TABLE ircd_log_categories (
+ category_id serial NOT NULL,
+ category text NOT NULL
+);
+
+INSERT INTO ircd_log_categories VALUES (1, 'Oper');
+INSERT INTO ircd_log_categories VALUES (2, 'Kill');
+INSERT INTO ircd_log_categories VALUES (3, 'Server Link');
+INSERT INTO ircd_log_categories VALUES (4, 'G/Z/K/E Line');
+INSERT INTO ircd_log_categories VALUES (5, 'Connect');
+INSERT INTO ircd_log_categories VALUES (6, 'Disconnect');
+INSERT INTO ircd_log_categories VALUES (7, 'Flooding');
+INSERT INTO ircd_log_categories VALUES (8, 'Load Module');
+
+ALTER TABLE ONLY ircd_log_categories
+ ADD CONSTRAINT ircd_log_categories_pkey PRIMARY KEY (category_id);
+
+
+
+CREATE TABLE ircd_log_hosts (
+ id serial NOT NULL,
+ hostname text
+);
+ALTER TABLE ONLY ircd_log_hosts
+ ADD CONSTRAINT ircd_log_hosts_pkey PRIMARY KEY (id);
diff --git a/extras/m_sqllog.sqlite3.sql b/extras/m_sqllog.sqlite3.sql
index 020cda1d3..a0772e5e3 100644
--- a/extras/m_sqllog.sqlite3.sql
+++ b/extras/m_sqllog.sqlite3.sql
@@ -1 +1,34 @@
-CREATE TABLE ircd_log ( id integer primary key, category_id integer, nick integer, host integer, source integer, dtime integer); CREATE TABLE ircd_log_categories ( category_id integer primary key, category text NOT NULL ); INSERT INTO "ircd_log_categories" VALUES(1, 'Oper'); INSERT INTO "ircd_log_categories" VALUES(2, 'Kill'); INSERT INTO "ircd_log_categories" VALUES(3, 'Server Link'); INSERT INTO "ircd_log_categories" VALUES(4, 'G/Z/K/E Line'); INSERT INTO "ircd_log_categories" VALUES(5, 'Connect'); INSERT INTO "ircd_log_categories" VALUES(6, 'Disconnect'); INSERT INTO "ircd_log_categories" VALUES(7, 'Flooding'); INSERT INTO "ircd_log_categories" VALUES(8, 'Load Module'); CREATE TABLE ircd_log_actors ( id integer primary key, actor text ); CREATE TABLE ircd_log_hosts ( id integer primary key, hostname text ); \ No newline at end of file
+CREATE TABLE ircd_log (
+id integer primary key,
+category_id integer,
+nick integer,
+host integer,
+source integer,
+dtime integer);
+
+
+CREATE TABLE ircd_log_categories (
+ category_id integer primary key,
+ category text NOT NULL
+);
+INSERT INTO "ircd_log_categories" VALUES(1, 'Oper');
+INSERT INTO "ircd_log_categories" VALUES(2, 'Kill');
+INSERT INTO "ircd_log_categories" VALUES(3, 'Server Link');
+INSERT INTO "ircd_log_categories" VALUES(4, 'G/Z/K/E Line');
+INSERT INTO "ircd_log_categories" VALUES(5, 'Connect');
+INSERT INTO "ircd_log_categories" VALUES(6, 'Disconnect');
+INSERT INTO "ircd_log_categories" VALUES(7, 'Flooding');
+INSERT INTO "ircd_log_categories" VALUES(8, 'Load Module');
+
+
+CREATE TABLE ircd_log_actors (
+ id integer primary key,
+ actor text
+);
+
+
+CREATE TABLE ircd_log_hosts (
+ id integer primary key,
+ hostname text
+);
+
diff --git a/extras/m_sqloper.mysql.sql b/extras/m_sqloper.mysql.sql
index ee993c771..293a2aa70 100644
--- a/extras/m_sqloper.mysql.sql
+++ b/extras/m_sqloper.mysql.sql
@@ -1 +1,24 @@
--- MySQL dump 9.11 -- -- Host: localhost Database: brain -- ------------------------------------------------------ -- Server version 4.0.20 -- -- Table structure for table `ircd_opers` -- CREATE TABLE ircd_opers ( id bigint(20) NOT NULL auto_increment, username text, password text, hostname text, type text, PRIMARY KEY (id) ) TYPE=MyISAM; -- -- Dumping data for table `ircd_opers` -- \ No newline at end of file
+-- MySQL dump 9.11
+--
+-- Host: localhost Database: brain
+-- ------------------------------------------------------
+-- Server version 4.0.20
+
+--
+-- Table structure for table `ircd_opers`
+--
+
+CREATE TABLE ircd_opers (
+ id bigint(20) NOT NULL auto_increment,
+ username text,
+ password text,
+ hostname text,
+ type text,
+ PRIMARY KEY (id)
+) TYPE=MyISAM;
+
+--
+-- Dumping data for table `ircd_opers`
+--
+
+
diff --git a/extras/m_sqloper.postgresql.sql b/extras/m_sqloper.postgresql.sql
index 199a656f6..fd640949f 100644
--- a/extras/m_sqloper.postgresql.sql
+++ b/extras/m_sqloper.postgresql.sql
@@ -1 +1,14 @@
--- -- PostgreSQL database dump -- CREATE TABLE ircd_opers ( id serial NOT NULL, username text, "password" text, hostname text, "type" text ); ALTER TABLE ONLY ircd_opers ADD CONSTRAINT ircd_opers_pkey PRIMARY KEY (id); \ No newline at end of file
+--
+-- PostgreSQL database dump
+--
+
+CREATE TABLE ircd_opers (
+ id serial NOT NULL,
+ username text,
+ "password" text,
+ hostname text,
+ "type" text
+);
+ALTER TABLE ONLY ircd_opers
+ ADD CONSTRAINT ircd_opers_pkey PRIMARY KEY (id);
+
diff --git a/extras/m_sqloper.sqlite3.sql b/extras/m_sqloper.sqlite3.sql
index e33f77a93..1bb2937b8 100644
--- a/extras/m_sqloper.sqlite3.sql
+++ b/extras/m_sqloper.sqlite3.sql
@@ -1 +1,7 @@
-CREATE TABLE ircd_opers ( id integer primary key, username text, password text, hostname text, type text); \ No newline at end of file
+CREATE TABLE ircd_opers (
+id integer primary key,
+username text,
+password text,
+hostname text,
+type text);
+
diff --git a/http/index.html b/http/index.html
index 0831e74af..305c40a2f 100644
--- a/http/index.html
+++ b/http/index.html
@@ -1 +1,12 @@
-<html> <head> <title> InspIRCd m_httpd.so Module </title> </head> <body> <h1>Nothing to see here folks, Move along...</h1> <h4>This is a placeholder page provided by the InspIRCd m_httpd.so module. Please replace this page with better content.</h4> <small>Powered by <a href="http://www.inspircd.org">InspIRCd</a></small> </body> </html> \ No newline at end of file
+<html>
+ <head>
+ <title>
+ InspIRCd m_httpd.so Module
+ </title>
+ </head>
+ <body>
+ <h1>Nothing to see here folks, Move along...</h1>
+ <h4>This is a placeholder page provided by the InspIRCd m_httpd.so module. Please replace this page with better content.</h4>
+ <small>Powered by <a href="http://www.inspircd.org">InspIRCd</a></small>
+ </body>
+</html>
diff --git a/include/base.h b/include/base.h
index 594e5bfe4..ba8184c3c 100644
--- a/include/base.h
+++ b/include/base.h
@@ -1 +1,228 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __BASE_H__ #define __BASE_H__ #include "inspircd_config.h" #include <time.h> #include <map> #include <deque> #include <string> /** Do we use this? -- Brain */ typedef void* VoidPointer; /** A private data store for an Extensible class */ typedef std::map<std::string,char*> ExtensibleStore; /** Needed */ class InspIRCd; /** The base class for all inspircd classes. * Wherever possible, all classes you create should inherit from this, * giving them the ability to be passed to various core functions * as 'anonymous' classes. */ class CoreExport classbase { public: /** Time that the object was instantiated (used for TS calculation etc) */ time_t age; /** Constructor. * Sets the object's time */ classbase(); /** Destructor. * Does sweet FA. */ ~classbase() { } }; /** class Extensible is the parent class of many classes such as userrec and chanrec. * class Extensible implements a system which allows modules to 'extend' the class by attaching data within * a map associated with the object. In this way modules can store their own custom information within user * objects, channel objects and server objects, without breaking other modules (this is more sensible than using * a flags variable, and each module defining bits within the flag as 'theirs' as it is less prone to conflict and * supports arbitary data storage). */ class CoreExport Extensible : public classbase { /** Private data store. * Holds all extensible metadata for the class. */ ExtensibleStore Extension_Items; public: /** Extend an Extensible class. * * @param key The key parameter is an arbitary string which identifies the extension data * @param p This parameter is a pointer to any data you wish to associate with the object * * You must provide a key to store the data as via the parameter 'key' and store the data * in the templated parameter 'p'. * The data will be inserted into the map. If the data already exists, you may not insert it * twice, Extensible::Extend will return false in this case. * * @return Returns true on success, false if otherwise */ template<typename T> bool Extend(const std::string &key, T* p) { /* This will only add an item if it doesnt already exist, * the return value is a std::pair of an iterator to the * element, and a bool saying if it was actually inserted. */ return this->Extension_Items.insert(std::make_pair(key, (char*)p)).second; } /** Extend an Extensible class. * * @param key The key parameter is an arbitary string which identifies the extension data * * You must provide a key to store the data as via the parameter 'key', this single-parameter * version takes no 'data' parameter, this is used purely for boolean values. * The key will be inserted into the map with a NULL 'data' pointer. If the key already exists * then you may not insert it twice, Extensible::Extend will return false in this case. * * @return Returns true on success, false if otherwise */ bool Extend(const std::string &key) { /* This will only add an item if it doesnt already exist, * the return value is a std::pair of an iterator to the * element, and a bool saying if it was actually inserted. */ return this->Extension_Items.insert(std::make_pair(key, (char*)NULL)).second; } /** Shrink an Extensible class. * * @param key The key parameter is an arbitary string which identifies the extension data * * You must provide a key name. The given key name will be removed from the classes data. If * you provide a nonexistent key (case is important) then the function will return false. * @return Returns true on success. */ bool Shrink(const std::string &key); /** Get an extension item. * * @param key The key parameter is an arbitary string which identifies the extension data * @param p If you provide a non-existent key, this value will be NULL. Otherwise a pointer to the item you requested will be placed in this templated parameter. * @return Returns true if the item was found and false if it was nor, regardless of wether 'p' is NULL. This allows you to store NULL values in Extensible. */ template<typename T> bool GetExt(const std::string &key, T* &p) { ExtensibleStore::iterator iter = this->Extension_Items.find(key); /* Find the item */ if(iter != this->Extension_Items.end()) { p = (T*)iter->second; /* Item found */ return true; } else { p = NULL; /* Item not found */ return false; } } /** Get an extension item. * * @param key The key parameter is an arbitary string which identifies the extension data * @return Returns true if the item was found and false if it was not. * * This single-parameter version only checks if the key exists, it does nothing with * the 'data' field and is probably only useful in conjunction with the single-parameter * version of Extend(). */ bool GetExt(const std::string &key) { return (this->Extension_Items.find(key) != this->Extension_Items.end()); } /** Get a list of all extension items names. * @param list A deque of strings to receive the list * @return This function writes a list of all extension items stored in this object by name into the given deque and returns void. */ void GetExtList(std::deque<std::string> &list); }; /** BoolSet is a utility class designed to hold eight bools in a bitmask. * Use BoolSet::Set and BoolSet::Get to set and get bools in the bitmask, * and Unset and Invert for special operations upon them. */ class CoreExport BoolSet : public classbase { /** Actual bit values */ char bits; public: /** The default constructor initializes the BoolSet to all values unset. */ BoolSet(); /** This constructor copies the default bitmask from a char */ BoolSet(char bitmask); /** The Set method sets one bool in the set. * * @param number The number of the item to set. This must be between 0 and 7. */ void Set(int number); /** The Get method returns the value of one bool in the set * * @param number The number of the item to retrieve. This must be between 0 and 7. * * @return True if the item is set, false if it is unset. */ bool Get(int number); /** The Unset method unsets one value in the set * * @param number The number of the item to set. This must be between 0 and 7. */ void Unset(int number); /** The Unset method inverts (flips) one value in the set * * @param number The number of the item to invert. This must be between 0 and 7. */ void Invert(int number); /** Compare two BoolSets */ bool operator==(BoolSet other); /** OR two BoolSets together */ BoolSet operator|(BoolSet other); /** AND two BoolSets together */ BoolSet operator&(BoolSet other); /** Assign one BoolSet to another */ bool operator=(BoolSet other); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __BASE_H__
+#define __BASE_H__
+
+#include "inspircd_config.h"
+#include <time.h>
+#include <map>
+#include <deque>
+#include <string>
+
+/** Do we use this? -- Brain */
+typedef void* VoidPointer;
+
+/** A private data store for an Extensible class */
+typedef std::map<std::string,char*> ExtensibleStore;
+
+/** Needed */
+class InspIRCd;
+
+/** The base class for all inspircd classes.
+ * Wherever possible, all classes you create should inherit from this,
+ * giving them the ability to be passed to various core functions
+ * as 'anonymous' classes.
+*/
+class CoreExport classbase
+{
+ public:
+ /** Time that the object was instantiated (used for TS calculation etc)
+ */
+ time_t age;
+
+ /** Constructor.
+ * Sets the object's time
+ */
+ classbase();
+
+ /** Destructor.
+ * Does sweet FA.
+ */
+ ~classbase() { }
+};
+
+/** class Extensible is the parent class of many classes such as userrec and chanrec.
+ * class Extensible implements a system which allows modules to 'extend' the class by attaching data within
+ * a map associated with the object. In this way modules can store their own custom information within user
+ * objects, channel objects and server objects, without breaking other modules (this is more sensible than using
+ * a flags variable, and each module defining bits within the flag as 'theirs' as it is less prone to conflict and
+ * supports arbitary data storage).
+ */
+class CoreExport Extensible : public classbase
+{
+ /** Private data store.
+ * Holds all extensible metadata for the class.
+ */
+ ExtensibleStore Extension_Items;
+
+public:
+
+ /** Extend an Extensible class.
+ *
+ * @param key The key parameter is an arbitary string which identifies the extension data
+ * @param p This parameter is a pointer to any data you wish to associate with the object
+ *
+ * You must provide a key to store the data as via the parameter 'key' and store the data
+ * in the templated parameter 'p'.
+ * The data will be inserted into the map. If the data already exists, you may not insert it
+ * twice, Extensible::Extend will return false in this case.
+ *
+ * @return Returns true on success, false if otherwise
+ */
+ template<typename T> bool Extend(const std::string &key, T* p)
+ {
+ /* This will only add an item if it doesnt already exist,
+ * the return value is a std::pair of an iterator to the
+ * element, and a bool saying if it was actually inserted.
+ */
+ return this->Extension_Items.insert(std::make_pair(key, (char*)p)).second;
+ }
+
+ /** Extend an Extensible class.
+ *
+ * @param key The key parameter is an arbitary string which identifies the extension data
+ *
+ * You must provide a key to store the data as via the parameter 'key', this single-parameter
+ * version takes no 'data' parameter, this is used purely for boolean values.
+ * The key will be inserted into the map with a NULL 'data' pointer. If the key already exists
+ * then you may not insert it twice, Extensible::Extend will return false in this case.
+ *
+ * @return Returns true on success, false if otherwise
+ */
+ bool Extend(const std::string &key)
+ {
+ /* This will only add an item if it doesnt already exist,
+ * the return value is a std::pair of an iterator to the
+ * element, and a bool saying if it was actually inserted.
+ */
+ return this->Extension_Items.insert(std::make_pair(key, (char*)NULL)).second;
+ }
+
+ /** Shrink an Extensible class.
+ *
+ * @param key The key parameter is an arbitary string which identifies the extension data
+ *
+ * You must provide a key name. The given key name will be removed from the classes data. If
+ * you provide a nonexistent key (case is important) then the function will return false.
+ * @return Returns true on success.
+ */
+ bool Shrink(const std::string &key);
+
+ /** Get an extension item.
+ *
+ * @param key The key parameter is an arbitary string which identifies the extension data
+ * @param p If you provide a non-existent key, this value will be NULL. Otherwise a pointer to the item you requested will be placed in this templated parameter.
+ * @return Returns true if the item was found and false if it was nor, regardless of wether 'p' is NULL. This allows you to store NULL values in Extensible.
+ */
+ template<typename T> bool GetExt(const std::string &key, T* &p)
+ {
+ ExtensibleStore::iterator iter = this->Extension_Items.find(key); /* Find the item */
+ if(iter != this->Extension_Items.end())
+ {
+ p = (T*)iter->second; /* Item found */
+ return true;
+ }
+ else
+ {
+ p = NULL; /* Item not found */
+ return false;
+ }
+ }
+
+ /** Get an extension item.
+ *
+ * @param key The key parameter is an arbitary string which identifies the extension data
+ * @return Returns true if the item was found and false if it was not.
+ *
+ * This single-parameter version only checks if the key exists, it does nothing with
+ * the 'data' field and is probably only useful in conjunction with the single-parameter
+ * version of Extend().
+ */
+ bool GetExt(const std::string &key)
+ {
+ return (this->Extension_Items.find(key) != this->Extension_Items.end());
+ }
+
+ /** Get a list of all extension items names.
+ * @param list A deque of strings to receive the list
+ * @return This function writes a list of all extension items stored in this object by name into the given deque and returns void.
+ */
+ void GetExtList(std::deque<std::string> &list);
+};
+
+/** BoolSet is a utility class designed to hold eight bools in a bitmask.
+ * Use BoolSet::Set and BoolSet::Get to set and get bools in the bitmask,
+ * and Unset and Invert for special operations upon them.
+ */
+class CoreExport BoolSet : public classbase
+{
+ /** Actual bit values */
+ char bits;
+
+ public:
+
+ /** The default constructor initializes the BoolSet to all values unset.
+ */
+ BoolSet();
+
+ /** This constructor copies the default bitmask from a char
+ */
+ BoolSet(char bitmask);
+
+ /** The Set method sets one bool in the set.
+ *
+ * @param number The number of the item to set. This must be between 0 and 7.
+ */
+ void Set(int number);
+
+ /** The Get method returns the value of one bool in the set
+ *
+ * @param number The number of the item to retrieve. This must be between 0 and 7.
+ *
+ * @return True if the item is set, false if it is unset.
+ */
+ bool Get(int number);
+
+ /** The Unset method unsets one value in the set
+ *
+ * @param number The number of the item to set. This must be between 0 and 7.
+ */
+ void Unset(int number);
+
+ /** The Unset method inverts (flips) one value in the set
+ *
+ * @param number The number of the item to invert. This must be between 0 and 7.
+ */
+ void Invert(int number);
+
+ /** Compare two BoolSets
+ */
+ bool operator==(BoolSet other);
+
+ /** OR two BoolSets together
+ */
+ BoolSet operator|(BoolSet other);
+
+ /** AND two BoolSets together
+ */
+ BoolSet operator&(BoolSet other);
+
+ /** Assign one BoolSet to another
+ */
+ bool operator=(BoolSet other);
+};
+
+
+#endif
+
diff --git a/include/channels.h b/include/channels.h
index ce7935c2d..b4fa1ca30 100644
--- a/include/channels.h
+++ b/include/channels.h
@@ -1 +1,551 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CHANNELS_H__ #define __CHANNELS_H__ #include "inspircd_config.h" #include "base.h" #include <time.h> #include <vector> #include <string> #include <map> /** RFC1459 channel modes */ enum ChannelModes { CM_TOPICLOCK = 't'-65, /* +t: Only operators can change topic */ CM_NOEXTERNAL = 'n'-65, /* +n: Only users in the channel can message it */ CM_INVITEONLY = 'i'-65, /* +i: Invite only */ CM_MODERATED = 'm'-65, /* +m: Only voices and above can talk */ CM_SECRET = 's'-65, /* +s: Secret channel */ CM_PRIVATE = 'p'-65, /* +p: Private channel */ CM_KEY = 'k'-65, /* +k: Keyed channel */ CM_LIMIT = 'l'-65 /* +l: Maximum user limit */ }; /* Forward declarations - needed */ class userrec; class chanrec; /** 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 classbase { public: /** Time the item was added */ time_t set_time; /** Who added the item */ char set_by[NICKMAX]; /** The actual item data */ char data[MAXBUF]; HostItem() { /* stub */ } virtual ~HostItem() { /* stub */ } }; /** A subclass of HostItem designed to hold channel bans (+b) */ class BanItem : public HostItem { }; /** Holds a complete ban list */ typedef std::vector<BanItem> BanList; /** A list of users on a channel */ typedef std::map<userrec*,std::string> CUList; /** Shorthand for CUList::iterator */ typedef CUList::iterator CUListIter; /** Shorthand for CUList::const_iterator */ typedef CUList::const_iterator CUListConstIter; /** A list of custom modes parameters on a channel */ typedef std::map<char,char*> CustomModeList; /** used to hold a channel and a users modes on that channel, e.g. +v, +h, +o */ enum UserChannelModes { UCMODE_OP = 1, /* Opped user */ UCMODE_VOICE = 2, /* Voiced user */ UCMODE_HOP = 4 /* Halfopped user */ }; /* Forward declaration -- required */ class InspIRCd; /** A stored prefix and its rank */ typedef std::pair<char, unsigned int> prefixtype; /** A list of prefixes set on a user in a channel */ typedef std::vector<prefixtype> pfxcontainer; /** A list of users with zero or more prefixes set on them */ typedef std::map<userrec*, std::vector<prefixtype> > prefixlist; /** Holds all relevent information for a channel. * This class represents a channel, and contains its name, modes, time created, topic, topic set time, * etc, and an instance of the BanList type. */ class CoreExport chanrec : public Extensible { private: /** Pointer to creator object */ InspIRCd* ServerInstance; /** Connect a chanrec to a userrec */ static chanrec* ForceChan(InspIRCd* Instance, chanrec* Ptr, userrec* user, const std::string &privs); /** Set default modes for the channel on creation */ void SetDefaultModes(); /** A list of prefixes associated with each user in the channel * (e.g. &%+ etc) */ prefixlist prefixes; /** Maximum number of bans (cached) */ int maxbans; public: /** The channel's name. */ char name[CHANMAX]; /** Modes for the channel. * This is not a null terminated string! It is a hash 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. */ char modes[64]; /** User lists. * There are four user lists, one for * all the users, one for the ops, one for * the halfops and another for the voices. */ CUList internal_userlist; /** Opped users. * There are four user lists, one for * all the users, one for the ops, one for * the halfops and another for the voices. */ CUList internal_op_userlist; /** Halfopped users. * There are four user lists, one for * all the users, one for the ops, one for * the halfops and another for the voices. */ CUList internal_halfop_userlist; /** Voiced users. * There are four user lists, one for * all the users, one for the ops, one for * the halfops and another for the voices. */ CUList internal_voice_userlist; /** Parameters for custom modes. * One for each custom mode letter. */ CustomModeList custom_mode_params; /** Channel topic. * If this is an empty string, no channel topic is set. */ char topic[MAXTOPIC]; /** Creation time. * This is a timestamp (TS) value. */ time_t created; /** Time topic was set. * If no topic was ever set, this will be equal to chanrec::created */ time_t topicset; /** The last user to set the topic. * If this member is an empty string, no topic was ever set. */ char setby[128]; /** Contains the channel user limit. * If this value is zero, there is no limit in place. */ short int limit; /** Contains the channel key. * If this value is an empty string, there is no channel key in place. */ char key[32]; /** 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 mode_on True if you want to set the mode or false if you want to remove it */ void SetMode(char mode,bool mode_on); /** Sets or unsets the parameters for a custom mode in a channels info * @param mode The mode character to set or unset * @param parameter The parameter string to associate with this mode character * @param mode_on True if you want to set the mode or false if you want to remove it */ void SetModeParam(char mode,const char* parameter,bool mode_on); /** 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 */ bool IsModeSet(char mode); /** Returns the parameter for a custom mode on a channel. * @param mode The mode character you wish to query * * For example if "+L #foo" is set, and you pass this method * 'L', it will return '#foo'. If the mode is not set on the * channel, or the mode has no parameters associated with it, * it will return an empty string. * * @return The parameter for this mode is returned, or an empty string */ std::string GetModeParameter(char mode); /** 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. * * @return The number of users on this channel */ long GetUserCounter(); /** Add a user pointer to the internal reference list * @param user The user to add * * The data inserted into the reference list is a table as it is * an arbitary pointer compared to other users by its memory address, * as this is a very fast 32 or 64 bit integer comparison. */ void AddUser(userrec* user); /** Add a user pointer to the internal reference list of opped users * @param user The user to add */ void AddOppedUser(userrec* user); /** Add a user pointer to the internal reference list of halfopped users * @param user The user to add */ void AddHalfoppedUser(userrec* user); /** Add a user pointer to the internal reference list of voiced users * @param user The user to add */ void AddVoicedUser(userrec* user); /** Delete a user pointer to the internal reference list * @param user The user to delete * @return number of users left on the channel after deletion of the user */ unsigned long DelUser(userrec* user); /** Delete a user pointer to the internal reference list of opped users * @param user The user to delete */ void DelOppedUser(userrec* user); /** Delete a user pointer to the internal reference list of halfopped users * @param user The user to delete */ void DelHalfoppedUser(userrec* user); /** Delete a user pointer to the internal reference list of voiced users * @param user The user to delete */ void DelVoicedUser(userrec* user); /** Obtain the internal reference list * The internal reference list contains a list of userrec*. * These are used for rapid comparison to determine * channel membership for PRIVMSG, NOTICE, QUIT, PART etc. * The resulting pointer to the vector should be considered * readonly and only modified via AddUser and DelUser. * * @return This function returns pointer to a map of userrec pointers (CUList*). */ CUList* GetUsers(); /** Obtain the internal reference list of opped users * @return This function returns pointer to a map of userrec pointers (CUList*). */ CUList* GetOppedUsers(); /** Obtain the internal reference list of halfopped users * @return This function returns pointer to a map of userrec pointers (CUList*). */ CUList* GetHalfoppedUsers(); /** Obtain the internal reference list of voiced users * @return This function returns pointer to a map of userrec pointers (CUList*). */ CUList* GetVoicedUsers(); /** Returns true if the user given is on the given channel. * @param The user to look for * @return True if the user is on this channel */ bool HasUser(userrec* user); /** Creates a channel record and initialises it with default values * @throw Nothing at present. */ chanrec(InspIRCd* Instance); /** 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 reason The reason for the kick * @return The number of users left on the channel. If this is zero * when the method returns, you MUST delete the chanrec immediately! */ long KickUser(userrec *src, userrec *user, const char* reason); /** Make the server kick user from this channel with the given reason. * @param user The user being kicked (must be on this channel) * @param reason The reason for the kick * @param triggerevents True if you wish this kick to trigger module events * @return The number of users left on the channel. If this is zero * when the method returns, you MUST delete the chanrec immediately! */ long ServerKickUser(userrec* user, const char* reason, bool triggerevents); /** 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 (optional) part reason * @return The number of users left on the channel. If this is zero * when the method returns, you MUST delete the chanrec immediately! */ long PartUser(userrec *user, const char* reason = NULL); /* Join a user to a channel. May be a channel that doesnt 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 key The key of the channel, if given * @param override If true, override all join restrictions such as +bkil * @return A pointer to the chanrec the user was joined to. A new chanrec may have * been created if the channel did not exist before the user was joined to it. * If the user could not be joined to a channel, the return value may be NULL. */ static chanrec* JoinUser(InspIRCd* ServerInstance, userrec *user, const char* cn, bool override, const char* key, time_t TS = 0); /** Write to a channel, from a user, using va_args for text * @param user User whos details to prefix the line with * @param text A printf-style format string which builds the output line without prefix * @param ... Zero or more POD types */ void WriteChannel(userrec* user, char* text, ...); /** Write to a channel, from a user, using std::string for text * @param user User whos details to prefix the line with * @param text A std::string containing the output line without prefix */ void WriteChannel(userrec* user, const std::string &text); /** Write to a channel, from a server, using va_args for text * @param ServName Server name to prefix the line with * @param text A printf-style format string which builds the output line without prefix * @param ... Zero or more POD type */ void WriteChannelWithServ(const char* ServName, const char* text, ...); /** Write to a channel, from a server, using std::string for text * @param ServName Server name to prefix the line with * @param text A std::string containing the output line without prefix */ void WriteChannelWithServ(const char* ServName, const std::string &text); /** Write to all users on a channel except a specific user, using va_args for text. * Internally, this calls WriteAllExcept(). * @param user User whos details to prefix the line with, and to omit from receipt of the message * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, * use the nick!user@host of the user. * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone * @param text A printf-style format string which builds the output line without prefix * @param ... Zero or more POD type */ void WriteAllExceptSender(userrec* user, bool serversource, char status, char* text, ...); /** Write to all users on a channel except a list of users, using va_args for text * @param user User whos details to prefix the line with, and to omit from receipt of the message * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, * use the nick!user@host of the user. * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone * @param except_list A list of users NOT to send the text to * @param text A printf-style format string which builds the output line without prefix * @param ... Zero or more POD type */ void WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, char* text, ...); /** Write to all users on a channel except a specific user, using std::string for text. * Internally, this calls WriteAllExcept(). * @param user User whos details to prefix the line with, and to omit from receipt of the message * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, * use the nick!user@host of the user. * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone * @param text A std::string containing the output line without prefix */ void WriteAllExceptSender(userrec* user, bool serversource, char status, const std::string& text); /** Write to all users on a channel except a list of users, using std::string for text * @param user User whos details to prefix the line with, and to omit from receipt of the message * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, * use the nick!user@host of the user. * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone * @param except_list A list of users NOT to send the text to * @param text A std::string containing the output line without prefix */ void WriteAllExcept(userrec* 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 * @param ulist The user list to send, NULL to use the * channel's default names list of everyone */ void UserList(userrec *user, CUList* ulist = NULL); /** Get the number of invisible users on this channel * @return Number of invisible users */ int CountInvisible(); /** Get a users status on this channel * @param user The user to look up * @return One of STATUS_OP, STATUS_HOP, STATUS_VOICE, or zero. */ int GetStatus(userrec *user); /** Get a users status on this channel in a bitmask * @param user The user to look up * @return A bitmask containing zero or more of STATUS_OP, STATUS_HOP, STATUS_VOICE */ int GetStatusFlags(userrec *user); /** 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(userrec *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(userrec* user); /** Get the value of a users prefix on this channel. * @param user The user to look up * @return The module or core-defined value of the users prefix. * The values for op, halfop and voice status are constants in * mode.h, and are OP_VALUE, HALFOP_VALUE, and VOICE_VALUE respectively. * If the value you are given does not match one of these three, it is * a module-defined mode, and it should be compared in proportion to * these three constants. For example a value greater than OP_VALUE * is a prefix of greater 'worth' than ops, and a value less than * VOICE_VALUE is of lesser 'worth' than a voice. */ unsigned int GetPrefixValue(userrec* 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(userrec* 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 prefix_rank The rank (value) of this prefix character * @param adding True if adding the prefix, false when removing */ void SetPrefix(userrec* user, char prefix, unsigned int prefix_rank, 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 */ bool IsBanned(userrec* user); /** Clears the cached max bans value */ void ResetMaxBans(); /** Destructor for chanrec */ virtual ~chanrec() { /* stub */ } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CHANNELS_H__
+#define __CHANNELS_H__
+
+#include "inspircd_config.h"
+#include "base.h"
+#include <time.h>
+#include <vector>
+#include <string>
+#include <map>
+
+/** RFC1459 channel modes
+ */
+enum ChannelModes {
+ CM_TOPICLOCK = 't'-65, /* +t: Only operators can change topic */
+ CM_NOEXTERNAL = 'n'-65, /* +n: Only users in the channel can message it */
+ CM_INVITEONLY = 'i'-65, /* +i: Invite only */
+ CM_MODERATED = 'm'-65, /* +m: Only voices and above can talk */
+ CM_SECRET = 's'-65, /* +s: Secret channel */
+ CM_PRIVATE = 'p'-65, /* +p: Private channel */
+ CM_KEY = 'k'-65, /* +k: Keyed channel */
+ CM_LIMIT = 'l'-65 /* +l: Maximum user limit */
+};
+
+/* Forward declarations - needed */
+class userrec;
+class chanrec;
+
+/** 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 classbase
+{
+ public:
+ /** Time the item was added
+ */
+ time_t set_time;
+ /** Who added the item
+ */
+ char set_by[NICKMAX];
+ /** The actual item data
+ */
+ char data[MAXBUF];
+
+ HostItem() { /* stub */ }
+ virtual ~HostItem() { /* stub */ }
+};
+
+/** A subclass of HostItem designed to hold channel bans (+b)
+ */
+class BanItem : public HostItem
+{
+};
+
+/** Holds a complete ban list
+ */
+typedef std::vector<BanItem> BanList;
+
+/** A list of users on a channel
+ */
+typedef std::map<userrec*,std::string> CUList;
+
+/** Shorthand for CUList::iterator
+ */
+typedef CUList::iterator CUListIter;
+
+/** Shorthand for CUList::const_iterator
+ */
+typedef CUList::const_iterator CUListConstIter;
+
+/** A list of custom modes parameters on a channel
+ */
+typedef std::map<char,char*> CustomModeList;
+
+
+/** used to hold a channel and a users modes on that channel, e.g. +v, +h, +o
+ */
+enum UserChannelModes {
+ UCMODE_OP = 1, /* Opped user */
+ UCMODE_VOICE = 2, /* Voiced user */
+ UCMODE_HOP = 4 /* Halfopped user */
+};
+
+/* Forward declaration -- required */
+class InspIRCd;
+
+/** A stored prefix and its rank
+ */
+typedef std::pair<char, unsigned int> prefixtype;
+
+/** A list of prefixes set on a user in a channel
+ */
+typedef std::vector<prefixtype> pfxcontainer;
+
+/** A list of users with zero or more prefixes set on them
+ */
+typedef std::map<userrec*, std::vector<prefixtype> > prefixlist;
+
+/** Holds all relevent information for a channel.
+ * This class represents a channel, and contains its name, modes, time created, topic, topic set time,
+ * etc, and an instance of the BanList type.
+ */
+class CoreExport chanrec : public Extensible
+{
+ private:
+
+ /** Pointer to creator object
+ */
+ InspIRCd* ServerInstance;
+
+ /** Connect a chanrec to a userrec
+ */
+ static chanrec* ForceChan(InspIRCd* Instance, chanrec* Ptr, userrec* user, const std::string &privs);
+
+ /** Set default modes for the channel on creation
+ */
+ void SetDefaultModes();
+
+ /** A list of prefixes associated with each user in the channel
+ * (e.g. &%+ etc)
+ */
+ prefixlist prefixes;
+
+ /** Maximum number of bans (cached)
+ */
+ int maxbans;
+
+ public:
+ /** The channel's name.
+ */
+ char name[CHANMAX];
+
+ /** Modes for the channel.
+ * This is not a null terminated string! It is a hash 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.
+ */
+ char modes[64];
+
+ /** User lists.
+ * There are four user lists, one for
+ * all the users, one for the ops, one for
+ * the halfops and another for the voices.
+ */
+ CUList internal_userlist;
+
+ /** Opped users.
+ * There are four user lists, one for
+ * all the users, one for the ops, one for
+ * the halfops and another for the voices.
+ */
+ CUList internal_op_userlist;
+
+ /** Halfopped users.
+ * There are four user lists, one for
+ * all the users, one for the ops, one for
+ * the halfops and another for the voices.
+ */
+ CUList internal_halfop_userlist;
+
+ /** Voiced users.
+ * There are four user lists, one for
+ * all the users, one for the ops, one for
+ * the halfops and another for the voices.
+ */
+ CUList internal_voice_userlist;
+
+ /** Parameters for custom modes.
+ * One for each custom mode letter.
+ */
+ CustomModeList custom_mode_params;
+
+ /** Channel topic.
+ * If this is an empty string, no channel topic is set.
+ */
+ char topic[MAXTOPIC];
+
+ /** Creation time.
+ * This is a timestamp (TS) value.
+ */
+ time_t created;
+
+ /** Time topic was set.
+ * If no topic was ever set, this will be equal to chanrec::created
+ */
+ time_t topicset;
+
+ /** The last user to set the topic.
+ * If this member is an empty string, no topic was ever set.
+ */
+ char setby[128];
+
+ /** Contains the channel user limit.
+ * If this value is zero, there is no limit in place.
+ */
+ short int limit;
+
+ /** Contains the channel key.
+ * If this value is an empty string, there is no channel key in place.
+ */
+ char key[32];
+
+ /** 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 mode_on True if you want to set the mode or false if you want to remove it
+ */
+ void SetMode(char mode,bool mode_on);
+
+ /** Sets or unsets the parameters for a custom mode in a channels info
+ * @param mode The mode character to set or unset
+ * @param parameter The parameter string to associate with this mode character
+ * @param mode_on True if you want to set the mode or false if you want to remove it
+ */
+ void SetModeParam(char mode,const char* parameter,bool mode_on);
+
+ /** 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
+ */
+ bool IsModeSet(char mode);
+
+ /** Returns the parameter for a custom mode on a channel.
+ * @param mode The mode character you wish to query
+ *
+ * For example if "+L #foo" is set, and you pass this method
+ * 'L', it will return '#foo'. If the mode is not set on the
+ * channel, or the mode has no parameters associated with it,
+ * it will return an empty string.
+ *
+ * @return The parameter for this mode is returned, or an empty string
+ */
+ std::string GetModeParameter(char mode);
+
+ /** 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.
+ *
+ * @return The number of users on this channel
+ */
+ long GetUserCounter();
+
+ /** Add a user pointer to the internal reference list
+ * @param user The user to add
+ *
+ * The data inserted into the reference list is a table as it is
+ * an arbitary pointer compared to other users by its memory address,
+ * as this is a very fast 32 or 64 bit integer comparison.
+ */
+ void AddUser(userrec* user);
+
+ /** Add a user pointer to the internal reference list of opped users
+ * @param user The user to add
+ */
+ void AddOppedUser(userrec* user);
+
+ /** Add a user pointer to the internal reference list of halfopped users
+ * @param user The user to add
+ */
+ void AddHalfoppedUser(userrec* user);
+
+ /** Add a user pointer to the internal reference list of voiced users
+ * @param user The user to add
+ */
+ void AddVoicedUser(userrec* user);
+
+ /** Delete a user pointer to the internal reference list
+ * @param user The user to delete
+ * @return number of users left on the channel after deletion of the user
+ */
+ unsigned long DelUser(userrec* user);
+
+ /** Delete a user pointer to the internal reference list of opped users
+ * @param user The user to delete
+ */
+ void DelOppedUser(userrec* user);
+
+ /** Delete a user pointer to the internal reference list of halfopped users
+ * @param user The user to delete
+ */
+ void DelHalfoppedUser(userrec* user);
+
+ /** Delete a user pointer to the internal reference list of voiced users
+ * @param user The user to delete
+ */
+ void DelVoicedUser(userrec* user);
+
+ /** Obtain the internal reference list
+ * The internal reference list contains a list of userrec*.
+ * These are used for rapid comparison to determine
+ * channel membership for PRIVMSG, NOTICE, QUIT, PART etc.
+ * The resulting pointer to the vector should be considered
+ * readonly and only modified via AddUser and DelUser.
+ *
+ * @return This function returns pointer to a map of userrec pointers (CUList*).
+ */
+ CUList* GetUsers();
+
+ /** Obtain the internal reference list of opped users
+ * @return This function returns pointer to a map of userrec pointers (CUList*).
+ */
+ CUList* GetOppedUsers();
+
+ /** Obtain the internal reference list of halfopped users
+ * @return This function returns pointer to a map of userrec pointers (CUList*).
+ */
+ CUList* GetHalfoppedUsers();
+
+ /** Obtain the internal reference list of voiced users
+ * @return This function returns pointer to a map of userrec pointers (CUList*).
+ */
+ CUList* GetVoicedUsers();
+
+ /** Returns true if the user given is on the given channel.
+ * @param The user to look for
+ * @return True if the user is on this channel
+ */
+ bool HasUser(userrec* user);
+
+ /** Creates a channel record and initialises it with default values
+ * @throw Nothing at present.
+ */
+ chanrec(InspIRCd* Instance);
+
+ /** 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 reason The reason for the kick
+ * @return The number of users left on the channel. If this is zero
+ * when the method returns, you MUST delete the chanrec immediately!
+ */
+ long KickUser(userrec *src, userrec *user, const char* reason);
+
+ /** Make the server kick user from this channel with the given reason.
+ * @param user The user being kicked (must be on this channel)
+ * @param reason The reason for the kick
+ * @param triggerevents True if you wish this kick to trigger module events
+ * @return The number of users left on the channel. If this is zero
+ * when the method returns, you MUST delete the chanrec immediately!
+ */
+ long ServerKickUser(userrec* user, const char* reason, bool triggerevents);
+
+ /** 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 (optional) part reason
+ * @return The number of users left on the channel. If this is zero
+ * when the method returns, you MUST delete the chanrec immediately!
+ */
+ long PartUser(userrec *user, const char* reason = NULL);
+
+ /* Join a user to a channel. May be a channel that doesnt 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 key The key of the channel, if given
+ * @param override If true, override all join restrictions such as +bkil
+ * @return A pointer to the chanrec the user was joined to. A new chanrec may have
+ * been created if the channel did not exist before the user was joined to it.
+ * If the user could not be joined to a channel, the return value may be NULL.
+ */
+ static chanrec* JoinUser(InspIRCd* ServerInstance, userrec *user, const char* cn, bool override, const char* key, time_t TS = 0);
+
+ /** Write to a channel, from a user, using va_args for text
+ * @param user User whos details to prefix the line with
+ * @param text A printf-style format string which builds the output line without prefix
+ * @param ... Zero or more POD types
+ */
+ void WriteChannel(userrec* user, char* text, ...);
+
+ /** Write to a channel, from a user, using std::string for text
+ * @param user User whos details to prefix the line with
+ * @param text A std::string containing the output line without prefix
+ */
+ void WriteChannel(userrec* user, const std::string &text);
+
+ /** Write to a channel, from a server, using va_args for text
+ * @param ServName Server name to prefix the line with
+ * @param text A printf-style format string which builds the output line without prefix
+ * @param ... Zero or more POD type
+ */
+ void WriteChannelWithServ(const char* ServName, const char* text, ...);
+
+ /** Write to a channel, from a server, using std::string for text
+ * @param ServName Server name to prefix the line with
+ * @param text A std::string containing the output line without prefix
+ */
+ void WriteChannelWithServ(const char* ServName, const std::string &text);
+
+ /** Write to all users on a channel except a specific user, using va_args for text.
+ * Internally, this calls WriteAllExcept().
+ * @param user User whos details to prefix the line with, and to omit from receipt of the message
+ * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
+ * use the nick!user@host of the user.
+ * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
+ * @param text A printf-style format string which builds the output line without prefix
+ * @param ... Zero or more POD type
+ */
+ void WriteAllExceptSender(userrec* user, bool serversource, char status, char* text, ...);
+
+ /** Write to all users on a channel except a list of users, using va_args for text
+ * @param user User whos details to prefix the line with, and to omit from receipt of the message
+ * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
+ * use the nick!user@host of the user.
+ * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
+ * @param except_list A list of users NOT to send the text to
+ * @param text A printf-style format string which builds the output line without prefix
+ * @param ... Zero or more POD type
+ */
+ void WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, char* text, ...);
+
+ /** Write to all users on a channel except a specific user, using std::string for text.
+ * Internally, this calls WriteAllExcept().
+ * @param user User whos details to prefix the line with, and to omit from receipt of the message
+ * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
+ * use the nick!user@host of the user.
+ * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
+ * @param text A std::string containing the output line without prefix
+ */
+ void WriteAllExceptSender(userrec* user, bool serversource, char status, const std::string& text);
+
+ /** Write to all users on a channel except a list of users, using std::string for text
+ * @param user User whos details to prefix the line with, and to omit from receipt of the message
+ * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
+ * use the nick!user@host of the user.
+ * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
+ * @param except_list A list of users NOT to send the text to
+ * @param text A std::string containing the output line without prefix
+ */
+ void WriteAllExcept(userrec* 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
+ * @param ulist The user list to send, NULL to use the
+ * channel's default names list of everyone
+ */
+ void UserList(userrec *user, CUList* ulist = NULL);
+
+ /** Get the number of invisible users on this channel
+ * @return Number of invisible users
+ */
+ int CountInvisible();
+
+ /** Get a users status on this channel
+ * @param user The user to look up
+ * @return One of STATUS_OP, STATUS_HOP, STATUS_VOICE, or zero.
+ */
+ int GetStatus(userrec *user);
+
+ /** Get a users status on this channel in a bitmask
+ * @param user The user to look up
+ * @return A bitmask containing zero or more of STATUS_OP, STATUS_HOP, STATUS_VOICE
+ */
+ int GetStatusFlags(userrec *user);
+
+ /** 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(userrec *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(userrec* user);
+
+ /** Get the value of a users prefix on this channel.
+ * @param user The user to look up
+ * @return The module or core-defined value of the users prefix.
+ * The values for op, halfop and voice status are constants in
+ * mode.h, and are OP_VALUE, HALFOP_VALUE, and VOICE_VALUE respectively.
+ * If the value you are given does not match one of these three, it is
+ * a module-defined mode, and it should be compared in proportion to
+ * these three constants. For example a value greater than OP_VALUE
+ * is a prefix of greater 'worth' than ops, and a value less than
+ * VOICE_VALUE is of lesser 'worth' than a voice.
+ */
+ unsigned int GetPrefixValue(userrec* 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(userrec* 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 prefix_rank The rank (value) of this prefix character
+ * @param adding True if adding the prefix, false when removing
+ */
+ void SetPrefix(userrec* user, char prefix, unsigned int prefix_rank, 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
+ */
+ bool IsBanned(userrec* user);
+
+ /** Clears the cached max bans value
+ */
+ void ResetMaxBans();
+
+ /** Destructor for chanrec
+ */
+ virtual ~chanrec() { /* stub */ }
+};
+
+#endif
diff --git a/include/command_parse.h b/include/command_parse.h
index ab567642e..e8240fcf9 100644
--- a/include/command_parse.h
+++ b/include/command_parse.h
@@ -1 +1,244 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __COMMAND_PARSE_H #define __COMMAND_PARSE_H #include <string> #include "users.h" #include "ctables.h" #include "typedefs.h" /** Required forward declaration */ class InspIRCd; /** A list of dll/so files containing the command handlers for the core */ typedef std::map<std::string, void*> SharedObjectList; /** This class handles command management and parsing. * It allows you to add and remove commands from the map, * call command handlers by name, and chop up comma seperated * parameters into multiple calls. */ class CoreExport CommandParser : public classbase { private: /** The creator of this class */ InspIRCd* ServerInstance; /** Parameter buffer */ std::vector<std::string> para; /** 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(char **command_p,char *parameters); /** Process a command from a user. * @param user The user to parse the command for * @param cmd The command string to process */ void ProcessCommand(userrec *user, std::string &cmd); /** Insert the default RFC1459 commands into the command hash. */ void SetupCommandTable(); /** Finds the init_command symbol in a .so file * @param v A function pointer to be initialized * @param h A valid shared object handle * @return True if the symbol could be found */ bool FindSym(void** v, void* h); /** A list of core-implemented modes and their shared object handles */ SharedObjectList RFCCommands; /** Load a command from a shared object on disk. * @param name The shared object to load (without path) */ void LoadCommand(const char* name); /** Removes a command if the sources match. Used as a helper for * safe hash_map delete while iter in RemoveCommands(const char* source). */ void RemoveCommand(nspace::hash_map<std::string,command_t*>::iterator safei, const char* source); public: /** Command list, a hash_map of command names to command_t* */ command_table cmdlist; /** Reload a core command. * This will only reload commands implemented by the core, * to reload a modular command, you must reload that module. * @param cmd The command to reload. This will cause the shared * object which implements this command to be closed, and then reloaded. * @return True if the command was reloaded, false if it could not be found * or another error occured */ bool ReloadCommand(const char* cmd); /** Default constructor. * @param Instance The creator of this class */ CommandParser(InspIRCd* Instance); /** Calls the handler for a given command. * @param commandname The command to find. This should be in uppercase. * @param parameters Parameter list as an array of array of char (that's not a typo). * @param pcnt The number of items in the parameters list * @param user The user to call the handler on behalf of * @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 char** parameters, int pcnt, userrec *user); /** Get the handler function for a command. * @param commandname The command required. Always use uppercase for this parameter. * @return a pointer to the command handler, or NULL */ command_t* 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, int pcnt, userrec * 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, * 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. * * @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 The number of items in the parameters list * @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 * @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. */ int LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra); /** 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. * * @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 The number of items in the parameters list * @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 * @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. */ int LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere); /** 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 */ void ProcessBuffer(std::string &buffer,userrec *user); /** Remove all commands relating to module 'source'. * @param source A module name which has introduced new commands * @return True This function returns true if commands were removed */ bool RemoveCommands(const char* source); /** Add a new command to the commands hash * @param f The new command_t to add to the list * @param so_handle The handle to the shared object where the command can be found. * Only core commands loaded via cmd_*.so files should set this parameter to anything * meaningful. Module authors should leave this parameter at its default of NULL. * @return True if the command was added */ bool CreateCommand(command_t *f, void* so_handle = NULL); }; /** Command handler class for the RELOAD command. * A command cant really reload itself, so this has to be in here. */ class cmd_reload : public command_t { public: /** Standard constructor */ cmd_reload (InspIRCd* Instance) : command_t(Instance,"RELOAD",'o',1) { syntax = "<core-command>"; } /** Handle RELOAD */ CmdResult Handle(const char** parameters, int pcnt, userrec *user); }; /** A lookup table of values for multiplier characters used by * InspIRCd::Duration(). In this lookup table, the indexes for * the ascii values 'm' and 'M' have the value '60', the indexes * for the ascii values 'D' and 'd' have a value of '86400', etc. */ const int duration_multi[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 86400, 1, 1, 1, 3600, 1, 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 604800, 1, 31536000, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 86400, 1, 1, 1, 3600, 1, 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 604800, 1, 31536000, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __COMMAND_PARSE_H
+#define __COMMAND_PARSE_H
+
+#include <string>
+#include "users.h"
+#include "ctables.h"
+#include "typedefs.h"
+
+/** Required forward declaration
+ */
+class InspIRCd;
+
+/** A list of dll/so files containing the command handlers for the core
+ */
+typedef std::map<std::string, void*> SharedObjectList;
+
+/** This class handles command management and parsing.
+ * It allows you to add and remove commands from the map,
+ * call command handlers by name, and chop up comma seperated
+ * parameters into multiple calls.
+ */
+class CoreExport CommandParser : public classbase
+{
+ private:
+ /** The creator of this class
+ */
+ InspIRCd* ServerInstance;
+
+ /** Parameter buffer
+ */
+ std::vector<std::string> para;
+
+ /** 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(char **command_p,char *parameters);
+
+ /** Process a command from a user.
+ * @param user The user to parse the command for
+ * @param cmd The command string to process
+ */
+ void ProcessCommand(userrec *user, std::string &cmd);
+
+ /** Insert the default RFC1459 commands into the command hash.
+ */
+ void SetupCommandTable();
+
+ /** Finds the init_command symbol in a .so file
+ * @param v A function pointer to be initialized
+ * @param h A valid shared object handle
+ * @return True if the symbol could be found
+ */
+ bool FindSym(void** v, void* h);
+
+ /** A list of core-implemented modes and their shared object handles
+ */
+ SharedObjectList RFCCommands;
+
+ /** Load a command from a shared object on disk.
+ * @param name The shared object to load (without path)
+ */
+ void LoadCommand(const char* name);
+
+ /** Removes a command if the sources match. Used as a helper for
+ * safe hash_map delete while iter in RemoveCommands(const char* source).
+ */
+ void RemoveCommand(nspace::hash_map<std::string,command_t*>::iterator safei, const char* source);
+
+
+ public:
+ /** Command list, a hash_map of command names to command_t*
+ */
+ command_table cmdlist;
+
+ /** Reload a core command.
+ * This will only reload commands implemented by the core,
+ * to reload a modular command, you must reload that module.
+ * @param cmd The command to reload. This will cause the shared
+ * object which implements this command to be closed, and then reloaded.
+ * @return True if the command was reloaded, false if it could not be found
+ * or another error occured
+ */
+ bool ReloadCommand(const char* cmd);
+
+ /** Default constructor.
+ * @param Instance The creator of this class
+ */
+ CommandParser(InspIRCd* Instance);
+
+ /** Calls the handler for a given command.
+ * @param commandname The command to find. This should be in uppercase.
+ * @param parameters Parameter list as an array of array of char (that's not a typo).
+ * @param pcnt The number of items in the parameters list
+ * @param user The user to call the handler on behalf of
+ * @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 char** parameters, int pcnt, userrec *user);
+
+ /** Get the handler function for a command.
+ * @param commandname The command required. Always use uppercase for this parameter.
+ * @return a pointer to the command handler, or NULL
+ */
+ command_t* 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, int pcnt, userrec * 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,
+ * 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.
+ *
+ * @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 The number of items in the parameters list
+ * @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
+ * @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.
+ */
+ int LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra);
+
+ /** 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.
+ *
+ * @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 The number of items in the parameters list
+ * @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
+ * @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.
+ */
+ int LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere);
+
+ /** 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
+ */
+ void ProcessBuffer(std::string &buffer,userrec *user);
+
+ /** Remove all commands relating to module 'source'.
+ * @param source A module name which has introduced new commands
+ * @return True This function returns true if commands were removed
+ */
+ bool RemoveCommands(const char* source);
+
+ /** Add a new command to the commands hash
+ * @param f The new command_t to add to the list
+ * @param so_handle The handle to the shared object where the command can be found.
+ * Only core commands loaded via cmd_*.so files should set this parameter to anything
+ * meaningful. Module authors should leave this parameter at its default of NULL.
+ * @return True if the command was added
+ */
+ bool CreateCommand(command_t *f, void* so_handle = NULL);
+};
+
+/** Command handler class for the RELOAD command.
+ * A command cant really reload itself, so this has to be in here.
+ */
+class cmd_reload : public command_t
+{
+ public:
+ /** Standard constructor
+ */
+ cmd_reload (InspIRCd* Instance) : command_t(Instance,"RELOAD",'o',1) { syntax = "<core-command>"; }
+ /** Handle RELOAD
+ */
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user);
+};
+
+/** A lookup table of values for multiplier characters used by
+ * InspIRCd::Duration(). In this lookup table, the indexes for
+ * the ascii values 'm' and 'M' have the value '60', the indexes
+ * for the ascii values 'D' and 'd' have a value of '86400', etc.
+ */
+const int duration_multi[] =
+{
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 86400, 1, 1, 1, 3600,
+ 1, 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 604800, 1, 31536000, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 86400, 1, 1, 1, 3600, 1, 1, 1, 1, 60,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 604800, 1, 31536000,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+#endif
+
diff --git a/include/commands/cmd_admin.h b/include/commands/cmd_admin.h
index d251891f8..108c4aa9f 100644
--- a/include/commands/cmd_admin.h
+++ b/include/commands/cmd_admin.h
@@ -1 +1,44 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_ADMIN_H__ #define __CMD_ADMIN_H__ #include "users.h" #include "channels.h" #include "ctables.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 cmd_admin : public command_t { public: /** Constructor for admin. */ cmd_admin (InspIRCd* Instance) : command_t(Instance,"ADMIN",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_ADMIN_H__
+#define __CMD_ADMIN_H__
+
+#include "users.h"
+#include "channels.h"
+#include "ctables.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 cmd_admin : public command_t
+{
+ public:
+ /** Constructor for admin.
+ */
+ cmd_admin (InspIRCd* Instance) : command_t(Instance,"ADMIN",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_away.h b/include/commands/cmd_away.h
index ce6a66abd..88d111c78 100644
--- a/include/commands/cmd_away.h
+++ b/include/commands/cmd_away.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_AWAY_H__ #define __CMD_AWAY_H__ // include the common header files #include "users.h" #include "channels.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 cmd_away : public command_t { public: /** Constructor for away. */ cmd_away (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_AWAY_H__
+#define __CMD_AWAY_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_away : public command_t
+{
+ public:
+ /** Constructor for away.
+ */
+ cmd_away (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_clearcache.h b/include/commands/cmd_clearcache.h
index abf5b8a54..6c5d66ac8 100644
--- a/include/commands/cmd_clearcache.h
+++ b/include/commands/cmd_clearcache.h
@@ -1 +1,44 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_ADMIN_H__ #define __CMD_ADMIN_H__ #include "users.h" #include "channels.h" #include "ctables.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 cmd_clearcache : public command_t { public: /** Constructor for clearcache. */ cmd_clearcache (InspIRCd* Instance) : command_t(Instance,"CLEARCACHE",'o',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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_ADMIN_H__
+#define __CMD_ADMIN_H__
+
+#include "users.h"
+#include "channels.h"
+#include "ctables.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 cmd_clearcache : public command_t
+{
+ public:
+ /** Constructor for clearcache.
+ */
+ cmd_clearcache (InspIRCd* Instance) : command_t(Instance,"CLEARCACHE",'o',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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_commands.h b/include/commands/cmd_commands.h
index 095a88557..62359dd5d 100644
--- a/include/commands/cmd_commands.h
+++ b/include/commands/cmd_commands.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_COMMANDS_H__ #define __CMD_COMMANDS_H__ // include the common header files #include "users.h" #include "channels.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 cmd_commands : public command_t { public: /** Constructor for commands. */ cmd_commands (InspIRCd* Instance) : command_t(Instance,"COMMANDS",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_COMMANDS_H__
+#define __CMD_COMMANDS_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_commands : public command_t
+{
+ public:
+ /** Constructor for commands.
+ */
+ cmd_commands (InspIRCd* Instance) : command_t(Instance,"COMMANDS",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_connect.h b/include/commands/cmd_connect.h
index bcb25c15a..01150fc68 100644
--- a/include/commands/cmd_connect.h
+++ b/include/commands/cmd_connect.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_CONNECT_H__ #define __CMD_CONNECT_H__ #include "users.h" #include "channels.h" #include "ctables.h" #include "modules.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 cmd_connect : public command_t { public: /** Constructor for connect. */ cmd_connect (InspIRCd* Instance) : command_t(Instance,"CONNECT",'o',1) { syntax = "<servername> [<remote-server>]"; } /** 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_CONNECT_H__
+#define __CMD_CONNECT_H__
+
+#include "users.h"
+#include "channels.h"
+#include "ctables.h"
+#include "modules.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 cmd_connect : public command_t
+{
+ public:
+ /** Constructor for connect.
+ */
+ cmd_connect (InspIRCd* Instance) : command_t(Instance,"CONNECT",'o',1) { syntax = "<servername> [<remote-server>]"; }
+ /** 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_die.h b/include/commands/cmd_die.h
index 3d88ec4c1..e3ee3ab9d 100644
--- a/include/commands/cmd_die.h
+++ b/include/commands/cmd_die.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_DIE_H__ #define __CMD_DIE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_die : public command_t { public: /** Constructor for die. */ cmd_die (InspIRCd* Instance) : command_t(Instance,"DIE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_DIE_H__
+#define __CMD_DIE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_die : public command_t
+{
+ public:
+ /** Constructor for die.
+ */
+ cmd_die (InspIRCd* Instance) : command_t(Instance,"DIE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_eline.h b/include/commands/cmd_eline.h
index 23fae3f03..594af9697 100644
--- a/include/commands/cmd_eline.h
+++ b/include/commands/cmd_eline.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_ELINE_H__ #define __CMD_ELINE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_eline : public command_t { public: /** Constructor for eline. */ cmd_eline (InspIRCd* Instance) : command_t(Instance,"ELINE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_ELINE_H__
+#define __CMD_ELINE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_eline : public command_t
+{
+ public:
+ /** Constructor for eline.
+ */
+ cmd_eline (InspIRCd* Instance) : command_t(Instance,"ELINE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_gline.h b/include/commands/cmd_gline.h
index f8a82dfa8..5fc8eccee 100644
--- a/include/commands/cmd_gline.h
+++ b/include/commands/cmd_gline.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_GLINE_H__ #define __CMD_GLINE_H__ // include the common header file #include "users.h" #include "channels.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 cmd_gline : public command_t { public: /** Constructor for gline. */ cmd_gline (InspIRCd* Instance) : command_t(Instance,"GLINE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_GLINE_H__
+#define __CMD_GLINE_H__
+
+// include the common header file
+
+#include "users.h"
+#include "channels.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 cmd_gline : public command_t
+{
+ public:
+ /** Constructor for gline.
+ */
+ cmd_gline (InspIRCd* Instance) : command_t(Instance,"GLINE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_info.h b/include/commands/cmd_info.h
index 00bb6d2f2..330c8bcf1 100644
--- a/include/commands/cmd_info.h
+++ b/include/commands/cmd_info.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_INFO_H__ #define __CMD_INFO_H__ // include the common header files #include "users.h" #include "channels.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 cmd_info : public command_t { public: /** Constructor for info. */ cmd_info (InspIRCd* Instance) : command_t(Instance,"INFO",0,0) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_INFO_H__
+#define __CMD_INFO_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_info : public command_t
+{
+ public:
+ /** Constructor for info.
+ */
+ cmd_info (InspIRCd* Instance) : command_t(Instance,"INFO",0,0) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_invite.h b/include/commands/cmd_invite.h
index db506fee9..13bd1f35b 100644
--- a/include/commands/cmd_invite.h
+++ b/include/commands/cmd_invite.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_INVITE_H__ #define __CMD_INVITE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_invite : public command_t { public: /** Constructor for invite. */ cmd_invite (InspIRCd* Instance) : command_t(Instance,"INVITE",0,0) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_INVITE_H__
+#define __CMD_INVITE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_invite : public command_t
+{
+ public:
+ /** Constructor for invite.
+ */
+ cmd_invite (InspIRCd* Instance) : command_t(Instance,"INVITE",0,0) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_ison.h b/include/commands/cmd_ison.h
index 143a3efd7..d0be58c23 100644
--- a/include/commands/cmd_ison.h
+++ b/include/commands/cmd_ison.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_ISON_H__ #define __CMD_ISON_H__ // include the common header files #include "users.h" #include "channels.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 cmd_ison : public command_t { public: /** Constructor for ison. */ cmd_ison (InspIRCd* Instance) : command_t(Instance,"ISON",0,0) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_ISON_H__
+#define __CMD_ISON_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_ison : public command_t
+{
+ public:
+ /** Constructor for ison.
+ */
+ cmd_ison (InspIRCd* Instance) : command_t(Instance,"ISON",0,0) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_join.h b/include/commands/cmd_join.h
index 65abdff59..6e343bb72 100644
--- a/include/commands/cmd_join.h
+++ b/include/commands/cmd_join.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_JOIN_H__ #define __CMD_JOIN_H__ // include the common header files #include "users.h" #include "channels.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 cmd_join : public command_t { public: /** Constructor for join. */ cmd_join (InspIRCd* Instance) : command_t(Instance,"JOIN",0,1) { syntax = "<channel>{,<channel>} {<key>{,<key>}}"; } /** 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_JOIN_H__
+#define __CMD_JOIN_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_join : public command_t
+{
+ public:
+ /** Constructor for join.
+ */
+ cmd_join (InspIRCd* Instance) : command_t(Instance,"JOIN",0,1) { syntax = "<channel>{,<channel>} {<key>{,<key>}}"; }
+ /** 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_kick.h b/include/commands/cmd_kick.h
index c8832f042..6a44d0c12 100644
--- a/include/commands/cmd_kick.h
+++ b/include/commands/cmd_kick.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_KICK_H__ #define __CMD_KICK_H__ // include the common header files #include "users.h" #include "channels.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 cmd_kick : public command_t { public: /** Constructor for kick. */ cmd_kick (InspIRCd* Instance) : command_t(Instance,"KICK",0,2) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_KICK_H__
+#define __CMD_KICK_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_kick : public command_t
+{
+ public:
+ /** Constructor for kick.
+ */
+ cmd_kick (InspIRCd* Instance) : command_t(Instance,"KICK",0,2) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_kill.h b/include/commands/cmd_kill.h
index 456744c26..8489599b6 100644
--- a/include/commands/cmd_kill.h
+++ b/include/commands/cmd_kill.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_KILL_H__ #define __CMD_KILL_H__ // include the common header files #include "users.h" #include "channels.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 cmd_kill : public command_t { public: /** Constructor for kill. */ cmd_kill (InspIRCd* Instance) : command_t(Instance,"KILL",'o',2) { syntax = "<nickname> <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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_KILL_H__
+#define __CMD_KILL_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_kill : public command_t
+{
+ public:
+ /** Constructor for kill.
+ */
+ cmd_kill (InspIRCd* Instance) : command_t(Instance,"KILL",'o',2) { syntax = "<nickname> <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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_kline.h b/include/commands/cmd_kline.h
index 22d04b558..053b7634d 100644
--- a/include/commands/cmd_kline.h
+++ b/include/commands/cmd_kline.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_KLINE_H__ #define __CMD_KLINE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_kline : public command_t { public: /** Constructor for kline. */ cmd_kline (InspIRCd* Instance) : command_t(Instance,"KLINE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_KLINE_H__
+#define __CMD_KLINE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_kline : public command_t
+{
+ public:
+ /** Constructor for kline.
+ */
+ cmd_kline (InspIRCd* Instance) : command_t(Instance,"KLINE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_links.h b/include/commands/cmd_links.h
index c0748ec7b..7d0eee9e5 100644
--- a/include/commands/cmd_links.h
+++ b/include/commands/cmd_links.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_LINKS_H__ #define __CMD_LINKS_H__ // include the common header files #include "users.h" #include "channels.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 cmd_links : public command_t { public: /** Constructor for links. */ cmd_links (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_LINKS_H__
+#define __CMD_LINKS_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_links : public command_t
+{
+ public:
+ /** Constructor for links.
+ */
+ cmd_links (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_list.h b/include/commands/cmd_list.h
index 01b330bd1..5c956863d 100644
--- a/include/commands/cmd_list.h
+++ b/include/commands/cmd_list.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_LIST_H__ #define __CMD_LIST_H__ // include the common header files #include "users.h" #include "channels.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 cmd_list : public command_t { public: /** Constructor for list. */ cmd_list (InspIRCd* Instance) : command_t(Instance,"LIST",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_LIST_H__
+#define __CMD_LIST_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_list : public command_t
+{
+ public:
+ /** Constructor for list.
+ */
+ cmd_list (InspIRCd* Instance) : command_t(Instance,"LIST",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_loadmodule.h b/include/commands/cmd_loadmodule.h
index e35fc5067..40ca03e81 100644
--- a/include/commands/cmd_loadmodule.h
+++ b/include/commands/cmd_loadmodule.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_LOADMODULE_H__ #define __CMD_LOADMODULE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_loadmodule : public command_t { public: /** Constructor for loadmodule. */ cmd_loadmodule (InspIRCd* Instance) : command_t(Instance,"LOADMODULE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_LOADMODULE_H__
+#define __CMD_LOADMODULE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_loadmodule : public command_t
+{
+ public:
+ /** Constructor for loadmodule.
+ */
+ cmd_loadmodule (InspIRCd* Instance) : command_t(Instance,"LOADMODULE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_lusers.h b/include/commands/cmd_lusers.h
index 3621fcb11..8f9226b4e 100644
--- a/include/commands/cmd_lusers.h
+++ b/include/commands/cmd_lusers.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_LUSERS_H__ #define __CMD_LUSERS_H__ // include the common header files #include "users.h" #include "channels.h" /** 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 cmd_lusers : public command_t { public: /** Constructor for lusers. */ cmd_lusers (InspIRCd* Instance) : command_t(Instance,"LUSERS",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_LUSERS_H__
+#define __CMD_LUSERS_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+/** 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 cmd_lusers : public command_t
+{
+ public:
+ /** Constructor for lusers.
+ */
+ cmd_lusers (InspIRCd* Instance) : command_t(Instance,"LUSERS",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_map.h b/include/commands/cmd_map.h
index e67e09b53..f1b586c9f 100644
--- a/include/commands/cmd_map.h
+++ b/include/commands/cmd_map.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_MAP_H__ #define __CMD_MAP_H__ // include the common header files #include "users.h" #include "channels.h" /** Handle /MAP. 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 cmd_map : public command_t { public: /** Constructor for map. */ cmd_map (InspIRCd* Instance) : command_t(Instance,"MAP",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_MAP_H__
+#define __CMD_MAP_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+/** Handle /MAP. 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 cmd_map : public command_t
+{
+ public:
+ /** Constructor for map.
+ */
+ cmd_map (InspIRCd* Instance) : command_t(Instance,"MAP",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_mode.h b/include/commands/cmd_mode.h
index a75bd8635..36e76bf66 100644
--- a/include/commands/cmd_mode.h
+++ b/include/commands/cmd_mode.h
@@ -1 +1,44 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_ADMIN_H__ #define __CMD_ADMIN_H__ #include "users.h" #include "channels.h" #include "ctables.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 cmd_mode : public command_t { public: /** Constructor for mode. */ cmd_mode (InspIRCd* Instance) : command_t(Instance,"MODE",0,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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_ADMIN_H__
+#define __CMD_ADMIN_H__
+
+#include "users.h"
+#include "channels.h"
+#include "ctables.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 cmd_mode : public command_t
+{
+ public:
+ /** Constructor for mode.
+ */
+ cmd_mode (InspIRCd* Instance) : command_t(Instance,"MODE",0,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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_modules.h b/include/commands/cmd_modules.h
index 02ada014a..372a87c05 100644
--- a/include/commands/cmd_modules.h
+++ b/include/commands/cmd_modules.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_MODULES_H__ #define __CMD_MODULES_H__ // include the common header files #include "users.h" #include "channels.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 cmd_modules : public command_t { public: /** Constructor for modules. */ cmd_modules (InspIRCd* Instance) : command_t(Instance,"MODULES",0,0) { syntax = "[debug]"; } /** 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_MODULES_H__
+#define __CMD_MODULES_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_modules : public command_t
+{
+ public:
+ /** Constructor for modules.
+ */
+ cmd_modules (InspIRCd* Instance) : command_t(Instance,"MODULES",0,0) { syntax = "[debug]"; }
+ /** 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_motd.h b/include/commands/cmd_motd.h
index 47c1ca987..75162274a 100644
--- a/include/commands/cmd_motd.h
+++ b/include/commands/cmd_motd.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_MOTD_H__ #define __CMD_MOTD_H__ // include the common header files #include <string> #include <vector> #include "inspircd.h" #include "users.h" #include "channels.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 cmd_motd : public command_t { public: /** Constructor for motd. */ cmd_motd (InspIRCd* Instance) : command_t(Instance,"MOTD",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_MOTD_H__
+#define __CMD_MOTD_H__
+
+// include the common header files
+
+#include <string>
+#include <vector>
+#include "inspircd.h"
+#include "users.h"
+#include "channels.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 cmd_motd : public command_t
+{
+ public:
+ /** Constructor for motd.
+ */
+ cmd_motd (InspIRCd* Instance) : command_t(Instance,"MOTD",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_names.h b/include/commands/cmd_names.h
index b1e78df3f..135e7946b 100644
--- a/include/commands/cmd_names.h
+++ b/include/commands/cmd_names.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_NAMES_H__ #define __CMD_NAMES_H__ // include the common header files #include "users.h" #include "channels.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 cmd_names : public command_t { public: /** Constructor for names. */ cmd_names (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_NAMES_H__
+#define __CMD_NAMES_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_names : public command_t
+{
+ public:
+ /** Constructor for names.
+ */
+ cmd_names (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_nick.h b/include/commands/cmd_nick.h
index b1485a27b..69df1514e 100644
--- a/include/commands/cmd_nick.h
+++ b/include/commands/cmd_nick.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_NICK_H__ #define __CMD_NICK_H__ // include the common header files #include "users.h" #include "channels.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 cmd_nick : public command_t { public: /** Constructor for nick. */ cmd_nick (InspIRCd* Instance) : command_t(Instance,"NICK",0,1,true) { syntax = "<newnick>"; } /** 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_NICK_H__
+#define __CMD_NICK_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_nick : public command_t
+{
+ public:
+ /** Constructor for nick.
+ */
+ cmd_nick (InspIRCd* Instance) : command_t(Instance,"NICK",0,1,true) { syntax = "<newnick>"; }
+ /** 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_notice.h b/include/commands/cmd_notice.h
index 402f4b3b1..ec4372066 100644
--- a/include/commands/cmd_notice.h
+++ b/include/commands/cmd_notice.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_NOTICE_H__ #define __CMD_NOTICE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_notice : public command_t { public: /** Constructor for notice. */ cmd_notice (InspIRCd* Instance) : command_t(Instance,"NOTICE",0,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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_NOTICE_H__
+#define __CMD_NOTICE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_notice : public command_t
+{
+ public:
+ /** Constructor for notice.
+ */
+ cmd_notice (InspIRCd* Instance) : command_t(Instance,"NOTICE",0,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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_oper.h b/include/commands/cmd_oper.h
index 0bfda174e..77c4b0125 100644
--- a/include/commands/cmd_oper.h
+++ b/include/commands/cmd_oper.h
@@ -1 +1,47 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_OPER_H__ #define __CMD_OPER_H__ // include the common header files #include "users.h" #include "channels.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 cmd_oper : public command_t { public: /** Constructor for oper. */ cmd_oper (InspIRCd* Instance) : command_t(Instance,"OPER",0,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 Handle(const char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_OPER_H__
+#define __CMD_OPER_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_oper : public command_t
+{
+ public:
+ /** Constructor for oper.
+ */
+ cmd_oper (InspIRCd* Instance) : command_t(Instance,"OPER",0,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 Handle(const char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_part.h b/include/commands/cmd_part.h
index e4ce48e08..5f5781874 100644
--- a/include/commands/cmd_part.h
+++ b/include/commands/cmd_part.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_PART_H__ #define __CMD_PART_H__ // include the common header files #include "users.h" #include "channels.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 cmd_part : public command_t { public: /** Constructor for part. */ cmd_part (InspIRCd* Instance) : command_t(Instance,"PART",0,1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_PART_H__
+#define __CMD_PART_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_part : public command_t
+{
+ public:
+ /** Constructor for part.
+ */
+ cmd_part (InspIRCd* Instance) : command_t(Instance,"PART",0,1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_pass.h b/include/commands/cmd_pass.h
index d3d2cb76b..53addf80f 100644
--- a/include/commands/cmd_pass.h
+++ b/include/commands/cmd_pass.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_PASS_H__ #define __CMD_PASS_H__ // include the common header files #include <string> #include <vector> #include "inspircd.h" #include "users.h" #include "channels.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 cmd_pass : public command_t { public: /** Constructor for pass. */ cmd_pass (InspIRCd* Instance) : command_t(Instance,"PASS",0,1,true) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_PASS_H__
+#define __CMD_PASS_H__
+
+// include the common header files
+
+#include <string>
+#include <vector>
+#include "inspircd.h"
+#include "users.h"
+#include "channels.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 cmd_pass : public command_t
+{
+ public:
+ /** Constructor for pass.
+ */
+ cmd_pass (InspIRCd* Instance) : command_t(Instance,"PASS",0,1,true) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_ping.h b/include/commands/cmd_ping.h
index 1e04eb18e..c5350eba5 100644
--- a/include/commands/cmd_ping.h
+++ b/include/commands/cmd_ping.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_PING_H__ #define __CMD_PING_H__ // include the common header files #include "users.h" #include "channels.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 cmd_ping : public command_t { public: /** Constructor for ping. */ cmd_ping (InspIRCd* Instance) : command_t(Instance,"PING",0,1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_PING_H__
+#define __CMD_PING_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_ping : public command_t
+{
+ public:
+ /** Constructor for ping.
+ */
+ cmd_ping (InspIRCd* Instance) : command_t(Instance,"PING",0,1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_pong.h b/include/commands/cmd_pong.h
index 73784b340..724ed0dfc 100644
--- a/include/commands/cmd_pong.h
+++ b/include/commands/cmd_pong.h
@@ -1 +1,46 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_PONG_H__ #define __CMD_PONG_H__ // include the common header files #include "inspircd.h" #include "users.h" #include "channels.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 cmd_pong : public command_t { public: /** Constructor for pong. */ cmd_pong (InspIRCd* Instance) : command_t(Instance,"PONG",0,1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_PONG_H__
+#define __CMD_PONG_H__
+
+// include the common header files
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.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 cmd_pong : public command_t
+{
+ public:
+ /** Constructor for pong.
+ */
+ cmd_pong (InspIRCd* Instance) : command_t(Instance,"PONG",0,1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_privmsg.h b/include/commands/cmd_privmsg.h
index 0b43d803c..17b7b0445 100644
--- a/include/commands/cmd_privmsg.h
+++ b/include/commands/cmd_privmsg.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_PRIVMSG_H__ #define __CMD_PRIVMSG_H__ // include the common header files #include "users.h" #include "channels.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 cmd_privmsg : public command_t { public: /** Constructor for privmsg. */ cmd_privmsg (InspIRCd* Instance) : command_t(Instance,"PRIVMSG",0,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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_PRIVMSG_H__
+#define __CMD_PRIVMSG_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_privmsg : public command_t
+{
+ public:
+ /** Constructor for privmsg.
+ */
+ cmd_privmsg (InspIRCd* Instance) : command_t(Instance,"PRIVMSG",0,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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_qline.h b/include/commands/cmd_qline.h
index 1aca556a8..5d69f9a9a 100644
--- a/include/commands/cmd_qline.h
+++ b/include/commands/cmd_qline.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_QLINE_H__ #define __CMD_QLINE_H__ // include the common header files #include "users.h" #include "channels.h" /** Handle /QLINE. 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 cmd_qline : public command_t { public: /** Constructor for qline. */ cmd_qline (InspIRCd* Instance) : command_t(Instance,"QLINE",'o',1) { syntax = "<nick> [<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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_QLINE_H__
+#define __CMD_QLINE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+/** Handle /QLINE. 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 cmd_qline : public command_t
+{
+ public:
+ /** Constructor for qline.
+ */
+ cmd_qline (InspIRCd* Instance) : command_t(Instance,"QLINE",'o',1) { syntax = "<nick> [<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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_quit.h b/include/commands/cmd_quit.h
index 2d3287d13..9c80130a2 100644
--- a/include/commands/cmd_quit.h
+++ b/include/commands/cmd_quit.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_QUIT_H__ #define __CMD_QUIT_H__ // include the common header files #include "users.h" #include "channels.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 cmd_quit : public command_t { public: /** Constructor for quit. */ cmd_quit (InspIRCd* Instance) : command_t(Instance,"QUIT",0,0,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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_QUIT_H__
+#define __CMD_QUIT_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_quit : public command_t
+{
+ public:
+ /** Constructor for quit.
+ */
+ cmd_quit (InspIRCd* Instance) : command_t(Instance,"QUIT",0,0,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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_rehash.h b/include/commands/cmd_rehash.h
index de8d9b6c6..f33ac141d 100644
--- a/include/commands/cmd_rehash.h
+++ b/include/commands/cmd_rehash.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_REHASH_H__ #define __CMD_REHASH_H__ // include the common header files #include "users.h" #include "channels.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 cmd_rehash : public command_t { public: /** Constructor for rehash. */ cmd_rehash (InspIRCd* Instance) : command_t(Instance,"REHASH",'o',0) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_REHASH_H__
+#define __CMD_REHASH_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_rehash : public command_t
+{
+ public:
+ /** Constructor for rehash.
+ */
+ cmd_rehash (InspIRCd* Instance) : command_t(Instance,"REHASH",'o',0) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_reloadmodule.h b/include/commands/cmd_reloadmodule.h
index 115ad5b19..76bc656f8 100644
--- a/include/commands/cmd_reloadmodule.h
+++ b/include/commands/cmd_reloadmodule.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_RELOADMODULE_H__ #define __CMD_RELOADMODULE_H__ // include the common header files #include "users.h" #include "channels.h" /** Handle /RELOADMODULE. 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 cmd_reloadmodule : public command_t { public: /** Constructor for reloadmodule. */ cmd_reloadmodule (InspIRCd* Instance) : command_t(Instance,"RELOADMODULE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_RELOADMODULE_H__
+#define __CMD_RELOADMODULE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+/** Handle /RELOADMODULE. 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 cmd_reloadmodule : public command_t
+{
+ public:
+ /** Constructor for reloadmodule.
+ */
+ cmd_reloadmodule (InspIRCd* Instance) : command_t(Instance,"RELOADMODULE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_restart.h b/include/commands/cmd_restart.h
index c227304aa..89a217a95 100644
--- a/include/commands/cmd_restart.h
+++ b/include/commands/cmd_restart.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_RESTART_H__ #define __CMD_RESTART_H__ // include the common header files #include <string> #include <deque> #include <vector> #include "users.h" #include "channels.h" /** Handle /RESTART. 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 cmd_restart : public command_t { public: /** Constructor for restart. */ cmd_restart (InspIRCd* Instance) : command_t(Instance,"RESTART",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_RESTART_H__
+#define __CMD_RESTART_H__
+
+// include the common header files
+
+#include <string>
+#include <deque>
+#include <vector>
+#include "users.h"
+#include "channels.h"
+
+/** Handle /RESTART. 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 cmd_restart : public command_t
+{
+ public:
+ /** Constructor for restart.
+ */
+ cmd_restart (InspIRCd* Instance) : command_t(Instance,"RESTART",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_rules.h b/include/commands/cmd_rules.h
index 2ade369ca..ffa7ea8bd 100644
--- a/include/commands/cmd_rules.h
+++ b/include/commands/cmd_rules.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_RULES_H__ #define __CMD_RULES_H__ // include the common header files #include <string> #include <vector> #include "inspircd.h" #include "users.h" #include "channels.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 cmd_rules : public command_t { public: /** Constructor for rules. */ cmd_rules (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_RULES_H__
+#define __CMD_RULES_H__
+
+// include the common header files
+
+#include <string>
+#include <vector>
+#include "inspircd.h"
+#include "users.h"
+#include "channels.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 cmd_rules : public command_t
+{
+ public:
+ /** Constructor for rules.
+ */
+ cmd_rules (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_server.h b/include/commands/cmd_server.h
index 92867ba6c..3c88c0ac6 100644
--- a/include/commands/cmd_server.h
+++ b/include/commands/cmd_server.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_SERVER_H__ #define __CMD_SERVER_H__ // include the common header files #include "users.h" #include "channels.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 cmd_server : public command_t { public: /** Constructor for server. */ cmd_server (InspIRCd* Instance) : command_t(Instance,"SERVER",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_SERVER_H__
+#define __CMD_SERVER_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_server : public command_t
+{
+ public:
+ /** Constructor for server.
+ */
+ cmd_server (InspIRCd* Instance) : command_t(Instance,"SERVER",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_squit.h b/include/commands/cmd_squit.h
index e5e20bbca..43cb115f7 100644
--- a/include/commands/cmd_squit.h
+++ b/include/commands/cmd_squit.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_SQUIT_H__ #define __CMD_SQUIT_H__ // include the common header files #include <string> #include <vector> #include "inspircd.h" #include "users.h" #include "channels.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 cmd_squit : public command_t { public: /** Constructor for squit. */ cmd_squit (InspIRCd* Instance) : command_t(Instance,"SQUIT",'o',1) { syntax = "<servername> [<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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_SQUIT_H__
+#define __CMD_SQUIT_H__
+
+// include the common header files
+
+#include <string>
+#include <vector>
+#include "inspircd.h"
+#include "users.h"
+#include "channels.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 cmd_squit : public command_t
+{
+ public:
+ /** Constructor for squit.
+ */
+ cmd_squit (InspIRCd* Instance) : command_t(Instance,"SQUIT",'o',1) { syntax = "<servername> [<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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_stats.h b/include/commands/cmd_stats.h
index e2aabef2f..b4c0c3784 100644
--- a/include/commands/cmd_stats.h
+++ b/include/commands/cmd_stats.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_STATS_H__ #define __CMD_STATS_H__ // include the common header files #include "inspircd.h" #include "users.h" #include "channels.h" DllExport void DoStats(InspIRCd* Instance, char statschar, userrec* user, string_list &results); /** 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 cmd_stats : public command_t { public: /** Constructor for stats. */ cmd_stats (InspIRCd* Instance) : command_t(Instance,"STATS",0,1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_STATS_H__
+#define __CMD_STATS_H__
+
+// include the common header files
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+
+DllExport void DoStats(InspIRCd* Instance, char statschar, userrec* user, string_list &results);
+
+/** 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 cmd_stats : public command_t
+{
+ public:
+ /** Constructor for stats.
+ */
+ cmd_stats (InspIRCd* Instance) : command_t(Instance,"STATS",0,1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_summon.h b/include/commands/cmd_summon.h
index 18e0871ab..717863f35 100644
--- a/include/commands/cmd_summon.h
+++ b/include/commands/cmd_summon.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_SUMMON_H__ #define __CMD_SUMMON_H__ // include the common header files #include <string> #include <vector> #include "inspircd.h" #include "users.h" #include "channels.h" /** Handle /SUMMON stub. 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 cmd_summon : public command_t { public: /** Constructor for summon. */ cmd_summon (InspIRCd* Instance) : command_t(Instance,"SUMMON",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_SUMMON_H__
+#define __CMD_SUMMON_H__
+
+// include the common header files
+
+#include <string>
+#include <vector>
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+
+/** Handle /SUMMON stub. 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 cmd_summon : public command_t
+{
+ public:
+ /** Constructor for summon.
+ */
+ cmd_summon (InspIRCd* Instance) : command_t(Instance,"SUMMON",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_time.h b/include/commands/cmd_time.h
index 395281a50..0a2dc2f95 100644
--- a/include/commands/cmd_time.h
+++ b/include/commands/cmd_time.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_TIME_H__ #define __CMD_TIME_H__ // include the common header files #include "users.h" #include "channels.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 cmd_time : public command_t { public: /** Constructor for time. */ cmd_time (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_TIME_H__
+#define __CMD_TIME_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_time : public command_t
+{
+ public:
+ /** Constructor for time.
+ */
+ cmd_time (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_topic.h b/include/commands/cmd_topic.h
index 05ecd7599..c3ac113ef 100644
--- a/include/commands/cmd_topic.h
+++ b/include/commands/cmd_topic.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_TOPIC_H__ #define __CMD_TOPIC_H__ // include the common header files #include "users.h" #include "channels.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 cmd_topic : public command_t { public: /** Constructor for topic. */ cmd_topic (InspIRCd* Instance) : command_t(Instance,"TOPIC",0,1) { syntax = "<channel> [<topic>]"; } /** 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_TOPIC_H__
+#define __CMD_TOPIC_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_topic : public command_t
+{
+ public:
+ /** Constructor for topic.
+ */
+ cmd_topic (InspIRCd* Instance) : command_t(Instance,"TOPIC",0,1) { syntax = "<channel> [<topic>]"; }
+ /** 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_trace.h b/include/commands/cmd_trace.h
index f9f8f2bb8..2c681363d 100644
--- a/include/commands/cmd_trace.h
+++ b/include/commands/cmd_trace.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_TRACE_H__ #define __CMD_TRACE_H__ // include the common header files #include "users.h" #include "channels.h" /** Handle /TRACE. 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 cmd_trace : public command_t { public: /** Constructor for trace. */ cmd_trace (InspIRCd* Instance) : command_t(Instance,"TRACE",'o',0) { syntax = "[<object>]"; } /** 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_TRACE_H__
+#define __CMD_TRACE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+/** Handle /TRACE. 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 cmd_trace : public command_t
+{
+ public:
+ /** Constructor for trace.
+ */
+ cmd_trace (InspIRCd* Instance) : command_t(Instance,"TRACE",'o',0) { syntax = "[<object>]"; }
+ /** 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_unloadmodule.h b/include/commands/cmd_unloadmodule.h
index b8e87e713..2f435576f 100644
--- a/include/commands/cmd_unloadmodule.h
+++ b/include/commands/cmd_unloadmodule.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_UNLOADMODULE_H__ #define __CMD_UNLOADMODULE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_unloadmodule : public command_t { public: /** Constructor for unloadmodule. */ cmd_unloadmodule (InspIRCd* Instance) : command_t(Instance,"UNLOADMODULE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_UNLOADMODULE_H__
+#define __CMD_UNLOADMODULE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_unloadmodule : public command_t
+{
+ public:
+ /** Constructor for unloadmodule.
+ */
+ cmd_unloadmodule (InspIRCd* Instance) : command_t(Instance,"UNLOADMODULE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_user.h b/include/commands/cmd_user.h
index d31484f06..eb835e290 100644
--- a/include/commands/cmd_user.h
+++ b/include/commands/cmd_user.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_USER_H__ #define __CMD_USER_H__ // include the common header files #include "users.h" #include "channels.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 cmd_user : public command_t { public: /** Constructor for user. */ cmd_user (InspIRCd* Instance) : command_t(Instance,"USER",0,4,true) { 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 Handle(const char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_USER_H__
+#define __CMD_USER_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_user : public command_t
+{
+ public:
+ /** Constructor for user.
+ */
+ cmd_user (InspIRCd* Instance) : command_t(Instance,"USER",0,4,true) { 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 Handle(const char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_userhost.h b/include/commands/cmd_userhost.h
index 374025745..0a72bcda2 100644
--- a/include/commands/cmd_userhost.h
+++ b/include/commands/cmd_userhost.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_USERHOST_H__ #define __CMD_USERHOST_H__ // include the common header files #include "users.h" #include "channels.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 cmd_userhost : public command_t { public: /** Constructor for userhost. */ cmd_userhost (InspIRCd* Instance) : command_t(Instance,"USERHOST",0,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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_USERHOST_H__
+#define __CMD_USERHOST_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_userhost : public command_t
+{
+ public:
+ /** Constructor for userhost.
+ */
+ cmd_userhost (InspIRCd* Instance) : command_t(Instance,"USERHOST",0,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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_users.h b/include/commands/cmd_users.h
index 9374c1de2..60341871d 100644
--- a/include/commands/cmd_users.h
+++ b/include/commands/cmd_users.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_USERS_H__ #define __CMD_USERS_H__ // include the common header files // #include <string> #include <vector> #include "inspircd.h" #include "users.h" #include "channels.h" /** Handle /USERS stub. 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 cmd_users : public command_t { public: /** Constructor for users. */ cmd_users (InspIRCd* Instance) : command_t(Instance,"USERS",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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_USERS_H__
+#define __CMD_USERS_H__
+
+// include the common header files
+//
+#include <string>
+#include <vector>
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+
+/** Handle /USERS stub. 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 cmd_users : public command_t
+{
+ public:
+ /** Constructor for users.
+ */
+ cmd_users (InspIRCd* Instance) : command_t(Instance,"USERS",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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_version.h b/include/commands/cmd_version.h
index 9ded1641a..824a9ef7e 100644
--- a/include/commands/cmd_version.h
+++ b/include/commands/cmd_version.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_VERSION_H__ #define __CMD_VERSION_H__ // include the common header files #include "users.h" #include "channels.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 cmd_version : public command_t { public: /** Constructor for version. */ cmd_version (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_VERSION_H__
+#define __CMD_VERSION_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_version : public command_t
+{
+ public:
+ /** Constructor for version.
+ */
+ cmd_version (InspIRCd* Instance) : command_t(Instance,"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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_wallops.h b/include/commands/cmd_wallops.h
index 514f6a5de..548ba53de 100644
--- a/include/commands/cmd_wallops.h
+++ b/include/commands/cmd_wallops.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_WALLOPS_H__ #define __CMD_WALLOPS_H__ // include the common header files #include "users.h" #include "channels.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 cmd_wallops : public command_t { public: /** Constructor for wallops. */ cmd_wallops (InspIRCd* Instance) : command_t(Instance,"WALLOPS",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_WALLOPS_H__
+#define __CMD_WALLOPS_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_wallops : public command_t
+{
+ public:
+ /** Constructor for wallops.
+ */
+ cmd_wallops (InspIRCd* Instance) : command_t(Instance,"WALLOPS",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_who.h b/include/commands/cmd_who.h
index e84de3d35..6ccfa3b5d 100644
--- a/include/commands/cmd_who.h
+++ b/include/commands/cmd_who.h
@@ -1 +1,59 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_WHO_H__ #define __CMD_WHO_H__ // include the common header files #include "users.h" #include "channels.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 cmd_who : public command_t { bool CanView(chanrec* chan, userrec* user); bool opt_viewopersonly; bool opt_showrealhost; bool opt_unlimit; bool opt_realname; bool opt_mode; bool opt_ident; bool opt_metadata; bool opt_port; bool opt_away; bool opt_local; bool opt_far; public: /** Constructor for who. */ cmd_who (InspIRCd* Instance) : command_t(Instance,"WHO",0,1) { syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]"; } void SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* 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 char** parameters, int pcnt, userrec *user); bool whomatch(userrec* user, const char* matchtext); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_WHO_H__
+#define __CMD_WHO_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_who : public command_t
+{
+ bool CanView(chanrec* chan, userrec* user);
+ bool opt_viewopersonly;
+ bool opt_showrealhost;
+ bool opt_unlimit;
+ bool opt_realname;
+ bool opt_mode;
+ bool opt_ident;
+ bool opt_metadata;
+ bool opt_port;
+ bool opt_away;
+ bool opt_local;
+ bool opt_far;
+ public:
+ /** Constructor for who.
+ */
+ cmd_who (InspIRCd* Instance) : command_t(Instance,"WHO",0,1) { syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]"; }
+ void SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* 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 char** parameters, int pcnt, userrec *user);
+ bool whomatch(userrec* user, const char* matchtext);
+};
+
+#endif
diff --git a/include/commands/cmd_whois.h b/include/commands/cmd_whois.h
index 5e0c0d4d3..d3086788a 100644
--- a/include/commands/cmd_whois.h
+++ b/include/commands/cmd_whois.h
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_WHOIS_H__ #define __CMD_WHOIS_H__ // include the common header files #include "users.h" #include "channels.h" const char* Spacify(char* n); DllExport void do_whois(InspIRCd* Instance, userrec* user, userrec* dest,unsigned long signon, unsigned long idle, const char* nick); /** 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 cmd_whois : public command_t { public: /** Constructor for whois. */ cmd_whois (InspIRCd* Instance) : command_t(Instance,"WHOIS",0,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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_WHOIS_H__
+#define __CMD_WHOIS_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+const char* Spacify(char* n);
+DllExport void do_whois(InspIRCd* Instance, userrec* user, userrec* dest,unsigned long signon, unsigned long idle, const char* nick);
+
+/** 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 cmd_whois : public command_t
+{
+ public:
+ /** Constructor for whois.
+ */
+ cmd_whois (InspIRCd* Instance) : command_t(Instance,"WHOIS",0,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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/commands/cmd_whowas.h b/include/commands/cmd_whowas.h
index bf020143c..58aa14bfd 100644
--- a/include/commands/cmd_whowas.h
+++ b/include/commands/cmd_whowas.h
@@ -1 +1,144 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_WHOWAS_H__ #define __CMD_WHOWAS_H__ // include the common header files #include "users.h" #include "channels.h" /* list of available internal commands */ enum Internals { WHOWAS_ADD = 1, WHOWAS_STATS = 2, WHOWAS_PRUNE = 3, WHOWAS_MAINTAIN = 4 }; /* Forward ref for timer */ class WhoWasMaintainTimer; /* Forward ref for typedefs */ class WhoWasGroup; /** InspTimer that is used to maintain the whowas list, called once an hour */ extern WhoWasMaintainTimer* timer; /** A group of users related by nickname */ typedef std::deque<WhoWasGroup*> whowas_set; /** Sets of users in the whowas system */ typedef std::map<irc::string,whowas_set*> whowas_users; /** Sets of time and users in whowas list */ typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo; /** Handle /WHOWAS. 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 cmd_whowas : public command_t { 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 */ whowas_users_fifo whowas_fifo; /* String holding stats so it can be collected */ std::string stats; public: cmd_whowas(InspIRCd* Instance); /** 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 char** parameters, int pcnt, userrec *user); /** 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 HandleInternal(const unsigned int id, const std::deque<classbase*> &parameters); void AddToWhoWas(userrec* user); void GetStats(Extensible* ext); void PruneWhoWas(time_t t); void MaintainWhoWas(time_t t); virtual ~cmd_whowas(); }; /** Used to hold WHOWAS information */ class WhoWasGroup : public classbase { public: /** Real host */ char* host; /** Displayed host */ char* dhost; /** Ident */ char* ident; /** Server name */ const char* server; /** Fullname (GECOS) */ char* gecos; /** Signon time */ time_t signon; /** Initialize this WhoQasFroup with a user */ WhoWasGroup(userrec* user); /** Destructor */ ~WhoWasGroup(); }; class WhoWasMaintainTimer : public InspTimer { private: InspIRCd* ServerInstance; public: WhoWasMaintainTimer(InspIRCd* Instance, long interval) : InspTimer(interval, Instance->Time(), true), ServerInstance(Instance) { } virtual void Tick(time_t TIME); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_WHOWAS_H__
+#define __CMD_WHOWAS_H__
+
+
+// include the common header files
+
+#include "users.h"
+#include "channels.h"
+
+/* list of available internal commands */
+enum Internals
+{
+ WHOWAS_ADD = 1,
+ WHOWAS_STATS = 2,
+ WHOWAS_PRUNE = 3,
+ WHOWAS_MAINTAIN = 4
+};
+
+/* Forward ref for timer */
+class WhoWasMaintainTimer;
+
+/* Forward ref for typedefs */
+class WhoWasGroup;
+
+/** InspTimer that is used to maintain the whowas list, called once an hour
+ */
+extern WhoWasMaintainTimer* timer;
+
+/** A group of users related by nickname
+ */
+typedef std::deque<WhoWasGroup*> whowas_set;
+
+/** Sets of users in the whowas system
+ */
+typedef std::map<irc::string,whowas_set*> whowas_users;
+
+/** Sets of time and users in whowas list
+ */
+typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo;
+
+/** Handle /WHOWAS. 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 cmd_whowas : public command_t
+{
+ 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
+ */
+ whowas_users_fifo whowas_fifo;
+
+ /* String holding stats so it can be collected
+ */
+ std::string stats;
+
+ public:
+ cmd_whowas(InspIRCd* Instance);
+ /** 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 char** parameters, int pcnt, userrec *user);
+ /** 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 HandleInternal(const unsigned int id, const std::deque<classbase*> &parameters);
+ void AddToWhoWas(userrec* user);
+ void GetStats(Extensible* ext);
+ void PruneWhoWas(time_t t);
+ void MaintainWhoWas(time_t t);
+ virtual ~cmd_whowas();
+};
+
+/** Used to hold WHOWAS information
+ */
+class WhoWasGroup : public classbase
+{
+ public:
+ /** Real host
+ */
+ char* host;
+ /** Displayed host
+ */
+ char* dhost;
+ /** Ident
+ */
+ char* ident;
+ /** Server name
+ */
+ const char* server;
+ /** Fullname (GECOS)
+ */
+ char* gecos;
+ /** Signon time
+ */
+ time_t signon;
+
+ /** Initialize this WhoQasFroup with a user
+ */
+ WhoWasGroup(userrec* user);
+ /** Destructor
+ */
+ ~WhoWasGroup();
+};
+
+class WhoWasMaintainTimer : public InspTimer
+{
+ private:
+ InspIRCd* ServerInstance;
+ public:
+ WhoWasMaintainTimer(InspIRCd* Instance, long interval)
+ : InspTimer(interval, Instance->Time(), true), ServerInstance(Instance)
+ {
+ }
+ virtual void Tick(time_t TIME);
+};
+
+#endif
diff --git a/include/commands/cmd_zline.h b/include/commands/cmd_zline.h
index acd7f2ffd..cf9edbfc6 100644
--- a/include/commands/cmd_zline.h
+++ b/include/commands/cmd_zline.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev. * E-mail: * <brain@chatspike.net> * <Craig@chatspike.net> * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CMD_ZLINE_H__ #define __CMD_ZLINE_H__ // include the common header files #include "users.h" #include "channels.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 cmd_zline : public command_t { public: /** Constructor for zline. */ cmd_zline (InspIRCd* Instance) : command_t(Instance,"ZLINE",'o',1) { 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 char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd is copyright (C) 2002-2007 ChatSpike-Dev.
+ * E-mail:
+ * <brain@chatspike.net>
+ * <Craig@chatspike.net>
+ *
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CMD_ZLINE_H__
+#define __CMD_ZLINE_H__
+
+// include the common header files
+
+#include "users.h"
+#include "channels.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 cmd_zline : public command_t
+{
+ public:
+ /** Constructor for zline.
+ */
+ cmd_zline (InspIRCd* Instance) : command_t(Instance,"ZLINE",'o',1) { 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 char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/include/configreader.h b/include/configreader.h
index 687ca4d8d..8a719f77e 100644
--- a/include/configreader.h
+++ b/include/configreader.h
@@ -1 +1,790 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_CONFIGREADER #define INSPIRCD_CONFIGREADER /* handy defines */ /** Determines if a channel op is exempt from given mode m, * in config of server instance s. */ #define CHANOPS_EXEMPT(s, m) (s->Config->ExemptChanOps[(unsigned char)m]) #include <sstream> #include <string> #include <vector> #include <map> #include "inspircd.h" #include "globals.h" #include "modules.h" #include "socketengine.h" #include "socket.h" /* Required forward definitions */ class ServerConfig; class InspIRCd; class InspSocket; /** Types of data in the core config */ enum ConfigDataType { DT_NOTHING = 0, /* No data */ DT_INTEGER = 1, /* Integer */ DT_CHARPTR = 2, /* Char pointer */ DT_BOOLEAN = 3, /* Boolean */ DT_ALLOW_NEWLINE = 128 /* New line characters allowed */ }; /** Holds a config value, either string, integer or boolean. * Callback functions receive one or more of these, either on * their own as a reference, or in a reference to a deque of them. * The callback function can then alter the values of the ValueItem * classes to validate the settings. */ class ValueItem { /** Actual data */ std::string v; public: /** Initialize with an int */ ValueItem(int value); /** Initialize with a bool */ ValueItem(bool value); /** Initialize with a char pointer */ ValueItem(char* value); /** Change value to a char pointer */ void Set(char* value); /** Change value to a const char pointer */ void Set(const char* val); /** Change value to an int */ void Set(int value); /** Get value as an int */ int GetInteger(); /** Get value as a string */ char* GetString(); /** Get value as a bool */ bool GetBool(); }; /** The base class of the container 'ValueContainer' * used internally by the core to hold core values. */ class ValueContainerBase { public: /** Constructor */ ValueContainerBase() { } /** Destructor */ virtual ~ValueContainerBase() { } }; /** ValueContainer is used to contain pointers to different * core values such as the server name, maximum number of * clients etc. * It is specialized to hold a data type, then pointed at * a value in the ServerConfig class. When the value has been * read and validated, the Set method is called to write the * value safely in a type-safe manner. */ template<typename T> class ValueContainer : public ValueContainerBase { /** Contained item */ T val; public: /** Initialize with nothing */ ValueContainer() { val = NULL; } /** Initialize with a value of type T */ ValueContainer(T Val) { val = Val; } /** Change value to type T of size s */ void Set(T newval, size_t s) { memcpy(val, newval, s); } }; /** A specialization of ValueContainer to hold a pointer to a bool */ typedef ValueContainer<bool*> ValueContainerBool; /** A specialization of ValueContainer to hold a pointer to * an unsigned int */ typedef ValueContainer<unsigned int*> ValueContainerUInt; /** A specialization of ValueContainer to hold a pointer to * a char array. */ typedef ValueContainer<char*> ValueContainerChar; /** A specialization of ValueContainer to hold a pointer to * an int */ typedef ValueContainer<int*> ValueContainerInt; /** A set of ValueItems used by multi-value validator functions */ typedef std::deque<ValueItem> ValueList; /** A callback for validating a single value */ typedef bool (*Validator)(ServerConfig* conf, const char*, const char*, ValueItem&); /** A callback for validating multiple value entries */ typedef bool (*MultiValidator)(ServerConfig* conf, const char*, char**, ValueList&, int*); /** A callback indicating the end of a group of entries */ typedef bool (*MultiNotify)(ServerConfig* conf, const char*); /** Holds a core configuration item and its callbacks */ struct InitialConfig { /** Tag name */ char* tag; /** Value name */ char* value; /** Default, if not defined */ char* default_value; /** Value containers */ ValueContainerBase* val; /** Data types */ ConfigDataType datatype; /** Validation function */ Validator validation_function; }; /** Holds a core configuration item and its callbacks * where there may be more than one item */ struct MultiConfig { /** Tag name */ const char* tag; /** One or more items within tag */ char* items[13]; /** One or more defaults for items within tags */ char* items_default[13]; /** One or more data types */ int datatype[13]; /** Initialization function */ MultiNotify init_function; /** Validation function */ MultiValidator validation_function; /** Completion function */ MultiNotify finish_function; }; /** A set of oper types */ typedef std::map<irc::string,char*> opertype_t; /** A Set of oper classes */ typedef std::map<irc::string,char*> operclass_t; /** This class holds the bulk of the runtime configuration for the ircd. * It allows for reading new config values, accessing configuration files, * and storage of the configuration data needed to run the ircd, such as * the servername, connect classes, /ADMIN data, MOTDs and filenames etc. */ class CoreExport ServerConfig : public Extensible { private: /** Creator/owner pointer */ InspIRCd* ServerInstance; /** This variable holds the names of all * files included from the main one. This * is used to make sure that no files are * recursively included. */ std::vector<std::string> include_stack; /** This private method processes one line of * configutation, appending errors to errorstream * and setting error if an error has occured. */ bool ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream); /** Process an include directive */ bool DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream); /** Check that there is only one of each configuration item */ bool CheckOnce(char* tag, bool bail, userrec* user); public: InspIRCd* GetInstance(); /** This holds all the information in the config file, * it's indexed by tag name to a vector of key/values. */ ConfigDataHash config_data; /** 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. */ char ServerName[MAXBUF]; /** Notice to give to users when they are Xlined */ char MoronBanner[MAXBUF]; /* Holds the network name the local server * belongs to. This is an arbitary field defined * by the administrator. */ char Network[MAXBUF]; /** Holds the description of the local server * as defined by the administrator. */ char ServerDesc[MAXBUF]; /** Holds the admin's name, for output in * the /ADMIN command. */ char AdminName[MAXBUF]; /** Holds the email address of the admin, * for output in the /ADMIN command. */ char AdminEmail[MAXBUF]; /** Holds the admin's nickname, for output * in the /ADMIN command */ char AdminNick[MAXBUF]; /** The admin-configured /DIE password */ char diepass[MAXBUF]; /** The admin-configured /RESTART password */ char restartpass[MAXBUF]; /** The pathname and filename of the message of the * day file, as defined by the administrator. */ char motd[MAXBUF]; /** The pathname and filename of the rules file, * as defined by the administrator. */ char rules[MAXBUF]; /** The quit prefix in use, or an empty string */ char PrefixQuit[MAXBUF]; /** The quit suffix in use, or an empty string */ char SuffixQuit[MAXBUF]; /** The fixed quit message in use, or an empty string */ char FixedQuit[MAXBUF]; /** The last string found within a <die> tag, or * an empty string. */ char DieValue[MAXBUF]; /** The DNS server to use for DNS queries */ char DNSServer[MAXBUF]; /** This variable contains a space-seperated list * of commands which are disabled by the * administrator of the server for non-opers. */ char DisabledCommands[MAXBUF]; /** The full path to the modules directory. * This is either set at compile time, or * overridden in the configuration file via * the <options> tag. */ char ModPath[1024]; /** The full pathname to the executable, as * given in argv[0] when the program starts. */ char MyExecutable[1024]; /** The file handle of the logfile. If this * value is NULL, the log file is not open, * probably due to a permissions error on * startup (this should not happen in normal * operation!). */ FILE *log_file; /** If this value is true, the owner of the * server specified -nofork on the command * line, causing the daemon to stay in the * foreground. */ bool nofork; /** If this value if true then all log * messages will be output, regardless of * the level given in the config file. * This is set with the -debug commandline * option. */ bool forcedebug; /** If this is true then log output will be * written to the logfile. This is the default. * If you put -nolog on the commandline then * the logfile will not be written. * This is meant to be used in conjunction with * -debug for debugging without filling up the * hard disk. */ bool writelog; /** If this value is true, halfops have been * enabled in the configuration file. */ bool AllowHalfop; /** If this is set to true, then mode lists (e.g * MODE #chan b) are hidden from unprivileged * users. */ bool HideModeLists[256]; /** If this is set to true, then channel operators * are exempt from this channel mode. Used for +Sc etc. */ bool ExemptChanOps[256]; /** The number of seconds the DNS subsystem * will wait before timing out any request. */ int dns_timeout; /** The size of the read() buffer in the user * handling code, used to read data into a user's * recvQ. */ int NetBufferSize; /** The value to be used for listen() backlogs * as default. */ int MaxConn; /** The soft limit value assigned to the irc server. * The IRC server will not allow more than this * number of local users. */ unsigned int SoftLimit; /** Maximum number of targets for a multi target command * such as PRIVMSG or KICK */ unsigned int MaxTargets; /** The maximum number of /WHO results allowed * in any single /WHO command. */ int MaxWhoResults; /** True if the DEBUG loglevel is selected. */ int debugging; /** The loglevel in use by the IRC server */ int LogLevel; /** How many seconds to wait before exiting * the program when /DIE is correctly issued. */ int DieDelay; /** True if we're going to hide netsplits as *.net *.split for non-opers */ bool HideSplits; /** True if we're going to hide ban reasons for non-opers (e.g. G-Lines, * K-Lines, Z-Lines) */ bool HideBans; /** Announce invites to the channel with a server notice */ bool AnnounceInvites; /** If this is enabled then operators will * see invisible (+i) channels in /whois. */ bool OperSpyWhois; /** Set to a non-empty string to obfuscate the server name of users in WHOIS */ char HideWhoisServer[MAXBUF]; /** Set to a non empty string to obfuscate nicknames prepended to a KILL. */ char HideKillsServer[MAXBUF]; /** The MOTD file, cached in a file_cache type. */ file_cache MOTD; /** The RULES file, cached in a file_cache type. */ file_cache RULES; /** The full pathname and filename of the PID * file as defined in the configuration. */ char PID[1024]; /** The connect classes in use by the IRC server. */ ClassVector Classes; /** A list of module names (names only, no paths) * which are currently loaded by the server. */ std::vector<std::string> module_names; /** A list of the classes for listening client ports */ std::vector<ListenSocket*> ports; /** Boolean sets of which modules implement which functions */ char implement_lists[255][255]; /** Global implementation list */ char global_implementation[255]; /** A list of ports claimed by IO Modules */ std::map<int,Module*> IOHookModule; std::map<InspSocket*, Module*> SocketIOHookModule; /** 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. */ char UserStats[MAXBUF]; /** The path and filename of the ircd.log file */ std::string logpath; /** Default channel modes */ char DefaultModes[MAXBUF]; /** Custom version string, which if defined can replace the system info in VERSION. */ char CustomVersion[MAXBUF]; /** 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; /** Directory where the inspircd binary resides */ std::string MyDir; /** 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, 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; /** All oper type definitions from the config file */ opertype_t opertypes; /** All oper class definitions from the config file */ operclass_t operclass; /** Saved argv from startup */ char** argv; /** Saved argc from startup */ int argc; /** Max channels per user */ unsigned int MaxChans; /** Oper max channels per user */ unsigned int OperMaxChans; /** Construct a new ServerConfig */ ServerConfig(InspIRCd* Instance); /** Clears the include stack in preperation for a Read() call. */ void ClearStack(); /** Update the 005 vector */ void Update005(); /** Send the 005 numerics (ISUPPORT) to a user */ void Send005(userrec* user); /** Read the entire configuration into memory * and initialize this class. All other methods * should be used only by the core. */ void Read(bool bail, userrec* user); /** Read a file into a file_cache object */ bool ReadFile(file_cache &F, const char* fname); /** Report a configuration error given in errormessage. * @param bail If this is set to true, the error is sent to the console, and the program exits * @param user If this is set to a non-null value, and bail is false, the errors are spooled to * this user as SNOTICEs. * If the parameter is NULL, the messages are spooled to all users via WriteOpers as SNOTICEs. */ void ReportConfigError(const std::string &errormessage, bool bail, userrec* user); /** Load 'filename' into 'target', with the new config parser everything is parsed into * tag/key/value at load-time rather than at read-value time. */ bool LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream); /** Load 'filename' into 'target', with the new config parser everything is parsed into * tag/key/value at load-time rather than at read-value time. */ bool LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream); /* Both these return true if the value existed or false otherwise */ /** Writes 'length' chars into 'result' as a string */ bool ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds = false); /** Writes 'length' chars into 'result' as a string */ bool ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds = false); /** Writes 'length' chars into 'result' as a string */ bool ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds = false); /** Writes 'length' chars into 'result' as a string */ bool ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds = false); /** Tries to convert the value to an integer and write it to 'result' */ bool ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result); /** Tries to convert the value to an integer and write it to 'result' */ bool ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result); /** Tries to convert the value to an integer and write it to 'result' */ bool ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result); /** Tries to convert the value to an integer and write it to 'result' */ bool ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result); /** Returns true if the value exists and has a true value, false otherwise */ bool ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index); /** Returns true if the value exists and has a true value, false otherwise */ bool ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index); /** Returns true if the value exists and has a true value, false otherwise */ bool ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index); /** Returns true if the value exists and has a true value, false otherwise */ bool ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index); /** Returns the number of occurences of tag in the config file */ int ConfValueEnum(ConfigDataHash &target, const char* tag); /** Returns the number of occurences of tag in the config file */ int ConfValueEnum(ConfigDataHash &target, const std::string &tag); /** Returns the numbers of vars inside the index'th 'tag in the config file */ int ConfVarEnum(ConfigDataHash &target, const char* tag, int index); /** Returns the numbers of vars inside the index'th 'tag in the config file */ int ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index); /** Get a pointer to the module which has hooked the given port. * @parameter port Port number * @return Returns a pointer to the hooking module, or NULL */ Module* GetIOHook(int port); /** Hook a module to a client port, so that it can receive notifications * of low-level port activity. * @param port The port number * @param Module the module to hook to the port * @return True if the hook was successful. */ bool AddIOHook(int port, Module* iomod); /** Delete a module hook from a client port. * @param port The port to detatch from * @return True if successful */ bool DelIOHook(int port); /** Get a pointer to the module which has hooked the given InspSocket class. * @parameter port Port number * @return Returns a pointer to the hooking module, or NULL */ Module* GetIOHook(InspSocket* is); /** Hook a module to an InspSocket class, so that it can receive notifications * of low-level socket activity. * @param iomod The module to hook to the socket * @param is The InspSocket to attach to * @return True if the hook was successful. */ bool AddIOHook(Module* iomod, InspSocket* is); /** Delete a module hook from an InspSocket. * @param is The InspSocket to detatch from. * @return True if the unhook was successful */ bool DelIOHook(InspSocket* is); /** Returns the fully qualified path to the inspircd directory * @return The full program directory */ std::string GetFullProgDir(); /** Returns true if a directory is valid (within the modules directory). * @param dirandfile The directory and filename to check * @return True if the directory is valid */ static bool DirValid(const char* dirandfile); /** Clean a filename, stripping the directories (and drives) from string. * @param name Directory to tidy * @return The cleaned filename */ static char* CleanFilename(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); }; /** Initialize the disabled commands list */ CoreExport bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance); /** Initialize the oper types */ bool InitTypes(ServerConfig* conf, const char* tag); /** Initialize the oper classes */ bool InitClasses(ServerConfig* conf, const char* tag); /** Initialize an oper type */ bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types); /** Initialize an oper class */ bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types); /** Finish initializing the oper types and classes */ bool DoneClassesAndTypes(ServerConfig* conf, const char* tag); #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_CONFIGREADER
+#define INSPIRCD_CONFIGREADER
+
+/* handy defines */
+
+/** Determines if a channel op is exempt from given mode m,
+ * in config of server instance s.
+ */
+#define CHANOPS_EXEMPT(s, m) (s->Config->ExemptChanOps[(unsigned char)m])
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
+#include "inspircd.h"
+#include "globals.h"
+#include "modules.h"
+#include "socketengine.h"
+#include "socket.h"
+
+/* Required forward definitions */
+class ServerConfig;
+class InspIRCd;
+class InspSocket;
+
+/** Types of data in the core config
+ */
+enum ConfigDataType
+{
+ DT_NOTHING = 0, /* No data */
+ DT_INTEGER = 1, /* Integer */
+ DT_CHARPTR = 2, /* Char pointer */
+ DT_BOOLEAN = 3, /* Boolean */
+ DT_ALLOW_NEWLINE = 128 /* New line characters allowed */
+};
+
+/** Holds a config value, either string, integer or boolean.
+ * Callback functions receive one or more of these, either on
+ * their own as a reference, or in a reference to a deque of them.
+ * The callback function can then alter the values of the ValueItem
+ * classes to validate the settings.
+ */
+class ValueItem
+{
+ /** Actual data */
+ std::string v;
+ public:
+ /** Initialize with an int */
+ ValueItem(int value);
+ /** Initialize with a bool */
+ ValueItem(bool value);
+ /** Initialize with a char pointer */
+ ValueItem(char* value);
+ /** Change value to a char pointer */
+ void Set(char* value);
+ /** Change value to a const char pointer */
+ void Set(const char* val);
+ /** Change value to an int */
+ void Set(int value);
+ /** Get value as an int */
+ int GetInteger();
+ /** Get value as a string */
+ char* GetString();
+ /** Get value as a bool */
+ bool GetBool();
+};
+
+/** The base class of the container 'ValueContainer'
+ * used internally by the core to hold core values.
+ */
+class ValueContainerBase
+{
+ public:
+ /** Constructor */
+ ValueContainerBase() { }
+ /** Destructor */
+ virtual ~ValueContainerBase() { }
+};
+
+/** ValueContainer is used to contain pointers to different
+ * core values such as the server name, maximum number of
+ * clients etc.
+ * It is specialized to hold a data type, then pointed at
+ * a value in the ServerConfig class. When the value has been
+ * read and validated, the Set method is called to write the
+ * value safely in a type-safe manner.
+ */
+template<typename T> class ValueContainer : public ValueContainerBase
+{
+ /** Contained item */
+ T val;
+ public:
+
+ /** Initialize with nothing */
+ ValueContainer()
+ {
+ val = NULL;
+ }
+
+ /** Initialize with a value of type T */
+ ValueContainer(T Val)
+ {
+ val = Val;
+ }
+
+ /** Change value to type T of size s */
+ void Set(T newval, size_t s)
+ {
+ memcpy(val, newval, s);
+ }
+};
+
+/** A specialization of ValueContainer to hold a pointer to a bool
+ */
+typedef ValueContainer<bool*> ValueContainerBool;
+
+/** A specialization of ValueContainer to hold a pointer to
+ * an unsigned int
+ */
+typedef ValueContainer<unsigned int*> ValueContainerUInt;
+
+/** A specialization of ValueContainer to hold a pointer to
+ * a char array.
+ */
+typedef ValueContainer<char*> ValueContainerChar;
+
+/** A specialization of ValueContainer to hold a pointer to
+ * an int
+ */
+typedef ValueContainer<int*> ValueContainerInt;
+
+/** A set of ValueItems used by multi-value validator functions
+ */
+typedef std::deque<ValueItem> ValueList;
+
+/** A callback for validating a single value
+ */
+typedef bool (*Validator)(ServerConfig* conf, const char*, const char*, ValueItem&);
+/** A callback for validating multiple value entries
+ */
+typedef bool (*MultiValidator)(ServerConfig* conf, const char*, char**, ValueList&, int*);
+/** A callback indicating the end of a group of entries
+ */
+typedef bool (*MultiNotify)(ServerConfig* conf, const char*);
+
+/** Holds a core configuration item and its callbacks
+ */
+struct InitialConfig
+{
+ /** Tag name */
+ char* tag;
+ /** Value name */
+ char* value;
+ /** Default, if not defined */
+ char* default_value;
+ /** Value containers */
+ ValueContainerBase* val;
+ /** Data types */
+ ConfigDataType datatype;
+ /** Validation function */
+ Validator validation_function;
+};
+
+/** Holds a core configuration item and its callbacks
+ * where there may be more than one item
+ */
+struct MultiConfig
+{
+ /** Tag name */
+ const char* tag;
+ /** One or more items within tag */
+ char* items[13];
+ /** One or more defaults for items within tags */
+ char* items_default[13];
+ /** One or more data types */
+ int datatype[13];
+ /** Initialization function */
+ MultiNotify init_function;
+ /** Validation function */
+ MultiValidator validation_function;
+ /** Completion function */
+ MultiNotify finish_function;
+};
+
+/** A set of oper types
+ */
+typedef std::map<irc::string,char*> opertype_t;
+
+/** A Set of oper classes
+ */
+typedef std::map<irc::string,char*> operclass_t;
+
+
+/** This class holds the bulk of the runtime configuration for the ircd.
+ * It allows for reading new config values, accessing configuration files,
+ * and storage of the configuration data needed to run the ircd, such as
+ * the servername, connect classes, /ADMIN data, MOTDs and filenames etc.
+ */
+class CoreExport ServerConfig : public Extensible
+{
+ private:
+ /** Creator/owner pointer
+ */
+ InspIRCd* ServerInstance;
+
+ /** This variable holds the names of all
+ * files included from the main one. This
+ * is used to make sure that no files are
+ * recursively included.
+ */
+ std::vector<std::string> include_stack;
+
+ /** This private method processes one line of
+ * configutation, appending errors to errorstream
+ * and setting error if an error has occured.
+ */
+ bool ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream);
+
+ /** Process an include directive
+ */
+ bool DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream);
+
+ /** Check that there is only one of each configuration item
+ */
+ bool CheckOnce(char* tag, bool bail, userrec* user);
+
+ public:
+
+ InspIRCd* GetInstance();
+
+ /** This holds all the information in the config file,
+ * it's indexed by tag name to a vector of key/values.
+ */
+ ConfigDataHash config_data;
+
+ /** 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.
+ */
+ char ServerName[MAXBUF];
+
+ /** Notice to give to users when they are Xlined
+ */
+ char MoronBanner[MAXBUF];
+
+ /* Holds the network name the local server
+ * belongs to. This is an arbitary field defined
+ * by the administrator.
+ */
+ char Network[MAXBUF];
+
+ /** Holds the description of the local server
+ * as defined by the administrator.
+ */
+ char ServerDesc[MAXBUF];
+
+ /** Holds the admin's name, for output in
+ * the /ADMIN command.
+ */
+ char AdminName[MAXBUF];
+
+ /** Holds the email address of the admin,
+ * for output in the /ADMIN command.
+ */
+ char AdminEmail[MAXBUF];
+
+ /** Holds the admin's nickname, for output
+ * in the /ADMIN command
+ */
+ char AdminNick[MAXBUF];
+
+ /** The admin-configured /DIE password
+ */
+ char diepass[MAXBUF];
+
+ /** The admin-configured /RESTART password
+ */
+ char restartpass[MAXBUF];
+
+ /** The pathname and filename of the message of the
+ * day file, as defined by the administrator.
+ */
+ char motd[MAXBUF];
+
+ /** The pathname and filename of the rules file,
+ * as defined by the administrator.
+ */
+ char rules[MAXBUF];
+
+ /** The quit prefix in use, or an empty string
+ */
+ char PrefixQuit[MAXBUF];
+
+ /** The quit suffix in use, or an empty string
+ */
+ char SuffixQuit[MAXBUF];
+
+ /** The fixed quit message in use, or an empty string
+ */
+ char FixedQuit[MAXBUF];
+
+ /** The last string found within a <die> tag, or
+ * an empty string.
+ */
+ char DieValue[MAXBUF];
+
+ /** The DNS server to use for DNS queries
+ */
+ char DNSServer[MAXBUF];
+
+ /** This variable contains a space-seperated list
+ * of commands which are disabled by the
+ * administrator of the server for non-opers.
+ */
+ char DisabledCommands[MAXBUF];
+
+ /** The full path to the modules directory.
+ * This is either set at compile time, or
+ * overridden in the configuration file via
+ * the <options> tag.
+ */
+ char ModPath[1024];
+
+ /** The full pathname to the executable, as
+ * given in argv[0] when the program starts.
+ */
+ char MyExecutable[1024];
+
+ /** The file handle of the logfile. If this
+ * value is NULL, the log file is not open,
+ * probably due to a permissions error on
+ * startup (this should not happen in normal
+ * operation!).
+ */
+ FILE *log_file;
+
+ /** If this value is true, the owner of the
+ * server specified -nofork on the command
+ * line, causing the daemon to stay in the
+ * foreground.
+ */
+ bool nofork;
+
+ /** If this value if true then all log
+ * messages will be output, regardless of
+ * the level given in the config file.
+ * This is set with the -debug commandline
+ * option.
+ */
+ bool forcedebug;
+
+ /** If this is true then log output will be
+ * written to the logfile. This is the default.
+ * If you put -nolog on the commandline then
+ * the logfile will not be written.
+ * This is meant to be used in conjunction with
+ * -debug for debugging without filling up the
+ * hard disk.
+ */
+ bool writelog;
+
+ /** If this value is true, halfops have been
+ * enabled in the configuration file.
+ */
+ bool AllowHalfop;
+
+ /** If this is set to true, then mode lists (e.g
+ * MODE #chan b) are hidden from unprivileged
+ * users.
+ */
+ bool HideModeLists[256];
+
+ /** If this is set to true, then channel operators
+ * are exempt from this channel mode. Used for +Sc etc.
+ */
+ bool ExemptChanOps[256];
+
+ /** The number of seconds the DNS subsystem
+ * will wait before timing out any request.
+ */
+ int dns_timeout;
+
+ /** The size of the read() buffer in the user
+ * handling code, used to read data into a user's
+ * recvQ.
+ */
+ int NetBufferSize;
+
+ /** The value to be used for listen() backlogs
+ * as default.
+ */
+ int MaxConn;
+
+ /** The soft limit value assigned to the irc server.
+ * The IRC server will not allow more than this
+ * number of local users.
+ */
+ unsigned int SoftLimit;
+
+ /** Maximum number of targets for a multi target command
+ * such as PRIVMSG or KICK
+ */
+ unsigned int MaxTargets;
+
+ /** The maximum number of /WHO results allowed
+ * in any single /WHO command.
+ */
+ int MaxWhoResults;
+
+ /** True if the DEBUG loglevel is selected.
+ */
+ int debugging;
+
+ /** The loglevel in use by the IRC server
+ */
+ int LogLevel;
+
+ /** How many seconds to wait before exiting
+ * the program when /DIE is correctly issued.
+ */
+ int DieDelay;
+
+ /** True if we're going to hide netsplits as *.net *.split for non-opers
+ */
+ bool HideSplits;
+
+ /** True if we're going to hide ban reasons for non-opers (e.g. G-Lines,
+ * K-Lines, Z-Lines)
+ */
+ bool HideBans;
+
+ /** Announce invites to the channel with a server notice
+ */
+ bool AnnounceInvites;
+
+ /** If this is enabled then operators will
+ * see invisible (+i) channels in /whois.
+ */
+ bool OperSpyWhois;
+
+ /** Set to a non-empty string to obfuscate the server name of users in WHOIS
+ */
+ char HideWhoisServer[MAXBUF];
+
+ /** Set to a non empty string to obfuscate nicknames prepended to a KILL.
+ */
+ char HideKillsServer[MAXBUF];
+
+ /** The MOTD file, cached in a file_cache type.
+ */
+ file_cache MOTD;
+
+ /** The RULES file, cached in a file_cache type.
+ */
+ file_cache RULES;
+
+ /** The full pathname and filename of the PID
+ * file as defined in the configuration.
+ */
+ char PID[1024];
+
+ /** The connect classes in use by the IRC server.
+ */
+ ClassVector Classes;
+
+ /** A list of module names (names only, no paths)
+ * which are currently loaded by the server.
+ */
+ std::vector<std::string> module_names;
+
+ /** A list of the classes for listening client ports
+ */
+ std::vector<ListenSocket*> ports;
+
+ /** Boolean sets of which modules implement which functions
+ */
+ char implement_lists[255][255];
+
+ /** Global implementation list
+ */
+ char global_implementation[255];
+
+ /** A list of ports claimed by IO Modules
+ */
+ std::map<int,Module*> IOHookModule;
+
+ std::map<InspSocket*, Module*> SocketIOHookModule;
+
+ /** 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.
+ */
+ char UserStats[MAXBUF];
+
+ /** The path and filename of the ircd.log file
+ */
+ std::string logpath;
+
+ /** Default channel modes
+ */
+ char DefaultModes[MAXBUF];
+
+ /** Custom version string, which if defined can replace the system info in VERSION.
+ */
+ char CustomVersion[MAXBUF];
+
+ /** 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;
+
+ /** Directory where the inspircd binary resides
+ */
+ std::string MyDir;
+
+ /** 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, 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;
+
+ /** All oper type definitions from the config file
+ */
+ opertype_t opertypes;
+
+ /** All oper class definitions from the config file
+ */
+ operclass_t operclass;
+
+ /** Saved argv from startup
+ */
+ char** argv;
+
+ /** Saved argc from startup
+ */
+ int argc;
+
+ /** Max channels per user
+ */
+ unsigned int MaxChans;
+
+ /** Oper max channels per user
+ */
+ unsigned int OperMaxChans;
+
+ /** Construct a new ServerConfig
+ */
+ ServerConfig(InspIRCd* Instance);
+
+ /** Clears the include stack in preperation for a Read() call.
+ */
+ void ClearStack();
+
+ /** Update the 005 vector
+ */
+ void Update005();
+
+ /** Send the 005 numerics (ISUPPORT) to a user
+ */
+ void Send005(userrec* user);
+
+ /** Read the entire configuration into memory
+ * and initialize this class. All other methods
+ * should be used only by the core.
+ */
+ void Read(bool bail, userrec* user);
+
+ /** Read a file into a file_cache object
+ */
+ bool ReadFile(file_cache &F, const char* fname);
+
+ /** Report a configuration error given in errormessage.
+ * @param bail If this is set to true, the error is sent to the console, and the program exits
+ * @param user If this is set to a non-null value, and bail is false, the errors are spooled to
+ * this user as SNOTICEs.
+ * If the parameter is NULL, the messages are spooled to all users via WriteOpers as SNOTICEs.
+ */
+ void ReportConfigError(const std::string &errormessage, bool bail, userrec* user);
+
+ /** Load 'filename' into 'target', with the new config parser everything is parsed into
+ * tag/key/value at load-time rather than at read-value time.
+ */
+ bool LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream);
+
+ /** Load 'filename' into 'target', with the new config parser everything is parsed into
+ * tag/key/value at load-time rather than at read-value time.
+ */
+ bool LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream);
+
+ /* Both these return true if the value existed or false otherwise */
+
+ /** Writes 'length' chars into 'result' as a string
+ */
+ bool ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds = false);
+ /** Writes 'length' chars into 'result' as a string
+ */
+ bool ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds = false);
+
+ /** Writes 'length' chars into 'result' as a string
+ */
+ bool ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds = false);
+ /** Writes 'length' chars into 'result' as a string
+ */
+ bool ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds = false);
+
+ /** Tries to convert the value to an integer and write it to 'result'
+ */
+ bool ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result);
+ /** Tries to convert the value to an integer and write it to 'result'
+ */
+ bool ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result);
+ /** Tries to convert the value to an integer and write it to 'result'
+ */
+ bool ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result);
+ /** Tries to convert the value to an integer and write it to 'result'
+ */
+ bool ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result);
+
+ /** Returns true if the value exists and has a true value, false otherwise
+ */
+ bool ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index);
+ /** Returns true if the value exists and has a true value, false otherwise
+ */
+ bool ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index);
+ /** Returns true if the value exists and has a true value, false otherwise
+ */
+ bool ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index);
+ /** Returns true if the value exists and has a true value, false otherwise
+ */
+ bool ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index);
+
+ /** Returns the number of occurences of tag in the config file
+ */
+ int ConfValueEnum(ConfigDataHash &target, const char* tag);
+ /** Returns the number of occurences of tag in the config file
+ */
+ int ConfValueEnum(ConfigDataHash &target, const std::string &tag);
+
+ /** Returns the numbers of vars inside the index'th 'tag in the config file
+ */
+ int ConfVarEnum(ConfigDataHash &target, const char* tag, int index);
+ /** Returns the numbers of vars inside the index'th 'tag in the config file
+ */
+ int ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index);
+
+ /** Get a pointer to the module which has hooked the given port.
+ * @parameter port Port number
+ * @return Returns a pointer to the hooking module, or NULL
+ */
+ Module* GetIOHook(int port);
+
+ /** Hook a module to a client port, so that it can receive notifications
+ * of low-level port activity.
+ * @param port The port number
+ * @param Module the module to hook to the port
+ * @return True if the hook was successful.
+ */
+ bool AddIOHook(int port, Module* iomod);
+
+ /** Delete a module hook from a client port.
+ * @param port The port to detatch from
+ * @return True if successful
+ */
+ bool DelIOHook(int port);
+
+ /** Get a pointer to the module which has hooked the given InspSocket class.
+ * @parameter port Port number
+ * @return Returns a pointer to the hooking module, or NULL
+ */
+ Module* GetIOHook(InspSocket* is);
+
+ /** Hook a module to an InspSocket class, so that it can receive notifications
+ * of low-level socket activity.
+ * @param iomod The module to hook to the socket
+ * @param is The InspSocket to attach to
+ * @return True if the hook was successful.
+ */
+ bool AddIOHook(Module* iomod, InspSocket* is);
+
+ /** Delete a module hook from an InspSocket.
+ * @param is The InspSocket to detatch from.
+ * @return True if the unhook was successful
+ */
+ bool DelIOHook(InspSocket* is);
+
+ /** Returns the fully qualified path to the inspircd directory
+ * @return The full program directory
+ */
+ std::string GetFullProgDir();
+
+ /** Returns true if a directory is valid (within the modules directory).
+ * @param dirandfile The directory and filename to check
+ * @return True if the directory is valid
+ */
+ static bool DirValid(const char* dirandfile);
+
+ /** Clean a filename, stripping the directories (and drives) from string.
+ * @param name Directory to tidy
+ * @return The cleaned filename
+ */
+ static char* CleanFilename(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);
+
+};
+
+/** Initialize the disabled commands list
+ */
+CoreExport bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance);
+
+/** Initialize the oper types
+ */
+bool InitTypes(ServerConfig* conf, const char* tag);
+
+/** Initialize the oper classes
+ */
+bool InitClasses(ServerConfig* conf, const char* tag);
+
+/** Initialize an oper type
+ */
+bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types);
+
+/** Initialize an oper class
+ */
+bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types);
+
+/** Finish initializing the oper types and classes
+ */
+bool DoneClassesAndTypes(ServerConfig* conf, const char* tag);
+
+#endif
+
diff --git a/include/connection.h b/include/connection.h
index 1641b976d..65d342447 100644
--- a/include/connection.h
+++ b/include/connection.h
@@ -1 +1,79 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CONNECTION_H__ #define __CONNECTION_H__ #include <time.h> #include "inspircd_config.h" #include "base.h" #include "socketengine.h" /** connection is the base class of userrec, and holds basic user properties. * This can be extended for holding other user-like objects in the future. */ class CoreExport connection : public EventHandler { public: /** Hostname of connection. * This should be valid as per RFC1035. */ char host[65]; /** Stats counter for bytes inbound */ int bytes_in; /** Stats counter for bytes outbound */ int bytes_out; /** Stats counter for commands inbound */ int cmds_in; /** Stats counter for commands outbound */ int cmds_out; /** True if user has authenticated, false if otherwise */ bool haspassed; /** Used by userrec to indicate the registration status of the connection * It is a bitfield of the REG_NICK, REG_USER and REG_ALL bits to indicate * the connection state. */ char registered; /** Time the connection was last pinged */ time_t lastping; /** Time the connection was created, set in the constructor. This * may be different from the time the user's classbase object was * created. */ time_t signon; /** Time that the connection last sent a message, used to calculate idle time */ time_t idle_lastmsg; /** Used by PING checking code */ time_t nping; }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CONNECTION_H__
+#define __CONNECTION_H__
+
+#include <time.h>
+#include "inspircd_config.h"
+#include "base.h"
+#include "socketengine.h"
+
+/** connection is the base class of userrec, and holds basic user properties.
+ * This can be extended for holding other user-like objects in the future.
+ */
+class CoreExport connection : public EventHandler
+{
+ public:
+ /** Hostname of connection.
+ * This should be valid as per RFC1035.
+ */
+ char host[65];
+
+ /** Stats counter for bytes inbound
+ */
+ int bytes_in;
+
+ /** Stats counter for bytes outbound
+ */
+ int bytes_out;
+
+ /** Stats counter for commands inbound
+ */
+ int cmds_in;
+
+ /** Stats counter for commands outbound
+ */
+ int cmds_out;
+
+ /** True if user has authenticated, false if otherwise
+ */
+ bool haspassed;
+
+ /** Used by userrec to indicate the registration status of the connection
+ * It is a bitfield of the REG_NICK, REG_USER and REG_ALL bits to indicate
+ * the connection state.
+ */
+ char registered;
+
+ /** Time the connection was last pinged
+ */
+ time_t lastping;
+
+ /** Time the connection was created, set in the constructor. This
+ * may be different from the time the user's classbase object was
+ * created.
+ */
+ time_t signon;
+
+ /** Time that the connection last sent a message, used to calculate idle time
+ */
+ time_t idle_lastmsg;
+
+ /** Used by PING checking code
+ */
+ time_t nping;
+};
+
+
+#endif
diff --git a/include/ctables.h b/include/ctables.h
index d8d76100e..e8a3337fd 100644
--- a/include/ctables.h
+++ b/include/ctables.h
@@ -1 +1,172 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CTABLES_H__ #define __CTABLES_H__ #include "inspircd_config.h" #include "hash_map.h" #include "base.h" /* Forward declarations - required */ class userrec; class InspIRCd; /** Used to indicate command success codes */ enum CmdResult { CMD_FAILURE = 0, /* Command exists, but failed */ CMD_SUCCESS = 1, /* Command exists, and succeeded */ CMD_INVALID = 2, /* Command doesnt exist at all! */ CMD_USER_DELETED = 3 /* User was deleted - DEPRECIATED */ }; /** For commands which should not be replicated to other * servers, we usually return CMD_FAILURE. this isnt readable, * so we define this alias for CMD_FAILURE called * CMD_LOCALONLY, which of course does the same thing but is * much more readable. */ #define CMD_LOCALONLY CMD_FAILURE /** A structure that defines a command. Every command available * in InspIRCd must be defined as derived from command_t. */ class CoreExport command_t : public Extensible { protected: /** Owner/Creator object */ InspIRCd* ServerInstance; public: /** Command name */ std::string command; /** User flags needed to execute the command or 0 */ char flags_needed; /** Minimum number of parameters command takes */ int min_params; /** used by /stats m */ long use_count; /** used by /stats m */ float total_bytes; /** used for resource tracking between modules */ std::string source; /** True if the command is disabled to non-opers */ bool disabled; /** True if the command can be issued before registering */ bool works_before_reg; /** Syntax string for the command, displayed if non-empty string. * This takes place of the text in the 'not enough parameters' numeric. */ std::string syntax; /** Create a new command. * @param Instance Pointer to creator class * @param cmd Command name. This must be UPPER CASE. * @param flags User modes required to execute the command. * For oper only commands, set this to 'o', otherwise use 0. * @param minpara Minimum parameters required for the command. * @param before_reg If this is set to true, the command will * be allowed before the user is 'registered' (has sent USER, * NICK, optionally PASS, and been resolved). */ command_t(InspIRCd* Instance, const std::string &cmd, char flags, int minpara, int before_reg = false) : ServerInstance(Instance), command(cmd), flags_needed(flags), min_params(minpara), disabled(false), works_before_reg(before_reg) { use_count = 0; total_bytes = 0; source = "<core>"; syntax = ""; } /** Handle the command from a user. * @param parameters The parameters for the command. * @param pcnt The number of parameters available in 'parameters' * @param user The user who issued the command. * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure. * If the command succeeds but should remain local to this server, * return CMD_LOCALONLY. */ virtual CmdResult Handle(const char** parameters, int pcnt, userrec* user) = 0; /** Handle an internal request from another command, the core, or a module * @param Command ID * @param Zero or more parameters, whos form is specified by the command ID. * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure. * If the command succeeds but should remain local to this server, * return CMD_LOCALONLY. */ virtual CmdResult HandleInternal(const unsigned int id, const std::deque<classbase*> &params) { return CMD_INVALID; } /** Handle the command from a server. * Not currently used in this version of InspIRCd. * @param parameters The parameters given * @param pcnt The number of parameters available * @param servername The server name which issued the command * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure. * If the command succeeds but should remain local to this server, * return CMD_LOCALONLY. */ virtual CmdResult HandleServer(const char** parameters, int pcnt, const std::string &servername) { return CMD_INVALID; } /** Disable or enable this command. * @param setting True to disable the command. */ void Disable(bool setting) { disabled = setting; } /** Obtain this command's disable state. * @return true if the command is currently disabled * (disabled commands can be used only by operators) */ bool IsDisabled() { return disabled; } /** @return true if the command works before registration. */ bool WorksBeforeReg() { return works_before_reg; } /** Standard constructor gubbins */ virtual ~command_t() {} }; /** A hash of commands used by the core */ typedef nspace::hash_map<std::string,command_t*> command_table; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CTABLES_H__
+#define __CTABLES_H__
+
+
+#include "inspircd_config.h"
+#include "hash_map.h"
+#include "base.h"
+
+/* Forward declarations - required */
+class userrec;
+class InspIRCd;
+
+/** Used to indicate command success codes
+ */
+enum CmdResult
+{
+ CMD_FAILURE = 0, /* Command exists, but failed */
+ CMD_SUCCESS = 1, /* Command exists, and succeeded */
+ CMD_INVALID = 2, /* Command doesnt exist at all! */
+ CMD_USER_DELETED = 3 /* User was deleted - DEPRECIATED */
+};
+
+/** For commands which should not be replicated to other
+ * servers, we usually return CMD_FAILURE. this isnt readable,
+ * so we define this alias for CMD_FAILURE called
+ * CMD_LOCALONLY, which of course does the same thing but is
+ * much more readable.
+ */
+#define CMD_LOCALONLY CMD_FAILURE
+
+
+/** A structure that defines a command. Every command available
+ * in InspIRCd must be defined as derived from command_t.
+ */
+class CoreExport command_t : public Extensible
+{
+ protected:
+ /** Owner/Creator object
+ */
+ InspIRCd* ServerInstance;
+ public:
+ /** Command name
+ */
+ std::string command;
+ /** User flags needed to execute the command or 0
+ */
+ char flags_needed;
+ /** Minimum number of parameters command takes
+ */
+ int min_params;
+ /** used by /stats m
+ */
+ long use_count;
+ /** used by /stats m
+ */
+ float total_bytes;
+ /** used for resource tracking between modules
+ */
+ std::string source;
+ /** True if the command is disabled to non-opers
+ */
+ bool disabled;
+ /** True if the command can be issued before registering
+ */
+ bool works_before_reg;
+ /** Syntax string for the command, displayed if non-empty string.
+ * This takes place of the text in the 'not enough parameters' numeric.
+ */
+ std::string syntax;
+
+ /** Create a new command.
+ * @param Instance Pointer to creator class
+ * @param cmd Command name. This must be UPPER CASE.
+ * @param flags User modes required to execute the command.
+ * For oper only commands, set this to 'o', otherwise use 0.
+ * @param minpara Minimum parameters required for the command.
+ * @param before_reg If this is set to true, the command will
+ * be allowed before the user is 'registered' (has sent USER,
+ * NICK, optionally PASS, and been resolved).
+ */
+ command_t(InspIRCd* Instance, const std::string &cmd, char flags, int minpara, int before_reg = false) : ServerInstance(Instance), command(cmd), flags_needed(flags), min_params(minpara), disabled(false), works_before_reg(before_reg)
+ {
+ use_count = 0;
+ total_bytes = 0;
+ source = "<core>";
+ syntax = "";
+ }
+
+ /** Handle the command from a user.
+ * @param parameters The parameters for the command.
+ * @param pcnt The number of parameters available in 'parameters'
+ * @param user The user who issued the command.
+ * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
+ * If the command succeeds but should remain local to this server,
+ * return CMD_LOCALONLY.
+ */
+ virtual CmdResult Handle(const char** parameters, int pcnt, userrec* user) = 0;
+
+ /** Handle an internal request from another command, the core, or a module
+ * @param Command ID
+ * @param Zero or more parameters, whos form is specified by the command ID.
+ * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
+ * If the command succeeds but should remain local to this server,
+ * return CMD_LOCALONLY.
+ */
+ virtual CmdResult HandleInternal(const unsigned int id, const std::deque<classbase*> &params)
+ {
+ return CMD_INVALID;
+ }
+
+ /** Handle the command from a server.
+ * Not currently used in this version of InspIRCd.
+ * @param parameters The parameters given
+ * @param pcnt The number of parameters available
+ * @param servername The server name which issued the command
+ * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
+ * If the command succeeds but should remain local to this server,
+ * return CMD_LOCALONLY.
+ */
+ virtual CmdResult HandleServer(const char** parameters, int pcnt, const std::string &servername)
+ {
+ return CMD_INVALID;
+ }
+
+ /** Disable or enable this command.
+ * @param setting True to disable the command.
+ */
+ void Disable(bool setting)
+ {
+ disabled = setting;
+ }
+
+ /** Obtain this command's disable state.
+ * @return true if the command is currently disabled
+ * (disabled commands can be used only by operators)
+ */
+ bool IsDisabled()
+ {
+ return disabled;
+ }
+
+ /** @return true if the command works before registration.
+ */
+ bool WorksBeforeReg()
+ {
+ return works_before_reg;
+ }
+
+ /** Standard constructor gubbins
+ */
+ virtual ~command_t() {}
+};
+
+/** A hash of commands used by the core
+ */
+typedef nspace::hash_map<std::string,command_t*> command_table;
+
+#endif
+
diff --git a/include/cull_list.h b/include/cull_list.h
index b2742e390..129f0d43d 100644
--- a/include/cull_list.h
+++ b/include/cull_list.h
@@ -1 +1,162 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __CULLLIST_H__ #define __CULLLIST_H__ // include the common header files #include <string> #include <deque> #include <vector> #include "users.h" #include "channels.h" class InspIRCd; /** The CullItem class holds a user and their quitmessage, * and is used internally by the CullList class to compile * a list of users which are to be culled when a long * operation (such as a netsplit) has completed. */ class CoreExport CullItem : public classbase { private: /** Holds a pointer to the user, * must be valid and can be a local or remote user. */ userrec* user; /** Holds the quit reason to use for this user. */ std::string reason; /** Holds the quit reason opers see, if different from users */ std::string oper_reason; /** Silent items dont generate an snotice. */ bool silent; public: /** Constrcutor. * Initializes the CullItem with a user pointer * and their quit reason * @param u The user to add * @param r The quit reason of the added user * @param ro The quit reason to show to opers only */ CullItem(userrec* u, std::string &r, const char* ro = ""); /** Constrcutor. * Initializes the CullItem with a user pointer * and their quit reason * @param u The user to add * @param r The quit reason of the added user * @param ro The quit reason to show to opers only */ CullItem(userrec* u, const char* r, const char* ro = ""); /** Make the quit silent a module is dealing with * displaying this users quit, so we shouldn't * send anything out. */ void MakeSilent(); /** Returns true if the quit for this user has been set * to silent. */ bool IsSilent(); /** Destructor */ ~CullItem(); /** Returns a pointer to the user */ userrec* GetUser(); /** Returns the user's quit reason */ std::string& GetReason(); /** Returns oper reason */ std::string& GetOperReason(); }; /** The CullList class can be used by modules, and is used * by the core, to compile large lists of users in preperation * to quitting them all at once. This is faster than quitting * them within the loop, as the loops become tighter with * little or no comparisons within them. The CullList class * operates by allowing the programmer to push users onto * the list, each with a seperate quit reason, and then, once * the list is complete, call a method to flush the list, * quitting all the users upon it. A CullList may hold local * or remote users, but it may only hold each user once. If * you attempt to add the same user twice, then the second * attempt will be ignored. */ class CoreExport CullList : public classbase { private: /** Creator of this CullList */ InspIRCd* ServerInstance; /** Holds a list of users already added for quick lookup */ std::map<userrec*, userrec*> exempt; /** Holds a list of users being quit. * See the information for CullItem for * more information. */ std::vector<CullItem> list; public: /** Constructor. * Clears the CullList::list and CullList::exempt * items. * @param Instance Creator of this CullList object */ CullList(InspIRCd* Instance); /** Adds a user to the cull list for later * removal via QUIT. * @param user The user to add * @param reason The quit reason of the user being added * @param o_reason The quit reason to show only to opers */ void AddItem(userrec* user, std::string &reason, const char* o_reason = ""); /** Adds a user to the cull list for later * removal via QUIT. * @param user The user to add * @param reason The quit reason of the user being added * @param o_reason The quit reason to show only to opers */ void AddItem(userrec* user, const char* reason, const char* o_reason = ""); /* Turn an item into a silent item (don't send out QUIT for this user) */ void MakeSilent(userrec* user); /** Applies the cull list, quitting all the users * on the list with their quit reasons all at once. * This is a very fast operation compared to * iterating the user list and comparing each one, * especially if there are multiple comparisons * to be done, or recursion. * @returns The number of users removed from IRC. */ int Apply(); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __CULLLIST_H__
+#define __CULLLIST_H__
+
+// include the common header files
+
+#include <string>
+#include <deque>
+#include <vector>
+#include "users.h"
+#include "channels.h"
+
+class InspIRCd;
+
+/** The CullItem class holds a user and their quitmessage,
+ * and is used internally by the CullList class to compile
+ * a list of users which are to be culled when a long
+ * operation (such as a netsplit) has completed.
+ */
+class CoreExport CullItem : public classbase
+{
+ private:
+ /** Holds a pointer to the user,
+ * must be valid and can be a local or remote user.
+ */
+ userrec* user;
+ /** Holds the quit reason to use for this user.
+ */
+ std::string reason;
+ /** Holds the quit reason opers see, if different from users
+ */
+ std::string oper_reason;
+ /** Silent items dont generate an snotice.
+ */
+ bool silent;
+ public:
+ /** Constrcutor.
+ * Initializes the CullItem with a user pointer
+ * and their quit reason
+ * @param u The user to add
+ * @param r The quit reason of the added user
+ * @param ro The quit reason to show to opers only
+ */
+ CullItem(userrec* u, std::string &r, const char* ro = "");
+ /** Constrcutor.
+ * Initializes the CullItem with a user pointer
+ * and their quit reason
+ * @param u The user to add
+ * @param r The quit reason of the added user
+ * @param ro The quit reason to show to opers only
+ */
+ CullItem(userrec* u, const char* r, const char* ro = "");
+
+ /** Make the quit silent a module is dealing with
+ * displaying this users quit, so we shouldn't
+ * send anything out.
+ */
+ void MakeSilent();
+
+ /** Returns true if the quit for this user has been set
+ * to silent.
+ */
+ bool IsSilent();
+
+ /** Destructor
+ */
+ ~CullItem();
+
+ /** Returns a pointer to the user
+ */
+ userrec* GetUser();
+ /** Returns the user's quit reason
+ */
+ std::string& GetReason();
+ /** Returns oper reason
+ */
+ std::string& GetOperReason();
+};
+
+/** The CullList class can be used by modules, and is used
+ * by the core, to compile large lists of users in preperation
+ * to quitting them all at once. This is faster than quitting
+ * them within the loop, as the loops become tighter with
+ * little or no comparisons within them. The CullList class
+ * operates by allowing the programmer to push users onto
+ * the list, each with a seperate quit reason, and then, once
+ * the list is complete, call a method to flush the list,
+ * quitting all the users upon it. A CullList may hold local
+ * or remote users, but it may only hold each user once. If
+ * you attempt to add the same user twice, then the second
+ * attempt will be ignored.
+ */
+class CoreExport CullList : public classbase
+{
+ private:
+ /** Creator of this CullList
+ */
+ InspIRCd* ServerInstance;
+
+ /** Holds a list of users already added for quick lookup
+ */
+ std::map<userrec*, userrec*> exempt;
+
+ /** Holds a list of users being quit.
+ * See the information for CullItem for
+ * more information.
+ */
+ std::vector<CullItem> list;
+
+ public:
+ /** Constructor.
+ * Clears the CullList::list and CullList::exempt
+ * items.
+ * @param Instance Creator of this CullList object
+ */
+ CullList(InspIRCd* Instance);
+
+ /** Adds a user to the cull list for later
+ * removal via QUIT.
+ * @param user The user to add
+ * @param reason The quit reason of the user being added
+ * @param o_reason The quit reason to show only to opers
+ */
+ void AddItem(userrec* user, std::string &reason, const char* o_reason = "");
+
+ /** Adds a user to the cull list for later
+ * removal via QUIT.
+ * @param user The user to add
+ * @param reason The quit reason of the user being added
+ * @param o_reason The quit reason to show only to opers
+ */
+ void AddItem(userrec* user, const char* reason, const char* o_reason = "");
+
+ /* Turn an item into a silent item (don't send out QUIT for this user)
+ */
+ void MakeSilent(userrec* user);
+
+ /** Applies the cull list, quitting all the users
+ * on the list with their quit reasons all at once.
+ * This is a very fast operation compared to
+ * iterating the user list and comparing each one,
+ * especially if there are multiple comparisons
+ * to be done, or recursion.
+ * @returns The number of users removed from IRC.
+ */
+ int Apply();
+};
+
+#endif
+
diff --git a/include/dns.h b/include/dns.h
index 279c2bc61..b00c57201 100644
--- a/include/dns.h
+++ b/include/dns.h
@@ -1 +1,520 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* 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 <string> #include "inspircd_config.h" #include "base.h" #include "socketengine.h" #include "socket.h" #include "hash_map.h" #include "hashcomp.h" using namespace std; using irc::sockets::insp_aton; using irc::sockets::insp_ntoa; using irc::sockets::insp_sockaddr; using irc::sockets::insp_inaddr; class InspIRCd; class Module; /** * Result status, used internally */ class CoreExport DNSResult : public classbase { 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; /** 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 */ DNSResult(int i, const std::string &res, unsigned long timetolive, const std::string &orig) : id(i), result(res), ttl(timetolive), original(orig) { } }; /** * 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 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 ttl The time-to-live value of the query result */ CachedQuery(const std::string &res, unsigned int ttl) : data(res) { expires = time(NULL) + ttl; } /** Returns the number of seconds remaining before this * cache item has expired and should be removed. */ int CalcTTLRemaining() { int n = (int)expires - (int)time(NULL); return (n < 0 ? 0 : n); } }; /** DNS cache information. Holds IPs mapped to hostnames, and hostnames mapped to IPs. */ #ifndef WIN32 typedef nspace::hash_map<irc::string, CachedQuery, nspace::hash<irc::string> > dnscache; #else typedef nspace::hash_map<irc::string, CachedQuery, nspace::hash_compare<irc::string> > dnscache; #endif /** * Error types that class Resolver can emit to its error method. */ enum ResolverError { RESOLVER_NOERROR = 0, RESOLVER_NSDOWN = 1, RESOLVER_NXDOMAIN = 2, RESOLVER_NOTREADY = 3, RESOLVER_BADIP = 4, RESOLVER_TIMEOUT = 5, RESLOVER_FORCEUNLOAD = 6 }; /** * A DNS request */ class DNSRequest; /** * A DNS packet header */ class DNSHeader; /** * A DNS Resource Record (rr) */ struct ResourceRecord; /** * Query and resource record types */ enum QueryType { DNS_QUERY_NONE = 0, /* Uninitialized Query */ DNS_QUERY_A = 1, /* 'A' record: an ipv4 address */ DNS_QUERY_CNAME = 5, /* 'CNAME' record: An alias */ DNS_QUERY_PTR = 12, /* 'PTR' record: a hostname */ DNS_QUERY_AAAA = 28, /* 'AAAA' record: an ipv6 address */ DNS_QUERY_PTR4 = 0xFFFD, /* Force 'PTR' to use IPV4 scemantics */ DNS_QUERY_PTR6 = 0xFFFE /* Force 'PTR' to use IPV6 scemantics */ }; #ifdef IPV6 const QueryType DNS_QUERY_FORWARD = DNS_QUERY_AAAA; #else const QueryType DNS_QUERY_FORWARD = DNS_QUERY_A; #endif const QueryType DNS_QUERY_REVERSE = DNS_QUERY_PTR; /** * 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 { PROTOCOL_IPV4 = 0, /* Forced to use ipv4 */ PROTOCOL_IPV6 = 1 /* Forced to use ipv6 */ }; /** * 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 : public Extensible { protected: /** * Pointer to creator */ InspIRCd* ServerInstance; /** * Pointer to creator module (if any, or NULL) */ Module* 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. If you just want to perform a forward * or reverse lookup, and you don't care wether you get ipv4 or ipv6, then use * the constants DNS_QUERY_FORWARD and DNS_QUERY_REVERSE, which automatically * select from 'A' record or 'AAAA' record lookups. However, if you want to resolve * a specific record type, 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. * If you attempt to resolve a 'PTR' record using DNS_QUERY_PTR, and InspIRCd is * built with ipv6 support, the 'PTR' record will be formatted to ipv6 specs, * e.g. x.x.x.x.x....ip6.arpa. otherwise it will be formatted to ipv4 specs, * e.g. x.x.x.x.in-addr.arpa. This translation is automatic. * To get around this automatic behaviour, you must use one of the values * DNS_QUERY_PTR4 or DNS_QUERY_PTR6 to force ipv4 or ipv6 behaviour on the lookup, * irrespective of what protocol InspIRCd has been built for. * @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(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator = NULL); /** * 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: /** * Creator/Owner object */ InspIRCd* ServerInstance; /** * The maximum value of a dns request id, * 16 bits wide, 0xFFFF. */ static const int MAX_REQUEST_ID = 0xFFFF; /** * A counter used to form part of the pseudo-random id */ int currid; /** * We have to turn off a few checks on received packets * when people are using 4in6 (e.g. ::ffff:xxxx). This is * a temporary kludge, Please let me know if you know how * to fix it. */ bool ip6munge; /** * 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: /** * Server address being used currently */ int socketfamily; #ifdef IPV6 in6_addr myserver6; #endif in_addr myserver4; /** * 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 the lookup of a hostname from an ip, * always using the protocol inspircd is built for, * e.g. use ipv6 reverse lookup when built for ipv6, * or ipv4 lookup when built for ipv4. */ int GetName(const insp_inaddr* ip); /** * 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(InspIRCd* Instance); /** * Re-initialize the DNS subsystem. */ void Rehash(); /** * Destructor */ ~DNS(); /** Portable random number generator, generates * its random number from the ircd stats counters, * effective user id, time of day and the rollover * counter (currid) */ unsigned long PRNG(); /** * 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 \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/*
+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 <string>
+#include "inspircd_config.h"
+#include "base.h"
+#include "socketengine.h"
+#include "socket.h"
+#include "hash_map.h"
+#include "hashcomp.h"
+
+using namespace std;
+using irc::sockets::insp_aton;
+using irc::sockets::insp_ntoa;
+using irc::sockets::insp_sockaddr;
+using irc::sockets::insp_inaddr;
+
+class InspIRCd;
+class Module;
+
+/**
+ * Result status, used internally
+ */
+class CoreExport DNSResult : public classbase
+{
+ 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;
+
+ /** 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
+ */
+ DNSResult(int i, const std::string &res, unsigned long timetolive, const std::string &orig) : id(i), result(res), ttl(timetolive), original(orig) { }
+};
+
+/**
+ * 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 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 ttl The time-to-live value of the query result
+ */
+ CachedQuery(const std::string &res, unsigned int ttl) : data(res)
+ {
+ expires = time(NULL) + ttl;
+ }
+
+ /** Returns the number of seconds remaining before this
+ * cache item has expired and should be removed.
+ */
+ int CalcTTLRemaining()
+ {
+ int n = (int)expires - (int)time(NULL);
+ return (n < 0 ? 0 : n);
+ }
+};
+
+/** DNS cache information. Holds IPs mapped to hostnames, and hostnames mapped to IPs.
+ */
+#ifndef WIN32
+typedef nspace::hash_map<irc::string, CachedQuery, nspace::hash<irc::string> > dnscache;
+#else
+typedef nspace::hash_map<irc::string, CachedQuery, nspace::hash_compare<irc::string> > dnscache;
+#endif
+
+/**
+ * Error types that class Resolver can emit to its error method.
+ */
+enum ResolverError
+{
+ RESOLVER_NOERROR = 0,
+ RESOLVER_NSDOWN = 1,
+ RESOLVER_NXDOMAIN = 2,
+ RESOLVER_NOTREADY = 3,
+ RESOLVER_BADIP = 4,
+ RESOLVER_TIMEOUT = 5,
+ RESLOVER_FORCEUNLOAD = 6
+};
+
+/**
+ * A DNS request
+ */
+class DNSRequest;
+
+/**
+ * A DNS packet header
+ */
+class DNSHeader;
+
+/**
+ * A DNS Resource Record (rr)
+ */
+struct ResourceRecord;
+
+/**
+ * Query and resource record types
+ */
+enum QueryType
+{
+ DNS_QUERY_NONE = 0, /* Uninitialized Query */
+ DNS_QUERY_A = 1, /* 'A' record: an ipv4 address */
+ DNS_QUERY_CNAME = 5, /* 'CNAME' record: An alias */
+ DNS_QUERY_PTR = 12, /* 'PTR' record: a hostname */
+ DNS_QUERY_AAAA = 28, /* 'AAAA' record: an ipv6 address */
+
+ DNS_QUERY_PTR4 = 0xFFFD, /* Force 'PTR' to use IPV4 scemantics */
+ DNS_QUERY_PTR6 = 0xFFFE /* Force 'PTR' to use IPV6 scemantics */
+};
+
+#ifdef IPV6
+const QueryType DNS_QUERY_FORWARD = DNS_QUERY_AAAA;
+#else
+const QueryType DNS_QUERY_FORWARD = DNS_QUERY_A;
+#endif
+const QueryType DNS_QUERY_REVERSE = DNS_QUERY_PTR;
+/**
+ * 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
+{
+ PROTOCOL_IPV4 = 0, /* Forced to use ipv4 */
+ PROTOCOL_IPV6 = 1 /* Forced to use ipv6 */
+};
+
+/**
+ * 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 : public Extensible
+{
+ protected:
+ /**
+ * Pointer to creator
+ */
+ InspIRCd* ServerInstance;
+ /**
+ * Pointer to creator module (if any, or NULL)
+ */
+ Module* 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. If you just want to perform a forward
+ * or reverse lookup, and you don't care wether you get ipv4 or ipv6, then use
+ * the constants DNS_QUERY_FORWARD and DNS_QUERY_REVERSE, which automatically
+ * select from 'A' record or 'AAAA' record lookups. However, if you want to resolve
+ * a specific record type, 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.
+ * If you attempt to resolve a 'PTR' record using DNS_QUERY_PTR, and InspIRCd is
+ * built with ipv6 support, the 'PTR' record will be formatted to ipv6 specs,
+ * e.g. x.x.x.x.x....ip6.arpa. otherwise it will be formatted to ipv4 specs,
+ * e.g. x.x.x.x.in-addr.arpa. This translation is automatic.
+ * To get around this automatic behaviour, you must use one of the values
+ * DNS_QUERY_PTR4 or DNS_QUERY_PTR6 to force ipv4 or ipv6 behaviour on the lookup,
+ * irrespective of what protocol InspIRCd has been built for.
+ * @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(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator = NULL);
+
+ /**
+ * 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:
+
+ /**
+ * Creator/Owner object
+ */
+ InspIRCd* ServerInstance;
+
+ /**
+ * The maximum value of a dns request id,
+ * 16 bits wide, 0xFFFF.
+ */
+ static const int MAX_REQUEST_ID = 0xFFFF;
+
+ /**
+ * A counter used to form part of the pseudo-random id
+ */
+ int currid;
+
+ /**
+ * We have to turn off a few checks on received packets
+ * when people are using 4in6 (e.g. ::ffff:xxxx). This is
+ * a temporary kludge, Please let me know if you know how
+ * to fix it.
+ */
+ bool ip6munge;
+
+ /**
+ * 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:
+
+ /**
+ * Server address being used currently
+ */
+ int socketfamily;
+#ifdef IPV6
+ in6_addr myserver6;
+#endif
+ in_addr myserver4;
+
+ /**
+ * 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 the lookup of a hostname from an ip,
+ * always using the protocol inspircd is built for,
+ * e.g. use ipv6 reverse lookup when built for ipv6,
+ * or ipv4 lookup when built for ipv4.
+ */
+ int GetName(const insp_inaddr* ip);
+
+ /**
+ * 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(InspIRCd* Instance);
+
+ /**
+ * Re-initialize the DNS subsystem.
+ */
+ void Rehash();
+
+ /**
+ * Destructor
+ */
+ ~DNS();
+
+ /** Portable random number generator, generates
+ * its random number from the ircd stats counters,
+ * effective user id, time of day and the rollover
+ * counter (currid)
+ */
+ unsigned long PRNG();
+
+ /**
+ * 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
+
diff --git a/include/dynamic.h b/include/dynamic.h
index 8f0f9e132..db46291c4 100644
--- a/include/dynamic.h
+++ b/include/dynamic.h
@@ -1 +1,127 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __DLL_H #define __DLL_H /** This typedef represents the init_module function within each module. * The init_module function is the only exported extern "C" declaration * in any module file. */ typedef void * (initfunc) (void); #include "inspircd_config.h" class InspIRCd; /** The DLLManager class is able to load a module file by filename, * and locate its init_module symbol. */ class CoreExport DLLManager { public: /** This constructor loads the module using dlopen() * @param ServerInstance The creator class of this object * @param fname The filename to load. This should be within * the modules dir. */ DLLManager(InspIRCd* ServerInstance, const char *fname); virtual ~DLLManager(); /** Get a symbol using dynamic linking. * @param v A function pointer, pointing at an init_module function * @param sym_name The symbol name to find, usually "init_module" * @return true if the symbol can be found, also the symbol will be put into v. */ bool GetSymbol(void **v, const char *sym_name); /** Get the last error from dlopen() or dlsym(). * @return The last error string, or NULL if no error has occured. */ char* LastError() { return err; } /** The module handle. * This is OS dependent, on POSIX platforms it is a pointer to a function * pointer (yes, really!) and on windows it is a library handle. */ void *h; protected: /** The last error string, or NULL */ char *err; }; /** This class is a specialized form of DLLManager designed to load InspIRCd modules. * It's job is to call the init_module function and receive a factory pointer. */ class CoreExport DLLFactoryBase : public DLLManager { public: /** Default constructor. * This constructor loads a module file by calling its DLLManager subclass constructor, * then finds the symbol using DLLManager::GetSymbol(), and calls the symbol, * obtaining a valid pointer to the init_module function */ DLLFactoryBase(InspIRCd* Instance, const char *fname, const char *func_name = 0); /** Default destructor. */ virtual ~DLLFactoryBase(); /** A function pointer to the factory function. */ void * (*factory_func)(void); }; /** This is the highest-level class of the DLLFactory system used to load InspIRCd modules. * Its job is to finally call the init_module function and obtain a pointer to a ModuleFactory. * This template is a container for ModuleFactory itself, so that it may 'plug' into ModuleFactory * and provide module loading capabilities transparently. */ template <class T> class CoreExport DLLFactory : public DLLFactoryBase { public: /** Default constructor. * This constructor passes its paramerers down through DLLFactoryBase and then DLLManager * to load the module, then calls the factory function to retrieve a pointer to a ModuleFactory * class. It is then down to the core to call the ModuleFactory::CreateModule() method and * receive a Module* which it can insert into its module lists. */ DLLFactory(InspIRCd* Instance, const char *fname, const char *func_name=0) : DLLFactoryBase(Instance, fname, func_name) { if (factory_func) factory = reinterpret_cast<T*>(factory_func()); else factory = reinterpret_cast<T*>(-1); } /** The destructor deletes the ModuleFactory pointer. */ ~DLLFactory() { if (factory) delete factory; } /** The ModuleFactory pointer. */ T *factory; }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __DLL_H
+#define __DLL_H
+
+/** This typedef represents the init_module function within each module.
+ * The init_module function is the only exported extern "C" declaration
+ * in any module file.
+ */
+typedef void * (initfunc) (void);
+
+#include "inspircd_config.h"
+
+class InspIRCd;
+
+/** The DLLManager class is able to load a module file by filename,
+ * and locate its init_module symbol.
+ */
+class CoreExport DLLManager
+{
+ public:
+ /** This constructor loads the module using dlopen()
+ * @param ServerInstance The creator class of this object
+ * @param fname The filename to load. This should be within
+ * the modules dir.
+ */
+ DLLManager(InspIRCd* ServerInstance, const char *fname);
+ virtual ~DLLManager();
+
+ /** Get a symbol using dynamic linking.
+ * @param v A function pointer, pointing at an init_module function
+ * @param sym_name The symbol name to find, usually "init_module"
+ * @return true if the symbol can be found, also the symbol will be put into v.
+ */
+ bool GetSymbol(void **v, const char *sym_name);
+
+ /** Get the last error from dlopen() or dlsym().
+ * @return The last error string, or NULL if no error has occured.
+ */
+ char* LastError()
+ {
+ return err;
+ }
+
+ /** The module handle.
+ * This is OS dependent, on POSIX platforms it is a pointer to a function
+ * pointer (yes, really!) and on windows it is a library handle.
+ */
+ void *h;
+
+ protected:
+
+ /** The last error string, or NULL
+ */
+ char *err;
+};
+
+/** This class is a specialized form of DLLManager designed to load InspIRCd modules.
+ * It's job is to call the init_module function and receive a factory pointer.
+ */
+class CoreExport DLLFactoryBase : public DLLManager
+{
+ public:
+ /** Default constructor.
+ * This constructor loads a module file by calling its DLLManager subclass constructor,
+ * then finds the symbol using DLLManager::GetSymbol(), and calls the symbol,
+ * obtaining a valid pointer to the init_module function
+ */
+ DLLFactoryBase(InspIRCd* Instance, const char *fname, const char *func_name = 0);
+
+ /** Default destructor.
+ */
+ virtual ~DLLFactoryBase();
+
+ /** A function pointer to the factory function.
+ */
+ void * (*factory_func)(void);
+};
+
+/** This is the highest-level class of the DLLFactory system used to load InspIRCd modules.
+ * Its job is to finally call the init_module function and obtain a pointer to a ModuleFactory.
+ * This template is a container for ModuleFactory itself, so that it may 'plug' into ModuleFactory
+ * and provide module loading capabilities transparently.
+ */
+template <class T> class CoreExport DLLFactory : public DLLFactoryBase
+{
+ public:
+ /** Default constructor.
+ * This constructor passes its paramerers down through DLLFactoryBase and then DLLManager
+ * to load the module, then calls the factory function to retrieve a pointer to a ModuleFactory
+ * class. It is then down to the core to call the ModuleFactory::CreateModule() method and
+ * receive a Module* which it can insert into its module lists.
+ */
+ DLLFactory(InspIRCd* Instance, const char *fname, const char *func_name=0) : DLLFactoryBase(Instance, fname, func_name)
+ {
+ if (factory_func)
+ factory = reinterpret_cast<T*>(factory_func());
+ else
+ factory = reinterpret_cast<T*>(-1);
+ }
+
+ /** The destructor deletes the ModuleFactory pointer.
+ */
+ ~DLLFactory()
+ {
+ if (factory)
+ delete factory;
+ }
+
+ /** The ModuleFactory pointer.
+ */
+ T *factory;
+};
+
+#endif
+
diff --git a/include/exitcodes.h b/include/exitcodes.h
index 84132c5ca..d68301984 100644
--- a/include/exitcodes.h
+++ b/include/exitcodes.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __EXITCODE_H__ #define __EXITCODE_H__ /** Valid exit codes to be used with InspIRCd::Exit() */ 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. */ }; /** Array that maps exit codes (ExitStatus types) to * human-readable strings to be shown on shutdown. */ extern const char * ExitCodes[]; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __EXITCODE_H__
+#define __EXITCODE_H__
+
+/** Valid exit codes to be used with InspIRCd::Exit()
+ */
+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. */
+};
+
+/** Array that maps exit codes (ExitStatus types) to
+ * human-readable strings to be shown on shutdown.
+ */
+extern const char * ExitCodes[];
+
+#endif
+
diff --git a/include/globals.h b/include/globals.h
index 5755c1fd0..4a01e454e 100644
--- a/include/globals.h
+++ b/include/globals.h
@@ -1 +1,39 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __WORLD_H #define __WORLD_H #include <string> #include <deque> #include <map> #include <vector> /** A cached text file stored with its contents as lines */ typedef std::deque<std::string> file_cache; /** A configuration key and value pair */ typedef std::pair< std::string, std::string > KeyVal; /** A list of related configuration keys and values */ typedef std::vector< KeyVal > KeyValList; /** An entire config file, built up of KeyValLists */ typedef std::multimap< std::string, KeyValList > ConfigDataHash; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __WORLD_H
+#define __WORLD_H
+
+#include <string>
+#include <deque>
+#include <map>
+#include <vector>
+
+/** A cached text file stored with its contents as lines
+ */
+typedef std::deque<std::string> file_cache;
+
+/** A configuration key and value pair
+ */
+typedef std::pair< std::string, std::string > KeyVal;
+
+/** A list of related configuration keys and values
+ */
+typedef std::vector< KeyVal > KeyValList;
+
+/** An entire config file, built up of KeyValLists
+ */
+typedef std::multimap< std::string, KeyValList > ConfigDataHash;
+
+#endif
+
diff --git a/include/hash_map.h b/include/hash_map.h
index 36e0b2b02..784a8bc3b 100644
--- a/include/hash_map.h
+++ b/include/hash_map.h
@@ -1 +1,33 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_HASHMAP_H #define INSPIRCD_HASHMAP_H #include "inspircd_config.h" /** Where hash_map is varies from compiler to compiler * as it is not standard. */ #ifndef WIN32 #include <ext/hash_map> /** Oddball linux namespace for hash_map */ #define nspace __gnu_cxx #else #include <hash_map> #define nspace stdext /** Oddball windows namespace for hash_map */ using stdext::hash_map; #endif #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_HASHMAP_H
+#define INSPIRCD_HASHMAP_H
+
+#include "inspircd_config.h"
+
+/** Where hash_map is varies from compiler to compiler
+ * as it is not standard.
+ */
+#ifndef WIN32
+#include <ext/hash_map>
+/** Oddball linux namespace for hash_map */
+#define nspace __gnu_cxx
+#else
+#include <hash_map>
+#define nspace stdext
+/** Oddball windows namespace for hash_map */
+using stdext::hash_map;
+#endif
+
+#endif
diff --git a/include/hashcomp.h b/include/hashcomp.h
index 47f051c80..0556f4399 100644
--- a/include/hashcomp.h
+++ b/include/hashcomp.h
@@ -1 +1,710 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef _HASHCOMP_H_ #define _HASHCOMP_H_ #include "inspircd_config.h" #include "socket.h" #include "hash_map.h" /******************************************************* * This file contains classes and templates that deal * with the comparison and hashing of 'irc strings'. * An 'irc string' is a string which compares in a * case insensitive manner, and as per RFC 1459 will * treat [ identical to {, ] identical to }, and \ * as identical to |. * * Our hashing functions are designed to accept * std::string and compare/hash them as type irc::string * by converting them internally. This makes them * backwards compatible with other code which is not * aware of irc::string. *******************************************************/ /** Required namespaces and symbols */ using namespace std; /** aton() */ using irc::sockets::insp_aton; /** nota() */ using irc::sockets::insp_ntoa; #ifndef LOWERMAP #define LOWERMAP /** A mapping of uppercase to lowercase, including scandinavian * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \ */ unsigned const char lowermap[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 */ }; #endif /** The irc namespace contains a number of helper classes. */ namespace irc { /** This class returns true if two strings match. * Case sensitivity is ignored, and the RFC 'character set' * is adhered to */ struct StrHashComp { /** The operator () does the actual comparison in hash_map */ bool operator()(const std::string& s1, const std::string& s2) const; }; /** The irc_char_traits class is used for RFC-style comparison of strings. * This class is used to implement irc::string, a case-insensitive, RFC- * comparing string class. */ struct irc_char_traits : std::char_traits<char> { /** Check if two chars match. * @param c1st First character * @param c2nd Second character * @return true if the characters are equal */ static bool eq(char c1st, char c2nd); /** Check if two chars do NOT match. * @param c1st First character * @param c2nd Second character * @return true if the characters are unequal */ static bool ne(char c1st, char c2nd); /** Check if one char is less than another. * @param c1st First character * @param c2nd Second character * @return true if c1st is less than c2nd */ static bool lt(char c1st, char c2nd); /** Compare two strings of size n. * @param str1 First string * @param str2 Second string * @param n Length to compare to * @return similar to strcmp, zero for equal, less than zero for str1 * being less and greater than zero for str1 being greater than str2. */ static CoreExport int compare(const char* str1, const char* str2, size_t n); /** Find a char within a string up to position n. * @param s1 String to find in * @param n Position to search up to * @param c Character to search for * @return Pointer to the first occurance of c in s1 */ static CoreExport const char* find(const char* s1, int n, char c); }; /** Compose a hex string from raw data. * @param raw The raw data to compose hex from * @pram 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 basic_string<char, irc_char_traits, 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** array, using overloaded * constructors. */ class CoreExport stringjoiner { private: /** Output string */ std::string joined; public: /** Join elements of a vector, between (and including) begin and end * @param seperator The string to seperate values with * @param sequence One or more items to seperate * @param begin The starting element in the sequence to be joined * @param end The ending element in the sequence to be joined */ stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end); /** Join elements of a deque, between (and including) begin and end * @param seperator The string to seperate values with * @param sequence One or more items to seperate * @param begin The starting element in the sequence to be joined * @param end The ending element in the sequence to be joined */ stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end); /** Join elements of an array of char arrays, between (and including) begin and end * @param seperator The string to seperate values with * @param sequence One or more items to seperate * @param begin The starting element in the sequence to be joined * @param end The ending element in the sequence to be joined */ stringjoiner(const std::string &seperator, const char** sequence, int begin, int end); /** Get the joined sequence * @return A reference to the joined string */ std::string& GetJoined(); }; /** irc::modestacker stacks mode sequences into a list. * It can then reproduce this list, clamped to a maximum of MAXMODES * values per line. */ class CoreExport modestacker { private: /** The mode sequence and its parameters */ std::deque<std::string> sequence; /** True if the mode sequence is initially adding * characters, false if it is initially removing * them */ bool adding; public: /** Construct a new modestacker. * @param add True if the stack is adding modes, * false if it is removing them */ modestacker(bool add); /** Push a modeletter and its parameter onto the stack. * No checking is performed as to if this mode actually * requires a parameter. If you stack invalid mode * sequences, they will be tidied if and when they are * passed to a mode parser. * @param modeletter The mode letter to insert * @param parameter The parameter for the mode */ void Push(char modeletter, const std::string &parameter); /** Push a modeletter without parameter onto the stack. * No checking is performed as to if this mode actually * requires a parameter. If you stack invalid mode * sequences, they will be tidied if and when they are * passed to a mode parser. * @param modeletter The mode letter to insert */ void Push(char modeletter); /** Push a '+' symbol onto the stack. */ void PushPlus(); /** Push a '-' symbol onto the stack. */ void PushMinus(); /** Return zero or more elements which form the * mode line. This will be clamped to a max of * MAXMODES items (MAXMODES-1 mode parameters and * one mode sequence string), and max_line_size * characters. As specified below, this function * should be called in a loop until it returns zero, * indicating there are no more modes to return. * @param result The deque to populate. This will * be cleared before it is used. * @param max_line_size The maximum size of the line * to build, in characters, seperate to MAXMODES. * @return The number of elements in the deque. * The function should be called repeatedly until it * returns 0, in case there are multiple lines of * mode changes to be obtained. */ int GetStackedLine(std::deque<std::string> &result, int max_line_size = 360); }; /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812. * It will split the string into 'tokens' each containing one parameter * from the string. * For instance, if it is instantiated with the string: * "PRIVMSG #test :foo bar baz qux" * then each successive call to tokenstream::GetToken() will return * "PRIVMSG", "#test", "foo bar baz qux", "". * Note that if the whole string starts with a colon this is not taken * to mean the string is all one parameter, and the first item in the * list will be ":item". This is to allow for parsing 'source' fields * from data. */ class CoreExport tokenstream { private: /** 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); ~tokenstream(); /** Fetch the next token from the stream as a std::string * @param token The next token available, or an empty string if none remain * @return True if tokens are left to be read, false if the last token was just retrieved. */ bool GetToken(std::string &token); /** Fetch the next token from the stream as an irc::string * @param token The next token available, or an empty string if none remain * @return True if tokens are left to be read, false if the last token was just retrieved. */ bool GetToken(irc::string &token); /** Fetch the next token from the stream as an integer * @param token The next token available, or undefined if none remain * @return True if tokens are left to be read, false if the last token was just retrieved. */ bool GetToken(int &token); /** Fetch the next token from the stream as a long integer * @param token The next token available, or undefined if none remain * @return True if tokens are left to be read, false if the last token was just retrieved. */ bool GetToken(long &token); }; /** 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 : public classbase { 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); virtual ~sepstream(); /** Fetch the next token from the stream * @return The next token is returned, or an empty string if none remain */ virtual const std::string GetToken(); /** 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 * a return value of 0 from portparser::GetToken(). If you attempt * to specify an illegal range (e.g. one where start >= end, or * start or end < 0) then GetToken() will return the first element * of the pair of numbers. */ class CoreExport portparser : public classbase { private: /** Used to split on commas */ commasepstream* sep; /** Current position in a range of ports */ long in_range; /** Starting port in a range of ports */ long range_begin; /** Ending port in a range of ports */ long range_end; /** Allow overlapped port ranges */ bool overlapped; /** Used to determine overlapping of ports * without O(n) algorithm being used */ std::map<long, bool> overlap_set; /** Returns true if val overlaps an existing range */ bool Overlaps(long val); public: /** Create a portparser and fill it with the provided data * @param source The source text to parse from * @param allow_overlapped Allow overlapped ranges */ portparser(const std::string &source, bool allow_overlapped = true); /** Frees the internal commasepstream object */ ~portparser(); /** Fetch the next token from the stream * @return The next port number is returned, or 0 if none remain */ long GetToken(); }; /** Used to hold a bitfield definition in dynamicbitmask. * You must be allocated one of these by dynamicbitmask::Allocate(), * you should not fill the values yourself! */ typedef std::pair<size_t, unsigned char> bitfield; /** The irc::dynamicbitmask class is used to maintain a bitmap of * boolean values, which can grow to any reasonable size no matter * how many bitfields are in it. * * It starts off at 32 bits in size, large enough to hold 32 boolean * values, with a memory allocation of 8 bytes. If you allocate more * than 32 bits, the class will grow the bitmap by 8 bytes at a time * for each set of 8 bitfields you allocate with the Allocate() * method. * * This method is designed so that multiple modules can be allocated * bit values in a bitmap dynamically, rather than having to define * costs in a fixed size unsigned integer and having the possibility * of collisions of values in different third party modules. * * IMPORTANT NOTE: * * To use this class, you must derive from it. * This is because each derived instance has its own freebits array * which can determine what bitfields are allocated on a TYPE BY TYPE * basis, e.g. an irc::dynamicbitmask type for userrecs, and one for * chanrecs, etc. You should inheret it in a very simple way as follows. * The base class will resize and maintain freebits as required, you are * just required to make the pointer static and specific to this class * type. * * \code * class mydbitmask : public irc::dynamicbitmask * { * private: * * static unsigned char* freebits; * * public: * * mydbitmask() : irc::dynamicbitmask() * { * freebits = new unsigned char[this->bits_size]; * memset(freebits, 0, this->bits_size); * } * * ~mydbitmask() * { * delete[] freebits; * } * * unsigned char* GetFreeBits() * { * return freebits; * } * * void SetFreeBits(unsigned char* freebt) * { * freebits = freebt; * } * }; * \endcode */ class CoreExport dynamicbitmask : public classbase { private: /** Data bits. We start with four of these, * and we grow the bitfield as we allocate * more than 32 entries with Allocate(). */ unsigned char* bits; protected: /** Current set size (size of freebits and bits). * Both freebits and bits will ALWAYS be the * same length. */ unsigned char bits_size; public: /** Allocate the initial memory for bits and * freebits and zero the memory. */ dynamicbitmask(); /** Free the memory used by bits and freebits */ virtual ~dynamicbitmask(); /** Allocate an irc::bitfield. * @return An irc::bitfield which can be used * with Get, Deallocate and Toggle methods. * @throw Can throw std::bad_alloc if there is * no ram left to grow the bitmask. */ bitfield Allocate(); /** Deallocate an irc::bitfield. * @param An irc::bitfield to deallocate. * @return True if the bitfield could be * deallocated, false if it could not. */ bool Deallocate(bitfield &pos); /** Toggle the value of a bitfield. * @param pos A bitfield to allocate, previously * allocated by dyamicbitmask::Allocate(). * @param state The state to set the field to. */ void Toggle(bitfield &pos, bool state); /** Get the value of a bitfield. * @param pos A bitfield to retrieve, previously * allocated by dyamicbitmask::Allocate(). * @return The value of the bitfield. * @throw Will throw ModuleException if the bitfield * you provide is outside of the range * 0 >= bitfield.first < size_bits. */ bool Get(bitfield &pos); /** Return the size in bytes allocated to the bits * array. * Note that the actual allocation is twice this, * as there are an equal number of bytes allocated * for the freebits array. */ unsigned char GetSize(); /** Get free bits mask */ virtual unsigned char* GetFreeBits() { return NULL; } /** Set free bits mask */ virtual void SetFreeBits(unsigned char* freebits) { } }; /** 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); } /* Define operators for using >> and << with irc::string to an ostream on an istream. */ /* This was endless fun. No. Really. */ /* It was also the first core change Ommeh made, if anyone cares */ /** Operator << for irc::string */ inline std::ostream& operator<<(std::ostream &os, const irc::string &str) { return os << str.c_str(); } /** Operator >> for irc::string */ inline std::istream& operator>>(std::istream &is, irc::string &str) { std::string tmp; is >> tmp; str = tmp.c_str(); return is; } /* Define operators for + and == with irc::string to std::string for easy assignment * and comparison * * Operator + */ inline std::string operator+ (std::string& leftval, irc::string& rightval) { return leftval + std::string(rightval.c_str()); } /* Define operators for + and == with irc::string to std::string for easy assignment * and comparison * * Operator + */ inline irc::string operator+ (irc::string& leftval, std::string& rightval) { return leftval + irc::string(rightval.c_str()); } /* Define operators for + and == with irc::string to std::string for easy assignment * and comparison * * Operator == */ inline bool operator== (const std::string& leftval, const irc::string& rightval) { return (leftval.c_str() == rightval); } /* Define operators for + and == with irc::string to std::string for easy assignment * and comparison * * Operator == */ inline bool operator== (const irc::string& leftval, const std::string& rightval) { return (leftval == rightval.c_str()); } /** Assign an irc::string to a std::string. */ inline std::string assign(const irc::string &other) { return other.c_str(); } /** Assign a std::string to an irc::string. */ inline irc::string assign(const std::string &other) { return other.c_str(); } /** Trim the leading and trailing spaces from a std::string. */ inline std::string& trim(std::string &str) { std::string::size_type start = str.find_first_not_of(" "); std::string::size_type end = str.find_last_not_of(" "); if (start == std::string::npos || end == std::string::npos) str = ""; else str = str.substr(start, end-start+1); return str; } /** Hashing stuff is totally different on vc++'s hash_map implementation, so to save a buttload of * #ifdefs we'll just do it all at once */ namespace nspace { /** Hashing function to hash irc::string */ #ifdef WINDOWS 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(), 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(), 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 template<> struct hash<irc::string> { /** Hash an irc::string using RFC1459 case sensitivity rules * @param s A string to hash * @return The hash value */ size_t operator()(const irc::string &s) const; }; template<> struct hash<std::string> { /** Hash a std::string using RFC1459 case sensitivity rules * @param s A string to hash * @return The hash value */ size_t operator()(const string &s) const; }; #endif /** Convert a string to lower case respecting RFC1459 * @param n A string to lowercase */ void strlower(char *n); } #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef _HASHCOMP_H_
+#define _HASHCOMP_H_
+
+#include "inspircd_config.h"
+#include "socket.h"
+#include "hash_map.h"
+
+/*******************************************************
+ * This file contains classes and templates that deal
+ * with the comparison and hashing of 'irc strings'.
+ * An 'irc string' is a string which compares in a
+ * case insensitive manner, and as per RFC 1459 will
+ * treat [ identical to {, ] identical to }, and \
+ * as identical to |.
+ *
+ * Our hashing functions are designed to accept
+ * std::string and compare/hash them as type irc::string
+ * by converting them internally. This makes them
+ * backwards compatible with other code which is not
+ * aware of irc::string.
+ *******************************************************/
+
+/** Required namespaces and symbols */
+using namespace std;
+
+/** aton() */
+using irc::sockets::insp_aton;
+
+/** nota() */
+using irc::sockets::insp_ntoa;
+
+#ifndef LOWERMAP
+#define LOWERMAP
+/** A mapping of uppercase to lowercase, including scandinavian
+ * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
+ */
+unsigned const char lowermap[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 */
+};
+#endif
+
+/** The irc namespace contains a number of helper classes.
+ */
+namespace irc
+{
+
+ /** This class returns true if two strings match.
+ * Case sensitivity is ignored, and the RFC 'character set'
+ * is adhered to
+ */
+ struct StrHashComp
+ {
+ /** The operator () does the actual comparison in hash_map
+ */
+ bool operator()(const std::string& s1, const std::string& s2) const;
+ };
+
+ /** The irc_char_traits class is used for RFC-style comparison of strings.
+ * This class is used to implement irc::string, a case-insensitive, RFC-
+ * comparing string class.
+ */
+ struct irc_char_traits : std::char_traits<char> {
+
+ /** Check if two chars match.
+ * @param c1st First character
+ * @param c2nd Second character
+ * @return true if the characters are equal
+ */
+ static bool eq(char c1st, char c2nd);
+
+ /** Check if two chars do NOT match.
+ * @param c1st First character
+ * @param c2nd Second character
+ * @return true if the characters are unequal
+ */
+ static bool ne(char c1st, char c2nd);
+
+ /** Check if one char is less than another.
+ * @param c1st First character
+ * @param c2nd Second character
+ * @return true if c1st is less than c2nd
+ */
+ static bool lt(char c1st, char c2nd);
+
+ /** Compare two strings of size n.
+ * @param str1 First string
+ * @param str2 Second string
+ * @param n Length to compare to
+ * @return similar to strcmp, zero for equal, less than zero for str1
+ * being less and greater than zero for str1 being greater than str2.
+ */
+ static CoreExport int compare(const char* str1, const char* str2, size_t n);
+
+ /** Find a char within a string up to position n.
+ * @param s1 String to find in
+ * @param n Position to search up to
+ * @param c Character to search for
+ * @return Pointer to the first occurance of c in s1
+ */
+ static CoreExport const char* find(const char* s1, int n, char c);
+ };
+
+ /** Compose a hex string from raw data.
+ * @param raw The raw data to compose hex from
+ * @pram 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 basic_string<char, irc_char_traits, 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** array, using overloaded
+ * constructors.
+ */
+ class CoreExport stringjoiner
+ {
+ private:
+ /** Output string
+ */
+ std::string joined;
+ public:
+ /** Join elements of a vector, between (and including) begin and end
+ * @param seperator The string to seperate values with
+ * @param sequence One or more items to seperate
+ * @param begin The starting element in the sequence to be joined
+ * @param end The ending element in the sequence to be joined
+ */
+ stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end);
+ /** Join elements of a deque, between (and including) begin and end
+ * @param seperator The string to seperate values with
+ * @param sequence One or more items to seperate
+ * @param begin The starting element in the sequence to be joined
+ * @param end The ending element in the sequence to be joined
+ */
+ stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end);
+ /** Join elements of an array of char arrays, between (and including) begin and end
+ * @param seperator The string to seperate values with
+ * @param sequence One or more items to seperate
+ * @param begin The starting element in the sequence to be joined
+ * @param end The ending element in the sequence to be joined
+ */
+ stringjoiner(const std::string &seperator, const char** sequence, int begin, int end);
+
+ /** Get the joined sequence
+ * @return A reference to the joined string
+ */
+ std::string& GetJoined();
+ };
+
+ /** irc::modestacker stacks mode sequences into a list.
+ * It can then reproduce this list, clamped to a maximum of MAXMODES
+ * values per line.
+ */
+ class CoreExport modestacker
+ {
+ private:
+ /** The mode sequence and its parameters
+ */
+ std::deque<std::string> sequence;
+ /** True if the mode sequence is initially adding
+ * characters, false if it is initially removing
+ * them
+ */
+ bool adding;
+ public:
+ /** Construct a new modestacker.
+ * @param add True if the stack is adding modes,
+ * false if it is removing them
+ */
+ modestacker(bool add);
+ /** Push a modeletter and its parameter onto the stack.
+ * No checking is performed as to if this mode actually
+ * requires a parameter. If you stack invalid mode
+ * sequences, they will be tidied if and when they are
+ * passed to a mode parser.
+ * @param modeletter The mode letter to insert
+ * @param parameter The parameter for the mode
+ */
+ void Push(char modeletter, const std::string &parameter);
+ /** Push a modeletter without parameter onto the stack.
+ * No checking is performed as to if this mode actually
+ * requires a parameter. If you stack invalid mode
+ * sequences, they will be tidied if and when they are
+ * passed to a mode parser.
+ * @param modeletter The mode letter to insert
+ */
+ void Push(char modeletter);
+ /** Push a '+' symbol onto the stack.
+ */
+ void PushPlus();
+ /** Push a '-' symbol onto the stack.
+ */
+ void PushMinus();
+ /** Return zero or more elements which form the
+ * mode line. This will be clamped to a max of
+ * MAXMODES items (MAXMODES-1 mode parameters and
+ * one mode sequence string), and max_line_size
+ * characters. As specified below, this function
+ * should be called in a loop until it returns zero,
+ * indicating there are no more modes to return.
+ * @param result The deque to populate. This will
+ * be cleared before it is used.
+ * @param max_line_size The maximum size of the line
+ * to build, in characters, seperate to MAXMODES.
+ * @return The number of elements in the deque.
+ * The function should be called repeatedly until it
+ * returns 0, in case there are multiple lines of
+ * mode changes to be obtained.
+ */
+ int GetStackedLine(std::deque<std::string> &result, int max_line_size = 360);
+ };
+
+ /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812.
+ * It will split the string into 'tokens' each containing one parameter
+ * from the string.
+ * For instance, if it is instantiated with the string:
+ * "PRIVMSG #test :foo bar baz qux"
+ * then each successive call to tokenstream::GetToken() will return
+ * "PRIVMSG", "#test", "foo bar baz qux", "".
+ * Note that if the whole string starts with a colon this is not taken
+ * to mean the string is all one parameter, and the first item in the
+ * list will be ":item". This is to allow for parsing 'source' fields
+ * from data.
+ */
+ class CoreExport tokenstream
+ {
+ private:
+ /** 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);
+ ~tokenstream();
+
+ /** Fetch the next token from the stream as a std::string
+ * @param token The next token available, or an empty string if none remain
+ * @return True if tokens are left to be read, false if the last token was just retrieved.
+ */
+ bool GetToken(std::string &token);
+
+ /** Fetch the next token from the stream as an irc::string
+ * @param token The next token available, or an empty string if none remain
+ * @return True if tokens are left to be read, false if the last token was just retrieved.
+ */
+ bool GetToken(irc::string &token);
+
+ /** Fetch the next token from the stream as an integer
+ * @param token The next token available, or undefined if none remain
+ * @return True if tokens are left to be read, false if the last token was just retrieved.
+ */
+ bool GetToken(int &token);
+
+ /** Fetch the next token from the stream as a long integer
+ * @param token The next token available, or undefined if none remain
+ * @return True if tokens are left to be read, false if the last token was just retrieved.
+ */
+ bool GetToken(long &token);
+ };
+
+ /** 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 : public classbase
+ {
+ 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);
+ virtual ~sepstream();
+
+ /** Fetch the next token from the stream
+ * @return The next token is returned, or an empty string if none remain
+ */
+ virtual const std::string GetToken();
+
+ /** 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
+ * a return value of 0 from portparser::GetToken(). If you attempt
+ * to specify an illegal range (e.g. one where start >= end, or
+ * start or end < 0) then GetToken() will return the first element
+ * of the pair of numbers.
+ */
+ class CoreExport portparser : public classbase
+ {
+ private:
+ /** Used to split on commas
+ */
+ commasepstream* sep;
+ /** Current position in a range of ports
+ */
+ long in_range;
+ /** Starting port in a range of ports
+ */
+ long range_begin;
+ /** Ending port in a range of ports
+ */
+ long range_end;
+ /** Allow overlapped port ranges
+ */
+ bool overlapped;
+ /** Used to determine overlapping of ports
+ * without O(n) algorithm being used
+ */
+ std::map<long, bool> overlap_set;
+ /** Returns true if val overlaps an existing range
+ */
+ bool Overlaps(long val);
+ public:
+ /** Create a portparser and fill it with the provided data
+ * @param source The source text to parse from
+ * @param allow_overlapped Allow overlapped ranges
+ */
+ portparser(const std::string &source, bool allow_overlapped = true);
+ /** Frees the internal commasepstream object
+ */
+ ~portparser();
+ /** Fetch the next token from the stream
+ * @return The next port number is returned, or 0 if none remain
+ */
+ long GetToken();
+ };
+
+ /** Used to hold a bitfield definition in dynamicbitmask.
+ * You must be allocated one of these by dynamicbitmask::Allocate(),
+ * you should not fill the values yourself!
+ */
+ typedef std::pair<size_t, unsigned char> bitfield;
+
+ /** The irc::dynamicbitmask class is used to maintain a bitmap of
+ * boolean values, which can grow to any reasonable size no matter
+ * how many bitfields are in it.
+ *
+ * It starts off at 32 bits in size, large enough to hold 32 boolean
+ * values, with a memory allocation of 8 bytes. If you allocate more
+ * than 32 bits, the class will grow the bitmap by 8 bytes at a time
+ * for each set of 8 bitfields you allocate with the Allocate()
+ * method.
+ *
+ * This method is designed so that multiple modules can be allocated
+ * bit values in a bitmap dynamically, rather than having to define
+ * costs in a fixed size unsigned integer and having the possibility
+ * of collisions of values in different third party modules.
+ *
+ * IMPORTANT NOTE:
+ *
+ * To use this class, you must derive from it.
+ * This is because each derived instance has its own freebits array
+ * which can determine what bitfields are allocated on a TYPE BY TYPE
+ * basis, e.g. an irc::dynamicbitmask type for userrecs, and one for
+ * chanrecs, etc. You should inheret it in a very simple way as follows.
+ * The base class will resize and maintain freebits as required, you are
+ * just required to make the pointer static and specific to this class
+ * type.
+ *
+ * \code
+ * class mydbitmask : public irc::dynamicbitmask
+ * {
+ * private:
+ *
+ * static unsigned char* freebits;
+ *
+ * public:
+ *
+ * mydbitmask() : irc::dynamicbitmask()
+ * {
+ * freebits = new unsigned char[this->bits_size];
+ * memset(freebits, 0, this->bits_size);
+ * }
+ *
+ * ~mydbitmask()
+ * {
+ * delete[] freebits;
+ * }
+ *
+ * unsigned char* GetFreeBits()
+ * {
+ * return freebits;
+ * }
+ *
+ * void SetFreeBits(unsigned char* freebt)
+ * {
+ * freebits = freebt;
+ * }
+ * };
+ * \endcode
+ */
+ class CoreExport dynamicbitmask : public classbase
+ {
+ private:
+ /** Data bits. We start with four of these,
+ * and we grow the bitfield as we allocate
+ * more than 32 entries with Allocate().
+ */
+ unsigned char* bits;
+ protected:
+ /** Current set size (size of freebits and bits).
+ * Both freebits and bits will ALWAYS be the
+ * same length.
+ */
+ unsigned char bits_size;
+ public:
+ /** Allocate the initial memory for bits and
+ * freebits and zero the memory.
+ */
+ dynamicbitmask();
+
+ /** Free the memory used by bits and freebits
+ */
+ virtual ~dynamicbitmask();
+
+ /** Allocate an irc::bitfield.
+ * @return An irc::bitfield which can be used
+ * with Get, Deallocate and Toggle methods.
+ * @throw Can throw std::bad_alloc if there is
+ * no ram left to grow the bitmask.
+ */
+ bitfield Allocate();
+
+ /** Deallocate an irc::bitfield.
+ * @param An irc::bitfield to deallocate.
+ * @return True if the bitfield could be
+ * deallocated, false if it could not.
+ */
+ bool Deallocate(bitfield &pos);
+
+ /** Toggle the value of a bitfield.
+ * @param pos A bitfield to allocate, previously
+ * allocated by dyamicbitmask::Allocate().
+ * @param state The state to set the field to.
+ */
+ void Toggle(bitfield &pos, bool state);
+
+ /** Get the value of a bitfield.
+ * @param pos A bitfield to retrieve, previously
+ * allocated by dyamicbitmask::Allocate().
+ * @return The value of the bitfield.
+ * @throw Will throw ModuleException if the bitfield
+ * you provide is outside of the range
+ * 0 >= bitfield.first < size_bits.
+ */
+ bool Get(bitfield &pos);
+
+ /** Return the size in bytes allocated to the bits
+ * array.
+ * Note that the actual allocation is twice this,
+ * as there are an equal number of bytes allocated
+ * for the freebits array.
+ */
+ unsigned char GetSize();
+
+ /** Get free bits mask
+ */
+ virtual unsigned char* GetFreeBits() { return NULL; }
+
+ /** Set free bits mask
+ */
+ virtual void SetFreeBits(unsigned char* freebits) { }
+ };
+
+ /** 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);
+}
+
+/* Define operators for using >> and << with irc::string to an ostream on an istream. */
+/* This was endless fun. No. Really. */
+/* It was also the first core change Ommeh made, if anyone cares */
+
+/** Operator << for irc::string
+ */
+inline std::ostream& operator<<(std::ostream &os, const irc::string &str) { return os << str.c_str(); }
+
+/** Operator >> for irc::string
+ */
+inline std::istream& operator>>(std::istream &is, irc::string &str)
+{
+ std::string tmp;
+ is >> tmp;
+ str = tmp.c_str();
+ return is;
+}
+
+/* Define operators for + and == with irc::string to std::string for easy assignment
+ * and comparison
+ *
+ * Operator +
+ */
+inline std::string operator+ (std::string& leftval, irc::string& rightval)
+{
+ return leftval + std::string(rightval.c_str());
+}
+
+/* Define operators for + and == with irc::string to std::string for easy assignment
+ * and comparison
+ *
+ * Operator +
+ */
+inline irc::string operator+ (irc::string& leftval, std::string& rightval)
+{
+ return leftval + irc::string(rightval.c_str());
+}
+
+/* Define operators for + and == with irc::string to std::string for easy assignment
+ * and comparison
+ *
+ * Operator ==
+ */
+inline bool operator== (const std::string& leftval, const irc::string& rightval)
+{
+ return (leftval.c_str() == rightval);
+}
+
+/* Define operators for + and == with irc::string to std::string for easy assignment
+ * and comparison
+ *
+ * Operator ==
+ */
+inline bool operator== (const irc::string& leftval, const std::string& rightval)
+{
+ return (leftval == rightval.c_str());
+}
+
+/** Assign an irc::string to a std::string.
+ */
+inline std::string assign(const irc::string &other) { return other.c_str(); }
+
+/** Assign a std::string to an irc::string.
+ */
+inline irc::string assign(const std::string &other) { return other.c_str(); }
+
+/** Trim the leading and trailing spaces from a std::string.
+ */
+inline std::string& trim(std::string &str)
+{
+ std::string::size_type start = str.find_first_not_of(" ");
+ std::string::size_type end = str.find_last_not_of(" ");
+ if (start == std::string::npos || end == std::string::npos)
+ str = "";
+ else
+ str = str.substr(start, end-start+1);
+
+ return str;
+}
+
+/** Hashing stuff is totally different on vc++'s hash_map implementation, so to save a buttload of
+ * #ifdefs we'll just do it all at once
+ */
+namespace nspace
+{
+ /** Hashing function to hash irc::string
+ */
+#ifdef WINDOWS
+ 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(), 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(), 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
+ template<> struct hash<irc::string>
+ {
+ /** Hash an irc::string using RFC1459 case sensitivity rules
+ * @param s A string to hash
+ * @return The hash value
+ */
+ size_t operator()(const irc::string &s) const;
+ };
+
+ template<> struct hash<std::string>
+ {
+ /** Hash a std::string using RFC1459 case sensitivity rules
+ * @param s A string to hash
+ * @return The hash value
+ */
+ size_t operator()(const string &s) const;
+ };
+#endif
+
+ /** Convert a string to lower case respecting RFC1459
+ * @param n A string to lowercase
+ */
+ void strlower(char *n);
+}
+
+#endif
+
diff --git a/include/inspircd.h b/include/inspircd.h
index 378ca4ec1..c3f8ed328 100644
--- a/include/inspircd.h
+++ b/include/inspircd.h
@@ -1 +1,1279 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __INSPIRCD_H__ #define __INSPIRCD_H__ #ifndef WIN32 #define DllExport #define CoreExport #define printf_c printf #else #include "inspircd_win32wrapper.h" /** Windows defines these already */ #undef DELETE #undef ERROR #endif #include <time.h> #include <string> #include <sstream> #include "inspircd_config.h" #include "users.h" #include "channels.h" #include "socket.h" #include "mode.h" #include "socketengine.h" #include "command_parse.h" #include "snomasks.h" #include "cull_list.h" /** 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 /** Debug levels for use with InspIRCd::Log() */ enum DebugLevel { DEBUG = 10, VERBOSE = 20, DEFAULT = 30, SPARSE = 40, NONE = 50 }; /** * This define is used in place of strcmp when we * want to check if a char* string contains only one * letter. Pretty fast, its just two compares and an * addition. */ #define IS_SINGLE(x,y) ( (*x == y) && (*(x+1) == 0) ) /** Delete a pointer, and NULL its value */ template<typename T> inline void DELETE(T* x) { delete x; x = NULL; } /** Template function to convert any input type to std::string */ template<typename T> inline std::string ConvNumeric(const T &in) { if (in == 0) return "0"; char res[MAXBUF]; char* out = res; T quotient = in; while (quotient) { *out = "0123456789"[ std::abs( (long)quotient % 10 ) ]; ++out; quotient /= 10; } if ( in < 0) *out++ = '-'; *out = 0; std::reverse(res,out); return res; } /** Template function to convert any input type to std::string */ inline std::string ConvToStr(const int in) { return ConvNumeric(in); } /** Template function to convert any input type to std::string */ inline std::string ConvToStr(const long in) { return ConvNumeric(in); } /** Template function to convert any input type to std::string */ inline std::string ConvToStr(const unsigned long in) { return ConvNumeric(in); } /** Template function to convert any input type to std::string */ inline std::string ConvToStr(const char* in) { return in; } /** Template function to convert any input type to std::string */ inline std::string ConvToStr(const bool in) { return (in ? "1" : "0"); } /** Template function to convert any input type to std::string */ inline std::string ConvToStr(char in) { return std::string(in,1); } /** Template function to convert any input type to std::string */ template <class T> inline std::string ConvToStr(const T &in) { std::stringstream tmp; if (!(tmp << in)) return std::string(); return tmp.str(); } /** Template function to convert any input type to any other type * (usually an integer or numeric type) */ template<typename T> inline long ConvToInt(const T &in) { std::stringstream tmp; if (!(tmp << in)) return 0; return atoi(tmp.str().c_str()); } /** Template function to convert integer to char, storing result in *res and * also returning the pointer to res. Based on Stuart Lowe's C/C++ Pages. * @param T input value * @param V result value * @param R base to convert to */ template<typename T, typename V, typename R> inline char* itoa(const T &in, V *res, R base) { if (base < 2 || base > 16) { *res = 0; return res; } char* out = res; int quotient = in; while (quotient) { *out = "0123456789abcdef"[ std::abs( quotient % base ) ]; ++out; quotient /= base; } if ( in < 0 && base == 10) *out++ = '-'; std::reverse( res, out ); *out = 0; return res; } /** This class contains various STATS counters * It is used by the InspIRCd class, which internally * has an instance of it. */ class serverstats : public classbase { public: /** Number of accepted connections */ unsigned long statsAccept; /** Number of failed accepts */ unsigned long statsRefused; /** Number of unknown commands seen */ unsigned long statsUnknown; /** Number of nickname collisions handled */ unsigned long statsCollisions; /** Number of DNS queries sent out */ unsigned long statsDns; /** 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; /** 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; /** Number of inbound connections seen */ unsigned long statsConnects; /** Total bytes of data transmitted */ double statsSent; /** Total bytes of data received */ double statsRecv; /** Cpu usage at last sample */ timeval LastCPU; /** Time last sample was read */ timeval LastSampled; /** 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.0), statsRecv(0.0) { } }; /* Forward declaration -- required */ class InspIRCd; /** This class implements a nonblocking log-writer. * Most people writing an ircd give little thought to their disk * i/o. On a congested system, disk writes can block for long * periods of time (e.g. if the system is busy and/or swapping * a lot). If we just use a blocking fprintf() call, this could * block for undesirable amounts of time (half of a second through * to whole seconds). We DO NOT want this, so we make our logfile * nonblocking and hook it into the SocketEngine. * NB: If the operating system does not support nonblocking file * I/O (linux seems to, as does freebsd) this will default to * blocking behaviour. */ class CoreExport FileLogger : public EventHandler { protected: /** The creator/owner of this object */ InspIRCd* ServerInstance; /** The log file (fd is inside this somewhere, * we get it out with fileno()) */ FILE* log; /** Buffer of pending log lines to be written */ std::string buffer; /** Number of write operations that have occured */ int writeops; public: /** The constructor takes an already opened logfile. */ FileLogger(InspIRCd* Instance, FILE* logfile); /** This returns false, logfiles are writeable. */ virtual bool Readable(); /** Handle pending write events. * This will flush any waiting data to disk. * If any data remains after the fprintf call, * another write event is scheduled to write * the rest of the data when possible. */ virtual void HandleEvent(EventType et, int errornum = 0); /** Write one or more preformatted log lines. * If the data cannot be written immediately, * this class will insert itself into the * SocketEngine, and register a write event, * and when the write event occurs it will * attempt again to write the data. */ void WriteLogLine(const std::string &line); /** Close the log file and cancel any events. */ virtual void Close(); /** Close the log file and cancel any events. * (indirectly call Close() */ virtual ~FileLogger(); }; /** A list of failed port bindings, used for informational purposes on startup */ typedef std::vector<std::pair<std::string, long> > FailedPortList; /** A list of ip addresses cross referenced against clone counts */ typedef std::map<irc::string, unsigned int> clonemap; /* Forward declaration - required */ class XLineManager; /** The main class of the irc server. * This class contains instances of all the other classes * in this software, with the exception of the base class, * classbase. Amongst other things, it contains a ModeParser, * a DNS object, a CommandParser object, and a list of active * Module objects, and facilities for Module objects to * interact with the core system it implements. You should * NEVER attempt to instantiate a class of type InspIRCd * yourself. If you do, this is equivalent to spawning a second * IRC server, and could have catastrophic consequences for the * program in terms of ram usage (basically, you could create * an obese forkbomb built from recursively spawning irc servers!) */ class CoreExport InspIRCd : public classbase { private: /** Holds a string describing the last module error to occur */ char MODERR[MAXBUF]; /** Remove a ModuleFactory pointer * @param j Index number of the ModuleFactory to remove */ void EraseFactory(int j); /** Remove a Module pointer * @param j Index number of the Module to remove */ void EraseModule(int j); /** Move a given module to a specific slot in the list * @param modulename The module name to relocate * @param slot The slot to move the module into */ void MoveTo(std::string modulename,int slot); /** Display the startup banner */ void Start(); /** Set up the signal handlers */ void SetSignals(); /** Daemonize the ircd and close standard input/output streams * @return True if the program daemonized succesfully */ bool DaemonSeed(); /** Moves the given module to the last slot in the list * @param modulename The module name to relocate */ void MoveToLast(std::string modulename); /** Moves the given module to the first slot in the list * @param modulename The module name to relocate */ void MoveToFirst(std::string modulename); /** Moves one module to be placed after another in the list * @param modulename The module name to relocate * @param after The module name to place the module after */ void MoveAfter(std::string modulename, std::string after); /** Moves one module to be placed before another in the list * @param modulename The module name to relocate * @param after The module name to place the module before */ void MoveBefore(std::string modulename, std::string before); /** Iterate the list of InspSocket objects, removing ones which have timed out * @param TIME the current time */ void DoSocketTimeouts(time_t TIME); /** Perform background user events such as PING checks * @param TIME the current time */ void DoBackgroundUserStuff(time_t TIME); /** 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(userrec* user); /** Total number of modules loaded into the ircd, minus one */ int ModCount; /** Logfile pathname specified on the commandline, or empty string */ char LogFileName[MAXBUF]; /** The feature names published by various modules */ featurelist Features; /** The interface names published by various modules */ interfacelist Interfaces; /** The current time, updated in the mainloop */ time_t TIME; /** The time that was recorded last time around the mainloop */ time_t OLDTIME; /** A 64k buffer used to read client lines into */ char ReadBuffer[65535]; /** Used when connecting clients */ insp_sockaddr client, server; /** Used when connecting clients */ socklen_t length; /** Nonblocking file writer */ FileLogger* Logger; /** Time offset in seconds * This offset is added to all calls to Time(). Use SetTimeDelta() to update */ int time_delta; public: /** InspSocket classes pending deletion after being closed. * We don't delete these immediately as this may cause a segmentation fault. */ std::map<InspSocket*,InspSocket*> SocketCull; /** Build the ISUPPORT string by triggering all modules On005Numeric events */ void BuildISupport(); /** Number of unregistered users online right now. * (Unregistered means before USER/NICK/dns) */ int unregistered_count; /** List of server names we've seen. */ servernamelist servernames; /** Time this ircd was booted */ time_t startup_time; /** Config file pathname specified on the commandline or via ./configure */ char ConfigFileName[MAXBUF]; /** Mode handler, handles mode setting and removal */ ModeParser* Modes; /** Command parser, handles client to server commands */ CommandParser* Parser; /** Socket engine, handles socket activity events */ SocketEngine* SE; /** Stats class, holds miscellaneous stats counters */ serverstats* stats; /** Server Config class, holds configuration file data */ ServerConfig* Config; /** Snomask manager - handles routing of snomask messages * to opers. */ SnomaskManager* SNO; /** Client list, a hash_map containing all clients, local and remote */ user_hash* clientlist; /** Channel list, a hash_map containing all channels */ chan_hash* chanlist; /** Local client list, a vector containing only local clients */ std::vector<userrec*> local_users; /** Oper list, a vector containing all local and remote opered users */ std::vector<userrec*> all_opers; /** Map of local ip addresses for clone counting */ clonemap local_clones; /** Map of global ip addresses for clone counting */ clonemap global_clones; /** DNS class, provides resolver facilities to the core and modules */ DNS* Res; /** Timer manager class, triggers InspTimer timer events */ TimerManager* Timers; /** X-Line manager. Handles G/K/Q/E line setting, removal and matching */ XLineManager* XLines; /** A list of Module* module classes * Note that this list is always exactly 255 in size. * The actual number of loaded modules is available from GetModuleCount() */ ModuleList modules; /** A list of ModuleFactory* module factories * Note that this list is always exactly 255 in size. * The actual number of loaded modules is available from GetModuleCount() */ FactoryList factory; /** The time we next call our ping timeout and reg timeout checks */ time_t next_call; /** Global cull list, will be processed on next iteration */ CullList GlobalCulls; /** Get the current time * Because this only calls time() once every time around the mainloop, * it is much faster than calling time() directly. * @param delta True to use the delta as an offset, false otherwise * @return The current time as an epoch value (time_t) */ time_t Time(bool delta = false); /** Set the time offset in seconds * This offset is added to Time() to offset the system time by the specified * number of seconds. * @param delta The number of seconds to offset * @return The old time delta */ int SetTimeDelta(int delta); /** Add a user to the local clone map * @param user The user to add */ void AddLocalClone(userrec* user); /** Add a user to the global clone map * @param user The user to add */ void AddGlobalClone(userrec* user); /** Number of users with a certain mode set on them */ int ModeCount(const char mode); /** Get the time offset in seconds * @return The current time delta (in seconds) */ int GetTimeDelta(); /** Process a user whos socket has been flagged as active * @param cu The user to process * @return There is no actual return value, however upon exit, the user 'cu' may have been * marked for deletion in the global CullList. */ void ProcessUser(userrec* cu); /** Get the total number of currently loaded modules * @return The number of loaded modules */ int GetModuleCount(); /** Find a module by name, and return a Module* to it. * This is preferred over iterating the module lists yourself. * @param name The module name to look up * @return A pointer to the module, or NULL if the module cannot be found */ Module* FindModule(const std::string &name); /** Bind all ports specified in the configuration file. * @param bail True if the function should bail back to the shell on failure * @param found_ports The actual number of ports found in the config, as opposed to the number actually bound * @return The number of ports actually bound without error */ int BindPorts(bool bail, int &found_ports, 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) * @return True if the port was bound successfully */ bool BindSocket(int sockfd, int port, char* addr, bool dolisten = true); /** Adds a server name to the list of servers we've seen * @param The servername to add */ void AddServerName(const std::string &servername); /** Finds a cached char* pointer of a server name, * This is used to optimize userrec by storing only the pointer to the name * @param The servername to find * @return A pointer to this name, gauranteed to never become invalid */ const char* FindServerNamePtr(const std::string &servername); /** Returns true if we've seen the given server name before * @param The servername to find * @return True if we've seen this server name before */ bool FindServerName(const std::string &servername); /** 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 char* servername); /** Write text to all opers connected to this server * @param text The text format string * @param ... Format args */ void WriteOpers(const char* text, ...); /** Write text to all opers connected to this server * @param text The text to send */ void WriteOpers(const std::string &text); /** Find a nickname in the nick hash * @param nick The nickname to find * @return A pointer to the user, or NULL if the user does not exist */ userrec* FindNick(const std::string &nick); /** Find a nickname in the nick hash * @param nick The nickname to find * @return A pointer to the user, or NULL if the user does not exist */ userrec* FindNick(const char* nick); /** 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 */ chanrec* 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 */ chanrec* FindChan(const char* chan); /** Called by the constructor to load all modules from the config file. */ void LoadAllModules(); /** Check for a 'die' tag in the config file, and abort if found * @return Depending on the configuration, this function may never return */ void CheckDie(); /** 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 */ void OpenLog(char** argv, int argc); /** Close the currently open log file */ void CloseLog(); /** Send a server notice to all local users * @param text The text format string to send * @param ... The format arguments */ void ServerNoticeAll(char* text, ...); /** Send a server message (PRIVMSG) to all local users * @param text The text format string to send * @param ... The format arguments */ void ServerPrivmsgAll(char* text, ...); /** Send text to all users with a specific set of modes * @param modes The modes to check against, without a +, e.g. 'og' * @param flags one of WM_OR or WM_AND. If you specify WM_OR, any one of the * mode characters in the first parameter causes receipt of the message, and * if you specify WM_OR, all the modes must be present. * @param text The text format string to send * @param ... The format arguments */ void WriteMode(const char* modes, int flags, const char* text, ...); /** Return true if a channel name is valid * @param chname A channel name to verify * @return True if the name is valid */ bool IsChannel(const char *chname); /** Rehash the local server * @param status This value is unused, and required for signal handler functions */ static void Rehash(int status); /** Causes the server to exit after unloading modules and * closing all open file descriptors. * * @param The exit code to give to the operating system * (See the ExitStatus enum for valid values) */ static void Exit(int status); /** Causes the server to exit immediately with exit code 0. * The status code is required for signal handlers, and ignored. */ static void QuickExit(int status); /** Return a count of users, unknown and known connections * @return The number of users */ int UserCount(); /** Return a count of fully registered connections only * @return The number of registered users */ int RegisteredUserCount(); /** Return a count of invisible (umode +i) users only * @return The number of invisible users */ int InvisibleUserCount(); /** Return a count of opered (umode +o) users only * @return The number of opers */ int OperCount(); /** Return a count of unregistered (before NICK/USER) users only * @return The number of unregistered (unknown) connections */ int UnregisteredUserCount(); /** Return a count of channels on the network * @return The number of channels */ long ChannelCount(); /** Return a count of local users on this server only * @return The number of local users */ long LocalUserCount(); /** Send an error notice to all local users, opered and unopered * @param s The error string to send */ void SendError(const std::string &s); /** For use with Module::Prioritize(). * When the return value of this function is returned from * Module::Prioritize(), this specifies that the module wishes * to be ordered exactly BEFORE 'modulename'. For more information * please see Module::Prioritize(). * @param modulename The module your module wants to be before in the call list * @returns a priority ID which the core uses to relocate the module in the list */ long PriorityBefore(const std::string &modulename); /** For use with Module::Prioritize(). * When the return value of this function is returned from * Module::Prioritize(), this specifies that the module wishes * to be ordered exactly AFTER 'modulename'. For more information please * see Module::Prioritize(). * @param modulename The module your module wants to be after in the call list * @returns a priority ID which the core uses to relocate the module in the list */ long PriorityAfter(const std::string &modulename); /** Publish a 'feature'. * There are two ways for a module to find another module it depends on. * Either by name, using InspIRCd::FindModule, or by feature, using this * function. A feature is an arbitary string which identifies something this * module can do. For example, if your module provides SSL support, but other * modules provide SSL support too, all the modules supporting SSL should * publish an identical 'SSL' feature. This way, any module requiring use * of SSL functions can just look up the 'SSL' feature using FindFeature, * then use the module pointer they are given. * @param FeatureName The case sensitive feature name to make available * @param Mod a pointer to your module class * @returns True on success, false if the feature is already published by * another module. */ bool PublishFeature(const std::string &FeatureName, Module* Mod); /** Publish a module to an 'interface'. * Modules which implement the same interface (the same way of communicating * with other modules) can publish themselves to an interface, using this * method. When they do so, they become part of a list of related or * compatible modules, and a third module may then query for that list * and know that all modules within that list offer the same API. * A prime example of this is the hashing modules, which all accept the * same types of Request class. Consider this to be similar to PublishFeature, * except for that multiple modules may publish the same 'feature'. * @param InterfaceName The case sensitive interface name to make available * @param Mod a pointer to your module class * @returns True on success, false on failure (there are currently no failure * cases) */ bool PublishInterface(const std::string &InterfaceName, Module* Mod); /** Return a pair saying how many other modules are currently using the * interfaces provided by module m. * @param m The module to count usage for * @return A pair, where the first value is the number of uses of the interface, * and the second value is the interface name being used. */ std::pair<int,std::string> GetInterfaceInstanceCount(Module* m); /** Mark your module as using an interface. * If you mark your module as using an interface, then that interface * module may not unload until your module has unloaded first. * This can be used to prevent crashes by ensuring code you depend on * is always in memory while your module is active. * @param InterfaceName The interface to use */ void UseInterface(const std::string &InterfaceName); /** Mark your module as finished with an interface. * If you used UseInterface() above, you should use this method when * your module is finished with the interface (usually in its destructor) * to allow the modules which implement the given interface to be unloaded. * @param InterfaceName The interface you are finished with using. */ void DoneWithInterface(const std::string &InterfaceName); /** Unpublish a 'feature'. * When your module exits, it must call this method for every feature it * is providing so that the feature table is cleaned up. * @param FeatureName the feature to remove */ bool UnpublishFeature(const std::string &FeatureName); /** Unpublish your module from an interface * When your module exits, it must call this method for every interface * it is part of so that the interfaces table is cleaned up. Only when * the last item is deleted from an interface does the interface get * removed. * @param InterfaceName the interface to be removed from * @param Mod The module to remove from the interface list */ bool UnpublishInterface(const std::string &InterfaceName, Module* Mod); /** Find a 'feature'. * There are two ways for a module to find another module it depends on. * Either by name, using InspIRCd::FindModule, or by feature, using the * InspIRCd::PublishFeature method. A feature is an arbitary string which * identifies something this module can do. For example, if your module * provides SSL support, but other modules provide SSL support too, all * the modules supporting SSL should publish an identical 'SSL' feature. * To find a module capable of providing the feature you want, simply * call this method with the feature name you are looking for. * @param FeatureName The feature name you wish to obtain the module for * @returns A pointer to a valid module class on success, NULL on failure. */ Module* FindFeature(const std::string &FeatureName); /** Find an 'interface'. * An interface is a list of modules which all implement the same API. * @param InterfaceName The Interface you wish to obtain the module * list of. * @return A pointer to a deque of Module*, or NULL if the interface * does not exist. */ modulelist* FindInterface(const std::string &InterfaceName); /** Given a pointer to a Module, return its filename * @param m The module pointer to identify * @return The module name or an empty string */ const std::string& GetModuleName(Module* m); /** Return true if a nickname is valid * @param n A nickname to verify * @return True if the nick is valid */ bool IsNick(const char* n); /** Return true if an ident is valid * @param An ident to verify * @return True if the ident is valid */ bool IsIdent(const char* n); /** Find a username by their file descriptor. * It is preferred to use this over directly accessing the fd_ref_table array. * @param socket The file descriptor of a user * @return A pointer to the user if the user exists locally on this descriptor */ userrec* FindDescriptor(int socket); /** Add a new mode to this server's mode parser * @param mh The modehandler to add * @param modechar The mode character this modehandler handles * @return True if the mode handler was added */ bool AddMode(ModeHandler* mh, const unsigned char modechar); /** Add a new mode watcher to this server's mode parser * @param mw The modewatcher to add * @return True if the modewatcher was added */ bool AddModeWatcher(ModeWatcher* mw); /** Delete a mode watcher from this server's mode parser * @param mw The modewatcher to delete * @return True if the modewatcher was deleted */ bool DelModeWatcher(ModeWatcher* mw); /** 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_t command handler object to add * @throw ModuleException Will throw ModuleExcption if the command already exists */ void AddCommand(command_t *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 pcnt The number of items you have given in the first parameter * @param user The user to send error messages to */ void SendMode(const char **parameters, int pcnt, userrec *user); /** Match two strings using pattern matching. * This operates identically to the global function match(), * except for that it takes std::string arguments rather than * const char* ones. * @param sliteral The literal string to match against * @param spattern The pattern to match against. CIDR and globs are supported. */ bool MatchText(const std::string &sliteral, const std::string &spattern); /** Call the handler for a given command. * @param commandname The command whos handler you wish to call * @param parameters The mode parameters * @param pcnt The number of items you have given in the first parameter * @param user The user to execute the command as * @return True if the command handler was called successfully */ CmdResult CallCommandHandler(const std::string &commandname, const char** parameters, int pcnt, userrec* user); /** Return true if the command is a module-implemented command and the given parameters are valid for it * @param parameters The mode parameters * @param pcnt The number of items you have given in the first parameter * @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, userrec* user); /** Add a gline and apply it * @param duration How long the line should last * @param source Who set the line * @param reason The reason for the line * @param hostmask The hostmask to set the line against */ void AddGLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask); /** Add a qline and apply it * @param duration How long the line should last * @param source Who set the line * @param reason The reason for the line * @param nickname The nickmask to set the line against */ void AddQLine(long duration, const std::string &source, const std::string &reason, const std::string &nickname); /** Add a zline and apply it * @param duration How long the line should last * @param source Who set the line * @param reason The reason for the line * @param ipaddr The ip-mask to set the line against */ void AddZLine(long duration, const std::string &source, const std::string &reason, const std::string &ipaddr); /** Add a kline and apply it * @param duration How long the line should last * @param source Who set the line * @param reason The reason for the line * @param hostmask The hostmask to set the line against */ void AddKLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask); /** Add an eline * @param duration How long the line should last * @param source Who set the line * @param reason The reason for the line * @param hostmask The hostmask to set the line against */ void AddELine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask); /** Delete a gline * @param hostmask The gline to delete * @return True if the item was removed */ bool DelGLine(const std::string &hostmask); /** Delete a qline * @param nickname The qline to delete * @return True if the item was removed */ bool DelQLine(const std::string &nickname); /** Delete a zline * @param ipaddr The zline to delete * @return True if the item was removed */ bool DelZLine(const std::string &ipaddr); /** Delete a kline * @param hostmask The kline to delete * @return True if the item was removed */ bool DelKLine(const std::string &hostmask); /** Delete an eline * @param hostmask The kline to delete * @return True if the item was removed */ bool DelELine(const std::string &hostmask); /** 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); /** Rehash the local server */ void RehashServer(); /** Return the channel whos index number matches that provided * @param The index number of the channel to fetch * @return A channel record, or NUll if index < 0 or index >= InspIRCd::ChannelCount() */ chanrec* GetChannelIndex(long index); /** Dump text to a user target, splitting it appropriately to fit * @param User the user to dump the text to * @param LinePrefix text to prefix each complete line with * @param TextStream the text to send to the user */ void DumpText(userrec* User, const std::string &LinePrefix, stringstream &TextStream); /** 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, userrec* 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, userrec* 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, userrec* user); /** 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); /** Attempt to compare an oper password to a string from the config file. * This will be passed to handling modules which will compare the data * against possible hashed equivalents in the input string. * @param data The data from the config file * @param input The data input by the oper * @param tagnum the tag number of the oper's tag in the config file * @return 0 if the strings match, 1 or -1 if they do not */ int OperPassCompare(const char* data,const char* input, int tagnum); /** 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 char* server); /** Returns true if the uline is 'silent' (doesnt generate * remote connect notices etc). */ bool SilentULine(const char* server); /** Returns the subversion revision ID of this ircd * @return The revision ID or an empty string */ std::string GetRevision(); /** Returns the full version string of this ircd * @return The version string */ std::string GetVersionString(); /** Attempt to write the process id to a given file * @param filename The PID file to attempt to write to * @return This function may bail if the file cannot be written */ void WritePID(const std::string &filename); /** Returns text describing the last module error * @return The last error message to occur */ char* ModuleError(); /** Load a given module file * @param filename The file to load * @return True if the module was found and loaded */ bool LoadModule(const char* filename); /** Unload a given module file * @param filename The file to unload * @return True if the module was unloaded */ bool UnloadModule(const char* filename); /** This constructor initialises all the subsystems and reads the config file. * @param argc The argument count passed to main() * @param argv The argument list passed to main() * @throw <anything> If anything is thrown from here and makes it to * you, you should probably just give up and go home. Yes, really. * It's that bad. Higher level classes should catch any non-fatal exceptions. */ InspIRCd(int argc, char** argv); /** Do one iteration of the mainloop * @param process_module_sockets True if module sockets are to be processed * this time around the event loop. The is the default. */ void DoOneIteration(bool process_module_sockets = true); /** Output a log message to the ircd.log file * The text will only be output if the current loglevel * is less than or equal to the level you provide * @param level A log level from the DebugLevel enum * @param text Format string of to write to the log * @param ... Format arguments of text to write to the log */ void Log(int level, const char* text, ...); /** Output a log message to the ircd.log file * The text will only be output if the current loglevel * is less than or equal to the level you provide * @param level A log level from the DebugLevel enum * @param text Text to write to the log */ void Log(int level, 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 text Text of the numeric */ void SendWhoisLine(userrec* user, userrec* 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(userrec* user, userrec* dest, int numeric, const char* format, ...); /** Quit a user for excess flood, and if they are not * fully registered yet, temporarily zline their IP. * @param current user to quit */ void FloodQuitUser(userrec* current); /** Restart the server. * This function will not return. If an error occurs, * it will throw an instance of CoreException. * @param reason The restart reason to show to all clients * @throw CoreException An instance of CoreException indicating the error from execv(). */ void Restart(const std::string &reason); /** Prepare the ircd for restart or shutdown. * This function unloads all modules which can be unloaded, * closes all open sockets, and closes the logfile. */ void Cleanup(); /** This copies the user and channel hash_maps into new hash maps. * This frees memory used by the hash_map allocator (which it neglects * to free, most of the time, using tons of ram) */ void RehashUsersAndChans(); /** Resets the cached max bans value on all channels. * Called by rehash. */ void ResetMaxBans(); /** Return a time_t as a human-readable string. */ std::string TimeString(time_t curtime); /** Begin execution of the server. * NOTE: this function NEVER returns. Internally, * after performing some initialisation routines, * it will repeatedly call DoOneIteration in a loop. * @return The return value for this function is undefined. */ int Run(); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __INSPIRCD_H__
+#define __INSPIRCD_H__
+
+#ifndef WIN32
+#define DllExport
+#define CoreExport
+#define printf_c printf
+#else
+#include "inspircd_win32wrapper.h"
+/** Windows defines these already */
+#undef DELETE
+#undef ERROR
+#endif
+
+#include <time.h>
+#include <string>
+#include <sstream>
+#include "inspircd_config.h"
+#include "users.h"
+#include "channels.h"
+#include "socket.h"
+#include "mode.h"
+#include "socketengine.h"
+#include "command_parse.h"
+#include "snomasks.h"
+#include "cull_list.h"
+
+/** 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
+
+/** Debug levels for use with InspIRCd::Log()
+ */
+enum DebugLevel
+{
+ DEBUG = 10,
+ VERBOSE = 20,
+ DEFAULT = 30,
+ SPARSE = 40,
+ NONE = 50
+};
+
+/**
+ * This define is used in place of strcmp when we
+ * want to check if a char* string contains only one
+ * letter. Pretty fast, its just two compares and an
+ * addition.
+ */
+#define IS_SINGLE(x,y) ( (*x == y) && (*(x+1) == 0) )
+
+/** Delete a pointer, and NULL its value
+ */
+template<typename T> inline void DELETE(T* x)
+{
+ delete x;
+ x = NULL;
+}
+
+/** Template function to convert any input type to std::string
+ */
+template<typename T> inline std::string ConvNumeric(const T &in)
+{
+ if (in == 0) return "0";
+ char res[MAXBUF];
+ char* out = res;
+ T quotient = in;
+ while (quotient) {
+ *out = "0123456789"[ std::abs( (long)quotient % 10 ) ];
+ ++out;
+ quotient /= 10;
+ }
+ if ( in < 0)
+ *out++ = '-';
+ *out = 0;
+ std::reverse(res,out);
+ return res;
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const int in)
+{
+ return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const long in)
+{
+ return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const unsigned long in)
+{
+ return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const char* in)
+{
+ return in;
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const bool in)
+{
+ return (in ? "1" : "0");
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(char in)
+{
+ return std::string(in,1);
+}
+
+/** Template function to convert any input type to std::string
+ */
+template <class T> inline std::string ConvToStr(const T &in)
+{
+ std::stringstream tmp;
+ if (!(tmp << in)) return std::string();
+ return tmp.str();
+}
+
+/** Template function to convert any input type to any other type
+ * (usually an integer or numeric type)
+ */
+template<typename T> inline long ConvToInt(const T &in)
+{
+ std::stringstream tmp;
+ if (!(tmp << in)) return 0;
+ return atoi(tmp.str().c_str());
+}
+
+/** Template function to convert integer to char, storing result in *res and
+ * also returning the pointer to res. Based on Stuart Lowe's C/C++ Pages.
+ * @param T input value
+ * @param V result value
+ * @param R base to convert to
+ */
+template<typename T, typename V, typename R> inline char* itoa(const T &in, V *res, R base)
+{
+ if (base < 2 || base > 16) { *res = 0; return res; }
+ char* out = res;
+ int quotient = in;
+ while (quotient) {
+ *out = "0123456789abcdef"[ std::abs( quotient % base ) ];
+ ++out;
+ quotient /= base;
+ }
+ if ( in < 0 && base == 10) *out++ = '-';
+ std::reverse( res, out );
+ *out = 0;
+ return res;
+}
+
+/** This class contains various STATS counters
+ * It is used by the InspIRCd class, which internally
+ * has an instance of it.
+ */
+class serverstats : public classbase
+{
+ public:
+ /** Number of accepted connections
+ */
+ unsigned long statsAccept;
+ /** Number of failed accepts
+ */
+ unsigned long statsRefused;
+ /** Number of unknown commands seen
+ */
+ unsigned long statsUnknown;
+ /** Number of nickname collisions handled
+ */
+ unsigned long statsCollisions;
+ /** Number of DNS queries sent out
+ */
+ unsigned long statsDns;
+ /** 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;
+ /** 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;
+ /** Number of inbound connections seen
+ */
+ unsigned long statsConnects;
+ /** Total bytes of data transmitted
+ */
+ double statsSent;
+ /** Total bytes of data received
+ */
+ double statsRecv;
+ /** Cpu usage at last sample
+ */
+ timeval LastCPU;
+ /** Time last sample was read
+ */
+ timeval LastSampled;
+ /** 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.0), statsRecv(0.0)
+ {
+ }
+};
+
+/* Forward declaration -- required */
+class InspIRCd;
+
+/** This class implements a nonblocking log-writer.
+ * Most people writing an ircd give little thought to their disk
+ * i/o. On a congested system, disk writes can block for long
+ * periods of time (e.g. if the system is busy and/or swapping
+ * a lot). If we just use a blocking fprintf() call, this could
+ * block for undesirable amounts of time (half of a second through
+ * to whole seconds). We DO NOT want this, so we make our logfile
+ * nonblocking and hook it into the SocketEngine.
+ * NB: If the operating system does not support nonblocking file
+ * I/O (linux seems to, as does freebsd) this will default to
+ * blocking behaviour.
+ */
+class CoreExport FileLogger : public EventHandler
+{
+ protected:
+ /** The creator/owner of this object
+ */
+ InspIRCd* ServerInstance;
+ /** The log file (fd is inside this somewhere,
+ * we get it out with fileno())
+ */
+ FILE* log;
+ /** Buffer of pending log lines to be written
+ */
+ std::string buffer;
+ /** Number of write operations that have occured
+ */
+ int writeops;
+ public:
+ /** The constructor takes an already opened logfile.
+ */
+ FileLogger(InspIRCd* Instance, FILE* logfile);
+ /** This returns false, logfiles are writeable.
+ */
+ virtual bool Readable();
+ /** Handle pending write events.
+ * This will flush any waiting data to disk.
+ * If any data remains after the fprintf call,
+ * another write event is scheduled to write
+ * the rest of the data when possible.
+ */
+ virtual void HandleEvent(EventType et, int errornum = 0);
+ /** Write one or more preformatted log lines.
+ * If the data cannot be written immediately,
+ * this class will insert itself into the
+ * SocketEngine, and register a write event,
+ * and when the write event occurs it will
+ * attempt again to write the data.
+ */
+ void WriteLogLine(const std::string &line);
+ /** Close the log file and cancel any events.
+ */
+ virtual void Close();
+ /** Close the log file and cancel any events.
+ * (indirectly call Close()
+ */
+ virtual ~FileLogger();
+};
+
+/** A list of failed port bindings, used for informational purposes on startup */
+typedef std::vector<std::pair<std::string, long> > FailedPortList;
+
+/** A list of ip addresses cross referenced against clone counts */
+typedef std::map<irc::string, unsigned int> clonemap;
+
+/* Forward declaration - required */
+class XLineManager;
+
+/** The main class of the irc server.
+ * This class contains instances of all the other classes
+ * in this software, with the exception of the base class,
+ * classbase. Amongst other things, it contains a ModeParser,
+ * a DNS object, a CommandParser object, and a list of active
+ * Module objects, and facilities for Module objects to
+ * interact with the core system it implements. You should
+ * NEVER attempt to instantiate a class of type InspIRCd
+ * yourself. If you do, this is equivalent to spawning a second
+ * IRC server, and could have catastrophic consequences for the
+ * program in terms of ram usage (basically, you could create
+ * an obese forkbomb built from recursively spawning irc servers!)
+ */
+class CoreExport InspIRCd : public classbase
+{
+ private:
+ /** Holds a string describing the last module error to occur
+ */
+ char MODERR[MAXBUF];
+
+ /** Remove a ModuleFactory pointer
+ * @param j Index number of the ModuleFactory to remove
+ */
+ void EraseFactory(int j);
+
+ /** Remove a Module pointer
+ * @param j Index number of the Module to remove
+ */
+ void EraseModule(int j);
+
+ /** Move a given module to a specific slot in the list
+ * @param modulename The module name to relocate
+ * @param slot The slot to move the module into
+ */
+ void MoveTo(std::string modulename,int slot);
+
+ /** Display the startup banner
+ */
+ void Start();
+
+ /** Set up the signal handlers
+ */
+ void SetSignals();
+
+ /** Daemonize the ircd and close standard input/output streams
+ * @return True if the program daemonized succesfully
+ */
+ bool DaemonSeed();
+
+ /** Moves the given module to the last slot in the list
+ * @param modulename The module name to relocate
+ */
+ void MoveToLast(std::string modulename);
+
+ /** Moves the given module to the first slot in the list
+ * @param modulename The module name to relocate
+ */
+ void MoveToFirst(std::string modulename);
+
+ /** Moves one module to be placed after another in the list
+ * @param modulename The module name to relocate
+ * @param after The module name to place the module after
+ */
+ void MoveAfter(std::string modulename, std::string after);
+
+ /** Moves one module to be placed before another in the list
+ * @param modulename The module name to relocate
+ * @param after The module name to place the module before
+ */
+ void MoveBefore(std::string modulename, std::string before);
+
+ /** Iterate the list of InspSocket objects, removing ones which have timed out
+ * @param TIME the current time
+ */
+ void DoSocketTimeouts(time_t TIME);
+
+ /** Perform background user events such as PING checks
+ * @param TIME the current time
+ */
+ void DoBackgroundUserStuff(time_t TIME);
+
+ /** 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(userrec* user);
+
+ /** Total number of modules loaded into the ircd, minus one
+ */
+ int ModCount;
+
+ /** Logfile pathname specified on the commandline, or empty string
+ */
+ char LogFileName[MAXBUF];
+
+ /** The feature names published by various modules
+ */
+ featurelist Features;
+
+ /** The interface names published by various modules
+ */
+ interfacelist Interfaces;
+
+ /** The current time, updated in the mainloop
+ */
+ time_t TIME;
+
+ /** The time that was recorded last time around the mainloop
+ */
+ time_t OLDTIME;
+
+ /** A 64k buffer used to read client lines into
+ */
+ char ReadBuffer[65535];
+
+ /** Used when connecting clients
+ */
+ insp_sockaddr client, server;
+
+ /** Used when connecting clients
+ */
+ socklen_t length;
+
+ /** Nonblocking file writer
+ */
+ FileLogger* Logger;
+
+ /** Time offset in seconds
+ * This offset is added to all calls to Time(). Use SetTimeDelta() to update
+ */
+ int time_delta;
+
+ public:
+
+ /** InspSocket classes pending deletion after being closed.
+ * We don't delete these immediately as this may cause a segmentation fault.
+ */
+ std::map<InspSocket*,InspSocket*> SocketCull;
+
+ /** Build the ISUPPORT string by triggering all modules On005Numeric events
+ */
+ void BuildISupport();
+
+ /** Number of unregistered users online right now.
+ * (Unregistered means before USER/NICK/dns)
+ */
+ int unregistered_count;
+
+ /** List of server names we've seen.
+ */
+ servernamelist servernames;
+
+ /** Time this ircd was booted
+ */
+ time_t startup_time;
+
+ /** Config file pathname specified on the commandline or via ./configure
+ */
+ char ConfigFileName[MAXBUF];
+
+ /** Mode handler, handles mode setting and removal
+ */
+ ModeParser* Modes;
+
+ /** Command parser, handles client to server commands
+ */
+ CommandParser* Parser;
+
+ /** Socket engine, handles socket activity events
+ */
+ SocketEngine* SE;
+
+ /** Stats class, holds miscellaneous stats counters
+ */
+ serverstats* stats;
+
+ /** Server Config class, holds configuration file data
+ */
+ ServerConfig* Config;
+
+ /** Snomask manager - handles routing of snomask messages
+ * to opers.
+ */
+ SnomaskManager* SNO;
+
+ /** Client list, a hash_map containing all clients, local and remote
+ */
+ user_hash* clientlist;
+
+ /** Channel list, a hash_map containing all channels
+ */
+ chan_hash* chanlist;
+
+ /** Local client list, a vector containing only local clients
+ */
+ std::vector<userrec*> local_users;
+
+ /** Oper list, a vector containing all local and remote opered users
+ */
+ std::vector<userrec*> all_opers;
+
+ /** Map of local ip addresses for clone counting
+ */
+ clonemap local_clones;
+
+ /** Map of global ip addresses for clone counting
+ */
+ clonemap global_clones;
+
+ /** DNS class, provides resolver facilities to the core and modules
+ */
+ DNS* Res;
+
+ /** Timer manager class, triggers InspTimer timer events
+ */
+ TimerManager* Timers;
+
+ /** X-Line manager. Handles G/K/Q/E line setting, removal and matching
+ */
+ XLineManager* XLines;
+
+ /** A list of Module* module classes
+ * Note that this list is always exactly 255 in size.
+ * The actual number of loaded modules is available from GetModuleCount()
+ */
+ ModuleList modules;
+
+ /** A list of ModuleFactory* module factories
+ * Note that this list is always exactly 255 in size.
+ * The actual number of loaded modules is available from GetModuleCount()
+ */
+ FactoryList factory;
+
+ /** The time we next call our ping timeout and reg timeout checks
+ */
+ time_t next_call;
+
+ /** Global cull list, will be processed on next iteration
+ */
+ CullList GlobalCulls;
+
+ /** Get the current time
+ * Because this only calls time() once every time around the mainloop,
+ * it is much faster than calling time() directly.
+ * @param delta True to use the delta as an offset, false otherwise
+ * @return The current time as an epoch value (time_t)
+ */
+ time_t Time(bool delta = false);
+
+ /** Set the time offset in seconds
+ * This offset is added to Time() to offset the system time by the specified
+ * number of seconds.
+ * @param delta The number of seconds to offset
+ * @return The old time delta
+ */
+ int SetTimeDelta(int delta);
+
+ /** Add a user to the local clone map
+ * @param user The user to add
+ */
+ void AddLocalClone(userrec* user);
+
+ /** Add a user to the global clone map
+ * @param user The user to add
+ */
+ void AddGlobalClone(userrec* user);
+
+ /** Number of users with a certain mode set on them
+ */
+ int ModeCount(const char mode);
+
+ /** Get the time offset in seconds
+ * @return The current time delta (in seconds)
+ */
+ int GetTimeDelta();
+
+ /** Process a user whos socket has been flagged as active
+ * @param cu The user to process
+ * @return There is no actual return value, however upon exit, the user 'cu' may have been
+ * marked for deletion in the global CullList.
+ */
+ void ProcessUser(userrec* cu);
+
+ /** Get the total number of currently loaded modules
+ * @return The number of loaded modules
+ */
+ int GetModuleCount();
+
+ /** Find a module by name, and return a Module* to it.
+ * This is preferred over iterating the module lists yourself.
+ * @param name The module name to look up
+ * @return A pointer to the module, or NULL if the module cannot be found
+ */
+ Module* FindModule(const std::string &name);
+
+ /** Bind all ports specified in the configuration file.
+ * @param bail True if the function should bail back to the shell on failure
+ * @param found_ports The actual number of ports found in the config, as opposed to the number actually bound
+ * @return The number of ports actually bound without error
+ */
+ int BindPorts(bool bail, int &found_ports, 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)
+ * @return True if the port was bound successfully
+ */
+ bool BindSocket(int sockfd, int port, char* addr, bool dolisten = true);
+
+ /** Adds a server name to the list of servers we've seen
+ * @param The servername to add
+ */
+ void AddServerName(const std::string &servername);
+
+ /** Finds a cached char* pointer of a server name,
+ * This is used to optimize userrec by storing only the pointer to the name
+ * @param The servername to find
+ * @return A pointer to this name, gauranteed to never become invalid
+ */
+ const char* FindServerNamePtr(const std::string &servername);
+
+ /** Returns true if we've seen the given server name before
+ * @param The servername to find
+ * @return True if we've seen this server name before
+ */
+ bool FindServerName(const std::string &servername);
+
+ /** 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 char* servername);
+
+ /** Write text to all opers connected to this server
+ * @param text The text format string
+ * @param ... Format args
+ */
+ void WriteOpers(const char* text, ...);
+
+ /** Write text to all opers connected to this server
+ * @param text The text to send
+ */
+ void WriteOpers(const std::string &text);
+
+ /** Find a nickname in the nick hash
+ * @param nick The nickname to find
+ * @return A pointer to the user, or NULL if the user does not exist
+ */
+ userrec* FindNick(const std::string &nick);
+
+ /** Find a nickname in the nick hash
+ * @param nick The nickname to find
+ * @return A pointer to the user, or NULL if the user does not exist
+ */
+ userrec* FindNick(const char* nick);
+
+ /** 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
+ */
+ chanrec* 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
+ */
+ chanrec* FindChan(const char* chan);
+
+ /** Called by the constructor to load all modules from the config file.
+ */
+ void LoadAllModules();
+
+ /** Check for a 'die' tag in the config file, and abort if found
+ * @return Depending on the configuration, this function may never return
+ */
+ void CheckDie();
+
+ /** 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
+ */
+ void OpenLog(char** argv, int argc);
+
+ /** Close the currently open log file
+ */
+ void CloseLog();
+
+ /** Send a server notice to all local users
+ * @param text The text format string to send
+ * @param ... The format arguments
+ */
+ void ServerNoticeAll(char* text, ...);
+
+ /** Send a server message (PRIVMSG) to all local users
+ * @param text The text format string to send
+ * @param ... The format arguments
+ */
+ void ServerPrivmsgAll(char* text, ...);
+
+ /** Send text to all users with a specific set of modes
+ * @param modes The modes to check against, without a +, e.g. 'og'
+ * @param flags one of WM_OR or WM_AND. If you specify WM_OR, any one of the
+ * mode characters in the first parameter causes receipt of the message, and
+ * if you specify WM_OR, all the modes must be present.
+ * @param text The text format string to send
+ * @param ... The format arguments
+ */
+ void WriteMode(const char* modes, int flags, const char* text, ...);
+
+ /** Return true if a channel name is valid
+ * @param chname A channel name to verify
+ * @return True if the name is valid
+ */
+ bool IsChannel(const char *chname);
+
+ /** Rehash the local server
+ * @param status This value is unused, and required for signal handler functions
+ */
+ static void Rehash(int status);
+
+ /** Causes the server to exit after unloading modules and
+ * closing all open file descriptors.
+ *
+ * @param The exit code to give to the operating system
+ * (See the ExitStatus enum for valid values)
+ */
+ static void Exit(int status);
+
+ /** Causes the server to exit immediately with exit code 0.
+ * The status code is required for signal handlers, and ignored.
+ */
+ static void QuickExit(int status);
+
+ /** Return a count of users, unknown and known connections
+ * @return The number of users
+ */
+ int UserCount();
+
+ /** Return a count of fully registered connections only
+ * @return The number of registered users
+ */
+ int RegisteredUserCount();
+
+ /** Return a count of invisible (umode +i) users only
+ * @return The number of invisible users
+ */
+ int InvisibleUserCount();
+
+ /** Return a count of opered (umode +o) users only
+ * @return The number of opers
+ */
+ int OperCount();
+
+ /** Return a count of unregistered (before NICK/USER) users only
+ * @return The number of unregistered (unknown) connections
+ */
+ int UnregisteredUserCount();
+
+ /** Return a count of channels on the network
+ * @return The number of channels
+ */
+ long ChannelCount();
+
+ /** Return a count of local users on this server only
+ * @return The number of local users
+ */
+ long LocalUserCount();
+
+ /** Send an error notice to all local users, opered and unopered
+ * @param s The error string to send
+ */
+ void SendError(const std::string &s);
+
+ /** For use with Module::Prioritize().
+ * When the return value of this function is returned from
+ * Module::Prioritize(), this specifies that the module wishes
+ * to be ordered exactly BEFORE 'modulename'. For more information
+ * please see Module::Prioritize().
+ * @param modulename The module your module wants to be before in the call list
+ * @returns a priority ID which the core uses to relocate the module in the list
+ */
+ long PriorityBefore(const std::string &modulename);
+
+ /** For use with Module::Prioritize().
+ * When the return value of this function is returned from
+ * Module::Prioritize(), this specifies that the module wishes
+ * to be ordered exactly AFTER 'modulename'. For more information please
+ * see Module::Prioritize().
+ * @param modulename The module your module wants to be after in the call list
+ * @returns a priority ID which the core uses to relocate the module in the list
+ */
+ long PriorityAfter(const std::string &modulename);
+
+ /** Publish a 'feature'.
+ * There are two ways for a module to find another module it depends on.
+ * Either by name, using InspIRCd::FindModule, or by feature, using this
+ * function. A feature is an arbitary string which identifies something this
+ * module can do. For example, if your module provides SSL support, but other
+ * modules provide SSL support too, all the modules supporting SSL should
+ * publish an identical 'SSL' feature. This way, any module requiring use
+ * of SSL functions can just look up the 'SSL' feature using FindFeature,
+ * then use the module pointer they are given.
+ * @param FeatureName The case sensitive feature name to make available
+ * @param Mod a pointer to your module class
+ * @returns True on success, false if the feature is already published by
+ * another module.
+ */
+ bool PublishFeature(const std::string &FeatureName, Module* Mod);
+
+ /** Publish a module to an 'interface'.
+ * Modules which implement the same interface (the same way of communicating
+ * with other modules) can publish themselves to an interface, using this
+ * method. When they do so, they become part of a list of related or
+ * compatible modules, and a third module may then query for that list
+ * and know that all modules within that list offer the same API.
+ * A prime example of this is the hashing modules, which all accept the
+ * same types of Request class. Consider this to be similar to PublishFeature,
+ * except for that multiple modules may publish the same 'feature'.
+ * @param InterfaceName The case sensitive interface name to make available
+ * @param Mod a pointer to your module class
+ * @returns True on success, false on failure (there are currently no failure
+ * cases)
+ */
+ bool PublishInterface(const std::string &InterfaceName, Module* Mod);
+
+ /** Return a pair saying how many other modules are currently using the
+ * interfaces provided by module m.
+ * @param m The module to count usage for
+ * @return A pair, where the first value is the number of uses of the interface,
+ * and the second value is the interface name being used.
+ */
+ std::pair<int,std::string> GetInterfaceInstanceCount(Module* m);
+
+ /** Mark your module as using an interface.
+ * If you mark your module as using an interface, then that interface
+ * module may not unload until your module has unloaded first.
+ * This can be used to prevent crashes by ensuring code you depend on
+ * is always in memory while your module is active.
+ * @param InterfaceName The interface to use
+ */
+ void UseInterface(const std::string &InterfaceName);
+
+ /** Mark your module as finished with an interface.
+ * If you used UseInterface() above, you should use this method when
+ * your module is finished with the interface (usually in its destructor)
+ * to allow the modules which implement the given interface to be unloaded.
+ * @param InterfaceName The interface you are finished with using.
+ */
+ void DoneWithInterface(const std::string &InterfaceName);
+
+ /** Unpublish a 'feature'.
+ * When your module exits, it must call this method for every feature it
+ * is providing so that the feature table is cleaned up.
+ * @param FeatureName the feature to remove
+ */
+ bool UnpublishFeature(const std::string &FeatureName);
+
+ /** Unpublish your module from an interface
+ * When your module exits, it must call this method for every interface
+ * it is part of so that the interfaces table is cleaned up. Only when
+ * the last item is deleted from an interface does the interface get
+ * removed.
+ * @param InterfaceName the interface to be removed from
+ * @param Mod The module to remove from the interface list
+ */
+ bool UnpublishInterface(const std::string &InterfaceName, Module* Mod);
+
+ /** Find a 'feature'.
+ * There are two ways for a module to find another module it depends on.
+ * Either by name, using InspIRCd::FindModule, or by feature, using the
+ * InspIRCd::PublishFeature method. A feature is an arbitary string which
+ * identifies something this module can do. For example, if your module
+ * provides SSL support, but other modules provide SSL support too, all
+ * the modules supporting SSL should publish an identical 'SSL' feature.
+ * To find a module capable of providing the feature you want, simply
+ * call this method with the feature name you are looking for.
+ * @param FeatureName The feature name you wish to obtain the module for
+ * @returns A pointer to a valid module class on success, NULL on failure.
+ */
+ Module* FindFeature(const std::string &FeatureName);
+
+ /** Find an 'interface'.
+ * An interface is a list of modules which all implement the same API.
+ * @param InterfaceName The Interface you wish to obtain the module
+ * list of.
+ * @return A pointer to a deque of Module*, or NULL if the interface
+ * does not exist.
+ */
+ modulelist* FindInterface(const std::string &InterfaceName);
+
+ /** Given a pointer to a Module, return its filename
+ * @param m The module pointer to identify
+ * @return The module name or an empty string
+ */
+ const std::string& GetModuleName(Module* m);
+
+ /** Return true if a nickname is valid
+ * @param n A nickname to verify
+ * @return True if the nick is valid
+ */
+ bool IsNick(const char* n);
+
+ /** Return true if an ident is valid
+ * @param An ident to verify
+ * @return True if the ident is valid
+ */
+ bool IsIdent(const char* n);
+
+ /** Find a username by their file descriptor.
+ * It is preferred to use this over directly accessing the fd_ref_table array.
+ * @param socket The file descriptor of a user
+ * @return A pointer to the user if the user exists locally on this descriptor
+ */
+ userrec* FindDescriptor(int socket);
+
+ /** Add a new mode to this server's mode parser
+ * @param mh The modehandler to add
+ * @param modechar The mode character this modehandler handles
+ * @return True if the mode handler was added
+ */
+ bool AddMode(ModeHandler* mh, const unsigned char modechar);
+
+ /** Add a new mode watcher to this server's mode parser
+ * @param mw The modewatcher to add
+ * @return True if the modewatcher was added
+ */
+ bool AddModeWatcher(ModeWatcher* mw);
+
+ /** Delete a mode watcher from this server's mode parser
+ * @param mw The modewatcher to delete
+ * @return True if the modewatcher was deleted
+ */
+ bool DelModeWatcher(ModeWatcher* mw);
+
+ /** 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_t command handler object to add
+ * @throw ModuleException Will throw ModuleExcption if the command already exists
+ */
+ void AddCommand(command_t *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 pcnt The number of items you have given in the first parameter
+ * @param user The user to send error messages to
+ */
+ void SendMode(const char **parameters, int pcnt, userrec *user);
+
+ /** Match two strings using pattern matching.
+ * This operates identically to the global function match(),
+ * except for that it takes std::string arguments rather than
+ * const char* ones.
+ * @param sliteral The literal string to match against
+ * @param spattern The pattern to match against. CIDR and globs are supported.
+ */
+ bool MatchText(const std::string &sliteral, const std::string &spattern);
+
+ /** Call the handler for a given command.
+ * @param commandname The command whos handler you wish to call
+ * @param parameters The mode parameters
+ * @param pcnt The number of items you have given in the first parameter
+ * @param user The user to execute the command as
+ * @return True if the command handler was called successfully
+ */
+ CmdResult CallCommandHandler(const std::string &commandname, const char** parameters, int pcnt, userrec* user);
+
+ /** Return true if the command is a module-implemented command and the given parameters are valid for it
+ * @param parameters The mode parameters
+ * @param pcnt The number of items you have given in the first parameter
+ * @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, userrec* user);
+
+ /** Add a gline and apply it
+ * @param duration How long the line should last
+ * @param source Who set the line
+ * @param reason The reason for the line
+ * @param hostmask The hostmask to set the line against
+ */
+ void AddGLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask);
+
+ /** Add a qline and apply it
+ * @param duration How long the line should last
+ * @param source Who set the line
+ * @param reason The reason for the line
+ * @param nickname The nickmask to set the line against
+ */
+ void AddQLine(long duration, const std::string &source, const std::string &reason, const std::string &nickname);
+
+ /** Add a zline and apply it
+ * @param duration How long the line should last
+ * @param source Who set the line
+ * @param reason The reason for the line
+ * @param ipaddr The ip-mask to set the line against
+ */
+ void AddZLine(long duration, const std::string &source, const std::string &reason, const std::string &ipaddr);
+
+ /** Add a kline and apply it
+ * @param duration How long the line should last
+ * @param source Who set the line
+ * @param reason The reason for the line
+ * @param hostmask The hostmask to set the line against
+ */
+ void AddKLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask);
+
+ /** Add an eline
+ * @param duration How long the line should last
+ * @param source Who set the line
+ * @param reason The reason for the line
+ * @param hostmask The hostmask to set the line against
+ */
+ void AddELine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask);
+
+ /** Delete a gline
+ * @param hostmask The gline to delete
+ * @return True if the item was removed
+ */
+ bool DelGLine(const std::string &hostmask);
+
+ /** Delete a qline
+ * @param nickname The qline to delete
+ * @return True if the item was removed
+ */
+ bool DelQLine(const std::string &nickname);
+
+ /** Delete a zline
+ * @param ipaddr The zline to delete
+ * @return True if the item was removed
+ */
+ bool DelZLine(const std::string &ipaddr);
+
+ /** Delete a kline
+ * @param hostmask The kline to delete
+ * @return True if the item was removed
+ */
+ bool DelKLine(const std::string &hostmask);
+
+ /** Delete an eline
+ * @param hostmask The kline to delete
+ * @return True if the item was removed
+ */
+ bool DelELine(const std::string &hostmask);
+
+ /** 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);
+
+ /** Rehash the local server
+ */
+ void RehashServer();
+
+ /** Return the channel whos index number matches that provided
+ * @param The index number of the channel to fetch
+ * @return A channel record, or NUll if index < 0 or index >= InspIRCd::ChannelCount()
+ */
+ chanrec* GetChannelIndex(long index);
+
+ /** Dump text to a user target, splitting it appropriately to fit
+ * @param User the user to dump the text to
+ * @param LinePrefix text to prefix each complete line with
+ * @param TextStream the text to send to the user
+ */
+ void DumpText(userrec* User, const std::string &LinePrefix, stringstream &TextStream);
+
+ /** 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, userrec* 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, userrec* 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, userrec* user);
+
+ /** 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);
+
+ /** Attempt to compare an oper password to a string from the config file.
+ * This will be passed to handling modules which will compare the data
+ * against possible hashed equivalents in the input string.
+ * @param data The data from the config file
+ * @param input The data input by the oper
+ * @param tagnum the tag number of the oper's tag in the config file
+ * @return 0 if the strings match, 1 or -1 if they do not
+ */
+ int OperPassCompare(const char* data,const char* input, int tagnum);
+
+ /** 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 char* server);
+
+ /** Returns true if the uline is 'silent' (doesnt generate
+ * remote connect notices etc).
+ */
+ bool SilentULine(const char* server);
+
+ /** Returns the subversion revision ID of this ircd
+ * @return The revision ID or an empty string
+ */
+ std::string GetRevision();
+
+ /** Returns the full version string of this ircd
+ * @return The version string
+ */
+ std::string GetVersionString();
+
+ /** Attempt to write the process id to a given file
+ * @param filename The PID file to attempt to write to
+ * @return This function may bail if the file cannot be written
+ */
+ void WritePID(const std::string &filename);
+
+ /** Returns text describing the last module error
+ * @return The last error message to occur
+ */
+ char* ModuleError();
+
+ /** Load a given module file
+ * @param filename The file to load
+ * @return True if the module was found and loaded
+ */
+ bool LoadModule(const char* filename);
+
+ /** Unload a given module file
+ * @param filename The file to unload
+ * @return True if the module was unloaded
+ */
+ bool UnloadModule(const char* filename);
+
+ /** This constructor initialises all the subsystems and reads the config file.
+ * @param argc The argument count passed to main()
+ * @param argv The argument list passed to main()
+ * @throw <anything> If anything is thrown from here and makes it to
+ * you, you should probably just give up and go home. Yes, really.
+ * It's that bad. Higher level classes should catch any non-fatal exceptions.
+ */
+ InspIRCd(int argc, char** argv);
+
+ /** Do one iteration of the mainloop
+ * @param process_module_sockets True if module sockets are to be processed
+ * this time around the event loop. The is the default.
+ */
+ void DoOneIteration(bool process_module_sockets = true);
+
+ /** Output a log message to the ircd.log file
+ * The text will only be output if the current loglevel
+ * is less than or equal to the level you provide
+ * @param level A log level from the DebugLevel enum
+ * @param text Format string of to write to the log
+ * @param ... Format arguments of text to write to the log
+ */
+ void Log(int level, const char* text, ...);
+
+ /** Output a log message to the ircd.log file
+ * The text will only be output if the current loglevel
+ * is less than or equal to the level you provide
+ * @param level A log level from the DebugLevel enum
+ * @param text Text to write to the log
+ */
+ void Log(int level, 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 text Text of the numeric
+ */
+ void SendWhoisLine(userrec* user, userrec* 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(userrec* user, userrec* dest, int numeric, const char* format, ...);
+
+ /** Quit a user for excess flood, and if they are not
+ * fully registered yet, temporarily zline their IP.
+ * @param current user to quit
+ */
+ void FloodQuitUser(userrec* current);
+
+ /** Restart the server.
+ * This function will not return. If an error occurs,
+ * it will throw an instance of CoreException.
+ * @param reason The restart reason to show to all clients
+ * @throw CoreException An instance of CoreException indicating the error from execv().
+ */
+ void Restart(const std::string &reason);
+
+ /** Prepare the ircd for restart or shutdown.
+ * This function unloads all modules which can be unloaded,
+ * closes all open sockets, and closes the logfile.
+ */
+ void Cleanup();
+
+ /** This copies the user and channel hash_maps into new hash maps.
+ * This frees memory used by the hash_map allocator (which it neglects
+ * to free, most of the time, using tons of ram)
+ */
+ void RehashUsersAndChans();
+
+ /** Resets the cached max bans value on all channels.
+ * Called by rehash.
+ */
+ void ResetMaxBans();
+
+ /** Return a time_t as a human-readable string.
+ */
+ std::string TimeString(time_t curtime);
+
+ /** Begin execution of the server.
+ * NOTE: this function NEVER returns. Internally,
+ * after performing some initialisation routines,
+ * it will repeatedly call DoOneIteration in a loop.
+ * @return The return value for this function is undefined.
+ */
+ int Run();
+};
+
+#endif
+
diff --git a/include/inspsocket.h b/include/inspsocket.h
index 98fbc018f..d165d64f2 100644
--- a/include/inspsocket.h
+++ b/include/inspsocket.h
@@ -1 +1,437 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __INSP_SOCKET_H__ #define __INSP_SOCKET_H__ #include <sstream> #include <string> #include <deque> #include "dns.h" #include "inspircd_config.h" #include "socket.h" #include "inspsocket.h" #include "timer.h" /** * States which a socket may be in */ enum InspSocketState { /** Socket disconnected */ I_DISCONNECTED, /** Socket connecting */ I_CONNECTING, /** Socket fully connected */ I_CONNECTED, /** Socket listening for connections */ I_LISTENING, /** Socket has an error */ I_ERROR }; /** * Error types which a socket may exhibit */ enum InspSocketError { /** Socket connect timed out */ I_ERR_TIMEOUT, /** Socket could not be created */ I_ERR_SOCKET, /** Socket could not connect (refused) */ I_ERR_CONNECT, /** Socket could not bind to local port/ip */ I_ERR_BIND, /** Socket could not reslve host (depreciated) */ I_ERR_RESOLVE, /** Socket could not write data */ I_ERR_WRITE, /** No more file descriptors left to create socket! */ I_ERR_NOMOREFDS }; /* Required forward declarations */ class InspSocket; class InspIRCd; using irc::sockets::insp_sockaddr; using irc::sockets::insp_inaddr; using irc::sockets::insp_ntoa; using irc::sockets::insp_aton; /** Used to time out socket connections */ class CoreExport SocketTimeout : public InspTimer { private: /** InspSocket the class is attached to */ InspSocket* sock; /** Server instance creating the timeout class */ InspIRCd* ServerInstance; /** File descriptor of class this is attached to */ int sfd; public: /** Create a socket timeout class * @param fd File descriptor of InspSocket * @pram Instance server instance to attach to * @param thesock InspSocket to attach to * @param secs_from_now Seconds from now to time out * @param now The current time */ SocketTimeout(int fd, InspIRCd* Instance, InspSocket* thesock, long secs_from_now, time_t now) : InspTimer(secs_from_now, now), sock(thesock), ServerInstance(Instance), sfd(fd) { }; /** Handle tick event */ virtual void Tick(time_t now); }; /** * InspSocket is an extendable socket class which modules * can use for TCP socket support. It is fully integrated * into InspIRCds socket loop and attaches its sockets to * the core's instance of the SocketEngine class, meaning * that any sockets you create have the same power and * abilities as a socket created by the core itself. * To use InspSocket, you must inherit a class from it, * and use the InspSocket constructors to establish connections * and bindings. */ class CoreExport InspSocket : public EventHandler { public: /** * Bind IP */ std::string cbindip; /** * Is hooked by a module for low level IO */ bool IsIOHooked; /** * Instance we were created by */ InspIRCd* Instance; /** * Timeout class or NULL */ SocketTimeout* Timeout; /** * Timeout length */ unsigned long timeout_val; /** * Socket output buffer (binary safe) */ std::deque<std::string> outbuffer; /** * The hostname connected to */ char host[MAXBUF]; /** * The port connected to, or the port * this socket is listening on */ int port; /** * The state for this socket, either * listening, connecting, connected * or error. */ InspSocketState state; /** * This value is true if the * socket has timed out. */ bool timeout; /** * Socket input buffer, used by read(). The class which * extends InspSocket is expected to implement an extendable * buffer which can grow much larger than 64k, * this buffer is just designed to be temporary storage. * space. */ char ibuf[65535]; /** * The IP address being connected * to stored in string form for * easy retrieval by accessors. */ char IP[MAXBUF]; /** * Used by accept() to indicate the * sizes of the sockaddr_in structures */ socklen_t length; /** Flushes the write buffer */ bool FlushWriteBuffer(); /** Set the queue sizes * This private method sets the operating system queue * sizes for this socket to 65535 so that it can queue * more information without application-level queueing * which was required in older software. */ void SetQueues(int nfd); /** When the socket has been marked as closing, this flag * will be set to true, then the next time the socket is * examined, the socket is deleted and closed. */ bool ClosePending; /** Set to true when we're waiting for a write event. * If this is true and a write event comes in, we * call the write instead of the read method. */ bool WaitingForWriteEvent; /** * Bind to an address * @param ip IP to bind to * @return True is the binding succeeded */ bool BindAddr(const std::string &ip); /** * The default constructor does nothing * and should not be used. */ InspSocket(InspIRCd* SI); /** * This constructor is used to associate * an existing connecting with an InspSocket * class. The given file descriptor must be * valid, and when initialized, the InspSocket * will be set with the given IP address * and placed in CONNECTED state. */ InspSocket(InspIRCd* SI, int newfd, const char* ip); /** * This constructor is used to create a new * socket, either listening for connections, or an outbound connection to another host. * Note that if you specify a hostname in the 'ipaddr' parameter, this class will not * connect. You must resolve your hostnames before passing them to InspSocket. To do so, * you should use the nonblocking class 'Resolver'. * @param ipaddr The IP to connect to, or bind to * @param port The port number to connect to, or bind to * @param listening true to listen on the given host:port pair, or false to connect to them * @param maxtime Number of seconds to wait, if connecting, before the connection times out and an OnTimeout() event is generated * @param connectbindip When creating an outbound connection, the IP to bind the connection to. If not defined, the port is not bound. * @return On exit, GetState() returns I_ERROR if an error occured, and errno can be used to read the socket error. */ InspSocket(InspIRCd* SI, const std::string &ipaddr, int port, bool listening, unsigned long maxtime, const std::string &connectbindip = ""); /** * This method is called when an outbound * connection on your socket is completed. * @return false to abort the connection, true to continue */ virtual bool OnConnected(); /** * This method is called when an error occurs. * A closed socket in itself is not an error, * however errors also generate close events. * @param e The error type which occured */ virtual void OnError(InspSocketError e); /** * When an established connection is terminated, * the OnDisconnect method is triggered. */ virtual int OnDisconnect(); /** * When there is data waiting to be read on a * socket, the OnDataReady() method is called. * Within this method, you *MUST* call the Read() * method to read any pending data. At its lowest * level, this event is signalled by the core via * the socket engine. If you return false from this * function, the core removes your socket from its * list and erases it from the socket engine, then * calls InspSocket::Close() and deletes it. * @return false to close the socket */ virtual bool OnDataReady(); /** * When it is ok to write to the socket, and a * write event was requested, this method is * triggered. Within this method you should call * write() or send() etc, to send data to the * other end of the socket. Further write events * will not be triggered unless you call WantWrite(). * @return false to close the socket */ virtual bool OnWriteReady(); /** * When an outbound connection fails, and the * attempt times out, you will receive this event. * The method will trigger once maxtime seconds are * reached (as given in the constructor) just * before the socket's descriptor is closed. * A failed DNS lookup may cause this event if * the DNS server is not responding, as well as * a failed connect() call, because DNS lookups are * nonblocking as implemented by this class. */ virtual void OnTimeout(); /** * Whenever close() is called, OnClose() will be * called first. Please note that this means * OnClose will be called alongside OnError(), * OnTimeout(), and Close(), and also when * cancelling a listening socket by calling * the destructor indirectly. */ virtual void OnClose(); /** * Reads all pending bytes from the socket * into a char* array which can be up to * 16 kilobytes in length. */ virtual char* Read(); /** * Returns the IP address associated with * this connection, or an empty string if * no IP address exists. */ std::string GetIP(); /** * Writes a std::string to the socket. No carriage * returns or linefeeds are appended to the string. * @param data The data to send */ virtual int Write(const std::string &data); /** * If your socket is a listening socket, when a new * connection comes in on the socket this method will * be called. Given the new file descriptor in the * parameters, and the IP, it is recommended you copy * them to a new instance of your socket class, * e.g.: * * MySocket* newsocket = new MySocket(newfd,ip); * * Once you have done this, you can then associate the * new socket with the core using Server::AddSocket(). */ virtual int OnIncomingConnection(int newfd, char* ip); /** * Changes the socket's state. The core uses this * to change socket states, and you should not call * it directly. */ void SetState(InspSocketState s); /** * Call this to receive the next write event * that comes along for this fd to the OnWriteReady * method. */ void WantWrite(); /** * Returns the current socket state. */ InspSocketState GetState(); /** * Only the core should call this function. * When called, it is assumed the socket is ready * to read data, and the method call routes the * event to the various methods of InspSocket * for you to handle. This can also cause the * socket's state to change. */ bool Poll(); /** * This method returns the socket's file descriptor * as assigned by the operating system, or -1 * if no descriptor has been assigned. */ int GetFd(); /** * This method causes the socket to close, and may * also be triggered by other methods such as OnTimeout * and OnError. */ virtual void Close(); /** * The destructor may implicitly call OnClose(), and * will close() and shutdown() the file descriptor * used for this socket. */ virtual ~InspSocket(); /** * This method attempts to connect to a hostname. * This only occurs on a non-listening socket. This * method is asyncronous. */ virtual bool DoConnect(); /** * This method marks the socket closed. * The next time the core examines a socket marked * as closed, the socket will be closed and the * memory reclaimed. * * NOTE: This method is DEPRECIATED and will be * ignored if called! */ void MarkAsClosed(); /** Handle event from EventHandler parent class */ void HandleEvent(EventType et, int errornum = 0); /** Returns true if this socket is readable */ bool Readable(); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __INSP_SOCKET_H__
+#define __INSP_SOCKET_H__
+
+#include <sstream>
+#include <string>
+#include <deque>
+#include "dns.h"
+#include "inspircd_config.h"
+#include "socket.h"
+#include "inspsocket.h"
+#include "timer.h"
+
+/**
+ * States which a socket may be in
+ */
+enum InspSocketState
+{
+ /** Socket disconnected */
+ I_DISCONNECTED,
+ /** Socket connecting */
+ I_CONNECTING,
+ /** Socket fully connected */
+ I_CONNECTED,
+ /** Socket listening for connections */
+ I_LISTENING,
+ /** Socket has an error */
+ I_ERROR
+};
+
+/**
+ * Error types which a socket may exhibit
+ */
+enum InspSocketError
+{
+ /** Socket connect timed out */
+ I_ERR_TIMEOUT,
+ /** Socket could not be created */
+ I_ERR_SOCKET,
+ /** Socket could not connect (refused) */
+ I_ERR_CONNECT,
+ /** Socket could not bind to local port/ip */
+ I_ERR_BIND,
+ /** Socket could not reslve host (depreciated) */
+ I_ERR_RESOLVE,
+ /** Socket could not write data */
+ I_ERR_WRITE,
+ /** No more file descriptors left to create socket! */
+ I_ERR_NOMOREFDS
+};
+
+/* Required forward declarations */
+class InspSocket;
+class InspIRCd;
+
+using irc::sockets::insp_sockaddr;
+using irc::sockets::insp_inaddr;
+using irc::sockets::insp_ntoa;
+using irc::sockets::insp_aton;
+
+/** Used to time out socket connections
+ */
+class CoreExport SocketTimeout : public InspTimer
+{
+ private:
+ /** InspSocket the class is attached to
+ */
+ InspSocket* sock;
+ /** Server instance creating the timeout class
+ */
+ InspIRCd* ServerInstance;
+ /** File descriptor of class this is attached to
+ */
+ int sfd;
+ public:
+ /** Create a socket timeout class
+ * @param fd File descriptor of InspSocket
+ * @pram Instance server instance to attach to
+ * @param thesock InspSocket to attach to
+ * @param secs_from_now Seconds from now to time out
+ * @param now The current time
+ */
+ SocketTimeout(int fd, InspIRCd* Instance, InspSocket* thesock, long secs_from_now, time_t now) : InspTimer(secs_from_now, now), sock(thesock), ServerInstance(Instance), sfd(fd) { };
+ /** Handle tick event
+ */
+ virtual void Tick(time_t now);
+};
+
+/**
+ * InspSocket is an extendable socket class which modules
+ * can use for TCP socket support. It is fully integrated
+ * into InspIRCds socket loop and attaches its sockets to
+ * the core's instance of the SocketEngine class, meaning
+ * that any sockets you create have the same power and
+ * abilities as a socket created by the core itself.
+ * To use InspSocket, you must inherit a class from it,
+ * and use the InspSocket constructors to establish connections
+ * and bindings.
+ */
+class CoreExport InspSocket : public EventHandler
+{
+ public:
+
+ /**
+ * Bind IP
+ */
+ std::string cbindip;
+
+ /**
+ * Is hooked by a module for low level IO
+ */
+ bool IsIOHooked;
+
+ /**
+ * Instance we were created by
+ */
+ InspIRCd* Instance;
+
+ /**
+ * Timeout class or NULL
+ */
+ SocketTimeout* Timeout;
+
+ /**
+ * Timeout length
+ */
+ unsigned long timeout_val;
+
+ /**
+ * Socket output buffer (binary safe)
+ */
+ std::deque<std::string> outbuffer;
+
+ /**
+ * The hostname connected to
+ */
+ char host[MAXBUF];
+
+ /**
+ * The port connected to, or the port
+ * this socket is listening on
+ */
+ int port;
+
+ /**
+ * The state for this socket, either
+ * listening, connecting, connected
+ * or error.
+ */
+ InspSocketState state;
+
+ /**
+ * This value is true if the
+ * socket has timed out.
+ */
+ bool timeout;
+
+ /**
+ * Socket input buffer, used by read(). The class which
+ * extends InspSocket is expected to implement an extendable
+ * buffer which can grow much larger than 64k,
+ * this buffer is just designed to be temporary storage.
+ * space.
+ */
+ char ibuf[65535];
+
+ /**
+ * The IP address being connected
+ * to stored in string form for
+ * easy retrieval by accessors.
+ */
+ char IP[MAXBUF];
+
+ /**
+ * Used by accept() to indicate the
+ * sizes of the sockaddr_in structures
+ */
+ socklen_t length;
+
+ /** Flushes the write buffer
+ */
+ bool FlushWriteBuffer();
+
+ /** Set the queue sizes
+ * This private method sets the operating system queue
+ * sizes for this socket to 65535 so that it can queue
+ * more information without application-level queueing
+ * which was required in older software.
+ */
+ void SetQueues(int nfd);
+
+ /** When the socket has been marked as closing, this flag
+ * will be set to true, then the next time the socket is
+ * examined, the socket is deleted and closed.
+ */
+ bool ClosePending;
+
+ /** Set to true when we're waiting for a write event.
+ * If this is true and a write event comes in, we
+ * call the write instead of the read method.
+ */
+ bool WaitingForWriteEvent;
+
+ /**
+ * Bind to an address
+ * @param ip IP to bind to
+ * @return True is the binding succeeded
+ */
+ bool BindAddr(const std::string &ip);
+
+ /**
+ * The default constructor does nothing
+ * and should not be used.
+ */
+ InspSocket(InspIRCd* SI);
+
+ /**
+ * This constructor is used to associate
+ * an existing connecting with an InspSocket
+ * class. The given file descriptor must be
+ * valid, and when initialized, the InspSocket
+ * will be set with the given IP address
+ * and placed in CONNECTED state.
+ */
+ InspSocket(InspIRCd* SI, int newfd, const char* ip);
+
+ /**
+ * This constructor is used to create a new
+ * socket, either listening for connections, or an outbound connection to another host.
+ * Note that if you specify a hostname in the 'ipaddr' parameter, this class will not
+ * connect. You must resolve your hostnames before passing them to InspSocket. To do so,
+ * you should use the nonblocking class 'Resolver'.
+ * @param ipaddr The IP to connect to, or bind to
+ * @param port The port number to connect to, or bind to
+ * @param listening true to listen on the given host:port pair, or false to connect to them
+ * @param maxtime Number of seconds to wait, if connecting, before the connection times out and an OnTimeout() event is generated
+ * @param connectbindip When creating an outbound connection, the IP to bind the connection to. If not defined, the port is not bound.
+ * @return On exit, GetState() returns I_ERROR if an error occured, and errno can be used to read the socket error.
+ */
+ InspSocket(InspIRCd* SI, const std::string &ipaddr, int port, bool listening, unsigned long maxtime, const std::string &connectbindip = "");
+
+ /**
+ * This method is called when an outbound
+ * connection on your socket is completed.
+ * @return false to abort the connection, true to continue
+ */
+ virtual bool OnConnected();
+
+ /**
+ * This method is called when an error occurs.
+ * A closed socket in itself is not an error,
+ * however errors also generate close events.
+ * @param e The error type which occured
+ */
+ virtual void OnError(InspSocketError e);
+
+ /**
+ * When an established connection is terminated,
+ * the OnDisconnect method is triggered.
+ */
+ virtual int OnDisconnect();
+
+ /**
+ * When there is data waiting to be read on a
+ * socket, the OnDataReady() method is called.
+ * Within this method, you *MUST* call the Read()
+ * method to read any pending data. At its lowest
+ * level, this event is signalled by the core via
+ * the socket engine. If you return false from this
+ * function, the core removes your socket from its
+ * list and erases it from the socket engine, then
+ * calls InspSocket::Close() and deletes it.
+ * @return false to close the socket
+ */
+ virtual bool OnDataReady();
+
+ /**
+ * When it is ok to write to the socket, and a
+ * write event was requested, this method is
+ * triggered. Within this method you should call
+ * write() or send() etc, to send data to the
+ * other end of the socket. Further write events
+ * will not be triggered unless you call WantWrite().
+ * @return false to close the socket
+ */
+ virtual bool OnWriteReady();
+
+ /**
+ * When an outbound connection fails, and the
+ * attempt times out, you will receive this event.
+ * The method will trigger once maxtime seconds are
+ * reached (as given in the constructor) just
+ * before the socket's descriptor is closed.
+ * A failed DNS lookup may cause this event if
+ * the DNS server is not responding, as well as
+ * a failed connect() call, because DNS lookups are
+ * nonblocking as implemented by this class.
+ */
+ virtual void OnTimeout();
+
+ /**
+ * Whenever close() is called, OnClose() will be
+ * called first. Please note that this means
+ * OnClose will be called alongside OnError(),
+ * OnTimeout(), and Close(), and also when
+ * cancelling a listening socket by calling
+ * the destructor indirectly.
+ */
+ virtual void OnClose();
+
+ /**
+ * Reads all pending bytes from the socket
+ * into a char* array which can be up to
+ * 16 kilobytes in length.
+ */
+ virtual char* Read();
+
+ /**
+ * Returns the IP address associated with
+ * this connection, or an empty string if
+ * no IP address exists.
+ */
+ std::string GetIP();
+
+ /**
+ * Writes a std::string to the socket. No carriage
+ * returns or linefeeds are appended to the string.
+ * @param data The data to send
+ */
+ virtual int Write(const std::string &data);
+
+ /**
+ * If your socket is a listening socket, when a new
+ * connection comes in on the socket this method will
+ * be called. Given the new file descriptor in the
+ * parameters, and the IP, it is recommended you copy
+ * them to a new instance of your socket class,
+ * e.g.:
+ *
+ * MySocket* newsocket = new MySocket(newfd,ip);
+ *
+ * Once you have done this, you can then associate the
+ * new socket with the core using Server::AddSocket().
+ */
+ virtual int OnIncomingConnection(int newfd, char* ip);
+
+ /**
+ * Changes the socket's state. The core uses this
+ * to change socket states, and you should not call
+ * it directly.
+ */
+ void SetState(InspSocketState s);
+
+ /**
+ * Call this to receive the next write event
+ * that comes along for this fd to the OnWriteReady
+ * method.
+ */
+ void WantWrite();
+
+ /**
+ * Returns the current socket state.
+ */
+ InspSocketState GetState();
+
+ /**
+ * Only the core should call this function.
+ * When called, it is assumed the socket is ready
+ * to read data, and the method call routes the
+ * event to the various methods of InspSocket
+ * for you to handle. This can also cause the
+ * socket's state to change.
+ */
+ bool Poll();
+
+ /**
+ * This method returns the socket's file descriptor
+ * as assigned by the operating system, or -1
+ * if no descriptor has been assigned.
+ */
+ int GetFd();
+
+ /**
+ * This method causes the socket to close, and may
+ * also be triggered by other methods such as OnTimeout
+ * and OnError.
+ */
+ virtual void Close();
+
+ /**
+ * The destructor may implicitly call OnClose(), and
+ * will close() and shutdown() the file descriptor
+ * used for this socket.
+ */
+ virtual ~InspSocket();
+
+ /**
+ * This method attempts to connect to a hostname.
+ * This only occurs on a non-listening socket. This
+ * method is asyncronous.
+ */
+ virtual bool DoConnect();
+
+ /**
+ * This method marks the socket closed.
+ * The next time the core examines a socket marked
+ * as closed, the socket will be closed and the
+ * memory reclaimed.
+ *
+ * NOTE: This method is DEPRECIATED and will be
+ * ignored if called!
+ */
+ void MarkAsClosed();
+
+ /** Handle event from EventHandler parent class
+ */
+ void HandleEvent(EventType et, int errornum = 0);
+
+ /** Returns true if this socket is readable
+ */
+ bool Readable();
+};
+
+#endif
+
diff --git a/include/inspstring.h b/include/inspstring.h
index 89abed94f..87024a9f8 100644
--- a/include/inspstring.h
+++ b/include/inspstring.h
@@ -1 +1,56 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __IN_INSPSTRING_H #define __IN_INSPSTRING_H #include "inspircd_config.h" #include <string.h> #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 */ CoreExport bool charremove(char* mp, char remove); /** strnewdup() is an implemenetation of strdup() which calls operator new * rather than malloc to allocate the new string, therefore allowing it to * be hooked into the C++ memory manager, and freed with operator delete. * This is required for windows, where we override operators new and delete * to allow for global allocation between modules and the core. */ inline char * strnewdup(const char * s1) { size_t len = strlen(s1) + 1; char * p = new char[len]; memcpy(p, s1, len); return p; } #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __IN_INSPSTRING_H
+#define __IN_INSPSTRING_H
+
+#include "inspircd_config.h"
+#include <string.h>
+#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
+ */
+CoreExport bool charremove(char* mp, char remove);
+
+/** strnewdup() is an implemenetation of strdup() which calls operator new
+ * rather than malloc to allocate the new string, therefore allowing it to
+ * be hooked into the C++ memory manager, and freed with operator delete.
+ * This is required for windows, where we override operators new and delete
+ * to allow for global allocation between modules and the core.
+ */
+inline char * strnewdup(const char * s1)
+{
+ size_t len = strlen(s1) + 1;
+ char * p = new char[len];
+ memcpy(p, s1, len);
+ return p;
+}
+
+#endif
+
diff --git a/include/mode.h b/include/mode.h
index 5f0835523..6d91de1a8 100644
--- a/include/mode.h
+++ b/include/mode.h
@@ -1 +1,519 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __MODE_H #define __MODE_H /* include the common header files */ #include <string> #include <deque> #include <vector> #include "users.h" #include "channels.h" #include "ctables.h" class InspIRCd; /** * Holds the values for different type of modes * that can exist, USER or CHANNEL type. */ enum ModeType { /** User mode */ MODETYPE_USER = 0, /** Channel mode */ MODETYPE_CHANNEL = 1 }; /** * Holds mode actions - modes can be allowed or denied. */ enum ModeAction { MODEACTION_DENY = 0, /* Drop the mode change, AND a parameter if its a parameterized mode */ 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 chanrec, and is told that user 'joebloggs' has the prefix * '$', and you dont know what $ means, then you can compare it to these three values to determine * its worth against them. For example if '$' had a value of 15000, you would know it is of higher * status than voice, but lower status than halfop. * No two modes should have equal prefix values. */ enum PrefixModeValue { /* +v */ VOICE_VALUE = 10000, /* +h */ HALFOP_VALUE = 20000, /* +o */ OP_VALUE = 30000 }; /** * Used by ModeHandler::ModeSet() to return the state of a mode upon a channel or user. * The pair contains an activity flag, true if the mode is set with the given parameter, * and the parameter of the mode (or the parameter provided) in the std::string. */ typedef std::pair<bool,std::string> ModePair; /** 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 * ModeParser::AddMode. When the mode you implement is * set by a user, the virtual function OnModeChange is * called. If you specify a value greater than 0 for * parameters_on or parameters_off, then when the mode is * set or unset respectively, std::string &parameter will * contain the parameter given by the user, else it will * contain an empty string. You may alter this parameter * string, and if you alter it to an empty string, and your * mode is expected to have a parameter, then this is * equivalent to returning MODEACTION_DENY. */ class CoreExport ModeHandler : public Extensible { protected: /** * Creator/owner pointer */ InspIRCd* ServerInstance; /** * The mode letter you're implementing. */ char mode; /** * Number of parameters when being set */ int n_params_on; /** * Number of parameters when being unset */ int n_params_off; /** * Mode is a 'list' mode. The behaviour * of your mode is now set entirely within * the class as of the 1.1 api, rather than * inside the mode parser as in the 1.0 api, * so the only use of this value (along with * IsListMode()) is for the core to determine * wether your module can produce 'lists' or not * (e.g. banlists, etc) */ bool list; /** * The mode type, either MODETYPE_USER or * MODETYPE_CHANNEL. */ ModeType m_type; /** * True if the mode requires oper status * to set. */ bool oper; /** Mode prefix, or 0 */ char prefix; /** Number of items with this mode set on them */ unsigned int count; public: /** * The constructor for ModeHandler initalizes the mode handler. * The constructor of any class you derive from ModeHandler should * probably call this constructor with the parameters set correctly. * @param modeletter The mode letter you wish to handle * @param parameters_on The number of parameters your mode takes when being set. Note that any nonzero value is treated as 1. * @param parameters_off The number of parameters your mode takes when being unset. Note that any nonzero value is treated as 1. * @param listmode Set to true if your mode is a listmode, e.g. it will respond to MODE #channel +modechar with a list of items * @param ModeType Set this to MODETYPE_USER for a usermode, or MODETYPE_CHANNEL for a channelmode. * @param operonly Set this to true if only opers should be allowed to set or unset the mode. * @param mprefix For listmodes where parameters are NICKNAMES which are on the channel (for example, +ohv), you may define a prefix. * When you define a prefix, it can be returned in NAMES, WHO etc if it has the highest value (as returned by GetPrefixRank()) * In the core, the only modes to implement prefixes are +ovh (ops, voice, halfop) which define the prefix characters @, % and + * and the rank values OP_VALUE, HALFOP_VALUE and VOICE_VALUE respectively. Any prefixes you define should have unique values proportional * to these three defaults or proportional to another mode in a module you depend on. See src/cmode_o.cpp as an example. */ ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix = 0); /** * The default destructor does nothing */ virtual ~ModeHandler(); /** * Returns true if the mode is a list mode */ bool IsListMode(); /** * Mode prefix or 0. If this is defined, you should * also implement GetPrefixRank() to return an integer * value for this mode prefix. */ char GetPrefix(); /** Get number of items with this mode set on them */ virtual unsigned int GetCount(); /** Adjust usage count returned by GetCount */ virtual void ChangeCount(int modifier); /** * 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 chanrec::GetPrefixValue() for * more information. */ virtual unsigned int GetPrefixRank(); /** * Returns the modes type */ ModeType GetModeType(); /** * Returns true if the mode can only be set/unset by an oper */ bool NeedsOper(); /** * Returns the number of parameters for the mode. Any non-zero * value should be considered to be equivalent to one. * @param adding If this is true, the number of parameters required to set the mode should be returned, otherwise the number of parameters required to unset the mode shall be returned. * @return The number of parameters the mode expects */ int GetNumParams(bool adding); /** * Returns the mode character this handler handles. * @return The mode character */ char GetModeChar(); /** * Called when a mode change for your mode occurs. * @param source Contains the user setting the mode. * @param dest For usermodes, contains the destination user the mode is being set on. For channelmodes, this is an undefined value. * @param channel For channel modes, contains the destination channel the modes are being set on. For usermodes, this is an undefined value. * @param parameter The parameter for your mode, if you indicated that your mode requires a parameter when being set or unset. Note that * if you alter this value, the new value becomes the one displayed and send out to the network, also, if you set this to an empty string * but you specified your mode REQUIRES a parameter, this is equivalent to returning MODEACTION_DENY and will prevent the mode from being * displayed. * @param adding This value is true when the mode is being set, or false when it is being unset. * @return MODEACTION_ALLOW to allow the mode, or MODEACTION_DENY to prevent the mode, also see the description of 'parameter'. */ virtual ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); /* Can change the mode parameter as its a ref */ /** * If your mode is a listmode, then this method will be called for displaying an item list, e.g. on MODE #channel +modechar * without any parameter or other modes in the command. * @param user The user issuing the command * @parameter channel The channel they're requesting an item list of (e.g. a banlist, or an exception list etc) */ virtual void DisplayList(userrec* user, chanrec* channel); /** * If your mode needs special action during a server sync to determine which side wins when comparing timestamps, * override this function and use it to return true or false. The default implementation just returns true if * theirs < ours. This will only be called for non-listmodes with parameters, when adding the mode and where * theirs == ours (therefore the default implementation will always return false). * @param theirs The timestamp of the remote side * @param ours The timestamp of the local side * @param their_param Their parameter if the mode has a parameter * @param our_param Our parameter if the mode has a parameter * @param channel The channel we are checking against * @return True if the other side wins the merge, false if we win the merge for this mode. */ virtual bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel); /** * When a remote server needs to bounce a set of modes, it will call this method for every mode * in the mode string to determine if the mode is set or not. * @param source of the mode change, this will be NULL for a server mode * @param dest Target user of the mode change, if this is a user mode * @param channel Target channel of the mode change, if this is a channel mode * @param parameter The parameter given for the mode change, or an empty string * @returns The first value of the pair should be true if the mode is set with the given parameter. * In the case of permissions modes such as channelmode +o, this should return true if the user given * as the parameter has the given privilage on the given channel. The string value of the pair will hold * the current setting for this mode set locally, when the bool is true, or, the parameter given. * This allows the local server to enforce our locally set parameters back to a remote server. */ virtual ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); /** * 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. * @param user The user which the server wants to remove your mode from */ virtual void RemoveMode(userrec* 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. * @param channel The channel which the server wants to remove your mode from */ virtual void RemoveMode(chanrec* channel); }; /** * The ModeWatcher class can be used to alter the behaviour of a mode implemented * by the core or by another module. To use ModeWatcher, derive a class from it, * and attach it to the mode using Server::AddModeWatcher and Server::DelModeWatcher. * A ModeWatcher will be called both before and after the mode change. */ class CoreExport ModeWatcher : public Extensible { protected: /** * Creator/owner pointer */ InspIRCd* ServerInstance; /** * The mode letter this class is watching */ char mode; /** * The mode type being watched (user or channel) */ ModeType m_type; public: /** * The constructor initializes the mode and the mode type */ ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type); /** * The default destructor does nothing. */ virtual ~ModeWatcher(); /** * Get the mode character being watched * @return The mode character being watched */ char GetModeChar(); /** * Get the mode type being watched * @return The mode type being watched (user or channel) */ ModeType GetModeType(); /** * Before the mode character is processed by its handler, this method will be called. * @param source The sender of the mode * @param dest The target user for the mode, if you are watching a user mode * @param channel The target channel for the mode, if you are watching a channel mode * @param parameter The parameter of the mode, if the mode is supposed to have a parameter. * 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 * @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(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type); /** * After the mode character has been processed by the ModeHandler, this method will be called. * @param source The sender of the mode * @param dest The target user for the mode, if you are watching a user mode * @param channel The target channel for the mode, if you are watching a channel mode * @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 * @type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL */ virtual void AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type); }; 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 : public classbase { private: /** * Creator/owner pointer */ InspIRCd* ServerInstance; /** 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. */ std::vector<ModeWatcher*> modewatchers[256]; /** Displays the current modes of a channel or user. * Used by ModeParser::Process. */ void DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text); /** 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; public: /** The constructor initializes all the RFC basic modes by using ModeParserAddMode(). */ ModeParser(InspIRCd* Instance); /** 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'. */ userrec* SanityChecks(userrec *user,const char *dest,chanrec *chan,int status); /** Grant a built in privilage (e.g. ops, halfops, voice) to a user on a channel */ const char* Grant(userrec *d,chanrec *chan,int MASK); /** Revoke a built in privilage (e.g. ops, halfops, voice) to a user on a channel */ const char* Revoke(userrec *d,chanrec *chan,int MASK); /** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out. * E.g. * * nick -> nick!*@* * * nick!ident -> nick!ident@* * * host.name -> *!*@host.name * * ident@host.name -> *!ident@host.name * * This method can be used on both IPV4 and IPV6 user masks. */ static void CleanMask(std::string &mask); /** Get the last string to be processed, as it was sent to the user or channel. * Use this to display a string you just sent to be parsed, as the actual output * may be different to what you sent after it has been 'cleaned up' by the parser. * @return Last parsed string, as seen by users. */ const std::string& GetLastParse(); /** Add a mode to the mode parser. The modeletter parameter * is purely to save on doing a lookup in the function, as * strictly it could be obtained via ModeHandler::GetModeChar(). * @return True if the mode was successfully added. */ bool AddMode(ModeHandler* mh, unsigned const char modeletter); /** 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 * (if it is a channel mode) to unset the mode on all objects. * This prevents modes staying in the system which no longer exist. * @param mh The mode handler to remove * @return True if the mode was successfully removed. */ bool DelMode(ModeHandler* mh); /** Add a mode watcher. * A mode watcher is triggered before and after a mode handler is * 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); /** 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 * information. * @param mw The ModeWatcher you want to delete * @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 pcnt The number of items in the parameters array * @param user The user setting or removing the modes. When the modes are set * by a server, an 'uninitialized' userrec is used, where *user::nick == NULL * and *user->server == NULL. * @param servermode True if a server is setting the mode. */ void Process(const char** parameters, int pcnt, userrec *user, bool servermode); /** Find the mode handler for a given mode and type. * @param modeletter mode letter to search for * @param type of mode to search for, user or channel * @returns a pointer to a ModeHandler class, or NULL of there isnt a handler for the given mode */ ModeHandler* FindMode(unsigned const char modeletter, ModeType mt); /** Find a mode handler by its prefix. * If there is no mode handler with the given prefix, NULL will be returned. * @param pfxletter The prefix to find, e.g. '@' * @return The mode handler which handles this prefix, or NULL if there is none. */ ModeHandler* FindPrefix(unsigned const char pfxletter); /** Returns a list of mode characters which are usermodes. * This is used in the 004 numeric when users connect. */ std::string UserModeList(); /** Returns a list of channel mode characters which are listmodes. * This is used in the 004 numeric when users connect. */ std::string ChannelModeList(); /** Returns a list of channel mode characters which take parameters. * This is used in the 004 numeric when users connect. */ std::string ParaModeList(); /** Generates the CHANMODES= 005 sequence */ std::string ChanModes(); /** Used by this class internally during std::sort and 005 generation */ static bool PrefixComparison(prefixtype one, prefixtype two); /** This returns the PREFIX=(ohv)@%+ section of the 005 numeric. */ std::string BuildPrefixes(); /** This returns the privilages of a user upon a channel, in the format of a mode change. * For example, if a user has privilages +avh, this will return the string "avh nick nick nick". * This is used by the core when cycling a user to refresh their hostname. You may use it for * similar purposes. * @param user The username to look up * @param channel The channel name to look up the privilages of the user for * @return The mode string. */ std::string ModeString(userrec* user, chanrec* channel); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __MODE_H
+#define __MODE_H
+
+/* include the common header files */
+#include <string>
+#include <deque>
+#include <vector>
+#include "users.h"
+#include "channels.h"
+#include "ctables.h"
+
+class InspIRCd;
+
+/**
+ * Holds the values for different type of modes
+ * that can exist, USER or CHANNEL type.
+ */
+enum ModeType
+{
+ /** User mode */
+ MODETYPE_USER = 0,
+ /** Channel mode */
+ MODETYPE_CHANNEL = 1
+};
+
+/**
+ * Holds mode actions - modes can be allowed or denied.
+ */
+enum ModeAction
+{
+ MODEACTION_DENY = 0, /* Drop the mode change, AND a parameter if its a parameterized mode */
+ 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 chanrec, and is told that user 'joebloggs' has the prefix
+ * '$', and you dont know what $ means, then you can compare it to these three values to determine
+ * its worth against them. For example if '$' had a value of 15000, you would know it is of higher
+ * status than voice, but lower status than halfop.
+ * No two modes should have equal prefix values.
+ */
+enum PrefixModeValue
+{
+ /* +v */
+ VOICE_VALUE = 10000,
+ /* +h */
+ HALFOP_VALUE = 20000,
+ /* +o */
+ OP_VALUE = 30000
+};
+
+/**
+ * Used by ModeHandler::ModeSet() to return the state of a mode upon a channel or user.
+ * The pair contains an activity flag, true if the mode is set with the given parameter,
+ * and the parameter of the mode (or the parameter provided) in the std::string.
+ */
+typedef std::pair<bool,std::string> ModePair;
+
+/** 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
+ * ModeParser::AddMode. When the mode you implement is
+ * set by a user, the virtual function OnModeChange is
+ * called. If you specify a value greater than 0 for
+ * parameters_on or parameters_off, then when the mode is
+ * set or unset respectively, std::string &parameter will
+ * contain the parameter given by the user, else it will
+ * contain an empty string. You may alter this parameter
+ * string, and if you alter it to an empty string, and your
+ * mode is expected to have a parameter, then this is
+ * equivalent to returning MODEACTION_DENY.
+ */
+class CoreExport ModeHandler : public Extensible
+{
+ protected:
+ /**
+ * Creator/owner pointer
+ */
+ InspIRCd* ServerInstance;
+ /**
+ * The mode letter you're implementing.
+ */
+ char mode;
+ /**
+ * Number of parameters when being set
+ */
+ int n_params_on;
+ /**
+ * Number of parameters when being unset
+ */
+ int n_params_off;
+ /**
+ * Mode is a 'list' mode. The behaviour
+ * of your mode is now set entirely within
+ * the class as of the 1.1 api, rather than
+ * inside the mode parser as in the 1.0 api,
+ * so the only use of this value (along with
+ * IsListMode()) is for the core to determine
+ * wether your module can produce 'lists' or not
+ * (e.g. banlists, etc)
+ */
+ bool list;
+ /**
+ * The mode type, either MODETYPE_USER or
+ * MODETYPE_CHANNEL.
+ */
+ ModeType m_type;
+ /**
+ * True if the mode requires oper status
+ * to set.
+ */
+ bool oper;
+
+ /** Mode prefix, or 0
+ */
+ char prefix;
+
+ /** Number of items with this mode set on them
+ */
+ unsigned int count;
+
+ public:
+ /**
+ * The constructor for ModeHandler initalizes the mode handler.
+ * The constructor of any class you derive from ModeHandler should
+ * probably call this constructor with the parameters set correctly.
+ * @param modeletter The mode letter you wish to handle
+ * @param parameters_on The number of parameters your mode takes when being set. Note that any nonzero value is treated as 1.
+ * @param parameters_off The number of parameters your mode takes when being unset. Note that any nonzero value is treated as 1.
+ * @param listmode Set to true if your mode is a listmode, e.g. it will respond to MODE #channel +modechar with a list of items
+ * @param ModeType Set this to MODETYPE_USER for a usermode, or MODETYPE_CHANNEL for a channelmode.
+ * @param operonly Set this to true if only opers should be allowed to set or unset the mode.
+ * @param mprefix For listmodes where parameters are NICKNAMES which are on the channel (for example, +ohv), you may define a prefix.
+ * When you define a prefix, it can be returned in NAMES, WHO etc if it has the highest value (as returned by GetPrefixRank())
+ * In the core, the only modes to implement prefixes are +ovh (ops, voice, halfop) which define the prefix characters @, % and +
+ * and the rank values OP_VALUE, HALFOP_VALUE and VOICE_VALUE respectively. Any prefixes you define should have unique values proportional
+ * to these three defaults or proportional to another mode in a module you depend on. See src/cmode_o.cpp as an example.
+ */
+ ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix = 0);
+ /**
+ * The default destructor does nothing
+ */
+ virtual ~ModeHandler();
+ /**
+ * Returns true if the mode is a list mode
+ */
+ bool IsListMode();
+ /**
+ * Mode prefix or 0. If this is defined, you should
+ * also implement GetPrefixRank() to return an integer
+ * value for this mode prefix.
+ */
+ char GetPrefix();
+ /** Get number of items with this mode set on them
+ */
+ virtual unsigned int GetCount();
+ /** Adjust usage count returned by GetCount
+ */
+ virtual void ChangeCount(int modifier);
+ /**
+ * 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 chanrec::GetPrefixValue() for
+ * more information.
+ */
+ virtual unsigned int GetPrefixRank();
+ /**
+ * Returns the modes type
+ */
+ ModeType GetModeType();
+ /**
+ * Returns true if the mode can only be set/unset by an oper
+ */
+ bool NeedsOper();
+ /**
+ * Returns the number of parameters for the mode. Any non-zero
+ * value should be considered to be equivalent to one.
+ * @param adding If this is true, the number of parameters required to set the mode should be returned, otherwise the number of parameters required to unset the mode shall be returned.
+ * @return The number of parameters the mode expects
+ */
+ int GetNumParams(bool adding);
+ /**
+ * Returns the mode character this handler handles.
+ * @return The mode character
+ */
+ char GetModeChar();
+
+ /**
+ * Called when a mode change for your mode occurs.
+ * @param source Contains the user setting the mode.
+ * @param dest For usermodes, contains the destination user the mode is being set on. For channelmodes, this is an undefined value.
+ * @param channel For channel modes, contains the destination channel the modes are being set on. For usermodes, this is an undefined value.
+ * @param parameter The parameter for your mode, if you indicated that your mode requires a parameter when being set or unset. Note that
+ * if you alter this value, the new value becomes the one displayed and send out to the network, also, if you set this to an empty string
+ * but you specified your mode REQUIRES a parameter, this is equivalent to returning MODEACTION_DENY and will prevent the mode from being
+ * displayed.
+ * @param adding This value is true when the mode is being set, or false when it is being unset.
+ * @return MODEACTION_ALLOW to allow the mode, or MODEACTION_DENY to prevent the mode, also see the description of 'parameter'.
+ */
+ virtual ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); /* Can change the mode parameter as its a ref */
+ /**
+ * If your mode is a listmode, then this method will be called for displaying an item list, e.g. on MODE #channel +modechar
+ * without any parameter or other modes in the command.
+ * @param user The user issuing the command
+ * @parameter channel The channel they're requesting an item list of (e.g. a banlist, or an exception list etc)
+ */
+ virtual void DisplayList(userrec* user, chanrec* channel);
+ /**
+ * If your mode needs special action during a server sync to determine which side wins when comparing timestamps,
+ * override this function and use it to return true or false. The default implementation just returns true if
+ * theirs < ours. This will only be called for non-listmodes with parameters, when adding the mode and where
+ * theirs == ours (therefore the default implementation will always return false).
+ * @param theirs The timestamp of the remote side
+ * @param ours The timestamp of the local side
+ * @param their_param Their parameter if the mode has a parameter
+ * @param our_param Our parameter if the mode has a parameter
+ * @param channel The channel we are checking against
+ * @return True if the other side wins the merge, false if we win the merge for this mode.
+ */
+ virtual bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel);
+
+ /**
+ * When a remote server needs to bounce a set of modes, it will call this method for every mode
+ * in the mode string to determine if the mode is set or not.
+ * @param source of the mode change, this will be NULL for a server mode
+ * @param dest Target user of the mode change, if this is a user mode
+ * @param channel Target channel of the mode change, if this is a channel mode
+ * @param parameter The parameter given for the mode change, or an empty string
+ * @returns The first value of the pair should be true if the mode is set with the given parameter.
+ * In the case of permissions modes such as channelmode +o, this should return true if the user given
+ * as the parameter has the given privilage on the given channel. The string value of the pair will hold
+ * the current setting for this mode set locally, when the bool is true, or, the parameter given.
+ * This allows the local server to enforce our locally set parameters back to a remote server.
+ */
+ virtual ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+
+ /**
+ * 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.
+ * @param user The user which the server wants to remove your mode from
+ */
+ virtual void RemoveMode(userrec* 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.
+ * @param channel The channel which the server wants to remove your mode from
+ */
+ virtual void RemoveMode(chanrec* channel);
+};
+
+/**
+ * The ModeWatcher class can be used to alter the behaviour of a mode implemented
+ * by the core or by another module. To use ModeWatcher, derive a class from it,
+ * and attach it to the mode using Server::AddModeWatcher and Server::DelModeWatcher.
+ * A ModeWatcher will be called both before and after the mode change.
+ */
+class CoreExport ModeWatcher : public Extensible
+{
+ protected:
+ /**
+ * Creator/owner pointer
+ */
+ InspIRCd* ServerInstance;
+ /**
+ * The mode letter this class is watching
+ */
+ char mode;
+ /**
+ * The mode type being watched (user or channel)
+ */
+ ModeType m_type;
+
+ public:
+ /**
+ * The constructor initializes the mode and the mode type
+ */
+ ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type);
+ /**
+ * The default destructor does nothing.
+ */
+ virtual ~ModeWatcher();
+
+ /**
+ * Get the mode character being watched
+ * @return The mode character being watched
+ */
+ char GetModeChar();
+ /**
+ * Get the mode type being watched
+ * @return The mode type being watched (user or channel)
+ */
+ ModeType GetModeType();
+
+ /**
+ * Before the mode character is processed by its handler, this method will be called.
+ * @param source The sender of the mode
+ * @param dest The target user for the mode, if you are watching a user mode
+ * @param channel The target channel for the mode, if you are watching a channel mode
+ * @param parameter The parameter of the mode, if the mode is supposed to have a parameter.
+ * 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
+ * @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(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type);
+ /**
+ * After the mode character has been processed by the ModeHandler, this method will be called.
+ * @param source The sender of the mode
+ * @param dest The target user for the mode, if you are watching a user mode
+ * @param channel The target channel for the mode, if you are watching a channel mode
+ * @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
+ * @type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL
+ */
+ virtual void AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type);
+};
+
+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 : public classbase
+{
+ private:
+ /**
+ * Creator/owner pointer
+ */
+ InspIRCd* ServerInstance;
+ /** 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.
+ */
+ std::vector<ModeWatcher*> modewatchers[256];
+ /** Displays the current modes of a channel or user.
+ * Used by ModeParser::Process.
+ */
+ void DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text);
+
+ /** 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;
+
+ public:
+
+ /** The constructor initializes all the RFC basic modes by using ModeParserAddMode().
+ */
+ ModeParser(InspIRCd* Instance);
+
+ /** 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'.
+ */
+ userrec* SanityChecks(userrec *user,const char *dest,chanrec *chan,int status);
+ /** Grant a built in privilage (e.g. ops, halfops, voice) to a user on a channel
+ */
+ const char* Grant(userrec *d,chanrec *chan,int MASK);
+ /** Revoke a built in privilage (e.g. ops, halfops, voice) to a user on a channel
+ */
+ const char* Revoke(userrec *d,chanrec *chan,int MASK);
+ /** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out.
+ * E.g.
+ *
+ * nick -> nick!*@*
+ *
+ * nick!ident -> nick!ident@*
+ *
+ * host.name -> *!*@host.name
+ *
+ * ident@host.name -> *!ident@host.name
+ *
+ * This method can be used on both IPV4 and IPV6 user masks.
+ */
+ static void CleanMask(std::string &mask);
+ /** Get the last string to be processed, as it was sent to the user or channel.
+ * Use this to display a string you just sent to be parsed, as the actual output
+ * may be different to what you sent after it has been 'cleaned up' by the parser.
+ * @return Last parsed string, as seen by users.
+ */
+ const std::string& GetLastParse();
+ /** Add a mode to the mode parser. The modeletter parameter
+ * is purely to save on doing a lookup in the function, as
+ * strictly it could be obtained via ModeHandler::GetModeChar().
+ * @return True if the mode was successfully added.
+ */
+ bool AddMode(ModeHandler* mh, unsigned const char modeletter);
+ /** 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
+ * (if it is a channel mode) to unset the mode on all objects.
+ * This prevents modes staying in the system which no longer exist.
+ * @param mh The mode handler to remove
+ * @return True if the mode was successfully removed.
+ */
+ bool DelMode(ModeHandler* mh);
+ /** Add a mode watcher.
+ * A mode watcher is triggered before and after a mode handler is
+ * 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);
+ /** 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
+ * information.
+ * @param mw The ModeWatcher you want to delete
+ * @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 pcnt The number of items in the parameters array
+ * @param user The user setting or removing the modes. When the modes are set
+ * by a server, an 'uninitialized' userrec is used, where *user::nick == NULL
+ * and *user->server == NULL.
+ * @param servermode True if a server is setting the mode.
+ */
+ void Process(const char** parameters, int pcnt, userrec *user, bool servermode);
+
+ /** Find the mode handler for a given mode and type.
+ * @param modeletter mode letter to search for
+ * @param type of mode to search for, user or channel
+ * @returns a pointer to a ModeHandler class, or NULL of there isnt a handler for the given mode
+ */
+ ModeHandler* FindMode(unsigned const char modeletter, ModeType mt);
+
+ /** Find a mode handler by its prefix.
+ * If there is no mode handler with the given prefix, NULL will be returned.
+ * @param pfxletter The prefix to find, e.g. '@'
+ * @return The mode handler which handles this prefix, or NULL if there is none.
+ */
+ ModeHandler* FindPrefix(unsigned const char pfxletter);
+
+ /** Returns a list of mode characters which are usermodes.
+ * This is used in the 004 numeric when users connect.
+ */
+ std::string UserModeList();
+
+ /** Returns a list of channel mode characters which are listmodes.
+ * This is used in the 004 numeric when users connect.
+ */
+ std::string ChannelModeList();
+
+ /** Returns a list of channel mode characters which take parameters.
+ * This is used in the 004 numeric when users connect.
+ */
+ std::string ParaModeList();
+
+ /** Generates the CHANMODES= 005 sequence
+ */
+ std::string ChanModes();
+ /** Used by this class internally during std::sort and 005 generation
+ */
+ static bool PrefixComparison(prefixtype one, prefixtype two);
+
+ /** This returns the PREFIX=(ohv)@%+ section of the 005 numeric.
+ */
+ std::string BuildPrefixes();
+
+ /** This returns the privilages of a user upon a channel, in the format of a mode change.
+ * For example, if a user has privilages +avh, this will return the string "avh nick nick nick".
+ * This is used by the core when cycling a user to refresh their hostname. You may use it for
+ * similar purposes.
+ * @param user The username to look up
+ * @param channel The channel name to look up the privilages of the user for
+ * @return The mode string.
+ */
+ std::string ModeString(userrec* user, chanrec* channel);
+};
+
+#endif
+
diff --git a/include/modes/cmode_b.h b/include/modes/cmode_b.h
index b9e403258..2fe3de614 100644
--- a/include/modes/cmode_b.h
+++ b/include/modes/cmode_b.h
@@ -1 +1,35 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" #include "channels.h" class InspIRCd; /** Channel mode +b */ class ModeChannelBan : public ModeHandler { private: BanItem b; public: ModeChannelBan(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); std::string& AddBan(userrec *user,std::string& dest,chanrec *chan,int status); std::string& DelBan(userrec *user,std::string& dest,chanrec *chan,int status); void DisplayList(userrec* user, chanrec* channel); ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); void RemoveMode(userrec* user); void RemoveMode(chanrec* channel); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+#include "channels.h"
+
+class InspIRCd;
+
+/** Channel mode +b
+ */
+class ModeChannelBan : public ModeHandler
+{
+ private:
+ BanItem b;
+ public:
+ ModeChannelBan(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ std::string& AddBan(userrec *user,std::string& dest,chanrec *chan,int status);
+ std::string& DelBan(userrec *user,std::string& dest,chanrec *chan,int status);
+ void DisplayList(userrec* user, chanrec* channel);
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+ void RemoveMode(userrec* user);
+ void RemoveMode(chanrec* channel);
+};
+
diff --git a/include/modes/cmode_h.h b/include/modes/cmode_h.h
index 9792fcb00..77d1d57ae 100644
--- a/include/modes/cmode_h.h
+++ b/include/modes/cmode_h.h
@@ -1 +1,34 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" #include "channels.h" class InspIRCd; /** Channel mode +h */ class ModeChannelHalfOp : public ModeHandler { private: public: ModeChannelHalfOp(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); std::string AddHalfOp(userrec *user,const char *dest,chanrec *chan,int status); std::string DelHalfOp(userrec *user,const char *dest,chanrec *chan,int status); ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); unsigned int GetPrefixRank(); void RemoveMode(chanrec* channel); void RemoveMode(userrec* user); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+#include "channels.h"
+
+class InspIRCd;
+
+/** Channel mode +h
+ */
+class ModeChannelHalfOp : public ModeHandler
+{
+ private:
+ public:
+ ModeChannelHalfOp(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ std::string AddHalfOp(userrec *user,const char *dest,chanrec *chan,int status);
+ std::string DelHalfOp(userrec *user,const char *dest,chanrec *chan,int status);
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+ unsigned int GetPrefixRank();
+ void RemoveMode(chanrec* channel);
+ void RemoveMode(userrec* user);
+};
+
diff --git a/include/modes/cmode_i.h b/include/modes/cmode_i.h
index 9df5314aa..fb177dc0f 100644
--- a/include/modes/cmode_i.h
+++ b/include/modes/cmode_i.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +i */ class ModeChannelInviteOnly : public ModeHandler { public: ModeChannelInviteOnly(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +i
+ */
+class ModeChannelInviteOnly : public ModeHandler
+{
+ public:
+ ModeChannelInviteOnly(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/cmode_k.h b/include/modes/cmode_k.h
index 695518bf9..51e8ffcaa 100644
--- a/include/modes/cmode_k.h
+++ b/include/modes/cmode_k.h
@@ -1 +1,29 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +k */ class ModeChannelKey : public ModeHandler { public: ModeChannelKey(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel); void RemoveMode(chanrec* channel); void RemoveMode(userrec* user); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +k
+ */
+class ModeChannelKey : public ModeHandler
+{
+ public:
+ ModeChannelKey(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+ bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel);
+ void RemoveMode(chanrec* channel);
+ void RemoveMode(userrec* user);
+};
diff --git a/include/modes/cmode_l.h b/include/modes/cmode_l.h
index 49228479f..63b4a1ef1 100644
--- a/include/modes/cmode_l.h
+++ b/include/modes/cmode_l.h
@@ -1 +1,27 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +l */ class ModeChannelLimit : public ModeHandler { public: ModeChannelLimit(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +l
+ */
+class ModeChannelLimit : public ModeHandler
+{
+ public:
+ ModeChannelLimit(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+ bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel);
+};
diff --git a/include/modes/cmode_m.h b/include/modes/cmode_m.h
index 885b0321e..52288de4c 100644
--- a/include/modes/cmode_m.h
+++ b/include/modes/cmode_m.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +m */ class ModeChannelModerated : public ModeHandler { public: ModeChannelModerated(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +m
+ */
+class ModeChannelModerated : public ModeHandler
+{
+ public:
+ ModeChannelModerated(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/cmode_n.h b/include/modes/cmode_n.h
index dcc0edccb..953e0d671 100644
--- a/include/modes/cmode_n.h
+++ b/include/modes/cmode_n.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +n */ class ModeChannelNoExternal : public ModeHandler { public: ModeChannelNoExternal(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +n
+ */
+class ModeChannelNoExternal : public ModeHandler
+{
+ public:
+ ModeChannelNoExternal(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/cmode_o.h b/include/modes/cmode_o.h
index 4198a3f20..c86fb4586 100644
--- a/include/modes/cmode_o.h
+++ b/include/modes/cmode_o.h
@@ -1 +1,34 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" #include "channels.h" class InspIRCd; /** Channel mode +o */ class ModeChannelOp : public ModeHandler { private: public: ModeChannelOp(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); std::string AddOp(userrec *user,const char *dest,chanrec *chan,int status); std::string DelOp(userrec *user,const char *dest,chanrec *chan,int status); ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); unsigned int GetPrefixRank(); void RemoveMode(chanrec* channel); void RemoveMode(userrec* user); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+#include "channels.h"
+
+class InspIRCd;
+
+/** Channel mode +o
+ */
+class ModeChannelOp : public ModeHandler
+{
+ private:
+ public:
+ ModeChannelOp(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ std::string AddOp(userrec *user,const char *dest,chanrec *chan,int status);
+ std::string DelOp(userrec *user,const char *dest,chanrec *chan,int status);
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+ unsigned int GetPrefixRank();
+ void RemoveMode(chanrec* channel);
+ void RemoveMode(userrec* user);
+};
+
diff --git a/include/modes/cmode_p.h b/include/modes/cmode_p.h
index 123827ea7..ad3f3ae89 100644
--- a/include/modes/cmode_p.h
+++ b/include/modes/cmode_p.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +p */ class ModeChannelPrivate : public ModeHandler { public: ModeChannelPrivate(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +p
+ */
+class ModeChannelPrivate : public ModeHandler
+{
+ public:
+ ModeChannelPrivate(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/cmode_s.h b/include/modes/cmode_s.h
index 383e5c083..0bc229c37 100644
--- a/include/modes/cmode_s.h
+++ b/include/modes/cmode_s.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +s */ class ModeChannelSecret : public ModeHandler { public: ModeChannelSecret(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +s
+ */
+class ModeChannelSecret : public ModeHandler
+{
+ public:
+ ModeChannelSecret(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/cmode_t.h b/include/modes/cmode_t.h
index 4ddd51730..8a7517ee3 100644
--- a/include/modes/cmode_t.h
+++ b/include/modes/cmode_t.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** Channel mode +t */ class ModeChannelTopicOps : public ModeHandler { public: ModeChannelTopicOps(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** Channel mode +t
+ */
+class ModeChannelTopicOps : public ModeHandler
+{
+ public:
+ ModeChannelTopicOps(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/cmode_v.h b/include/modes/cmode_v.h
index a1d40ddc4..75420a6d5 100644
--- a/include/modes/cmode_v.h
+++ b/include/modes/cmode_v.h
@@ -1 +1,34 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" #include "channels.h" class InspIRCd; /** Channel mode +v */ class ModeChannelVoice : public ModeHandler { private: public: ModeChannelVoice(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); std::string AddVoice(userrec *user,const char *dest,chanrec *chan,int status); std::string DelVoice(userrec *user,const char *dest,chanrec *chan,int status); ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter); unsigned int GetPrefixRank(); void RemoveMode(userrec* user); void RemoveMode(chanrec* channel); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+#include "channels.h"
+
+class InspIRCd;
+
+/** Channel mode +v
+ */
+class ModeChannelVoice : public ModeHandler
+{
+ private:
+ public:
+ ModeChannelVoice(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ std::string AddVoice(userrec *user,const char *dest,chanrec *chan,int status);
+ std::string DelVoice(userrec *user,const char *dest,chanrec *chan,int status);
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter);
+ unsigned int GetPrefixRank();
+ void RemoveMode(userrec* user);
+ void RemoveMode(chanrec* channel);
+};
+
diff --git a/include/modes/umode_i.h b/include/modes/umode_i.h
index 4319ac0a5..cc7d15102 100644
--- a/include/modes/umode_i.h
+++ b/include/modes/umode_i.h
@@ -1 +1,26 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** User mode +i */ class ModeUserInvisible : public ModeHandler { public: ModeUserInvisible(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); unsigned int GetCount(); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** User mode +i
+ */
+class ModeUserInvisible : public ModeHandler
+{
+ public:
+ ModeUserInvisible(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ unsigned int GetCount();
+};
diff --git a/include/modes/umode_n.h b/include/modes/umode_n.h
index 9a944d41c..cd1275acc 100644
--- a/include/modes/umode_n.h
+++ b/include/modes/umode_n.h
@@ -1 +1,25 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** User mode +n */ class ModeUserServerNoticeMask : public ModeHandler { public: ModeUserServerNoticeMask(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** User mode +n
+ */
+class ModeUserServerNoticeMask : public ModeHandler
+{
+ public:
+ ModeUserServerNoticeMask(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+};
diff --git a/include/modes/umode_o.h b/include/modes/umode_o.h
index 7548a087a..7dfdb4128 100644
--- a/include/modes/umode_o.h
+++ b/include/modes/umode_o.h
@@ -1 +1,26 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** User mode +o */ class ModeUserOperator : public ModeHandler { public: ModeUserOperator(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); unsigned int GetCount(); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** User mode +o
+ */
+class ModeUserOperator : public ModeHandler
+{
+ public:
+ ModeUserOperator(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ unsigned int GetCount();
+};
diff --git a/include/modes/umode_s.h b/include/modes/umode_s.h
index 98264298b..cda223eee 100644
--- a/include/modes/umode_s.h
+++ b/include/modes/umode_s.h
@@ -1 +1,26 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** User mode +s */ class ModeUserServerNotice : public ModeHandler { public: ModeUserServerNotice(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); unsigned int GetCount(); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** User mode +s
+ */
+class ModeUserServerNotice : public ModeHandler
+{
+ public:
+ ModeUserServerNotice(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ unsigned int GetCount();
+};
diff --git a/include/modes/umode_w.h b/include/modes/umode_w.h
index 26e214460..271e959c4 100644
--- a/include/modes/umode_w.h
+++ b/include/modes/umode_w.h
@@ -1 +1,26 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "mode.h" class InspIRCd; /** User mode +w */ class ModeUserWallops : public ModeHandler { public: ModeUserWallops(InspIRCd* Instance); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding); unsigned int GetCount(); }; \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "mode.h"
+
+class InspIRCd;
+
+/** User mode +w
+ */
+class ModeUserWallops : public ModeHandler
+{
+ public:
+ ModeUserWallops(InspIRCd* Instance);
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding);
+ unsigned int GetCount();
+};
diff --git a/include/modules.h b/include/modules.h
index 51d117b17..389fa6184 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -1 +1,1696 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __MODULES_H #define __MODULES_H /** Used with OnAccessCheck() method of modules */ enum AccessControlType { ACR_DEFAULT, // Do default action (act as if the module isnt even loaded) ACR_DENY, // deny the action ACR_ALLOW, // allow the action AC_KICK, // a user is being kicked AC_DEOP, // a user is being deopped AC_OP, // a user is being opped AC_VOICE, // a user is being voiced AC_DEVOICE, // a user is being devoiced AC_HALFOP, // a user is being halfopped AC_DEHALFOP, // a user is being dehalfopped AC_INVITE, // a user is being invited AC_GENERAL_MODE // a channel mode is being changed }; /** Used to define a set of behavior bits for a module */ enum ModuleFlags { 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_SERVICEPROVIDER = 4, // module provides a service to other modules (can be a dependency) VF_COMMON = 8 // module needs to be common on all servers in a network to link }; /** Used with SendToMode() */ enum WriteModeFlags { WM_AND = 1, WM_OR = 2 }; /** Used to represent an event type, for user, channel or server */ enum TargetTypeFlags { TYPE_USER = 1, TYPE_CHANNEL, TYPE_SERVER, TYPE_OTHER }; /** Used to represent wether a message was PRIVMSG or NOTICE */ enum MessageType { MSG_PRIVMSG = 0, MSG_NOTICE = 1 }; #include "globals.h" #include "dynamic.h" #include "base.h" #include "ctables.h" #include "inspsocket.h" #include <string> #include <deque> #include <sstream> #include "timer.h" #include "mode.h" #include "dns.h" /** If you change the module API, change this value. * If you have enabled ipv6, the sizes of structs is * different, and modules will be incompatible with * ipv4 servers, so this value will be ten times as * high on ipv6 servers. */ #define NATIVE_API_VERSION 11025 #ifdef IPV6 #define API_VERSION (NATIVE_API_VERSION * 10) #else #define API_VERSION (NATIVE_API_VERSION * 1) #endif class ServerConfig; /* Forward-delacare module for ModuleMessage etc */ class Module; /** Low level definition of a FileReader classes file cache area - * a text file seperated into lines. */ typedef std::deque<std::string> file_cache; /** A set of strings. */ typedef file_cache string_list; /** Holds a list of 'published features' for modules. */ typedef std::map<std::string,Module*> featurelist; /** Holds a list of modules which implement an interface */ typedef std::deque<Module*> modulelist; /** Holds a list of all modules which implement interfaces, by interface name */ typedef std::map<std::string, std::pair<int, modulelist> > interfacelist; /** * 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));' */ #define FOREACH_MOD(y,x) if (ServerInstance->Config->global_implementation[y] > 0) { \ for (int _i = 0; _i <= ServerInstance->GetModuleCount(); _i++) { \ if (ServerInstance->Config->implement_lists[_i][y]) \ try \ { \ ServerInstance->modules[_i]->x ; \ } \ catch (CoreException& modexcept) \ { \ ServerInstance->Log(DEFAULT,"Exception cought: %s",modexcept.GetReason()); \ } \ } \ } /** * This #define allows us to call a method in all * loaded modules in a readable simple way and pass * an instance pointer to the macro. e.g.: * 'FOREACH_MOD_I(Instance, OnConnect, OnConnect(user));' */ #define FOREACH_MOD_I(z,y,x) if (z->Config->global_implementation[y] > 0) { \ for (int _i = 0; _i <= z->GetModuleCount(); _i++) { \ if (z->Config->implement_lists[_i][y]) \ try \ { \ z->modules[_i]->x ; \ } \ catch (CoreException& modexcept) \ { \ z->Log(DEFAULT,"Exception cought: %s",modexcept.GetReason()); \ } \ } \ } /** * This define is similar to the one above but returns a result in MOD_RESULT. * The first module to return a nonzero result is the value to be accepted, * and any modules after are ignored. */ #define FOREACH_RESULT(y,x) { if (ServerInstance->Config->global_implementation[y] > 0) { \ MOD_RESULT = 0; \ for (int _i = 0; _i <= ServerInstance->GetModuleCount(); _i++) { \ if (ServerInstance->Config->implement_lists[_i][y]) { \ try \ { \ int res = ServerInstance->modules[_i]->x ; \ if (res != 0) { \ MOD_RESULT = res; \ break; \ } \ } \ catch (CoreException& modexcept) \ { \ ServerInstance->Log(DEFAULT,"Exception cought: %s",modexcept.GetReason()); \ } \ } \ } \ } \ } /** * This define is similar to the one above but returns a result in MOD_RESULT. * The first module to return a nonzero result is the value to be accepted, * and any modules after are ignored. */ #define FOREACH_RESULT_I(z,y,x) { if (z->Config->global_implementation[y] > 0) { \ MOD_RESULT = 0; \ for (int _i = 0; _i <= z->GetModuleCount(); _i++) { \ if (z->Config->implement_lists[_i][y]) { \ try \ { \ int res = z->modules[_i]->x ; \ if (res != 0) { \ MOD_RESULT = res; \ break; \ } \ } \ catch (CoreException& modexcept) \ { \ z->Log(DEBUG,"Exception cought: %s",modexcept.GetReason()); \ } \ } \ } \ } \ } /** Represents a non-local user. * (in fact, any FD less than -1 does) */ #define FD_MAGIC_NUMBER -42 /* Useful macros */ #ifdef WINDOWS /** Is a local user */ #define IS_LOCAL(x) ((x->GetFd() > -1)) #else /** Is a local user */ #define IS_LOCAL(x) ((x->GetFd() > -1) && (x->GetFd() <= MAX_DESCRIPTORS)) #endif /** Is a remote user */ #define IS_REMOTE(x) (x->GetFd() < 0) /** Is a module created user */ #define IS_MODULE_CREATED(x) (x->GetFd() == FD_MAGIC_NUMBER) /** Is an oper */ #define IS_OPER(x) (*x->oper) /** Is away */ #define IS_AWAY(x) (*x->awaymsg) /** Holds a module's Version information. * The four members (set by the constructor only) indicate details as to the version number * of a module. A class of type Version is returned by the GetVersion method of the Module class. * The flags and API values represent the module flags and API version of the module. * The API version of a module must match the API version of the core exactly for the module to * load successfully. */ class CoreExport Version : public classbase { public: /** Version numbers, build number, flags and API version */ const int Major, Minor, Revision, Build, Flags, API; /** Initialize version class */ Version(int major, int minor, int revision, int build, int flags, int api_ver); }; /** The ModuleMessage class is the base class of Request and Event * This class is used to represent a basic data structure which is passed * between modules for safe inter-module communications. */ class CoreExport ModuleMessage : public Extensible { public: /** Destructor */ virtual ~ModuleMessage() {}; }; /** 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 ModuleMessage { protected: /** This member holds a pointer to arbitary data set by the emitter of the message */ char* data; /** 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* id; /** This is a pointer to the sender of the message, which can be used to * directly trigger events, or to create a reply. */ Module* source; /** The single destination of the Request */ Module* dest; public: /** Create a new Request * This is for the 'old' way of casting whatever the data is * to char* and hoping you get the right thing at the other end. * This is slowly being depreciated in favor of the 'new' way. */ Request(char* anydata, Module* src, Module* dst); /** 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. This is * much safer as there are no casts not confirmed by * the ID string, and all casts are child->parent and * can be checked at runtime with dynamic_cast<>() */ Request(Module* src, Module* dst, const char* idstr); /** Fetch the Request data */ char* GetData(); /** Fetch the ID string */ const char* GetId(); /** Fetch the request source */ Module* GetSource(); /** Fetch the request destination (should be 'this' in the receiving module) */ Module* GetDest(); /** Send the Request. * Upon returning the result will be arbitary data returned by the module you * sent the request to. It is up to your module to know what this data is and * how to deal with it. */ char* 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 ModuleMessage { protected: /** This member holds a pointer to arbitary data set by the emitter of the message */ char* data; /** This is a pointer to the sender of the message, which can be used to * directly trigger events, or to create a reply. */ Module* source; /** The event identifier. * This is arbitary text which should be used to distinguish * one type of event from another. */ std::string id; public: /** Create a new Event */ Event(char* anydata, Module* src, const std::string &eventid); /** Get the Event data */ char* GetData(); /** Get the event Source */ Module* GetSource(); /** Get the event ID. * Use this to determine the event type for safe casting of the data */ std::string GetEventID(); /** Send the Event. * The return result of an Event::Send() will always be NULL as * no replies are expected. */ char* Send(InspIRCd* ServerInstance); }; /** This class can be used on its own to represent an exception, or derived to represent a module-specific exception. * When a module whishes to abort, e.g. within a constructor, it should throw an exception using ModuleException or * a class derived from ModuleException. If a module throws an exception during its constructor, the module will not * be loaded. If this happens, the error message returned by ModuleException::GetReason will be displayed to the user * attempting to load the module, or dumped to the console if the ircd is currently loading for the first time. */ class CoreExport CoreException : public std::exception { protected: /** Holds the error message to be displayed */ const std::string err; /** Source of the exception */ const std::string source; public: /** Default constructor, just uses the error mesage 'Core threw an exception'. */ CoreException() : err("Core threw an exception"), source("The core") {} /** This constructor can be used to specify an error message before throwing. */ 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. */ 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. * Actually no, it does nothing. Never mind. * @throws Nothing! */ 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. */ virtual const char* GetReason() { return err.c_str(); } virtual const char* GetSource() { return source.c_str(); } }; class CoreExport ModuleException : public CoreException { public: /** Default constructor, just uses the error mesage 'Module threw an exception'. */ ModuleException() : CoreException("Module threw an exception", "A Module") {} /** This constructor can be used to specify an error message before throwing. */ ModuleException(const std::string &message) : CoreException(message, "A Module") {} /** This destructor solves world hunger, cancels the world debt, and causes the world to end. * Actually no, it does nothing. Never mind. * @throws Nothing! */ virtual ~ModuleException() throw() {}; }; /** Priority types which can be returned from Module::Prioritize() */ enum Priority { PRIORITY_FIRST, PRIORITY_DONTCARE, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER }; /** Implementation-specific flags which may be set in Module::Implements() */ enum Implementation { I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart, I_OnRehash, I_OnServerRaw, 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_OnSyncChannelMetaData, I_OnSyncUserMetaData, I_OnDecodeMetaData, I_ProtoSendMode, I_ProtoSendMetaData, I_OnWallops, I_OnChangeHost, I_OnChangeName, I_OnAddGLine, I_OnAddZLine, I_OnAddQLine, I_OnAddKLine, I_OnAddELine, I_OnDelGLine, I_OnDelZLine, I_OnDelKLine, I_OnDelELine, I_OnDelQLine, I_OnCleanup, I_OnUserPostNick, I_OnAccessCheck, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule, I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnUserRrgister, I_OnCheckInvite, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnStats, I_OnChangeLocalUserHost, I_OnChangeLocalUserGecos, I_OnLocalTopicChange, I_OnPostLocalTopicChange, I_OnEvent, I_OnRequest, I_OnOperCompre, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan, I_OnDelBan, I_OnRawSocketAccept, I_OnRawSocketClose, I_OnRawSocketWrite, I_OnRawSocketRead, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnOperCompare, I_OnChannelDelete, I_OnPostOper, I_OnSyncOtherMetaData, I_OnSetAway, I_OnCancelAway, I_OnUserList, I_OnPostCommand, I_OnPostJoin, I_OnWhoisLine, I_OnBuildExemptList, I_OnRawSocketConnect, I_OnGarbageCollect, I_OnBufferFlushed }; /** Base class for all InspIRCd modules * This class is the base class for InspIRCd modules. All modules must inherit from this class, * its methods will be called when irc server events occur. class inherited from module must be * instantiated by the ModuleFactory class (see relevent section) for the module to be initialised. */ class CoreExport Module : public Extensible { protected: /** Creator/owner pointer */ InspIRCd* ServerInstance; public: /** Default constructor. * Creates a module class. * @param Me An instance of the InspIRCd class which will be saved into ServerInstance for your use * \exception ModuleException Throwing this class, or any class derived from ModuleException, causes loading of the module to abort. */ Module(InspIRCd* Me); /** Default destructor. * destroys a module class */ virtual ~Module(); /** Returns the version number of a Module. * The method should return a Version object with its version information assigned via * Version::Version */ virtual Version GetVersion(); /** The Implements function specifies which methods a module should receive events for. * The char* parameter passed to this function contains a set of true or false values * (1 or 0) which indicate wether each function is implemented. You must use the Iimplementation * enum (documented elsewhere on this page) to mark functions as active. For example, to * receive events for OnUserJoin(): * * Implements[I_OnUserJoin] = 1; * * @param The implement list */ virtual void Implements(char* Implements); /** Used to set the 'priority' of a module (e.g. when it is called in relation to other modules. * Some modules prefer to be called before other modules, due to their design. For example, a * module which is expected to operate on complete information would expect to be placed last, so * that any other modules which wish to adjust that information would execute before it, to be sure * its information is correct. You can change your module's priority by returning one of: * * PRIORITY_FIRST - To place your module first in the list * * PRIORITY_LAST - To place your module last in the list * * PRIORITY_DONTCARE - To leave your module as it is (this is the default value, if you do not implement this function) * * The result of InspIRCd::PriorityBefore() - To move your module before another named module * * The result of InspIRCd::PriorityLast() - To move your module after another named module * * For a good working example of this method call, please see src/modules/m_spanningtree.cpp * and src/modules/m_hostchange.so which make use of it. It is highly recommended that unless * your module has a real need to reorder its priority, it should not implement this function, * as many modules changing their priorities can make the system redundant. */ virtual Priority Prioritize(); /** Called when a user connects. * The details of the connecting user are available to you in the parameter userrec *user * @param user The user who is connecting */ virtual void OnUserConnect(userrec* user); /** Called when a user quits. * The details of the exiting user are available to you in the parameter userrec *user * This event is only called when the user is fully registered when they quit. To catch * raw disconnections, use the OnUserDisconnect method. * @param user The user who is quitting * @param message The user's quit message (as seen by non-opers) * @param oper_message The user's quit message (as seen by opers) */ virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message); /** Called whenever a user's socket is closed. * The details of the exiting user are available to you in the parameter userrec *user * This event is called for all users, registered or not, as a cleanup method for modules * which might assign resources to user, such as dns lookups, objects and sockets. * @param user The user who is disconnecting */ virtual void OnUserDisconnect(userrec* user); /** Called whenever a channel is deleted, either by QUIT, KICK or PART. * @param chan The channel being deleted */ virtual void OnChannelDelete(chanrec* chan); /** Called when a user joins a channel. * The details of the joining user are available to you in the parameter userrec *user, * and the details of the channel they have joined is available in the variable chanrec *channel * @param user The user who is joining * @param channel The channel being joined * @param silent Change this to true if you want to conceal the JOIN command from the other users * of the channel (useful for modules such as auditorium) */ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent); /** Called after a user joins a channel * Identical to OnUserJoin, but called immediately afterwards, when any linking module has * seen the join. * @param user The user who is joining * @param channel The channel being joined */ virtual void OnPostJoin(userrec* user, chanrec* channel); /** Called when a user parts a channel. * The details of the leaving user are available to you in the parameter userrec *user, * and the details of the channel they have left is available in the variable chanrec *channel * @param user The user who is parting * @param channel The channel being parted * @param partmessage The part message, or an empty string * @param silent Change this to true if you want to conceal the PART command from the other users * of the channel (useful for modules such as auditorium) */ virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent); /** Called on rehash. * This method is called prior to a /REHASH or when a SIGHUP is received from the operating * system. You should use it to reload any files so that your module keeps in step with the * rest of the application. If a parameter is given, the core has done nothing. The module * receiving the event can decide if this parameter has any relevence to it. * @param user The user performing the rehash, if any -- if this is server initiated, the * value of this variable will be NULL. * @param parameter The (optional) parameter given to REHASH from the user. */ virtual void OnRehash(userrec* user, const std::string &parameter); /** Called when a raw command is transmitted or received. * This method is the lowest level of handler available to a module. It will be called with raw * data which is passing through a connected socket. If you wish, you may munge this data by changing * the string parameter "raw". If you do this, after your function exits it will immediately be * cut down to 510 characters plus a carriage return and linefeed. For INBOUND messages only (where * inbound is set to true) the value of user will be the userrec of the connection sending the * data. This is not possible for outbound data because the data may be being routed to multiple targets. * @param raw The raw string in RFC1459 format * @param inbound A flag to indicate wether the data is coming into the daemon or going out to the user * @param user The user record sending the text, when inbound == true. */ virtual void OnServerRaw(std::string &raw, bool inbound, userrec* user); /** Called whenever a user is about to join a channel, before any processing is done. * Returning a value of 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, * notices etc. This is useful for modules which may want to mimic +b, +k, +l etc. Returning -1 from * this function forces the join to be allowed, bypassing restrictions such as banlists, invite, keys etc. * * IMPORTANT NOTE! * * If the user joins a NEW channel which does not exist yet, OnUserPreJoin will be called BEFORE the channel * record is created. This will cause chanrec* chan to be NULL. There is very little you can do in form of * processing on the actual channel record at this point, however the channel NAME will still be passed in * char* cname, so that you could for example implement a channel blacklist or whitelist, etc. * @param user The user joining the channel * @param chan If the channel is a new channel, this will be NULL, otherwise it will be a pointer to the channel being joined * @param cname The channel name being joined. For new channels this is valid where chan is not. * @param privs A string containing the users privilages when joining the channel. For new channels this will contain "@". * You may alter this string to alter the user's modes on the channel. * @return 1 To prevent the join, 0 to allow it. */ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs); /** Called whenever a user is about to be kicked. * Returning a value of 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, * notices etc. * @param source The user issuing the kick * @param user The user being kicked * @param chan The channel the user is being kicked from * @param reason The kick reason * @return 1 to prevent the kick, 0 to continue normally, -1 to explicitly allow the kick regardless of normal operation */ virtual int OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason); /** Called whenever a user is kicked. * If this method is called, the kick is already underway and cannot be prevented, so * to prevent a kick, please use Module::OnUserPreKick instead of this method. * @param source The user issuing the kick * @param user The user being kicked * @param chan The channel the user is being kicked from * @param reason The kick reason * @param silent Change this to true if you want to conceal the PART command from the other users * of the channel (useful for modules such as auditorium) */ virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent); /** Called whenever a user opers locally. * The userrec will contain the oper mode 'o' as this function is called after any modifications * are made to the user's structure by the core. * @param user The user who is opering up * @param opertype The opers type name */ virtual void OnOper(userrec* user, const std::string &opertype); /** Called after a user opers locally. * This is identical to Module::OnOper(), except it is called after OnOper so that other modules * can be gauranteed to already have processed the oper-up, for example m_spanningtree has sent * out the OPERTYPE, etc. * @param user The user who is opering up * @param opertype The opers type name */ virtual void OnPostOper(userrec* user, const std::string &opertype); /** Called whenever a user types /INFO. * The userrec will contain the information of the user who typed the command. Modules may use this * method to output their own credits in /INFO (which is the ircd's version of an about box). * It is purposefully not possible to modify any info that has already been output, or halt the list. * You must write a 371 numeric to the user, containing your info in the following format: * * &lt;nick&gt; :information here * * @param user The user issuing /INFO */ virtual void OnInfo(userrec* 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(userrec* source, userrec* 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, * notices etc. This is useful for modules which may want to filter invites to channels. * @param source The user who is issuing the INVITE * @param dest The user being invited * @param channel The channel the user is being invited to * @return 1 to deny the invite, 0 to allow */ virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel); /** Called after a user has been successfully invited to a channel. * You cannot prevent the invite from occuring using this function, to do that, * use OnUserPreInvite instead. * @param source The user who is issuing the INVITE * @param dest The user being invited * @param channel The channel the user is being invited to */ virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel); /** Called whenever a user is about to PRIVMSG A user or a channel, before any processing is done. * Returning any nonzero value from this function stops the process immediately, causing no * output to be sent to the user by the core. If you do this you must produce your own numerics, * notices etc. This is useful for modules which may want to filter or redirect messages. * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user, * you must cast dest to a userrec* otherwise you must cast it to a chanrec*, this is the details * of where the message is destined to be sent. * @param user The user sending the message * @param dest The target of the message (chanrec* or userrec*) * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) * @param text Changeable text being sent by the user * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone. * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender. * It will be ignored for private messages. * @return 1 to deny the NOTICE, 0 to allow it */ virtual int OnUserPreMessage(userrec* 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 userrec* otherwise you must cast it to a chanrec*, 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 (chanrec* or userrec*) * @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 int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list); /** Called whenever the server wants to build the exemption list for a channel, but is not directly doing a PRIVMSG or NOTICE. * For example, the spanningtree protocol will call this event when passing a privmsg on (but not processing it directly). * @param message_type The message type, either MSG_PRIVMSG or MSG_NOTICE * @param chan The channel to build the exempt list of * @param sender The original sender of the PRIVMSG or NOTICE * @param status The status char to be used for the channel list * @param exempt_list The exempt list to be populated */ virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list); /** 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 userrec 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). * 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 int OnUserPreNick(userrec* user, const std::string &newnick); /** Called after any PRIVMSG sent from a user. * The dest variable contains a userrec* if target_type is TYPE_USER and a chanrec* * if target_type is TYPE_CHANNEL. * @param user The user sending the message * @param dest The target of the message * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) * @param text the text being sent by the user * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone. */ virtual void OnUserMessage(userrec* 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 userrec* if target_type is TYPE_USER and a chanrec* * 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. */ virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); /** Called after every MODE command sent from a user * The dest variable contains a userrec* if target_type is TYPE_USER and a chanrec* * if target_type is TYPE_CHANNEL. The text variable contains the remainder of the * mode string after the target, e.g. "+wsi" or "+ooo nick1 nick2 nick3". * @param user The user sending the MODEs * @param dest The target of the modes (userrec* or chanrec*) * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) * @param text The actual modes and their parameters if any */ virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text); /** Allows modules to alter or create server descriptions * Whenever a module requires a server description, for example for display in * WHOIS, this function is called in all modules. You may change or define the * description given in std::string &description. If you do, this description * will be shown in the WHOIS fields. * @param servername The servername being searched for * @param description Alterable server description for this server */ virtual void OnGetServerDescription(const std::string &servername,std::string &description); /** Allows modules to synchronize data which relates to users during a netburst. * When this function is called, it will be called from the module which implements * the linking protocol. This currently is m_spanningtree.so. A pointer to this module * is given in Module* proto, so that you may call its methods such as ProtoSendMode * (see below). This function will be called for every user visible on your side * of the burst, allowing you to for example set modes, etc. Do not use this call to * synchronize data which you have stored using class Extensible -- There is a specialist * function OnSyncUserMetaData and OnSyncChannelMetaData for this! * @param user The user being syncronized * @param proto A pointer to the module handling network protocol * @param opaque An opaque pointer set by the protocol module, should not be modified! */ virtual void OnSyncUser(userrec* user, Module* proto, void* opaque); /** Allows modules to synchronize data which relates to channels during a netburst. * When this function is called, it will be called from the module which implements * the linking protocol. This currently is m_spanningtree.so. A pointer to this module * is given in Module* proto, so that you may call its methods such as ProtoSendMode * (see below). This function will be called for every user visible on your side * of the burst, allowing you to for example set modes, etc. 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! * * For a good example of how to use this function, please see src/modules/m_chanprotect.cpp * * @param chan The channel being syncronized * @param proto A pointer to the module handling network protocol * @param opaque An opaque pointer set by the protocol module, should not be modified! */ virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque); /* Allows modules to syncronize metadata related to 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 userrec or chanrec 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 it belongs * to your module. For a good example of how to use this method, see src/modules/m_swhois.cpp. * @param chan The channel whos metadata is 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 extname The extensions name which is being searched for * @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. */ virtual void OnSyncChannelMetaData(chanrec* chan, Module* proto,void* opaque, const std::string &extname, bool displayable = false); /* Allows modules to syncronize metadata related to users 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 userrec or chanrec 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 user The user whos metadata is 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 extname The extensions name which is being searched for * @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. */ virtual void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable = false); /* 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 userrec or chanrec 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. */ virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false); /** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module. * Please see src/modules/m_swhois.cpp for a working example of how to use this method call. * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL * @param target The chanrec* or userrec* that data should be added to * @param extname The extension name which is being sent * @param extdata The extension data, encoded at the other end by an identical module through OnSyncChannelMetaData or OnSyncUserMetaData */ virtual void OnDecodeMetaData(int target_type, void* 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 chanrec* or userrec* that modes should be sent for * @param modeline The modes and parameters to be sent */ virtual void ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline); /** 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_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL * @param target The chanrec* or userrec* 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, int target_type, void* 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(userrec* 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 * @param newhost The new hostname being set */ virtual void OnChangeHost(userrec* user, const std::string &newhost); /** Called whenever a user's GECOS (realname) is changed. * This event triggers after the name has been set. * @param user The user who's GECOS is being changed * @param gecos The new GECOS being set on the user */ virtual void OnChangeName(userrec* user, const std::string &gecos); /** Called whenever a gline is added by a local user. * This method is triggered after the line is added. * @param duration The duration of the line in seconds * @param source The sender of the line * @param reason The reason text to be displayed * @param hostmask The hostmask to add */ virtual void OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); /** Called whenever a zline is added by a local user. * This method is triggered after the line is added. * @param duration The duration of the line in seconds * @param source The sender of the line * @param reason The reason text to be displayed * @param ipmask The hostmask to add */ virtual void OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask); /** Called whenever a kline is added by a local user. * This method is triggered after the line is added. * @param duration The duration of the line in seconds * @param source The sender of the line * @param reason The reason text to be displayed * @param hostmask The hostmask to add */ virtual void OnAddKLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); /** Called whenever a qline is added by a local user. * This method is triggered after the line is added. * @param duration The duration of the line in seconds * @param source The sender of the line * @param reason The reason text to be displayed * @param nickmask The hostmask to add */ virtual void OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask); /** Called whenever a eline is added by a local user. * This method is triggered after the line is added. * @param duration The duration of the line in seconds * @param source The sender of the line * @param reason The reason text to be displayed * @param hostmask The hostmask to add */ virtual void OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); /** Called whenever a gline is deleted. * This method is triggered after the line is deleted. * @param source The user removing the line * @param hostmask The hostmask to delete */ virtual void OnDelGLine(userrec* source, const std::string &hostmask); /** Called whenever a zline is deleted. * This method is triggered after the line is deleted. * @param source The user removing the line * @param hostmask The hostmask to delete */ virtual void OnDelZLine(userrec* source, const std::string &ipmask); /** Called whenever a kline is deleted. * This method is triggered after the line is deleted. * @param source The user removing the line * @param hostmask The hostmask to delete */ virtual void OnDelKLine(userrec* source, const std::string &hostmask); /** Called whenever a qline is deleted. * This method is triggered after the line is deleted. * @param source The user removing the line * @param hostmask The hostmask to delete */ virtual void OnDelQLine(userrec* source, const std::string &nickmask); /** Called whenever a eline is deleted. * This method is triggered after the line is deleted. * @param source The user removing the line * @param hostmask The hostmask to delete */ virtual void OnDelELine(userrec* source, const std::string &hostmask); /** Called before your module is unloaded to clean up Extensibles. * This method is called once for every user and channel on the network, * so that when your module unloads it may clear up any remaining data * in the form of Extensibles added using Extensible::Extend(). * If the target_type variable is TYPE_USER, then void* item refers to * a userrec*, otherwise it refers to a chanrec*. * @param target_type The type of item being cleaned * @param item A pointer to the item's class */ virtual void OnCleanup(int target_type, void* item); /** Called after any nickchange, local or remote. This can be used to track users after nickchanges * have been applied. Please note that although you can see remote nickchanges through this function, you should * NOT make any changes to the userrec 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). * Because this method is called after the nickchange is taken place, no return values are possible * to indicate forbidding of the nick change. Use OnUserPreNick for this. * @param user The user changing their nick * @param oldnick The old nickname of the user before the nickchange */ virtual void OnUserPostNick(userrec* user, const std::string &oldnick); /** Called before an action which requires a channel privilage check. * This function is called before many functions which check a users status on a channel, for example * before opping a user, deopping a user, kicking a user, etc. * There are several values for access_type which indicate for what reason access is being checked. * These are:<br><br> * AC_KICK (0) - A user is being kicked<br> * AC_DEOP (1) - a user is being deopped<br> * AC_OP (2) - a user is being opped<br> * AC_VOICE (3) - a user is being voiced<br> * AC_DEVOICE (4) - a user is being devoiced<br> * AC_HALFOP (5) - a user is being halfopped<br> * AC_DEHALFOP (6) - a user is being dehalfopped<br> * AC_INVITE () - a user is being invited<br> * AC_GENERAL_MODE (8) - a user channel mode is being changed<br><br> * Upon returning from your function you must return either ACR_DEFAULT, to indicate the module wishes * to do nothing, or ACR_DENY where approprate to deny the action, and ACR_ALLOW where appropriate to allow * the action. Please note that in the case of some access checks (such as AC_GENERAL_MODE) access may be * denied 'upstream' causing other checks such as AC_DEOP to not be reached. Be very careful with use of the * AC_GENERAL_MODE type, as it may inadvertently override the behaviour of other modules. When the access_type * is AC_GENERAL_MODE, the destination of the mode will be NULL (as it has not yet been determined). * @param source The source of the access check * @param dest The destination of the access check * @param channel The channel which is being checked * @param access_type See above */ virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type); /** 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); /** Called when a client is disconnected by KILL. * If a client is killed by a server, e.g. a nickname collision or protocol error, * source is NULL. * Return 1 from this function to prevent the kill, and 0 from this function to allow * it as normal. If you prevent the kill no output will be sent to the client, it is * down to your module to generate this information. * NOTE: It is NOT advisable to stop kills which originate from servers or remote users. * If you do so youre risking race conditions, desyncs and worse! * @param source The user sending the KILL * @param dest The user being killed * @param reason The kill reason * @return 1 to prevent the kill, 0 to allow */ virtual int OnKill(userrec* source, userrec* 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 */ virtual void OnRemoteKill(userrec* source, userrec* 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, * your module may decide to enable some extra features if it sees that you have * for example loaded "m_killwidgets.so" with "m_makewidgets.so". It is highly * recommended that modules do *NOT* bail if they cannot satisfy dependencies, * but instead operate under reduced functionality, unless the dependency is * absolutely neccessary (e.g. a module that extends the features of another * module). * @param mod A pointer to the new module * @param name The new module's filename */ virtual void OnLoadModule(Module* mod,const std::string &name); /** Called whenever a module is unloaded. * 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, * your module may decide to enable some extra features if it sees that you have * for example loaded "m_killwidgets.so" with "m_makewidgets.so". It is highly * recommended that modules do *NOT* bail if they cannot satisfy dependencies, * but instead operate under reduced functionality, unless the dependency is * absolutely neccessary (e.g. a module that extends the features of another * module). * @param mod Pointer to the module being unloaded (still valid) * @param name The filename of the module being unloaded */ virtual void OnUnloadModule(Module* mod,const std::string &name); /** Called once every five seconds for background processing. * This timer can be used to control timed features. Its period is not accurate * enough to be used as a clock, but it is gauranteed to be called at least once in * any five second period, directly from the main loop of the server. * @param curtime The current timer derived from time(2) */ virtual void OnBackgroundTimer(time_t curtime); /** Called whenever any command is about to be executed. * This event occurs for all registered commands, wether they are registered in the core, * or another module, and for invalid commands. Invalid commands may only be sent to this * function when the value of validated is false. By returning 1 from this method you may prevent the * command being executed. If you do this, no output is created by the core, and it is * down to your module to produce any output neccessary. * Note that unless you return 1, you should not destroy any structures (e.g. by using * InspIRCd::QuitUser) otherwise when the command's handler function executes after your * method returns, it will be passed an invalid pointer to the user object and crash!) * @param command The command being executed * @param parameters An array of array of characters containing the parameters for the command * @param pcnt The nuimber of parameters passed to the command * @param user the user issuing the command * @param validated True if the command has passed all checks, e.g. it is recognised, has enough parameters, the user has permission to execute it, etc. * @param original_line The entire original line as passed to the parser from the user * @return 1 to block the command, 0 to allow */ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line); /** Called after any command has been executed. * This event occurs for all registered commands, wether they are registered in the core, * or another module, but it will not occur for invalid commands (e.g. ones which do not * exist within the command table). The result code returned by the command handler is * provided. * @param command The command being executed * @param parameters An array of array of characters containing the parameters for the command * @param pcnt The nuimber of parameters passed to the command * @param user the user issuing the command * @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE * @param original_line The entire original line as passed to the parser from the user */ virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line); /** Called to check if a user who is connecting can now be allowed to register * If any modules return false for this function, the user is held in the waiting * state until all modules return true. For example a module which implements ident * lookups will continue to return false for a user until their ident lookup is completed. * Note that the registration timeout for a user overrides these checks, if the registration * timeout is reached, the user is disconnected even if modules report that the user is * not ready to connect. * @param user The user to check * @return true to indicate readiness, false if otherwise */ virtual bool OnCheckReady(userrec* user); /** Called whenever a user is about to register their connection (e.g. before the user * is sent the MOTD etc). Modules can use this method if they are performing a function * which must be done before the actual connection is completed (e.g. ident lookups, * dnsbl lookups, etc). * Note that you should NOT delete the user record here by causing a disconnection! * Use OnUserConnect for that instead. * @param user The user registering * @return 1 to indicate user quit, 0 to continue */ virtual int OnUserRegister(userrec* user); /** Called whenever a user joins a channel, to determine if invite checks should go ahead or not. * This method will always be called for each join, wether or not the channel is actually +i, and * determines the outcome of an if statement around the whole section of invite checking code. * return 1 to explicitly allow the join to go ahead or 0 to ignore the event. * @param user The user joining the channel * @param chan The channel being joined * @return 1 to explicitly allow the join, 0 to proceed as normal */ virtual int OnCheckInvite(userrec* user, chanrec* chan); /** 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 * determines the outcome of an if statement around the whole section of key checking code. * if the user specified no key, the keygiven string will be a valid but empty value. * return 1 to explicitly allow the join to go ahead or 0 to ignore the event. * @param user The user joining the channel * @param chan The channel being joined * @return 1 to explicitly allow the join, 0 to proceed as normal */ virtual int OnCheckKey(userrec* user, chanrec* chan, const std::string &keygiven); /** Called whenever a user joins a channel, to determine if channel limit checks should go ahead or not. * This method will always be called for each join, wether or not the channel is actually +l, and * determines the outcome of an if statement around the whole section of channel limit checking code. * return 1 to explicitly allow the join to go ahead or 0 to ignore the event. * @param user The user joining the channel * @param chan The channel being joined * @return 1 to explicitly allow the join, 0 to proceed as normal */ virtual int OnCheckLimit(userrec* user, chanrec* chan); /** Called whenever a user joins a channel, to determine if banlist checks should go ahead or not. * This method will always be called for each join, wether or not the user actually matches a channel ban, and * determines the outcome of an if statement around the whole section of ban checking code. * return 1 to explicitly allow the join to go ahead or 0 to ignore the event. * @param user The user joining the channel * @param chan The channel being joined * @return 1 to explicitly allow the join, 0 to proceed as normal */ virtual int OnCheckBan(userrec* user, chanrec* chan); /** Called on all /STATS commands * This method is triggered for all /STATS use, including stats symbols handled by the core. * @param symbol the symbol provided to /STATS * @param user the user issuing the /STATS command * @param results A string_list to append results into. You should put all your results * into this string_list, rather than displaying them directly, so that your handler will * work when remote STATS queries are received. * @return 1 to block the /STATS from being processed by the core, 0 to allow it */ virtual int OnStats(char symbol, userrec* user, string_list &results); /** Called whenever a change of a local users displayed host is attempted. * Return 1 to deny the host change, or 0 to allow it. * @param user The user whos host will be changed * @param newhost The new hostname * @return 1 to deny the host change, 0 to allow */ virtual int OnChangeLocalUserHost(userrec* user, const std::string &newhost); /** Called whenever a change of a local users GECOS (fullname field) is attempted. * return 1 to deny the name change, or 0 to allow it. * @param user The user whos GECOS will be changed * @param newhost The new GECOS * @return 1 to deny the GECOS change, 0 to allow */ virtual int OnChangeLocalUserGECOS(userrec* user, const std::string &newhost); /** Called whenever a topic is changed by a local user. * Return 1 to deny the topic change, or 0 to allow it. * @param user The user changing the topic * @param chan The channels who's topic is being changed * @param topic The actual topic text * @param 1 to block the topic change, 0 to allow */ virtual int OnLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic); /** Called whenever a local topic has been changed. * To block topic changes you must use OnLocalTopicChange instead. * @param user The user changing the topic * @param chan The channels who's topic is being changed * @param topic The actual topic text */ virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic); /** Called whenever an Event class is sent to all module by another module. * Please see the documentation of Event::Send() for further information. The Event sent can * always be assumed to be non-NULL, you should *always* check the value of Event::GetEventID() * before doing anything to the event data, and you should *not* change the event data in any way! * @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. * Please see the documentation of Request::Send() for further information. The Request sent * can always be assumed to be non-NULL, you should not change the request object or its data. * Your method may return arbitary data in the char* result which the requesting module * may be able to use for pre-determined purposes (e.g. the results of an SQL query, etc). * @param request The Request class being received */ virtual char* OnRequest(Request* request); /** Called whenever an oper password is to be compared to what a user has input. * The password field (from the config file) is in 'password' and is to be compared against * 'input'. This method allows for encryption of oper passwords and much more besides. * You should return a nonzero value if you want to allow the comparison or zero if you wish * to do nothing. * @param password The oper's password * @param input The password entered * @param tagnumber The tag number (from the configuration file) of this oper's tag * @return 1 to match the passwords, 0 to do nothing. -1 to not match, and not continue. */ virtual int OnOperCompare(const std::string &password, const std::string &input, int tagnumber); /** 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(userrec* 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. * This is called for both local and remote users. * @param user The user who is connecting */ virtual void OnPostConnect(userrec* 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 int OnAddBan(userrec* source, chanrec* 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 int OnDelBan(userrec* source, chanrec* channel,const std::string &banmask); /** 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 fd The file descriptor returned from accept() * @param ip The IP address of the connecting user * @param localport The local port number the user connected to */ virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport); /** Called immediately before any write() operation on a user's socket in the core. Because * this event is a low level event no user information is associated with it. It is intended * for use by modules which may wrap connections within another API such as SSL for example. * return a non-zero result if you have handled the write operation, in which case the core * will not call write(). * @param fd The file descriptor of the socket * @param buffer A char* buffer being written * @param Number of characters to write * @return Number of characters actually written or 0 if you didn't handle the operation */ virtual int OnRawSocketWrite(int fd, const char* buffer, int count); /** Called immediately before any socket is closed. When this event is called, shutdown() * has not yet been called on the socket. * @param fd The file descriptor of the socket prior to close() */ virtual void OnRawSocketClose(int fd); /** Called immediately upon connection of an outbound InspSocket which has been hooked * by a module. * @param fd The file descriptor of the socket immediately after connect() */ virtual void OnRawSocketConnect(int fd); /** Called immediately before any read() operation on a client socket in the core. * This occurs AFTER the select() or poll() so there is always data waiting to be read * when this event occurs. * Your event should return 1 if it has handled the reading itself, which prevents the core * just using read(). You should place any data read into buffer, up to but NOT GREATER THAN * the value of count. The value of readresult must be identical to an actual result that might * be returned from the read() system call, for example, number of bytes read upon success, * 0 upon EOF or closed socket, and -1 for error. If your function returns a nonzero value, * you MUST set readresult. * @param fd The file descriptor of the socket * @param buffer A char* buffer being read to * @param count The size of the buffer * @param readresult The amount of characters read, or 0 * @return nonzero if the event was handled, in which case readresult must be valid on exit */ virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult); /** Called whenever a user sets away. * This method has no parameter for the away message, as it is available in the * user record as userrec::awaymsg. * @param user The user setting away */ virtual void OnSetAway(userrec* user); /** Called when a user cancels their away state. * @param user The user returning from away */ virtual void OnCancelAway(userrec* user); /** Called whenever a NAMES list is requested. * You can produce the nameslist yourself, overriding the current list, * and if you do you must return 1. If you do not handle the names list, * return 0. * @param The user requesting the NAMES list * @param Ptr The channel the NAMES list is requested for * @param userlist The user list for the channel (you may change this pointer. * If you want to change the values, take a copy first, and change the copy, then * point the pointer at your copy) * @return 1 to prevent the user list being sent to the client, 0 to allow it */ virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &userlist); /** 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 userrec 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 int OnWhoisLine(userrec* user, userrec* 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 * method is called when it is time to do that. */ virtual void OnGarbageCollect(); /** Called whenever a user's write buffer has been completely sent. * This is called when the user's write buffer is completely empty, and * there are no more pending bytes to be written and no pending write events * in the socket engine's queue. This may be used to refill the buffer with * data which is being spooled in a controlled manner, e.g. LIST lines. * @param user The user who's buffer is now empty. */ virtual void OnBufferFlushed(userrec* user); }; #define CONF_NOT_A_NUMBER 0x000010 #define CONF_NOT_UNSIGNED 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 classbase { protected: InspIRCd* ServerInstance; /** The contents of the configuration file * This protected member should never be accessed by a module (and cannot be accessed unless the * core is changed). It will contain a pointer to the configuration file data with unneeded data * (such as comments) stripped from it. */ ConfigDataHash* data; /** Used to store errors */ std::ostringstream* errorlog; /** If we're using our own config data hash or not */ bool privatehash; /** True if an error occured reading the config file */ bool readerror; /** 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(InspIRCd* Instance); /** Overloaded constructor. * This constructor initialises the ConfigReader class to read a user-specified config file */ ConfigReader(InspIRCd* Instance, const std::string &filename); /** 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. 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 */ long ReadInteger(const std::string &tag, const std::string &name, int index, bool needs_unsigned); /** 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. */ long ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool needs_unsigned); /** 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); /** Returns true if a config file is valid. * This method is partially implemented and will only return false if the config * file does not exist or could not be opened. */ bool Verify(); /** Dumps the list of errors in a config file to an output location. If bail is true, * then the program will abort. If bail is false and user points to a valid user * record, the error report will be spooled to the given user by means of NOTICE. * if bool is false AND user is false, the error report will be spooled to all opers * by means of a NOTICE to all opers. */ void DumpErrors(bool bail,userrec* user); /** Returns the number of items within a tag. * For example if the tag was &lt;test tag="blah" data="foo"&gt; then this * function would return 2. Spaces and newlines both qualify as valid seperators * between values. */ int EnumerateValues(const std::string &tag, int index); }; /** 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 { InspIRCd* ServerInstance; /** The file contents */ file_cache 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(InspIRCd* Instance); /** 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(InspIRCd* Instance, 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(); }; /** Instantiates classes inherited from Module. * This class creates a class inherited from type Module, using new. This is to allow for modules * to create many different variants of Module, dependent on architecture, configuration, etc. * In most cases, the simple class shown in the example module m_foobar.so will suffice for most * modules. */ class CoreExport ModuleFactory : public classbase { public: /** The default constructor does nothing. */ ModuleFactory() { } /** The default destructor does nothing */ virtual ~ModuleFactory() { } /** Creates a new module. * Your inherited class of ModuleFactory must return a pointer to your Module class * using this method. */ virtual Module * CreateModule(InspIRCd* Me) = 0; }; /** A DLLFactory (designed to load shared objects) containing a ModuleFactory. */ typedef DLLFactory<ModuleFactory> ircd_module; /** A list of loaded Modules */ typedef std::vector<Module*> ModuleList; /** A list of loaded ModuleFactories */ typedef std::vector<ircd_module*> FactoryList; /** This definition is used as shorthand for the various classes * and functions needed to make a module loadable by the OS. * It defines the class factory and external init_module function. */ #define MODULE_INIT(y) \ class Factory : public ModuleFactory \ { \ public: \ virtual Module * CreateModule(InspIRCd* Me) \ { \ return new y(Me); \ } \ }; \ extern "C" DllExport void * init_module(void) \ { \ return new Factory; \ } #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __MODULES_H
+#define __MODULES_H
+
+/** Used with OnAccessCheck() method of modules
+ */
+enum AccessControlType {
+ ACR_DEFAULT, // Do default action (act as if the module isnt even loaded)
+ ACR_DENY, // deny the action
+ ACR_ALLOW, // allow the action
+ AC_KICK, // a user is being kicked
+ AC_DEOP, // a user is being deopped
+ AC_OP, // a user is being opped
+ AC_VOICE, // a user is being voiced
+ AC_DEVOICE, // a user is being devoiced
+ AC_HALFOP, // a user is being halfopped
+ AC_DEHALFOP, // a user is being dehalfopped
+ AC_INVITE, // a user is being invited
+ AC_GENERAL_MODE // a channel mode is being changed
+};
+
+/** Used to define a set of behavior bits for a module
+ */
+enum ModuleFlags {
+ 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_SERVICEPROVIDER = 4, // module provides a service to other modules (can be a dependency)
+ VF_COMMON = 8 // module needs to be common on all servers in a network to link
+};
+
+/** Used with SendToMode()
+ */
+enum WriteModeFlags {
+ WM_AND = 1,
+ WM_OR = 2
+};
+
+/** Used to represent an event type, for user, channel or server
+ */
+enum TargetTypeFlags {
+ TYPE_USER = 1,
+ TYPE_CHANNEL,
+ TYPE_SERVER,
+ TYPE_OTHER
+};
+
+/** Used to represent wether a message was PRIVMSG or NOTICE
+ */
+enum MessageType {
+ MSG_PRIVMSG = 0,
+ MSG_NOTICE = 1
+};
+
+#include "globals.h"
+#include "dynamic.h"
+#include "base.h"
+#include "ctables.h"
+#include "inspsocket.h"
+#include <string>
+#include <deque>
+#include <sstream>
+#include "timer.h"
+#include "mode.h"
+#include "dns.h"
+
+/** If you change the module API, change this value.
+ * If you have enabled ipv6, the sizes of structs is
+ * different, and modules will be incompatible with
+ * ipv4 servers, so this value will be ten times as
+ * high on ipv6 servers.
+ */
+#define NATIVE_API_VERSION 11025
+#ifdef IPV6
+#define API_VERSION (NATIVE_API_VERSION * 10)
+#else
+#define API_VERSION (NATIVE_API_VERSION * 1)
+#endif
+
+class ServerConfig;
+
+/* Forward-delacare module for ModuleMessage etc
+ */
+class Module;
+
+/** Low level definition of a FileReader classes file cache area -
+ * a text file seperated into lines.
+ */
+typedef std::deque<std::string> file_cache;
+
+/** A set of strings.
+ */
+typedef file_cache string_list;
+
+/** Holds a list of 'published features' for modules.
+ */
+typedef std::map<std::string,Module*> featurelist;
+
+/** Holds a list of modules which implement an interface
+ */
+typedef std::deque<Module*> modulelist;
+
+/** Holds a list of all modules which implement interfaces, by interface name
+ */
+typedef std::map<std::string, std::pair<int, modulelist> > interfacelist;
+
+/**
+ * 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));'
+ */
+#define FOREACH_MOD(y,x) if (ServerInstance->Config->global_implementation[y] > 0) { \
+ for (int _i = 0; _i <= ServerInstance->GetModuleCount(); _i++) { \
+ if (ServerInstance->Config->implement_lists[_i][y]) \
+ try \
+ { \
+ ServerInstance->modules[_i]->x ; \
+ } \
+ catch (CoreException& modexcept) \
+ { \
+ ServerInstance->Log(DEFAULT,"Exception cought: %s",modexcept.GetReason()); \
+ } \
+ } \
+ }
+
+/**
+ * This #define allows us to call a method in all
+ * loaded modules in a readable simple way and pass
+ * an instance pointer to the macro. e.g.:
+ * 'FOREACH_MOD_I(Instance, OnConnect, OnConnect(user));'
+ */
+#define FOREACH_MOD_I(z,y,x) if (z->Config->global_implementation[y] > 0) { \
+ for (int _i = 0; _i <= z->GetModuleCount(); _i++) { \
+ if (z->Config->implement_lists[_i][y]) \
+ try \
+ { \
+ z->modules[_i]->x ; \
+ } \
+ catch (CoreException& modexcept) \
+ { \
+ z->Log(DEFAULT,"Exception cought: %s",modexcept.GetReason()); \
+ } \
+ } \
+}
+/**
+ * This define is similar to the one above but returns a result in MOD_RESULT.
+ * The first module to return a nonzero result is the value to be accepted,
+ * and any modules after are ignored.
+ */
+#define FOREACH_RESULT(y,x) { if (ServerInstance->Config->global_implementation[y] > 0) { \
+ MOD_RESULT = 0; \
+ for (int _i = 0; _i <= ServerInstance->GetModuleCount(); _i++) { \
+ if (ServerInstance->Config->implement_lists[_i][y]) { \
+ try \
+ { \
+ int res = ServerInstance->modules[_i]->x ; \
+ if (res != 0) { \
+ MOD_RESULT = res; \
+ break; \
+ } \
+ } \
+ catch (CoreException& modexcept) \
+ { \
+ ServerInstance->Log(DEFAULT,"Exception cought: %s",modexcept.GetReason()); \
+ } \
+ } \
+ } \
+ } \
+ }
+
+/**
+ * This define is similar to the one above but returns a result in MOD_RESULT.
+ * The first module to return a nonzero result is the value to be accepted,
+ * and any modules after are ignored.
+ */
+#define FOREACH_RESULT_I(z,y,x) { if (z->Config->global_implementation[y] > 0) { \
+ MOD_RESULT = 0; \
+ for (int _i = 0; _i <= z->GetModuleCount(); _i++) { \
+ if (z->Config->implement_lists[_i][y]) { \
+ try \
+ { \
+ int res = z->modules[_i]->x ; \
+ if (res != 0) { \
+ MOD_RESULT = res; \
+ break; \
+ } \
+ } \
+ catch (CoreException& modexcept) \
+ { \
+ z->Log(DEBUG,"Exception cought: %s",modexcept.GetReason()); \
+ } \
+ } \
+ } \
+ } \
+}
+
+/** Represents a non-local user.
+ * (in fact, any FD less than -1 does)
+ */
+#define FD_MAGIC_NUMBER -42
+
+/* Useful macros */
+#ifdef WINDOWS
+/** Is a local user */
+#define IS_LOCAL(x) ((x->GetFd() > -1))
+#else
+/** Is a local user */
+#define IS_LOCAL(x) ((x->GetFd() > -1) && (x->GetFd() <= MAX_DESCRIPTORS))
+#endif
+/** Is a remote user */
+#define IS_REMOTE(x) (x->GetFd() < 0)
+/** Is a module created user */
+#define IS_MODULE_CREATED(x) (x->GetFd() == FD_MAGIC_NUMBER)
+/** Is an oper */
+#define IS_OPER(x) (*x->oper)
+/** Is away */
+#define IS_AWAY(x) (*x->awaymsg)
+
+/** Holds a module's Version information.
+ * The four members (set by the constructor only) indicate details as to the version number
+ * of a module. A class of type Version is returned by the GetVersion method of the Module class.
+ * The flags and API values represent the module flags and API version of the module.
+ * The API version of a module must match the API version of the core exactly for the module to
+ * load successfully.
+ */
+class CoreExport Version : public classbase
+{
+ public:
+ /** Version numbers, build number, flags and API version
+ */
+ const int Major, Minor, Revision, Build, Flags, API;
+
+ /** Initialize version class
+ */
+ Version(int major, int minor, int revision, int build, int flags, int api_ver);
+};
+
+/** The ModuleMessage class is the base class of Request and Event
+ * This class is used to represent a basic data structure which is passed
+ * between modules for safe inter-module communications.
+ */
+class CoreExport ModuleMessage : public Extensible
+{
+ public:
+ /** Destructor
+ */
+ virtual ~ModuleMessage() {};
+};
+
+/** 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 ModuleMessage
+{
+ protected:
+ /** This member holds a pointer to arbitary data set by the emitter of the message
+ */
+ char* data;
+ /** 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* id;
+ /** This is a pointer to the sender of the message, which can be used to
+ * directly trigger events, or to create a reply.
+ */
+ Module* source;
+ /** The single destination of the Request
+ */
+ Module* dest;
+ public:
+ /** Create a new Request
+ * This is for the 'old' way of casting whatever the data is
+ * to char* and hoping you get the right thing at the other end.
+ * This is slowly being depreciated in favor of the 'new' way.
+ */
+ Request(char* anydata, Module* src, Module* dst);
+ /** 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. This is
+ * much safer as there are no casts not confirmed by
+ * the ID string, and all casts are child->parent and
+ * can be checked at runtime with dynamic_cast<>()
+ */
+ Request(Module* src, Module* dst, const char* idstr);
+ /** Fetch the Request data
+ */
+ char* GetData();
+ /** Fetch the ID string
+ */
+ const char* GetId();
+ /** Fetch the request source
+ */
+ Module* GetSource();
+ /** Fetch the request destination (should be 'this' in the receiving module)
+ */
+ Module* GetDest();
+ /** Send the Request.
+ * Upon returning the result will be arbitary data returned by the module you
+ * sent the request to. It is up to your module to know what this data is and
+ * how to deal with it.
+ */
+ char* 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 ModuleMessage
+{
+ protected:
+ /** This member holds a pointer to arbitary data set by the emitter of the message
+ */
+ char* data;
+ /** This is a pointer to the sender of the message, which can be used to
+ * directly trigger events, or to create a reply.
+ */
+ Module* source;
+ /** The event identifier.
+ * This is arbitary text which should be used to distinguish
+ * one type of event from another.
+ */
+ std::string id;
+ public:
+ /** Create a new Event
+ */
+ Event(char* anydata, Module* src, const std::string &eventid);
+ /** Get the Event data
+ */
+ char* GetData();
+ /** Get the event Source
+ */
+ Module* GetSource();
+ /** Get the event ID.
+ * Use this to determine the event type for safe casting of the data
+ */
+ std::string GetEventID();
+ /** Send the Event.
+ * The return result of an Event::Send() will always be NULL as
+ * no replies are expected.
+ */
+ char* Send(InspIRCd* ServerInstance);
+};
+
+/** This class can be used on its own to represent an exception, or derived to represent a module-specific exception.
+ * When a module whishes to abort, e.g. within a constructor, it should throw an exception using ModuleException or
+ * a class derived from ModuleException. If a module throws an exception during its constructor, the module will not
+ * be loaded. If this happens, the error message returned by ModuleException::GetReason will be displayed to the user
+ * attempting to load the module, or dumped to the console if the ircd is currently loading for the first time.
+ */
+class CoreExport CoreException : public std::exception
+{
+ protected:
+ /** Holds the error message to be displayed
+ */
+ const std::string err;
+ /** Source of the exception
+ */
+ const std::string source;
+ public:
+ /** Default constructor, just uses the error mesage 'Core threw an exception'.
+ */
+ CoreException() : err("Core threw an exception"), source("The core") {}
+ /** This constructor can be used to specify an error message before throwing.
+ */
+ 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.
+ */
+ 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.
+ * Actually no, it does nothing. Never mind.
+ * @throws Nothing!
+ */
+ 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.
+ */
+ virtual const char* GetReason()
+ {
+ return err.c_str();
+ }
+
+ virtual const char* GetSource()
+ {
+ return source.c_str();
+ }
+};
+
+class CoreExport ModuleException : public CoreException
+{
+ public:
+ /** Default constructor, just uses the error mesage 'Module threw an exception'.
+ */
+ ModuleException() : CoreException("Module threw an exception", "A Module") {}
+
+ /** This constructor can be used to specify an error message before throwing.
+ */
+ ModuleException(const std::string &message) : CoreException(message, "A Module") {}
+ /** This destructor solves world hunger, cancels the world debt, and causes the world to end.
+ * Actually no, it does nothing. Never mind.
+ * @throws Nothing!
+ */
+ virtual ~ModuleException() throw() {};
+};
+
+/** Priority types which can be returned from Module::Prioritize()
+ */
+enum Priority { PRIORITY_FIRST, PRIORITY_DONTCARE, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER };
+
+/** Implementation-specific flags which may be set in Module::Implements()
+ */
+enum Implementation { I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart, I_OnRehash, I_OnServerRaw,
+ 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_OnSyncChannelMetaData, I_OnSyncUserMetaData,
+ I_OnDecodeMetaData, I_ProtoSendMode, I_ProtoSendMetaData, I_OnWallops, I_OnChangeHost, I_OnChangeName, I_OnAddGLine,
+ I_OnAddZLine, I_OnAddQLine, I_OnAddKLine, I_OnAddELine, I_OnDelGLine, I_OnDelZLine, I_OnDelKLine, I_OnDelELine, I_OnDelQLine,
+ I_OnCleanup, I_OnUserPostNick, I_OnAccessCheck, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule, I_OnUnloadModule,
+ I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnUserRrgister, I_OnCheckInvite,
+ I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnStats, I_OnChangeLocalUserHost, I_OnChangeLocalUserGecos, I_OnLocalTopicChange,
+ I_OnPostLocalTopicChange, I_OnEvent, I_OnRequest, I_OnOperCompre, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan, I_OnDelBan,
+ I_OnRawSocketAccept, I_OnRawSocketClose, I_OnRawSocketWrite, I_OnRawSocketRead, I_OnChangeLocalUserGECOS, I_OnUserRegister,
+ I_OnOperCompare, I_OnChannelDelete, I_OnPostOper, I_OnSyncOtherMetaData, I_OnSetAway, I_OnCancelAway, I_OnUserList,
+ I_OnPostCommand, I_OnPostJoin, I_OnWhoisLine, I_OnBuildExemptList, I_OnRawSocketConnect, I_OnGarbageCollect, I_OnBufferFlushed };
+
+/** Base class for all InspIRCd modules
+ * This class is the base class for InspIRCd modules. All modules must inherit from this class,
+ * its methods will be called when irc server events occur. class inherited from module must be
+ * instantiated by the ModuleFactory class (see relevent section) for the module to be initialised.
+ */
+class CoreExport Module : public Extensible
+{
+ protected:
+ /** Creator/owner pointer
+ */
+ InspIRCd* ServerInstance;
+ public:
+
+ /** Default constructor.
+ * Creates a module class.
+ * @param Me An instance of the InspIRCd class which will be saved into ServerInstance for your use
+ * \exception ModuleException Throwing this class, or any class derived from ModuleException, causes loading of the module to abort.
+ */
+ Module(InspIRCd* Me);
+
+ /** Default destructor.
+ * destroys a module class
+ */
+ virtual ~Module();
+
+ /** Returns the version number of a Module.
+ * The method should return a Version object with its version information assigned via
+ * Version::Version
+ */
+ virtual Version GetVersion();
+
+ /** The Implements function specifies which methods a module should receive events for.
+ * The char* parameter passed to this function contains a set of true or false values
+ * (1 or 0) which indicate wether each function is implemented. You must use the Iimplementation
+ * enum (documented elsewhere on this page) to mark functions as active. For example, to
+ * receive events for OnUserJoin():
+ *
+ * Implements[I_OnUserJoin] = 1;
+ *
+ * @param The implement list
+ */
+ virtual void Implements(char* Implements);
+
+ /** Used to set the 'priority' of a module (e.g. when it is called in relation to other modules.
+ * Some modules prefer to be called before other modules, due to their design. For example, a
+ * module which is expected to operate on complete information would expect to be placed last, so
+ * that any other modules which wish to adjust that information would execute before it, to be sure
+ * its information is correct. You can change your module's priority by returning one of:
+ *
+ * PRIORITY_FIRST - To place your module first in the list
+ *
+ * PRIORITY_LAST - To place your module last in the list
+ *
+ * PRIORITY_DONTCARE - To leave your module as it is (this is the default value, if you do not implement this function)
+ *
+ * The result of InspIRCd::PriorityBefore() - To move your module before another named module
+ *
+ * The result of InspIRCd::PriorityLast() - To move your module after another named module
+ *
+ * For a good working example of this method call, please see src/modules/m_spanningtree.cpp
+ * and src/modules/m_hostchange.so which make use of it. It is highly recommended that unless
+ * your module has a real need to reorder its priority, it should not implement this function,
+ * as many modules changing their priorities can make the system redundant.
+ */
+ virtual Priority Prioritize();
+
+ /** Called when a user connects.
+ * The details of the connecting user are available to you in the parameter userrec *user
+ * @param user The user who is connecting
+ */
+ virtual void OnUserConnect(userrec* user);
+
+ /** Called when a user quits.
+ * The details of the exiting user are available to you in the parameter userrec *user
+ * This event is only called when the user is fully registered when they quit. To catch
+ * raw disconnections, use the OnUserDisconnect method.
+ * @param user The user who is quitting
+ * @param message The user's quit message (as seen by non-opers)
+ * @param oper_message The user's quit message (as seen by opers)
+ */
+ virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message);
+
+ /** Called whenever a user's socket is closed.
+ * The details of the exiting user are available to you in the parameter userrec *user
+ * This event is called for all users, registered or not, as a cleanup method for modules
+ * which might assign resources to user, such as dns lookups, objects and sockets.
+ * @param user The user who is disconnecting
+ */
+ virtual void OnUserDisconnect(userrec* user);
+
+ /** Called whenever a channel is deleted, either by QUIT, KICK or PART.
+ * @param chan The channel being deleted
+ */
+ virtual void OnChannelDelete(chanrec* chan);
+
+ /** Called when a user joins a channel.
+ * The details of the joining user are available to you in the parameter userrec *user,
+ * and the details of the channel they have joined is available in the variable chanrec *channel
+ * @param user The user who is joining
+ * @param channel The channel being joined
+ * @param silent Change this to true if you want to conceal the JOIN command from the other users
+ * of the channel (useful for modules such as auditorium)
+ */
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent);
+
+ /** Called after a user joins a channel
+ * Identical to OnUserJoin, but called immediately afterwards, when any linking module has
+ * seen the join.
+ * @param user The user who is joining
+ * @param channel The channel being joined
+ */
+ virtual void OnPostJoin(userrec* user, chanrec* channel);
+
+ /** Called when a user parts a channel.
+ * The details of the leaving user are available to you in the parameter userrec *user,
+ * and the details of the channel they have left is available in the variable chanrec *channel
+ * @param user The user who is parting
+ * @param channel The channel being parted
+ * @param partmessage The part message, or an empty string
+ * @param silent Change this to true if you want to conceal the PART command from the other users
+ * of the channel (useful for modules such as auditorium)
+ */
+ virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent);
+
+ /** Called on rehash.
+ * This method is called prior to a /REHASH or when a SIGHUP is received from the operating
+ * system. You should use it to reload any files so that your module keeps in step with the
+ * rest of the application. If a parameter is given, the core has done nothing. The module
+ * receiving the event can decide if this parameter has any relevence to it.
+ * @param user The user performing the rehash, if any -- if this is server initiated, the
+ * value of this variable will be NULL.
+ * @param parameter The (optional) parameter given to REHASH from the user.
+ */
+ virtual void OnRehash(userrec* user, const std::string &parameter);
+
+ /** Called when a raw command is transmitted or received.
+ * This method is the lowest level of handler available to a module. It will be called with raw
+ * data which is passing through a connected socket. If you wish, you may munge this data by changing
+ * the string parameter "raw". If you do this, after your function exits it will immediately be
+ * cut down to 510 characters plus a carriage return and linefeed. For INBOUND messages only (where
+ * inbound is set to true) the value of user will be the userrec of the connection sending the
+ * data. This is not possible for outbound data because the data may be being routed to multiple targets.
+ * @param raw The raw string in RFC1459 format
+ * @param inbound A flag to indicate wether the data is coming into the daemon or going out to the user
+ * @param user The user record sending the text, when inbound == true.
+ */
+ virtual void OnServerRaw(std::string &raw, bool inbound, userrec* user);
+
+ /** Called whenever a user is about to join a channel, before any processing is done.
+ * Returning a value of 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,
+ * notices etc. This is useful for modules which may want to mimic +b, +k, +l etc. Returning -1 from
+ * this function forces the join to be allowed, bypassing restrictions such as banlists, invite, keys etc.
+ *
+ * IMPORTANT NOTE!
+ *
+ * If the user joins a NEW channel which does not exist yet, OnUserPreJoin will be called BEFORE the channel
+ * record is created. This will cause chanrec* chan to be NULL. There is very little you can do in form of
+ * processing on the actual channel record at this point, however the channel NAME will still be passed in
+ * char* cname, so that you could for example implement a channel blacklist or whitelist, etc.
+ * @param user The user joining the channel
+ * @param chan If the channel is a new channel, this will be NULL, otherwise it will be a pointer to the channel being joined
+ * @param cname The channel name being joined. For new channels this is valid where chan is not.
+ * @param privs A string containing the users privilages when joining the channel. For new channels this will contain "@".
+ * You may alter this string to alter the user's modes on the channel.
+ * @return 1 To prevent the join, 0 to allow it.
+ */
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs);
+
+ /** Called whenever a user is about to be kicked.
+ * Returning a value of 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,
+ * notices etc.
+ * @param source The user issuing the kick
+ * @param user The user being kicked
+ * @param chan The channel the user is being kicked from
+ * @param reason The kick reason
+ * @return 1 to prevent the kick, 0 to continue normally, -1 to explicitly allow the kick regardless of normal operation
+ */
+ virtual int OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason);
+
+ /** Called whenever a user is kicked.
+ * If this method is called, the kick is already underway and cannot be prevented, so
+ * to prevent a kick, please use Module::OnUserPreKick instead of this method.
+ * @param source The user issuing the kick
+ * @param user The user being kicked
+ * @param chan The channel the user is being kicked from
+ * @param reason The kick reason
+ * @param silent Change this to true if you want to conceal the PART command from the other users
+ * of the channel (useful for modules such as auditorium)
+ */
+ virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent);
+
+ /** Called whenever a user opers locally.
+ * The userrec will contain the oper mode 'o' as this function is called after any modifications
+ * are made to the user's structure by the core.
+ * @param user The user who is opering up
+ * @param opertype The opers type name
+ */
+ virtual void OnOper(userrec* user, const std::string &opertype);
+
+ /** Called after a user opers locally.
+ * This is identical to Module::OnOper(), except it is called after OnOper so that other modules
+ * can be gauranteed to already have processed the oper-up, for example m_spanningtree has sent
+ * out the OPERTYPE, etc.
+ * @param user The user who is opering up
+ * @param opertype The opers type name
+ */
+ virtual void OnPostOper(userrec* user, const std::string &opertype);
+
+ /** Called whenever a user types /INFO.
+ * The userrec will contain the information of the user who typed the command. Modules may use this
+ * method to output their own credits in /INFO (which is the ircd's version of an about box).
+ * It is purposefully not possible to modify any info that has already been output, or halt the list.
+ * You must write a 371 numeric to the user, containing your info in the following format:
+ *
+ * &lt;nick&gt; :information here
+ *
+ * @param user The user issuing /INFO
+ */
+ virtual void OnInfo(userrec* 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(userrec* source, userrec* 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,
+ * notices etc. This is useful for modules which may want to filter invites to channels.
+ * @param source The user who is issuing the INVITE
+ * @param dest The user being invited
+ * @param channel The channel the user is being invited to
+ * @return 1 to deny the invite, 0 to allow
+ */
+ virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel);
+
+ /** Called after a user has been successfully invited to a channel.
+ * You cannot prevent the invite from occuring using this function, to do that,
+ * use OnUserPreInvite instead.
+ * @param source The user who is issuing the INVITE
+ * @param dest The user being invited
+ * @param channel The channel the user is being invited to
+ */
+ virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel);
+
+ /** Called whenever a user is about to PRIVMSG A user or a channel, before any processing is done.
+ * Returning any nonzero value from this function stops the process immediately, causing no
+ * output to be sent to the user by the core. If you do this you must produce your own numerics,
+ * notices etc. This is useful for modules which may want to filter or redirect messages.
+ * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user,
+ * you must cast dest to a userrec* otherwise you must cast it to a chanrec*, this is the details
+ * of where the message is destined to be sent.
+ * @param user The user sending the message
+ * @param dest The target of the message (chanrec* or userrec*)
+ * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
+ * @param text Changeable text being sent by the user
+ * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
+ * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender.
+ * It will be ignored for private messages.
+ * @return 1 to deny the NOTICE, 0 to allow it
+ */
+ virtual int OnUserPreMessage(userrec* 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 userrec* otherwise you must cast it to a chanrec*, 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 (chanrec* or userrec*)
+ * @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 int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
+
+ /** Called whenever the server wants to build the exemption list for a channel, but is not directly doing a PRIVMSG or NOTICE.
+ * For example, the spanningtree protocol will call this event when passing a privmsg on (but not processing it directly).
+ * @param message_type The message type, either MSG_PRIVMSG or MSG_NOTICE
+ * @param chan The channel to build the exempt list of
+ * @param sender The original sender of the PRIVMSG or NOTICE
+ * @param status The status char to be used for the channel list
+ * @param exempt_list The exempt list to be populated
+ */
+ virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list);
+
+ /** 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 userrec 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).
+ * 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 int OnUserPreNick(userrec* user, const std::string &newnick);
+
+ /** Called after any PRIVMSG sent from a user.
+ * The dest variable contains a userrec* if target_type is TYPE_USER and a chanrec*
+ * if target_type is TYPE_CHANNEL.
+ * @param user The user sending the message
+ * @param dest The target of the message
+ * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
+ * @param text the text being sent by the user
+ * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
+ */
+ virtual void OnUserMessage(userrec* 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 userrec* if target_type is TYPE_USER and a chanrec*
+ * 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.
+ */
+ virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
+
+ /** Called after every MODE command sent from a user
+ * The dest variable contains a userrec* if target_type is TYPE_USER and a chanrec*
+ * if target_type is TYPE_CHANNEL. The text variable contains the remainder of the
+ * mode string after the target, e.g. "+wsi" or "+ooo nick1 nick2 nick3".
+ * @param user The user sending the MODEs
+ * @param dest The target of the modes (userrec* or chanrec*)
+ * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
+ * @param text The actual modes and their parameters if any
+ */
+ virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text);
+
+ /** Allows modules to alter or create server descriptions
+ * Whenever a module requires a server description, for example for display in
+ * WHOIS, this function is called in all modules. You may change or define the
+ * description given in std::string &description. If you do, this description
+ * will be shown in the WHOIS fields.
+ * @param servername The servername being searched for
+ * @param description Alterable server description for this server
+ */
+ virtual void OnGetServerDescription(const std::string &servername,std::string &description);
+
+ /** Allows modules to synchronize data which relates to users during a netburst.
+ * When this function is called, it will be called from the module which implements
+ * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
+ * is given in Module* proto, so that you may call its methods such as ProtoSendMode
+ * (see below). This function will be called for every user visible on your side
+ * of the burst, allowing you to for example set modes, etc. Do not use this call to
+ * synchronize data which you have stored using class Extensible -- There is a specialist
+ * function OnSyncUserMetaData and OnSyncChannelMetaData for this!
+ * @param user The user being syncronized
+ * @param proto A pointer to the module handling network protocol
+ * @param opaque An opaque pointer set by the protocol module, should not be modified!
+ */
+ virtual void OnSyncUser(userrec* user, Module* proto, void* opaque);
+
+ /** Allows modules to synchronize data which relates to channels during a netburst.
+ * When this function is called, it will be called from the module which implements
+ * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
+ * is given in Module* proto, so that you may call its methods such as ProtoSendMode
+ * (see below). This function will be called for every user visible on your side
+ * of the burst, allowing you to for example set modes, etc. 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!
+ *
+ * For a good example of how to use this function, please see src/modules/m_chanprotect.cpp
+ *
+ * @param chan The channel being syncronized
+ * @param proto A pointer to the module handling network protocol
+ * @param opaque An opaque pointer set by the protocol module, should not be modified!
+ */
+ virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque);
+
+ /* Allows modules to syncronize metadata related to 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 userrec or chanrec 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 it belongs
+ * to your module. For a good example of how to use this method, see src/modules/m_swhois.cpp.
+ * @param chan The channel whos metadata is 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 extname The extensions name which is being searched for
+ * @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.
+ */
+ virtual void OnSyncChannelMetaData(chanrec* chan, Module* proto,void* opaque, const std::string &extname, bool displayable = false);
+
+ /* Allows modules to syncronize metadata related to users 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 userrec or chanrec 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 user The user whos metadata is 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 extname The extensions name which is being searched for
+ * @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.
+ */
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable = false);
+
+ /* 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 userrec or chanrec 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.
+ */
+ virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false);
+
+ /** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module.
+ * Please see src/modules/m_swhois.cpp for a working example of how to use this method call.
+ * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
+ * @param target The chanrec* or userrec* that data should be added to
+ * @param extname The extension name which is being sent
+ * @param extdata The extension data, encoded at the other end by an identical module through OnSyncChannelMetaData or OnSyncUserMetaData
+ */
+ virtual void OnDecodeMetaData(int target_type, void* 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 chanrec* or userrec* that modes should be sent for
+ * @param modeline The modes and parameters to be sent
+ */
+ virtual void ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline);
+
+ /** 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_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
+ * @param target The chanrec* or userrec* 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, int target_type, void* 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(userrec* 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
+ * @param newhost The new hostname being set
+ */
+ virtual void OnChangeHost(userrec* user, const std::string &newhost);
+
+ /** Called whenever a user's GECOS (realname) is changed.
+ * This event triggers after the name has been set.
+ * @param user The user who's GECOS is being changed
+ * @param gecos The new GECOS being set on the user
+ */
+ virtual void OnChangeName(userrec* user, const std::string &gecos);
+
+ /** Called whenever a gline is added by a local user.
+ * This method is triggered after the line is added.
+ * @param duration The duration of the line in seconds
+ * @param source The sender of the line
+ * @param reason The reason text to be displayed
+ * @param hostmask The hostmask to add
+ */
+ virtual void OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
+
+ /** Called whenever a zline is added by a local user.
+ * This method is triggered after the line is added.
+ * @param duration The duration of the line in seconds
+ * @param source The sender of the line
+ * @param reason The reason text to be displayed
+ * @param ipmask The hostmask to add
+ */
+ virtual void OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask);
+
+ /** Called whenever a kline is added by a local user.
+ * This method is triggered after the line is added.
+ * @param duration The duration of the line in seconds
+ * @param source The sender of the line
+ * @param reason The reason text to be displayed
+ * @param hostmask The hostmask to add
+ */
+ virtual void OnAddKLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
+
+ /** Called whenever a qline is added by a local user.
+ * This method is triggered after the line is added.
+ * @param duration The duration of the line in seconds
+ * @param source The sender of the line
+ * @param reason The reason text to be displayed
+ * @param nickmask The hostmask to add
+ */
+ virtual void OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask);
+
+ /** Called whenever a eline is added by a local user.
+ * This method is triggered after the line is added.
+ * @param duration The duration of the line in seconds
+ * @param source The sender of the line
+ * @param reason The reason text to be displayed
+ * @param hostmask The hostmask to add
+ */
+ virtual void OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
+
+ /** Called whenever a gline is deleted.
+ * This method is triggered after the line is deleted.
+ * @param source The user removing the line
+ * @param hostmask The hostmask to delete
+ */
+ virtual void OnDelGLine(userrec* source, const std::string &hostmask);
+
+ /** Called whenever a zline is deleted.
+ * This method is triggered after the line is deleted.
+ * @param source The user removing the line
+ * @param hostmask The hostmask to delete
+ */
+ virtual void OnDelZLine(userrec* source, const std::string &ipmask);
+
+ /** Called whenever a kline is deleted.
+ * This method is triggered after the line is deleted.
+ * @param source The user removing the line
+ * @param hostmask The hostmask to delete
+ */
+ virtual void OnDelKLine(userrec* source, const std::string &hostmask);
+
+ /** Called whenever a qline is deleted.
+ * This method is triggered after the line is deleted.
+ * @param source The user removing the line
+ * @param hostmask The hostmask to delete
+ */
+ virtual void OnDelQLine(userrec* source, const std::string &nickmask);
+
+ /** Called whenever a eline is deleted.
+ * This method is triggered after the line is deleted.
+ * @param source The user removing the line
+ * @param hostmask The hostmask to delete
+ */
+ virtual void OnDelELine(userrec* source, const std::string &hostmask);
+
+ /** Called before your module is unloaded to clean up Extensibles.
+ * This method is called once for every user and channel on the network,
+ * so that when your module unloads it may clear up any remaining data
+ * in the form of Extensibles added using Extensible::Extend().
+ * If the target_type variable is TYPE_USER, then void* item refers to
+ * a userrec*, otherwise it refers to a chanrec*.
+ * @param target_type The type of item being cleaned
+ * @param item A pointer to the item's class
+ */
+ virtual void OnCleanup(int target_type, void* item);
+
+ /** Called after any nickchange, local or remote. This can be used to track users after nickchanges
+ * have been applied. Please note that although you can see remote nickchanges through this function, you should
+ * NOT make any changes to the userrec 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).
+ * Because this method is called after the nickchange is taken place, no return values are possible
+ * to indicate forbidding of the nick change. Use OnUserPreNick for this.
+ * @param user The user changing their nick
+ * @param oldnick The old nickname of the user before the nickchange
+ */
+ virtual void OnUserPostNick(userrec* user, const std::string &oldnick);
+
+ /** Called before an action which requires a channel privilage check.
+ * This function is called before many functions which check a users status on a channel, for example
+ * before opping a user, deopping a user, kicking a user, etc.
+ * There are several values for access_type which indicate for what reason access is being checked.
+ * These are:<br><br>
+ * AC_KICK (0) - A user is being kicked<br>
+ * AC_DEOP (1) - a user is being deopped<br>
+ * AC_OP (2) - a user is being opped<br>
+ * AC_VOICE (3) - a user is being voiced<br>
+ * AC_DEVOICE (4) - a user is being devoiced<br>
+ * AC_HALFOP (5) - a user is being halfopped<br>
+ * AC_DEHALFOP (6) - a user is being dehalfopped<br>
+ * AC_INVITE () - a user is being invited<br>
+ * AC_GENERAL_MODE (8) - a user channel mode is being changed<br><br>
+ * Upon returning from your function you must return either ACR_DEFAULT, to indicate the module wishes
+ * to do nothing, or ACR_DENY where approprate to deny the action, and ACR_ALLOW where appropriate to allow
+ * the action. Please note that in the case of some access checks (such as AC_GENERAL_MODE) access may be
+ * denied 'upstream' causing other checks such as AC_DEOP to not be reached. Be very careful with use of the
+ * AC_GENERAL_MODE type, as it may inadvertently override the behaviour of other modules. When the access_type
+ * is AC_GENERAL_MODE, the destination of the mode will be NULL (as it has not yet been determined).
+ * @param source The source of the access check
+ * @param dest The destination of the access check
+ * @param channel The channel which is being checked
+ * @param access_type See above
+ */
+ virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type);
+
+ /** 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);
+
+ /** Called when a client is disconnected by KILL.
+ * If a client is killed by a server, e.g. a nickname collision or protocol error,
+ * source is NULL.
+ * Return 1 from this function to prevent the kill, and 0 from this function to allow
+ * it as normal. If you prevent the kill no output will be sent to the client, it is
+ * down to your module to generate this information.
+ * NOTE: It is NOT advisable to stop kills which originate from servers or remote users.
+ * If you do so youre risking race conditions, desyncs and worse!
+ * @param source The user sending the KILL
+ * @param dest The user being killed
+ * @param reason The kill reason
+ * @return 1 to prevent the kill, 0 to allow
+ */
+ virtual int OnKill(userrec* source, userrec* 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
+ */
+ virtual void OnRemoteKill(userrec* source, userrec* 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,
+ * your module may decide to enable some extra features if it sees that you have
+ * for example loaded "m_killwidgets.so" with "m_makewidgets.so". It is highly
+ * recommended that modules do *NOT* bail if they cannot satisfy dependencies,
+ * but instead operate under reduced functionality, unless the dependency is
+ * absolutely neccessary (e.g. a module that extends the features of another
+ * module).
+ * @param mod A pointer to the new module
+ * @param name The new module's filename
+ */
+ virtual void OnLoadModule(Module* mod,const std::string &name);
+
+ /** Called whenever a module is unloaded.
+ * 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,
+ * your module may decide to enable some extra features if it sees that you have
+ * for example loaded "m_killwidgets.so" with "m_makewidgets.so". It is highly
+ * recommended that modules do *NOT* bail if they cannot satisfy dependencies,
+ * but instead operate under reduced functionality, unless the dependency is
+ * absolutely neccessary (e.g. a module that extends the features of another
+ * module).
+ * @param mod Pointer to the module being unloaded (still valid)
+ * @param name The filename of the module being unloaded
+ */
+ virtual void OnUnloadModule(Module* mod,const std::string &name);
+
+ /** Called once every five seconds for background processing.
+ * This timer can be used to control timed features. Its period is not accurate
+ * enough to be used as a clock, but it is gauranteed to be called at least once in
+ * any five second period, directly from the main loop of the server.
+ * @param curtime The current timer derived from time(2)
+ */
+ virtual void OnBackgroundTimer(time_t curtime);
+
+ /** Called whenever any command is about to be executed.
+ * This event occurs for all registered commands, wether they are registered in the core,
+ * or another module, and for invalid commands. Invalid commands may only be sent to this
+ * function when the value of validated is false. By returning 1 from this method you may prevent the
+ * command being executed. If you do this, no output is created by the core, and it is
+ * down to your module to produce any output neccessary.
+ * Note that unless you return 1, you should not destroy any structures (e.g. by using
+ * InspIRCd::QuitUser) otherwise when the command's handler function executes after your
+ * method returns, it will be passed an invalid pointer to the user object and crash!)
+ * @param command The command being executed
+ * @param parameters An array of array of characters containing the parameters for the command
+ * @param pcnt The nuimber of parameters passed to the command
+ * @param user the user issuing the command
+ * @param validated True if the command has passed all checks, e.g. it is recognised, has enough parameters, the user has permission to execute it, etc.
+ * @param original_line The entire original line as passed to the parser from the user
+ * @return 1 to block the command, 0 to allow
+ */
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line);
+
+ /** Called after any command has been executed.
+ * This event occurs for all registered commands, wether they are registered in the core,
+ * or another module, but it will not occur for invalid commands (e.g. ones which do not
+ * exist within the command table). The result code returned by the command handler is
+ * provided.
+ * @param command The command being executed
+ * @param parameters An array of array of characters containing the parameters for the command
+ * @param pcnt The nuimber of parameters passed to the command
+ * @param user the user issuing the command
+ * @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE
+ * @param original_line The entire original line as passed to the parser from the user
+ */
+ virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line);
+
+ /** Called to check if a user who is connecting can now be allowed to register
+ * If any modules return false for this function, the user is held in the waiting
+ * state until all modules return true. For example a module which implements ident
+ * lookups will continue to return false for a user until their ident lookup is completed.
+ * Note that the registration timeout for a user overrides these checks, if the registration
+ * timeout is reached, the user is disconnected even if modules report that the user is
+ * not ready to connect.
+ * @param user The user to check
+ * @return true to indicate readiness, false if otherwise
+ */
+ virtual bool OnCheckReady(userrec* user);
+
+ /** Called whenever a user is about to register their connection (e.g. before the user
+ * is sent the MOTD etc). Modules can use this method if they are performing a function
+ * which must be done before the actual connection is completed (e.g. ident lookups,
+ * dnsbl lookups, etc).
+ * Note that you should NOT delete the user record here by causing a disconnection!
+ * Use OnUserConnect for that instead.
+ * @param user The user registering
+ * @return 1 to indicate user quit, 0 to continue
+ */
+ virtual int OnUserRegister(userrec* user);
+
+ /** Called whenever a user joins a channel, to determine if invite checks should go ahead or not.
+ * This method will always be called for each join, wether or not the channel is actually +i, and
+ * determines the outcome of an if statement around the whole section of invite checking code.
+ * return 1 to explicitly allow the join to go ahead or 0 to ignore the event.
+ * @param user The user joining the channel
+ * @param chan The channel being joined
+ * @return 1 to explicitly allow the join, 0 to proceed as normal
+ */
+ virtual int OnCheckInvite(userrec* user, chanrec* chan);
+
+ /** 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
+ * determines the outcome of an if statement around the whole section of key checking code.
+ * if the user specified no key, the keygiven string will be a valid but empty value.
+ * return 1 to explicitly allow the join to go ahead or 0 to ignore the event.
+ * @param user The user joining the channel
+ * @param chan The channel being joined
+ * @return 1 to explicitly allow the join, 0 to proceed as normal
+ */
+ virtual int OnCheckKey(userrec* user, chanrec* chan, const std::string &keygiven);
+
+ /** Called whenever a user joins a channel, to determine if channel limit checks should go ahead or not.
+ * This method will always be called for each join, wether or not the channel is actually +l, and
+ * determines the outcome of an if statement around the whole section of channel limit checking code.
+ * return 1 to explicitly allow the join to go ahead or 0 to ignore the event.
+ * @param user The user joining the channel
+ * @param chan The channel being joined
+ * @return 1 to explicitly allow the join, 0 to proceed as normal
+ */
+ virtual int OnCheckLimit(userrec* user, chanrec* chan);
+
+ /** Called whenever a user joins a channel, to determine if banlist checks should go ahead or not.
+ * This method will always be called for each join, wether or not the user actually matches a channel ban, and
+ * determines the outcome of an if statement around the whole section of ban checking code.
+ * return 1 to explicitly allow the join to go ahead or 0 to ignore the event.
+ * @param user The user joining the channel
+ * @param chan The channel being joined
+ * @return 1 to explicitly allow the join, 0 to proceed as normal
+ */
+ virtual int OnCheckBan(userrec* user, chanrec* chan);
+
+ /** Called on all /STATS commands
+ * This method is triggered for all /STATS use, including stats symbols handled by the core.
+ * @param symbol the symbol provided to /STATS
+ * @param user the user issuing the /STATS command
+ * @param results A string_list to append results into. You should put all your results
+ * into this string_list, rather than displaying them directly, so that your handler will
+ * work when remote STATS queries are received.
+ * @return 1 to block the /STATS from being processed by the core, 0 to allow it
+ */
+ virtual int OnStats(char symbol, userrec* user, string_list &results);
+
+ /** Called whenever a change of a local users displayed host is attempted.
+ * Return 1 to deny the host change, or 0 to allow it.
+ * @param user The user whos host will be changed
+ * @param newhost The new hostname
+ * @return 1 to deny the host change, 0 to allow
+ */
+ virtual int OnChangeLocalUserHost(userrec* user, const std::string &newhost);
+
+ /** Called whenever a change of a local users GECOS (fullname field) is attempted.
+ * return 1 to deny the name change, or 0 to allow it.
+ * @param user The user whos GECOS will be changed
+ * @param newhost The new GECOS
+ * @return 1 to deny the GECOS change, 0 to allow
+ */
+ virtual int OnChangeLocalUserGECOS(userrec* user, const std::string &newhost);
+
+ /** Called whenever a topic is changed by a local user.
+ * Return 1 to deny the topic change, or 0 to allow it.
+ * @param user The user changing the topic
+ * @param chan The channels who's topic is being changed
+ * @param topic The actual topic text
+ * @param 1 to block the topic change, 0 to allow
+ */
+ virtual int OnLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic);
+
+ /** Called whenever a local topic has been changed.
+ * To block topic changes you must use OnLocalTopicChange instead.
+ * @param user The user changing the topic
+ * @param chan The channels who's topic is being changed
+ * @param topic The actual topic text
+ */
+ virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic);
+
+ /** Called whenever an Event class is sent to all module by another module.
+ * Please see the documentation of Event::Send() for further information. The Event sent can
+ * always be assumed to be non-NULL, you should *always* check the value of Event::GetEventID()
+ * before doing anything to the event data, and you should *not* change the event data in any way!
+ * @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.
+ * Please see the documentation of Request::Send() for further information. The Request sent
+ * can always be assumed to be non-NULL, you should not change the request object or its data.
+ * Your method may return arbitary data in the char* result which the requesting module
+ * may be able to use for pre-determined purposes (e.g. the results of an SQL query, etc).
+ * @param request The Request class being received
+ */
+ virtual char* OnRequest(Request* request);
+
+ /** Called whenever an oper password is to be compared to what a user has input.
+ * The password field (from the config file) is in 'password' and is to be compared against
+ * 'input'. This method allows for encryption of oper passwords and much more besides.
+ * You should return a nonzero value if you want to allow the comparison or zero if you wish
+ * to do nothing.
+ * @param password The oper's password
+ * @param input The password entered
+ * @param tagnumber The tag number (from the configuration file) of this oper's tag
+ * @return 1 to match the passwords, 0 to do nothing. -1 to not match, and not continue.
+ */
+ virtual int OnOperCompare(const std::string &password, const std::string &input, int tagnumber);
+
+ /** 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(userrec* 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.
+ * This is called for both local and remote users.
+ * @param user The user who is connecting
+ */
+ virtual void OnPostConnect(userrec* 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 int OnAddBan(userrec* source, chanrec* 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 int OnDelBan(userrec* source, chanrec* channel,const std::string &banmask);
+
+ /** 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 fd The file descriptor returned from accept()
+ * @param ip The IP address of the connecting user
+ * @param localport The local port number the user connected to
+ */
+ virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport);
+
+ /** Called immediately before any write() operation on a user's socket in the core. Because
+ * this event is a low level event no user information is associated with it. It is intended
+ * for use by modules which may wrap connections within another API such as SSL for example.
+ * return a non-zero result if you have handled the write operation, in which case the core
+ * will not call write().
+ * @param fd The file descriptor of the socket
+ * @param buffer A char* buffer being written
+ * @param Number of characters to write
+ * @return Number of characters actually written or 0 if you didn't handle the operation
+ */
+ virtual int OnRawSocketWrite(int fd, const char* buffer, int count);
+
+ /** Called immediately before any socket is closed. When this event is called, shutdown()
+ * has not yet been called on the socket.
+ * @param fd The file descriptor of the socket prior to close()
+ */
+ virtual void OnRawSocketClose(int fd);
+
+ /** Called immediately upon connection of an outbound InspSocket which has been hooked
+ * by a module.
+ * @param fd The file descriptor of the socket immediately after connect()
+ */
+ virtual void OnRawSocketConnect(int fd);
+
+ /** Called immediately before any read() operation on a client socket in the core.
+ * This occurs AFTER the select() or poll() so there is always data waiting to be read
+ * when this event occurs.
+ * Your event should return 1 if it has handled the reading itself, which prevents the core
+ * just using read(). You should place any data read into buffer, up to but NOT GREATER THAN
+ * the value of count. The value of readresult must be identical to an actual result that might
+ * be returned from the read() system call, for example, number of bytes read upon success,
+ * 0 upon EOF or closed socket, and -1 for error. If your function returns a nonzero value,
+ * you MUST set readresult.
+ * @param fd The file descriptor of the socket
+ * @param buffer A char* buffer being read to
+ * @param count The size of the buffer
+ * @param readresult The amount of characters read, or 0
+ * @return nonzero if the event was handled, in which case readresult must be valid on exit
+ */
+ virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult);
+
+ /** Called whenever a user sets away.
+ * This method has no parameter for the away message, as it is available in the
+ * user record as userrec::awaymsg.
+ * @param user The user setting away
+ */
+ virtual void OnSetAway(userrec* user);
+
+ /** Called when a user cancels their away state.
+ * @param user The user returning from away
+ */
+ virtual void OnCancelAway(userrec* user);
+
+ /** Called whenever a NAMES list is requested.
+ * You can produce the nameslist yourself, overriding the current list,
+ * and if you do you must return 1. If you do not handle the names list,
+ * return 0.
+ * @param The user requesting the NAMES list
+ * @param Ptr The channel the NAMES list is requested for
+ * @param userlist The user list for the channel (you may change this pointer.
+ * If you want to change the values, take a copy first, and change the copy, then
+ * point the pointer at your copy)
+ * @return 1 to prevent the user list being sent to the client, 0 to allow it
+ */
+ virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &userlist);
+
+ /** 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 userrec 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 int OnWhoisLine(userrec* user, userrec* 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
+ * method is called when it is time to do that.
+ */
+ virtual void OnGarbageCollect();
+
+ /** Called whenever a user's write buffer has been completely sent.
+ * This is called when the user's write buffer is completely empty, and
+ * there are no more pending bytes to be written and no pending write events
+ * in the socket engine's queue. This may be used to refill the buffer with
+ * data which is being spooled in a controlled manner, e.g. LIST lines.
+ * @param user The user who's buffer is now empty.
+ */
+ virtual void OnBufferFlushed(userrec* user);
+};
+
+
+#define CONF_NOT_A_NUMBER 0x000010
+#define CONF_NOT_UNSIGNED 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 classbase
+{
+ protected:
+ InspIRCd* ServerInstance;
+ /** The contents of the configuration file
+ * This protected member should never be accessed by a module (and cannot be accessed unless the
+ * core is changed). It will contain a pointer to the configuration file data with unneeded data
+ * (such as comments) stripped from it.
+ */
+ ConfigDataHash* data;
+ /** Used to store errors
+ */
+ std::ostringstream* errorlog;
+ /** If we're using our own config data hash or not
+ */
+ bool privatehash;
+ /** True if an error occured reading the config file
+ */
+ bool readerror;
+ /** 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(InspIRCd* Instance);
+ /** Overloaded constructor.
+ * This constructor initialises the ConfigReader class to read a user-specified config file
+ */
+ ConfigReader(InspIRCd* Instance, const std::string &filename);
+ /** 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. 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
+ */
+ long ReadInteger(const std::string &tag, const std::string &name, int index, bool needs_unsigned);
+ /** 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.
+ */
+ long ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool needs_unsigned);
+
+ /** 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);
+ /** Returns true if a config file is valid.
+ * This method is partially implemented and will only return false if the config
+ * file does not exist or could not be opened.
+ */
+ bool Verify();
+ /** Dumps the list of errors in a config file to an output location. If bail is true,
+ * then the program will abort. If bail is false and user points to a valid user
+ * record, the error report will be spooled to the given user by means of NOTICE.
+ * if bool is false AND user is false, the error report will be spooled to all opers
+ * by means of a NOTICE to all opers.
+ */
+ void DumpErrors(bool bail,userrec* user);
+
+ /** Returns the number of items within a tag.
+ * For example if the tag was &lt;test tag="blah" data="foo"&gt; then this
+ * function would return 2. Spaces and newlines both qualify as valid seperators
+ * between values.
+ */
+ int EnumerateValues(const std::string &tag, int index);
+};
+
+
+
+/** 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
+{
+ InspIRCd* ServerInstance;
+ /** The file contents
+ */
+ file_cache 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(InspIRCd* Instance);
+
+ /** 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(InspIRCd* Instance, 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();
+};
+
+
+/** Instantiates classes inherited from Module.
+ * This class creates a class inherited from type Module, using new. This is to allow for modules
+ * to create many different variants of Module, dependent on architecture, configuration, etc.
+ * In most cases, the simple class shown in the example module m_foobar.so will suffice for most
+ * modules.
+ */
+class CoreExport ModuleFactory : public classbase
+{
+ public:
+ /** The default constructor does nothing.
+ */
+ ModuleFactory() { }
+ /** The default destructor does nothing
+ */
+ virtual ~ModuleFactory() { }
+ /** Creates a new module.
+ * Your inherited class of ModuleFactory must return a pointer to your Module class
+ * using this method.
+ */
+ virtual Module * CreateModule(InspIRCd* Me) = 0;
+};
+
+/** A DLLFactory (designed to load shared objects) containing a ModuleFactory.
+ */
+typedef DLLFactory<ModuleFactory> ircd_module;
+
+/** A list of loaded Modules
+ */
+typedef std::vector<Module*> ModuleList;
+
+/** A list of loaded ModuleFactories
+ */
+typedef std::vector<ircd_module*> FactoryList;
+
+/** This definition is used as shorthand for the various classes
+ * and functions needed to make a module loadable by the OS.
+ * It defines the class factory and external init_module function.
+ */
+#define MODULE_INIT(y) \
+ class Factory : public ModuleFactory \
+ { \
+ public: \
+ virtual Module * CreateModule(InspIRCd* Me) \
+ { \
+ return new y(Me); \
+ } \
+ }; \
+ extern "C" DllExport void * init_module(void) \
+ { \
+ return new Factory; \
+ }
+
+#endif
+
diff --git a/include/snomasks.h b/include/snomasks.h
index b318fdc26..db8be55f8 100644
--- a/include/snomasks.h
+++ b/include/snomasks.h
@@ -1 +1,85 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __SNOMASKS_H__ #define __SNOMASKS_H__ #include <string> #include <vector> #include <map> #include "configreader.h" #include "inspircd.h" /** A list of snomasks which are valid, and their descriptive texts */ typedef std::map<char, std::string> SnoList; /** Snomask manager handles routing of SNOMASK (usermode +n) 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 : public Extensible { private: /** Creator/owner */ InspIRCd* ServerInstance; /** Currently active snomask list */ SnoList SnoMasks; /** Set up the default (core available) snomask chars */ void SetupDefaults(); public: /** Create a new SnomaskManager */ SnomaskManager(InspIRCd* Instance); /** Delete SnomaskManager */ ~SnomaskManager(); /** Enable a snomask. * @param letter The snomask letter to enable. Once enabled, * server notices may be routed to users with this letter in * their list, and users may add this letter to their list. * @param description The descriptive text sent along with any * server notices, at the start of the notice, e.g. "GLOBOPS". * @return True if the snomask was enabled, false if it already * exists. */ bool EnableSnomask(char letter, const std::string &description); /** Disable a snomask. * @param letter The snomask letter to disable. * @return True if the snomask was disabled, false if it didn't * exist. */ bool DisableSnomask(char letter); /** Write to all users with a given snomask. * @param letter The snomask letter to write to * @param text The text to send to the users */ void WriteToSnoMask(char letter, const std::string &text); /** Write to all users with a given snomask. * @param letter The snomask letter to write to * @param text A format string containing text to send * @param ... Format arguments */ void WriteToSnoMask(char letter, const char* text, ...); /** Check if a snomask is enabled. * @param letter The snomask letter to check. * @return True if the snomask has been enabled. */ bool IsEnabled(char letter); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SNOMASKS_H__
+#define __SNOMASKS_H__
+
+#include <string>
+#include <vector>
+#include <map>
+#include "configreader.h"
+#include "inspircd.h"
+
+/** A list of snomasks which are valid, and their descriptive texts
+ */
+typedef std::map<char, std::string> SnoList;
+
+/** Snomask manager handles routing of SNOMASK (usermode +n) 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 : public Extensible
+{
+ private:
+ /** Creator/owner
+ */
+ InspIRCd* ServerInstance;
+ /** Currently active snomask list
+ */
+ SnoList SnoMasks;
+ /** Set up the default (core available) snomask chars
+ */
+ void SetupDefaults();
+ public:
+ /** Create a new SnomaskManager
+ */
+ SnomaskManager(InspIRCd* Instance);
+ /** Delete SnomaskManager
+ */
+ ~SnomaskManager();
+
+ /** Enable a snomask.
+ * @param letter The snomask letter to enable. Once enabled,
+ * server notices may be routed to users with this letter in
+ * their list, and users may add this letter to their list.
+ * @param description The descriptive text sent along with any
+ * server notices, at the start of the notice, e.g. "GLOBOPS".
+ * @return True if the snomask was enabled, false if it already
+ * exists.
+ */
+ bool EnableSnomask(char letter, const std::string &description);
+ /** Disable a snomask.
+ * @param letter The snomask letter to disable.
+ * @return True if the snomask was disabled, false if it didn't
+ * exist.
+ */
+ bool DisableSnomask(char letter);
+ /** Write to all users with a given snomask.
+ * @param letter The snomask letter to write to
+ * @param text The text to send to the users
+ */
+ void WriteToSnoMask(char letter, const std::string &text);
+ /** Write to all users with a given snomask.
+ * @param letter The snomask letter to write to
+ * @param text A format string containing text to send
+ * @param ... Format arguments
+ */
+ void WriteToSnoMask(char letter, const char* text, ...);
+ /** Check if a snomask is enabled.
+ * @param letter The snomask letter to check.
+ * @return True if the snomask has been enabled.
+ */
+ bool IsEnabled(char letter);
+};
+
+#endif
diff --git a/include/socket.h b/include/socket.h
index 458cbe690..57725b95f 100644
--- a/include/socket.h
+++ b/include/socket.h
@@ -1 +1,225 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_SOCKET_H #define INSPIRCD_SOCKET_H #ifndef WIN32 #include <arpa/inet.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <unistd.h> #include <fcntl.h> #include <netdb.h> #else #include "inspircd_win32wrapper.h" #endif #include <errno.h> #include "inspircd_config.h" #include "socketengine.h" /* Accept Define */ #ifdef CONFIG_USE_IOCP /* IOCP wrapper for accept() */ #define _accept(s, addr, addrlen) __accept_socket(s, addr, addrlen, m_acceptEvent) /* IOCP wrapper for getsockname() */ #define _getsockname(fd, sockptr, socklen) __getsockname(fd, sockptr, socklen, m_acceptEvent) /* IOCP wrapper for recvfrom() */ #define _recvfrom(s, buf, len, flags, from, fromlen) __recvfrom(s, buf, len, flags, from, fromlen, ((IOCPEngine*)ServerInstance->SE)->udp_ov) #else /* No wrapper for recvfrom() */ #define _recvfrom recvfrom /* No wrapper for accept() */ #define _accept accept /* No wrapper for getsockname() */ #define _getsockname getsockname #endif /* Contains irc-specific definitions */ namespace irc { /** This namespace contains various protocol-independent helper classes. * It also contains some types which are often used by the core and modules * in place of inet_* functions and types. */ namespace sockets { /* macros to the relevant system address description structs */ #ifdef IPV6 /** insp_sockaddr for ipv6 */ typedef struct sockaddr_in6 insp_sockaddr; /** insp_inaddr for ipv6 */ typedef struct in6_addr insp_inaddr; #define AF_FAMILY AF_INET6 #define PF_PROTOCOL PF_INET6 #else /** insp_sockaddr for ipv4 */ typedef struct sockaddr_in insp_sockaddr; /** insp_inaddr for ipv4 */ typedef struct in_addr insp_inaddr; #define AF_FAMILY AF_INET #define PF_PROTOCOL PF_INET #endif /** Match raw binary data using CIDR rules. * * This function will use binary comparison to compare the * two bit sequences, address and mask, up to mask_bits * bits in size. If they match, it will return true. * @param address The whole address, of 4 or 16 bytes in length * @param mask The mask, from 1 to 16 bytes in length, anything * from 1 to 128 bits of which is significant * @param mask_Bits How many bits of the mask parameter are significant * for this comparison. * @returns True if the first mask_bits of address matches the first * mask_bits of mask. */ CoreExport bool MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits); /** Match CIDR, without matching username/nickname parts. * * This function will compare a human-readable address against a human- * readable CIDR mask, for example 1.2.3.4 against 1.2.0.0/16. This * method supports both IPV4 and IPV6 addresses. * @param address The human readable address, e.g. 1.2.3.4 * @param cidr_mask The human readable mask, e.g. 1.2.0.0/16 * @return True if the mask matches the address */ CoreExport bool MatchCIDR(const char* address, const char* cidr_mask); /** Match CIDR, including an optional username/nickname part. * * This function will compare a human-readable address (plus * optional username and nickname) against a human-readable * CIDR mask, for example joe!bloggs\@1.2.3.4 against * *!bloggs\@1.2.0.0/16. This method supports both IPV4 and * IPV6 addresses. * @param address The human readable address, e.g. fred\@1.2.3.4 * @param cidr_mask The human readable mask, e.g. *\@1.2.0.0/16 * @return True if the mask matches the address */ CoreExport bool MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username); /** Convert an insp_inaddr into human readable form. * * @param n An insp_inaddr (IP address) structure * @return A human-readable address. IPV6 addresses * will be shortened to remove fields which are 0. */ CoreExport const char* insp_ntoa(insp_inaddr n); /** Convert a human-readable address into an insp_inaddr. * * @param a A human-readable address * @param n An insp_inaddr struct which the result * will be copied into on success. * @return This method will return a negative value if address * does not contain a valid address family. 0 if the address is * does not contain a valid string representing a valid network * address. A positive value is returned if the network address * was successfully converted. * or any other number upon failure. */ CoreExport int insp_aton(const char* a, insp_inaddr* n); /** Make a socket file descriptor a blocking socket * @param s A valid file descriptor */ CoreExport void Blocking(int s); /** Make a socket file descriptor into a nonblocking socket * @param s A valid file descriptor */ CoreExport void NonBlocking(int s); /** Create a new valid file descriptor using socket() * @return On return this function will return a value >= 0 for success, * or a negative value upon failure (negative values are invalid file * descriptors) */ CoreExport int OpenTCPSocket(char* addr, int socktype = SOCK_STREAM); } } /** This class handles incoming connections on client ports. * It will create a new userrec for every valid connection * and assign it a file descriptor. */ class CoreExport ListenSocket : public EventHandler { protected: /** The creator/owner of this object */ InspIRCd* ServerInstance; /** Socket description (shown in stats p) */ std::string desc; /** Socket address family */ int family; /** Address socket is bound to */ std::string bind_addr; /** Port socket is bound to */ int bind_port; public: /** Create a new listening socket */ ListenSocket(InspIRCd* Instance, int port, char* addr); /** Handle an I/O event */ void HandleEvent(EventType et, int errornum = 0); /** Close the socket */ ~ListenSocket(); /** Set descriptive text */ void SetDescription(const std::string &description) { desc = description; } /** Get description for socket */ const std::string& GetDescription() { return desc; } /** Get port number for socket */ int GetPort() { return bind_port; } /** Get IP address socket is bound to */ std::string &GetIP() { return bind_addr; } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_SOCKET_H
+#define INSPIRCD_SOCKET_H
+
+#ifndef WIN32
+
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netdb.h>
+
+#else
+
+#include "inspircd_win32wrapper.h"
+
+#endif
+
+#include <errno.h>
+#include "inspircd_config.h"
+#include "socketengine.h"
+
+/* Accept Define */
+#ifdef CONFIG_USE_IOCP
+/* IOCP wrapper for accept() */
+#define _accept(s, addr, addrlen) __accept_socket(s, addr, addrlen, m_acceptEvent)
+/* IOCP wrapper for getsockname() */
+#define _getsockname(fd, sockptr, socklen) __getsockname(fd, sockptr, socklen, m_acceptEvent)
+/* IOCP wrapper for recvfrom() */
+#define _recvfrom(s, buf, len, flags, from, fromlen) __recvfrom(s, buf, len, flags, from, fromlen, ((IOCPEngine*)ServerInstance->SE)->udp_ov)
+#else
+/* No wrapper for recvfrom() */
+#define _recvfrom recvfrom
+/* No wrapper for accept() */
+#define _accept accept
+/* No wrapper for getsockname() */
+#define _getsockname getsockname
+#endif
+
+/* Contains irc-specific definitions */
+namespace irc
+{
+ /** This namespace contains various protocol-independent helper classes.
+ * It also contains some types which are often used by the core and modules
+ * in place of inet_* functions and types.
+ */
+ namespace sockets
+ {
+
+ /* macros to the relevant system address description structs */
+#ifdef IPV6
+ /** insp_sockaddr for ipv6
+ */
+ typedef struct sockaddr_in6 insp_sockaddr;
+ /** insp_inaddr for ipv6
+ */
+ typedef struct in6_addr insp_inaddr;
+#define AF_FAMILY AF_INET6
+#define PF_PROTOCOL PF_INET6
+
+#else
+ /** insp_sockaddr for ipv4
+ */
+ typedef struct sockaddr_in insp_sockaddr;
+ /** insp_inaddr for ipv4
+ */
+ typedef struct in_addr insp_inaddr;
+#define AF_FAMILY AF_INET
+#define PF_PROTOCOL PF_INET
+
+#endif
+ /** Match raw binary data using CIDR rules.
+ *
+ * This function will use binary comparison to compare the
+ * two bit sequences, address and mask, up to mask_bits
+ * bits in size. If they match, it will return true.
+ * @param address The whole address, of 4 or 16 bytes in length
+ * @param mask The mask, from 1 to 16 bytes in length, anything
+ * from 1 to 128 bits of which is significant
+ * @param mask_Bits How many bits of the mask parameter are significant
+ * for this comparison.
+ * @returns True if the first mask_bits of address matches the first
+ * mask_bits of mask.
+ */
+ CoreExport bool MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits);
+
+ /** Match CIDR, without matching username/nickname parts.
+ *
+ * This function will compare a human-readable address against a human-
+ * readable CIDR mask, for example 1.2.3.4 against 1.2.0.0/16. This
+ * method supports both IPV4 and IPV6 addresses.
+ * @param address The human readable address, e.g. 1.2.3.4
+ * @param cidr_mask The human readable mask, e.g. 1.2.0.0/16
+ * @return True if the mask matches the address
+ */
+ CoreExport bool MatchCIDR(const char* address, const char* cidr_mask);
+
+ /** Match CIDR, including an optional username/nickname part.
+ *
+ * This function will compare a human-readable address (plus
+ * optional username and nickname) against a human-readable
+ * CIDR mask, for example joe!bloggs\@1.2.3.4 against
+ * *!bloggs\@1.2.0.0/16. This method supports both IPV4 and
+ * IPV6 addresses.
+ * @param address The human readable address, e.g. fred\@1.2.3.4
+ * @param cidr_mask The human readable mask, e.g. *\@1.2.0.0/16
+ * @return True if the mask matches the address
+ */
+ CoreExport bool MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username);
+
+ /** Convert an insp_inaddr into human readable form.
+ *
+ * @param n An insp_inaddr (IP address) structure
+ * @return A human-readable address. IPV6 addresses
+ * will be shortened to remove fields which are 0.
+ */
+ CoreExport const char* insp_ntoa(insp_inaddr n);
+
+ /** Convert a human-readable address into an insp_inaddr.
+ *
+ * @param a A human-readable address
+ * @param n An insp_inaddr struct which the result
+ * will be copied into on success.
+ * @return This method will return a negative value if address
+ * does not contain a valid address family. 0 if the address is
+ * does not contain a valid string representing a valid network
+ * address. A positive value is returned if the network address
+ * was successfully converted.
+
+ * or any other number upon failure.
+ */
+ CoreExport int insp_aton(const char* a, insp_inaddr* n);
+
+ /** Make a socket file descriptor a blocking socket
+ * @param s A valid file descriptor
+ */
+ CoreExport void Blocking(int s);
+
+ /** Make a socket file descriptor into a nonblocking socket
+ * @param s A valid file descriptor
+ */
+ CoreExport void NonBlocking(int s);
+
+ /** Create a new valid file descriptor using socket()
+ * @return On return this function will return a value >= 0 for success,
+ * or a negative value upon failure (negative values are invalid file
+ * descriptors)
+ */
+ CoreExport int OpenTCPSocket(char* addr, int socktype = SOCK_STREAM);
+ }
+}
+
+/** This class handles incoming connections on client ports.
+ * It will create a new userrec for every valid connection
+ * and assign it a file descriptor.
+ */
+class CoreExport ListenSocket : public EventHandler
+{
+ protected:
+ /** The creator/owner of this object
+ */
+ InspIRCd* ServerInstance;
+ /** Socket description (shown in stats p) */
+ std::string desc;
+ /** Socket address family */
+ int family;
+ /** Address socket is bound to */
+ std::string bind_addr;
+ /** Port socket is bound to */
+ int bind_port;
+ public:
+ /** Create a new listening socket
+ */
+ ListenSocket(InspIRCd* Instance, int port, char* addr);
+ /** Handle an I/O event
+ */
+ void HandleEvent(EventType et, int errornum = 0);
+ /** Close the socket
+ */
+ ~ListenSocket();
+ /** Set descriptive text
+ */
+ void SetDescription(const std::string &description)
+ {
+ desc = description;
+ }
+ /** Get description for socket
+ */
+ const std::string& GetDescription()
+ {
+ return desc;
+ }
+ /** Get port number for socket
+ */
+ int GetPort()
+ {
+ return bind_port;
+ }
+ /** Get IP address socket is bound to
+ */
+ std::string &GetIP()
+ {
+ return bind_addr;
+ }
+};
+
+#endif
+
diff --git a/include/socketengine.h b/include/socketengine.h
index e34aa3941..ce701beff 100644
--- a/include/socketengine.h
+++ b/include/socketengine.h
@@ -1 +1,296 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __SOCKETENGINE__ #define __SOCKETENGINE__ #include <vector> #include <string> #include <map> #include "inspircd_config.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 }; class InspIRCd; /** This class is a basic I/O handler class. * Any object which wishes to receive basic I/O events * from the socketengine must derive from this class and * implement the HandleEvent() method. The derived class * must then be added to SocketEngine using the method * SocketEngine::AddFd(), after which point the derived * class will receive events to its HandleEvent() method. * The derived class should also implement one of Readable() * and Writeable(). In the current implementation, only * Readable() is used. If this returns true, the socketengine * inserts a readable socket. If it is false, the socketengine * inserts a writeable socket. The derived class should never * change the value this function returns without first * deleting the socket from the socket engine. The only * requirement beyond this for an event handler is that it * must have a file descriptor. What this file descriptor * is actually attached to is completely up to you. */ class CoreExport EventHandler : public Extensible { protected: /** File descriptor. * All events which can be handled * must have a file descriptor. * This allows you to add events for * sockets, fifo's, pipes, and various * other forms of IPC. */ int fd; public: /** Get the current file descriptor * @return The file descriptor of this handler */ int GetFd(); /** Set a new file desciptor * @param FD The new file descriptor. Do not * call this method without first deleting the * object from the SocketEngine if you have * added it to a SocketEngine instance. */ void SetFd(int FD); /** Constructor */ EventHandler() {} /** Destructor */ virtual ~EventHandler() {} /** Override this function to indicate readability. * @return This should return true if the function * wishes to receive EVENT_READ events. Do not change * what this function returns while the event handler * is still added to a SocketEngine instance! * If this function is unimplemented, the base class * will return true. * * NOTE: You cannot set both Readable() and * Writeable() to true. If you wish to receive * a write event for your object, you must call * SocketEngine::WantWrite() instead. This will * trigger your objects next EVENT_WRITE type event. */ virtual bool Readable(); /** Override this function to indicate writeability. * @return This should return true if the function * wishes to receive EVENT_WRITE events. Do not change * what this function returns while the event handler * is still added to a SocketEngine instance! * If this function is unimplemented, the base class * will return false. * * NOTE: You cannot set both Readable() and * Writeable() to true. If you wish to receive * a write event for your object, you must call * SocketEngine::WantWrite() instead. This will * trigger your objects next EVENT_WRITE type event. */ virtual bool Writeable(); /** 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, depending on what your functions * Readable() and Writeable() returns and wether you * previously made a call to SocketEngine::WantWrite(). * @param et either one of EVENT_READ for read events, * and EVENT_WRITE for write events. */ virtual void HandleEvent(EventType et, int errornum = 0) = 0; #ifdef WINDOWS /** "Fake" file descriptor. This is windows-specific. */ int m_internalFd; /** Pointer to read event. We delete this so the buffer can't be used * after the socket is deleted, and so it doesn't leak memory */ void* m_readEvent; /** Pointer to a write event. */ void* m_writeEvent; /** Pointer to an accept event. */ void* m_acceptEvent; #endif }; /** Provides basic file-descriptor-based I/O support. * The actual socketengine class presents the * same interface on all operating systems, but * its private members and internal behaviour * should be treated as blackboxed, and vary * from system to system and upon the config * settings chosen by the server admin. The current * version supports select, epoll and kqueue. * The configure script will enable a socket engine * based upon what OS is detected, and will derive * a class from SocketEngine based upon what it finds. * The derived classes file will also implement a * classfactory, SocketEngineFactory, which will * create a derived instance of SocketEngine using * polymorphism so that the core and modules do not * have to be aware of which SocketEngine derived * class they are using. */ class CoreExport SocketEngine : public Extensible { protected: /** Owner/Creator */ InspIRCd* ServerInstance; /** Handle to socket engine, where needed. */ int EngineHandle; /** Current number of descriptors in the engine */ int CurrentSetSize; /** Reference table, contains all current handlers */ EventHandler* ref[MAX_DESCRIPTORS]; public: /** Constructor. * The constructor transparently initializes * the socket engine which the ircd is using. * Please note that if there is a catastrophic * failure (for example, you try and enable * epoll on a 2.4 linux kernel) then this * function may bail back to the shell. * @param Instance The creator/owner of this object */ SocketEngine(InspIRCd* Instance); /** Destructor. * The destructor transparently tidies up * any resources used by the socket engine. */ virtual ~SocketEngine(); /** Add an EventHandler object to the engine. * Use AddFd to add a file descriptor to the * engine and have the socket engine monitor * it. You must provide an object derived from * EventHandler which implements HandleEvent() * and optionally Readable() and Writeable(). * @param eh An event handling object to add */ virtual bool AddFd(EventHandler* eh); /** If you call this function and pass it an * event handler, that event handler will * receive the next available write event, * even if the socket is a readable socket only. * Developers should avoid constantly keeping * an eventhandler in the writeable state, * as this will consume large amounts of * CPU time. * @param eh An event handler which wants to * receive the next writeability event. */ virtual void WantWrite(EventHandler* eh); /** Returns the maximum number of file descriptors * you may store in the socket engine at any one time. * @return The maximum fd value */ virtual int GetMaxFds(); /** Returns the number of file descriptor slots * which are available for storing fds. * @return The number of remaining fd's */ virtual int GetRemainingFds(); /** Delete an event handler from the engine. * This function call deletes an EventHandler * from the engine, returning true if it succeeded * and false if it failed. This does not free the * EventHandler pointer using delete, if this is * required you must do this yourself. * Note on forcing deletes. DO NOT DO THIS! This is * extremely dangerous and will most likely render the * socketengine dead. This was added only for handling * very rare cases where broken 3rd party libs destroys * the OS socket beyond our control. If you can't explain * in minute details why forcing is absolutely necessary * then you don't need it. That was a NO! * @param eh The event handler object to remove * @param force *DANGEROUS* See method description! * @return True if the event handler was removed */ virtual bool DelFd(EventHandler* eh, bool force = false); /** 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); /** 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); /** Waits for events and dispatches them to handlers. * Please note that this doesnt wait long, only * a couple of milliseconds. It returns the number of * events which occured during this call. * This method will dispatch events to their handlers * by calling their EventHandler::HandleEvent() * methods with the neccessary EventType value. * @return The number of events which have occured. */ virtual int DispatchEvents(); /** 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(); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SOCKETENGINE__
+#define __SOCKETENGINE__
+
+#include <vector>
+#include <string>
+#include <map>
+#include "inspircd_config.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
+};
+
+class InspIRCd;
+
+/** This class is a basic I/O handler class.
+ * Any object which wishes to receive basic I/O events
+ * from the socketengine must derive from this class and
+ * implement the HandleEvent() method. The derived class
+ * must then be added to SocketEngine using the method
+ * SocketEngine::AddFd(), after which point the derived
+ * class will receive events to its HandleEvent() method.
+ * The derived class should also implement one of Readable()
+ * and Writeable(). In the current implementation, only
+ * Readable() is used. If this returns true, the socketengine
+ * inserts a readable socket. If it is false, the socketengine
+ * inserts a writeable socket. The derived class should never
+ * change the value this function returns without first
+ * deleting the socket from the socket engine. The only
+ * requirement beyond this for an event handler is that it
+ * must have a file descriptor. What this file descriptor
+ * is actually attached to is completely up to you.
+ */
+class CoreExport EventHandler : public Extensible
+{
+ protected:
+ /** File descriptor.
+ * All events which can be handled
+ * must have a file descriptor.
+ * This allows you to add events for
+ * sockets, fifo's, pipes, and various
+ * other forms of IPC.
+ */
+ int fd;
+ public:
+ /** Get the current file descriptor
+ * @return The file descriptor of this handler
+ */
+ int GetFd();
+
+ /** Set a new file desciptor
+ * @param FD The new file descriptor. Do not
+ * call this method without first deleting the
+ * object from the SocketEngine if you have
+ * added it to a SocketEngine instance.
+ */
+ void SetFd(int FD);
+
+ /** Constructor
+ */
+ EventHandler() {}
+
+ /** Destructor
+ */
+ virtual ~EventHandler() {}
+
+ /** Override this function to indicate readability.
+ * @return This should return true if the function
+ * wishes to receive EVENT_READ events. Do not change
+ * what this function returns while the event handler
+ * is still added to a SocketEngine instance!
+ * If this function is unimplemented, the base class
+ * will return true.
+ *
+ * NOTE: You cannot set both Readable() and
+ * Writeable() to true. If you wish to receive
+ * a write event for your object, you must call
+ * SocketEngine::WantWrite() instead. This will
+ * trigger your objects next EVENT_WRITE type event.
+ */
+ virtual bool Readable();
+
+ /** Override this function to indicate writeability.
+ * @return This should return true if the function
+ * wishes to receive EVENT_WRITE events. Do not change
+ * what this function returns while the event handler
+ * is still added to a SocketEngine instance!
+ * If this function is unimplemented, the base class
+ * will return false.
+ *
+ * NOTE: You cannot set both Readable() and
+ * Writeable() to true. If you wish to receive
+ * a write event for your object, you must call
+ * SocketEngine::WantWrite() instead. This will
+ * trigger your objects next EVENT_WRITE type event.
+ */
+ virtual bool Writeable();
+
+ /** 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, depending on what your functions
+ * Readable() and Writeable() returns and wether you
+ * previously made a call to SocketEngine::WantWrite().
+ * @param et either one of EVENT_READ for read events,
+ * and EVENT_WRITE for write events.
+ */
+ virtual void HandleEvent(EventType et, int errornum = 0) = 0;
+
+#ifdef WINDOWS
+
+ /** "Fake" file descriptor. This is windows-specific.
+ */
+ int m_internalFd;
+
+ /** Pointer to read event. We delete this so the buffer can't be used
+ * after the socket is deleted, and so it doesn't leak memory
+ */
+ void* m_readEvent;
+ /** Pointer to a write event.
+ */
+ void* m_writeEvent;
+ /** Pointer to an accept event.
+ */
+ void* m_acceptEvent;
+
+#endif
+};
+
+/** Provides basic file-descriptor-based I/O support.
+ * The actual socketengine class presents the
+ * same interface on all operating systems, but
+ * its private members and internal behaviour
+ * should be treated as blackboxed, and vary
+ * from system to system and upon the config
+ * settings chosen by the server admin. The current
+ * version supports select, epoll and kqueue.
+ * The configure script will enable a socket engine
+ * based upon what OS is detected, and will derive
+ * a class from SocketEngine based upon what it finds.
+ * The derived classes file will also implement a
+ * classfactory, SocketEngineFactory, which will
+ * create a derived instance of SocketEngine using
+ * polymorphism so that the core and modules do not
+ * have to be aware of which SocketEngine derived
+ * class they are using.
+ */
+class CoreExport SocketEngine : public Extensible
+{
+protected:
+ /** Owner/Creator
+ */
+ InspIRCd* ServerInstance;
+ /** Handle to socket engine, where needed.
+ */
+ int EngineHandle;
+ /** Current number of descriptors in the engine
+ */
+ int CurrentSetSize;
+ /** Reference table, contains all current handlers
+ */
+ EventHandler* ref[MAX_DESCRIPTORS];
+public:
+
+ /** Constructor.
+ * The constructor transparently initializes
+ * the socket engine which the ircd is using.
+ * Please note that if there is a catastrophic
+ * failure (for example, you try and enable
+ * epoll on a 2.4 linux kernel) then this
+ * function may bail back to the shell.
+ * @param Instance The creator/owner of this object
+ */
+ SocketEngine(InspIRCd* Instance);
+
+ /** Destructor.
+ * The destructor transparently tidies up
+ * any resources used by the socket engine.
+ */
+ virtual ~SocketEngine();
+
+ /** Add an EventHandler object to the engine.
+ * Use AddFd to add a file descriptor to the
+ * engine and have the socket engine monitor
+ * it. You must provide an object derived from
+ * EventHandler which implements HandleEvent()
+ * and optionally Readable() and Writeable().
+ * @param eh An event handling object to add
+ */
+ virtual bool AddFd(EventHandler* eh);
+
+ /** If you call this function and pass it an
+ * event handler, that event handler will
+ * receive the next available write event,
+ * even if the socket is a readable socket only.
+ * Developers should avoid constantly keeping
+ * an eventhandler in the writeable state,
+ * as this will consume large amounts of
+ * CPU time.
+ * @param eh An event handler which wants to
+ * receive the next writeability event.
+ */
+ virtual void WantWrite(EventHandler* eh);
+
+ /** Returns the maximum number of file descriptors
+ * you may store in the socket engine at any one time.
+ * @return The maximum fd value
+ */
+ virtual int GetMaxFds();
+
+ /** Returns the number of file descriptor slots
+ * which are available for storing fds.
+ * @return The number of remaining fd's
+ */
+ virtual int GetRemainingFds();
+
+ /** Delete an event handler from the engine.
+ * This function call deletes an EventHandler
+ * from the engine, returning true if it succeeded
+ * and false if it failed. This does not free the
+ * EventHandler pointer using delete, if this is
+ * required you must do this yourself.
+ * Note on forcing deletes. DO NOT DO THIS! This is
+ * extremely dangerous and will most likely render the
+ * socketengine dead. This was added only for handling
+ * very rare cases where broken 3rd party libs destroys
+ * the OS socket beyond our control. If you can't explain
+ * in minute details why forcing is absolutely necessary
+ * then you don't need it. That was a NO!
+ * @param eh The event handler object to remove
+ * @param force *DANGEROUS* See method description!
+ * @return True if the event handler was removed
+ */
+ virtual bool DelFd(EventHandler* eh, bool force = false);
+
+ /** 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);
+
+ /** 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);
+
+ /** Waits for events and dispatches them to handlers.
+ * Please note that this doesnt wait long, only
+ * a couple of milliseconds. It returns the number of
+ * events which occured during this call.
+ * This method will dispatch events to their handlers
+ * by calling their EventHandler::HandleEvent()
+ * methods with the neccessary EventType value.
+ * @return The number of events which have occured.
+ */
+ virtual int DispatchEvents();
+
+ /** 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();
+};
+
+#endif
+
diff --git a/include/socketengine_epoll.h b/include/socketengine_epoll.h
index ddb738fb9..736a109eb 100644
--- a/include/socketengine_epoll.h
+++ b/include/socketengine_epoll.h
@@ -1 +1,64 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __SOCKETENGINE_EPOLL__ #define __SOCKETENGINE_EPOLL__ #include <vector> #include <string> #include <map> #include "inspircd_config.h" #include "globals.h" #include "inspircd.h" #include "socketengine.h" #include <sys/epoll.h> #define EP_DELAY 5 class InspIRCd; /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll(). */ class EPollEngine : public SocketEngine { private: /** These are used by epoll() to hold socket events */ struct epoll_event events[MAX_DESCRIPTORS]; public: /** Create a new EPollEngine * @param Instance The creator of this object */ EPollEngine(InspIRCd* Instance); /** Delete an EPollEngine */ virtual ~EPollEngine(); virtual bool AddFd(EventHandler* eh); virtual int GetMaxFds(); virtual int GetRemainingFds(); virtual bool DelFd(EventHandler* eh, bool force = false); virtual int DispatchEvents(); virtual std::string GetName(); virtual void WantWrite(EventHandler* eh); }; /** Creates a SocketEngine */ class SocketEngineFactory { public: /** Create a new instance of SocketEngine based on EpollEngine */ SocketEngine* Create(InspIRCd* Instance) { return new EPollEngine(Instance); } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SOCKETENGINE_EPOLL__
+#define __SOCKETENGINE_EPOLL__
+
+#include <vector>
+#include <string>
+#include <map>
+#include "inspircd_config.h"
+#include "globals.h"
+#include "inspircd.h"
+#include "socketengine.h"
+#include <sys/epoll.h>
+#define EP_DELAY 5
+
+class InspIRCd;
+
+/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
+ */
+class EPollEngine : public SocketEngine
+{
+private:
+ /** These are used by epoll() to hold socket events
+ */
+ struct epoll_event events[MAX_DESCRIPTORS];
+public:
+ /** Create a new EPollEngine
+ * @param Instance The creator of this object
+ */
+ EPollEngine(InspIRCd* Instance);
+ /** Delete an EPollEngine
+ */
+ virtual ~EPollEngine();
+ virtual bool AddFd(EventHandler* eh);
+ virtual int GetMaxFds();
+ virtual int GetRemainingFds();
+ virtual bool DelFd(EventHandler* eh, bool force = false);
+ virtual int DispatchEvents();
+ virtual std::string GetName();
+ virtual void WantWrite(EventHandler* eh);
+};
+
+/** Creates a SocketEngine
+ */
+class SocketEngineFactory
+{
+public:
+ /** Create a new instance of SocketEngine based on EpollEngine
+ */
+ SocketEngine* Create(InspIRCd* Instance) { return new EPollEngine(Instance); }
+};
+
+#endif
diff --git a/include/socketengine_iocp.h b/include/socketengine_iocp.h
index ac27c86e3..f4825c6b4 100644
--- a/include/socketengine_iocp.h
+++ b/include/socketengine_iocp.h
@@ -1 +1,226 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __SOCKETENGINE_IOCP__ #define __SOCKETENGINE_IOCP__ #define READ_BUFFER_SIZE 600 #define USING_IOCP 1 #include "inspircd_config.h" #include "inspircd_win32wrapper.h" #include "globals.h" #include "inspircd.h" #include "socketengine.h" /** Socket overlapped event types */ enum SocketIOEvent { /** Read ready */ SOCKET_IO_EVENT_READ_READY = 0, /** Write ready */ SOCKET_IO_EVENT_WRITE_READY = 1, /** Accept ready */ SOCKET_IO_EVENT_ACCEPT = 2, /** Error occured */ SOCKET_IO_EVENT_ERROR = 3, /** Number of events */ NUM_SOCKET_IO_EVENTS = 4, }; /** Represents a windows overlapped IO event */ class Overlapped { public: /** Overlap event */ OVERLAPPED m_overlap; /** Type of event */ SocketIOEvent m_event; #ifdef WIN64 /** Parameters */ unsigned __int64 m_params; #else /** Parameters */ unsigned long m_params; #endif /** Create an overlapped event */ Overlapped(SocketIOEvent ev, int params) : m_event(ev), m_params(params) { memset(&m_overlap, 0, sizeof(OVERLAPPED)); } }; /** Specific to UDP sockets with overlapped IO */ struct udp_overlap { unsigned char udp_buffer[600]; unsigned long udp_len; sockaddr udp_sockaddr[2]; unsigned long udp_sockaddr_len; }; /** Specific to accepting sockets with overlapped IO */ struct accept_overlap { int socket; char buf[1024]; }; /** Implementation of SocketEngine that implements windows IO Completion Ports */ class IOCPEngine : public SocketEngine { /** Creates a "fake" file descriptor for use with an IOCP socket. * This is a little slow, but it isnt called too much. We'll fix it * in a future release. * @return -1 if there are no free slots, and an integer if it finds one. */ __inline int GenerateFd(int RealFd) { int index_hash = RealFd % MAX_DESCRIPTORS; if(ref[index_hash] == 0) return index_hash; else { register int i = 0; for(; i < MAX_DESCRIPTORS; ++i) if(ref[i] == 0) return i; } return -1; } /** Global I/O completion port that sockets attach to. */ HANDLE m_completionPort; /** This is kinda shitty... :/ for getting an address from a real fd. */ map<int, EventHandler*> m_binding; public: /** Creates an IOCP Socket Engine * @param Instance The creator of this object */ IOCPEngine(InspIRCd* Instance); /** Deletes an IOCP socket engine and all the attached sockets */ ~IOCPEngine(); /** Adds an event handler to the completion port, and sets up initial events. * @param eh EventHandler to add * @return True if success, false if no room */ bool AddFd(EventHandler* eh); /** Gets the maximum number of file descriptors that this engine can handle. * @return The number of file descriptors */ __inline int GetMaxFds() { return MAX_DESCRIPTORS; } /** Gets the number of free/remaining file descriptors under this engine. * @return Remaining count */ __inline int GetRemainingFds() { register int count = 0; register int i = 0; for(; i < MAX_DESCRIPTORS; ++i) if(ref[i] == 0) ++count; return count; } /** Removes a file descriptor from the set, preventing it from receiving any more events * @return True if remove was successful, false otherwise */ bool DelFd(EventHandler* eh, bool force = false); /** Called every loop to handle input/output events for all sockets under this engine * @return The number of "changed" sockets. */ int DispatchEvents(); /** Gets the name of this socket engine as a string. * @return string of socket engine name */ std::string GetName(); /** Queues a Write event on the specified event handler. * @param eh EventHandler that needs data sent on */ void WantWrite(EventHandler* eh); /** Posts a completion event on the specified socket. * @param eh EventHandler for message * @param type Event Type * @param param Event Parameter * @return True if added, false if not */ bool PostCompletionEvent(EventHandler* eh, SocketIOEvent type, int param); /** Posts a read event on the specified socket * @param eh EventHandler (socket) */ void PostReadEvent(EventHandler* eh); /** Posts an accept event on the specified socket * @param eh EventHandler (socket) */ void PostAcceptEvent(EventHandler* eh); /** 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 */ EventHandler* GetRef(int fd); /** 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 */ 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 */ EventHandler* GetIntRef(int fd); /** Holds the preallocated buffer passed to WSARecvFrom * function. Yes, I know, it's a dirty hack. */ udp_overlap * udp_ov; }; /** Creates a SocketEngine */ class SocketEngineFactory { public: /** Create a new instance of SocketEngine based on IOCPEngine */ SocketEngine* Create(InspIRCd* Instance) { return new IOCPEngine(Instance); } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SOCKETENGINE_IOCP__
+#define __SOCKETENGINE_IOCP__
+
+#define READ_BUFFER_SIZE 600
+#define USING_IOCP 1
+
+#include "inspircd_config.h"
+#include "inspircd_win32wrapper.h"
+#include "globals.h"
+#include "inspircd.h"
+#include "socketengine.h"
+
+/** Socket overlapped event types
+ */
+enum SocketIOEvent
+{
+ /** Read ready */
+ SOCKET_IO_EVENT_READ_READY = 0,
+ /** Write ready */
+ SOCKET_IO_EVENT_WRITE_READY = 1,
+ /** Accept ready */
+ SOCKET_IO_EVENT_ACCEPT = 2,
+ /** Error occured */
+ SOCKET_IO_EVENT_ERROR = 3,
+ /** Number of events */
+ NUM_SOCKET_IO_EVENTS = 4,
+};
+
+/** Represents a windows overlapped IO event
+ */
+class Overlapped
+{
+ public:
+ /** Overlap event */
+ OVERLAPPED m_overlap;
+ /** Type of event */
+ SocketIOEvent m_event;
+#ifdef WIN64
+ /** Parameters */
+ unsigned __int64 m_params;
+#else
+ /** Parameters */
+ unsigned long m_params;
+#endif
+ /** Create an overlapped event
+ */
+ Overlapped(SocketIOEvent ev, int params) : m_event(ev), m_params(params)
+ {
+ memset(&m_overlap, 0, sizeof(OVERLAPPED));
+ }
+};
+
+/** Specific to UDP sockets with overlapped IO
+ */
+struct udp_overlap
+{
+ unsigned char udp_buffer[600];
+ unsigned long udp_len;
+ sockaddr udp_sockaddr[2];
+ unsigned long udp_sockaddr_len;
+};
+
+/** Specific to accepting sockets with overlapped IO
+ */
+struct accept_overlap
+{
+ int socket;
+ char buf[1024];
+};
+
+/** Implementation of SocketEngine that implements windows IO Completion Ports
+ */
+class IOCPEngine : public SocketEngine
+{
+ /** Creates a "fake" file descriptor for use with an IOCP socket.
+ * This is a little slow, but it isnt called too much. We'll fix it
+ * in a future release.
+ * @return -1 if there are no free slots, and an integer if it finds one.
+ */
+ __inline int GenerateFd(int RealFd)
+ {
+ int index_hash = RealFd % MAX_DESCRIPTORS;
+ if(ref[index_hash] == 0)
+ return index_hash;
+ else
+ {
+ register int i = 0;
+ for(; i < MAX_DESCRIPTORS; ++i)
+ if(ref[i] == 0)
+ return i;
+ }
+ return -1;
+ }
+
+ /** Global I/O completion port that sockets attach to.
+ */
+ HANDLE m_completionPort;
+
+ /** This is kinda shitty... :/ for getting an address from a real fd.
+ */
+ map<int, EventHandler*> m_binding;
+
+public:
+ /** Creates an IOCP Socket Engine
+ * @param Instance The creator of this object
+ */
+ IOCPEngine(InspIRCd* Instance);
+
+ /** Deletes an IOCP socket engine and all the attached sockets
+ */
+ ~IOCPEngine();
+
+ /** Adds an event handler to the completion port, and sets up initial events.
+ * @param eh EventHandler to add
+ * @return True if success, false if no room
+ */
+ bool AddFd(EventHandler* eh);
+
+ /** Gets the maximum number of file descriptors that this engine can handle.
+ * @return The number of file descriptors
+ */
+ __inline int GetMaxFds() { return MAX_DESCRIPTORS; }
+
+ /** Gets the number of free/remaining file descriptors under this engine.
+ * @return Remaining count
+ */
+ __inline int GetRemainingFds()
+ {
+ register int count = 0;
+ register int i = 0;
+ for(; i < MAX_DESCRIPTORS; ++i)
+ if(ref[i] == 0)
+ ++count;
+ return count;
+ }
+
+ /** Removes a file descriptor from the set, preventing it from receiving any more events
+ * @return True if remove was successful, false otherwise
+ */
+ bool DelFd(EventHandler* eh, bool force = false);
+
+ /** Called every loop to handle input/output events for all sockets under this engine
+ * @return The number of "changed" sockets.
+ */
+ int DispatchEvents();
+
+ /** Gets the name of this socket engine as a string.
+ * @return string of socket engine name
+ */
+ std::string GetName();
+
+ /** Queues a Write event on the specified event handler.
+ * @param eh EventHandler that needs data sent on
+ */
+ void WantWrite(EventHandler* eh);
+
+ /** Posts a completion event on the specified socket.
+ * @param eh EventHandler for message
+ * @param type Event Type
+ * @param param Event Parameter
+ * @return True if added, false if not
+ */
+ bool PostCompletionEvent(EventHandler* eh, SocketIOEvent type, int param);
+
+ /** Posts a read event on the specified socket
+ * @param eh EventHandler (socket)
+ */
+ void PostReadEvent(EventHandler* eh);
+
+ /** Posts an accept event on the specified socket
+ * @param eh EventHandler (socket)
+ */
+ void PostAcceptEvent(EventHandler* eh);
+
+ /** 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
+ */
+ EventHandler* GetRef(int fd);
+
+ /** 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
+ */
+ 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
+ */
+ EventHandler* GetIntRef(int fd);
+
+ /** Holds the preallocated buffer passed to WSARecvFrom
+ * function. Yes, I know, it's a dirty hack.
+ */
+ udp_overlap * udp_ov;
+};
+
+/** Creates a SocketEngine
+ */
+class SocketEngineFactory
+{
+public:
+ /** Create a new instance of SocketEngine based on IOCPEngine
+ */
+ SocketEngine* Create(InspIRCd* Instance) { return new IOCPEngine(Instance); }
+};
+
+#endif
+
diff --git a/include/socketengine_kqueue.h b/include/socketengine_kqueue.h
index b05d40520..e7413d4bb 100644
--- a/include/socketengine_kqueue.h
+++ b/include/socketengine_kqueue.h
@@ -1 +1,68 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __SOCKETENGINE_KQUEUE__ #define __SOCKETENGINE_KQUEUE__ #include <vector> #include <string> #include <map> #include "inspircd_config.h" #include "globals.h" #include "inspircd.h" #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> #include "socketengine.h" class InspIRCd; /** A specialisation of the SocketEngine class, designed to use FreeBSD kqueue(). */ class KQueueEngine : public SocketEngine { private: /** These are used by kqueue() to hold socket events */ struct kevent ke_list[MAX_DESCRIPTORS]; /** This is a specialised time value used by kqueue() */ struct timespec ts; public: /** Create a new KQueueEngine * @param Instance The creator of this object */ KQueueEngine(InspIRCd* Instance); /** Delete a KQueueEngine */ virtual ~KQueueEngine(); virtual bool AddFd(EventHandler* eh); virtual int GetMaxFds(); virtual int GetRemainingFds(); virtual bool DelFd(EventHandler* eh, bool force = false); virtual int DispatchEvents(); virtual std::string GetName(); virtual void WantWrite(EventHandler* eh); }; /** Creates a SocketEngine */ class SocketEngineFactory { public: /** Create a new instance of SocketEngine based on KQueueEngine */ SocketEngine* Create(InspIRCd* Instance) { return new KQueueEngine(Instance); } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SOCKETENGINE_KQUEUE__
+#define __SOCKETENGINE_KQUEUE__
+
+#include <vector>
+#include <string>
+#include <map>
+#include "inspircd_config.h"
+#include "globals.h"
+#include "inspircd.h"
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include "socketengine.h"
+
+class InspIRCd;
+
+/** A specialisation of the SocketEngine class, designed to use FreeBSD kqueue().
+ */
+class KQueueEngine : public SocketEngine
+{
+private:
+ /** These are used by kqueue() to hold socket events
+ */
+ struct kevent ke_list[MAX_DESCRIPTORS];
+ /** This is a specialised time value used by kqueue()
+ */
+ struct timespec ts;
+public:
+ /** Create a new KQueueEngine
+ * @param Instance The creator of this object
+ */
+ KQueueEngine(InspIRCd* Instance);
+ /** Delete a KQueueEngine
+ */
+ virtual ~KQueueEngine();
+ virtual bool AddFd(EventHandler* eh);
+ virtual int GetMaxFds();
+ virtual int GetRemainingFds();
+ virtual bool DelFd(EventHandler* eh, bool force = false);
+ virtual int DispatchEvents();
+ virtual std::string GetName();
+ virtual void WantWrite(EventHandler* eh);
+};
+
+/** Creates a SocketEngine
+ */
+class SocketEngineFactory
+{
+ public:
+ /** Create a new instance of SocketEngine based on KQueueEngine
+ */
+ SocketEngine* Create(InspIRCd* Instance) { return new KQueueEngine(Instance); }
+};
+
+#endif
diff --git a/include/socketengine_ports.h b/include/socketengine_ports.h
index 72b5da671..a5951a138 100644
--- a/include/socketengine_ports.h
+++ b/include/socketengine_ports.h
@@ -1 +1,68 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #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 "globals.h" #include "inspircd.h" #include "socketengine.h" #include <port.h> class InspIRCd; /** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports */ class PortsEngine : public SocketEngine { private: /** These are used by epoll() to hold socket events */ port_event_t events[MAX_DESCRIPTORS]; public: /** Create a new PortsEngine * @param Instance The creator of this object */ PortsEngine(InspIRCd* Instance); /** Delete a PortsEngine */ virtual ~PortsEngine(); virtual bool AddFd(EventHandler* eh); virtual int GetMaxFds(); virtual int GetRemainingFds(); virtual bool DelFd(EventHandler* eh, bool force = false); virtual int DispatchEvents(); virtual std::string GetName(); virtual void WantWrite(EventHandler* eh); }; /** Creates a SocketEngine */ class SocketEngineFactory { public: /** Create a new instance of SocketEngine based on PortsEngine */ SocketEngine* Create(InspIRCd* Instance) { return new PortsEngine(Instance); } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#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 "globals.h"
+#include "inspircd.h"
+#include "socketengine.h"
+#include <port.h>
+
+class InspIRCd;
+
+/** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports
+ */
+class PortsEngine : public SocketEngine
+{
+private:
+ /** These are used by epoll() to hold socket events
+ */
+ port_event_t events[MAX_DESCRIPTORS];
+public:
+ /** Create a new PortsEngine
+ * @param Instance The creator of this object
+ */
+ PortsEngine(InspIRCd* Instance);
+ /** Delete a PortsEngine
+ */
+ virtual ~PortsEngine();
+ virtual bool AddFd(EventHandler* eh);
+ virtual int GetMaxFds();
+ virtual int GetRemainingFds();
+ virtual bool DelFd(EventHandler* eh, bool force = false);
+ virtual int DispatchEvents();
+ virtual std::string GetName();
+ virtual void WantWrite(EventHandler* eh);
+};
+
+/** Creates a SocketEngine
+ */
+class SocketEngineFactory
+{
+public:
+ /** Create a new instance of SocketEngine based on PortsEngine
+ */
+ SocketEngine* Create(InspIRCd* Instance) { return new PortsEngine(Instance); }
+};
+
+#endif
+
diff --git a/include/socketengine_select.h b/include/socketengine_select.h
index 553acdea2..2126e8ec5 100644
--- a/include/socketengine_select.h
+++ b/include/socketengine_select.h
@@ -1 +1,69 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __SOCKETENGINE_SELECT__ #define __SOCKETENGINE_SELECT__ #include <vector> #include <string> #include <map> #include <sys/select.h> #include "inspircd_config.h" #include "globals.h" #include "inspircd.h" #include "socketengine.h" class InspIRCd; /** A specialisation of the SocketEngine class, designed to use traditional select(). */ class SelectEngine : public SocketEngine { private: /** Because select() does not track an fd list for us between calls, we have one of our own */ std::map<int,int> fds; /** List of writeable ones (WantWrite()) */ bool writeable[MAX_DESCRIPTORS]; /** The read set and write set, populated before each call to select(). */ fd_set wfdset, rfdset, errfdset; public: /** Create a new SelectEngine * @param Instance The creator of this object */ SelectEngine(InspIRCd* Instance); /** Delete a SelectEngine */ virtual ~SelectEngine(); virtual bool AddFd(EventHandler* eh); virtual int GetMaxFds(); virtual int GetRemainingFds(); virtual bool DelFd(EventHandler* eh, bool force = false); virtual int DispatchEvents(); virtual std::string GetName(); virtual void WantWrite(EventHandler* eh); }; /** Creates a SocketEngine */ class SocketEngineFactory { public: /** Create a new instance of SocketEngine based on SelectEngine */ SocketEngine* Create(InspIRCd* Instance) { return new SelectEngine(Instance); } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SOCKETENGINE_SELECT__
+#define __SOCKETENGINE_SELECT__
+
+#include <vector>
+#include <string>
+#include <map>
+#include <sys/select.h>
+#include "inspircd_config.h"
+#include "globals.h"
+#include "inspircd.h"
+#include "socketengine.h"
+
+class InspIRCd;
+
+/** A specialisation of the SocketEngine class, designed to use traditional select().
+ */
+class SelectEngine : public SocketEngine
+{
+private:
+ /** Because select() does not track an fd list for us between calls, we have one of our own
+ */
+ std::map<int,int> fds;
+ /** List of writeable ones (WantWrite())
+ */
+ bool writeable[MAX_DESCRIPTORS];
+ /** The read set and write set, populated before each call to select().
+ */
+ fd_set wfdset, rfdset, errfdset;
+public:
+ /** Create a new SelectEngine
+ * @param Instance The creator of this object
+ */
+ SelectEngine(InspIRCd* Instance);
+ /** Delete a SelectEngine
+ */
+ virtual ~SelectEngine();
+ virtual bool AddFd(EventHandler* eh);
+ virtual int GetMaxFds();
+ virtual int GetRemainingFds();
+ virtual bool DelFd(EventHandler* eh, bool force = false);
+ virtual int DispatchEvents();
+ virtual std::string GetName();
+ virtual void WantWrite(EventHandler* eh);
+};
+
+/** Creates a SocketEngine
+ */
+class SocketEngineFactory
+{
+public:
+ /** Create a new instance of SocketEngine based on SelectEngine
+ */
+ SocketEngine* Create(InspIRCd* Instance) { return new SelectEngine(Instance); }
+};
+
+#endif
diff --git a/include/timer.h b/include/timer.h
index ad4f5ef87..ef8b82e31 100644
--- a/include/timer.h
+++ b/include/timer.h
@@ -1 +1,156 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_TIMER_H #define INSPIRCD_TIMER_H class InspIRCd; /** Timer class for one-second resolution timers * InspTimer provides a facility which allows module * developers to create one-shot timers. The timer * can be made to trigger at any time up to a one-second * resolution. To use InspTimer, inherit a class from * InspTimer, then insert your inherited class into the * queue using Server::AddTimer(). The Tick() method of * your object (which you should override) will be called * at the given time. */ class CoreExport InspTimer : public Extensible { private: /** The triggering time */ time_t trigger; /** Number of seconds between triggers */ long 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 */ InspTimer(long secs_from_now,time_t now, bool repeating = false) { trigger = now + secs_from_now; secs = secs_from_now; repeat = repeating; } /** Default destructor, does nothing. */ virtual ~InspTimer() { } /** Retrieve the current triggering time */ virtual time_t GetTimer() { return trigger; } /** Called when the timer ticks. * You should override this method with some useful code to * handle the tick event. */ virtual void Tick(time_t TIME) = 0; /** Returns true if this timer is set to repeat */ bool GetRepeat() { return repeat; } /** Returns the interval (number of seconds between ticks) * of this timer object. */ long GetSecs() { return secs; } /** 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 InspTimer::Tick() method is dangerous and may * cause a segmentation fault. Calling CancelRepeat() * is safe in this case. */ void CancelRepeat() { repeat = false; } }; /** This class manages sets of InspTimers, 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 : public Extensible { protected: /** A group of timers all set to trigger at the same time */ typedef std::vector<InspTimer*> timergroup; /** A map of timergroups, each group has a specific trigger time */ typedef std::map<time_t, timergroup*> timerlist; /** Set when ticking timers, to prevent deletion while iterating */ bool CantDeleteHere; /** Creating server instance */ InspIRCd* ServerInstance; private: /** The current timer set, a map of timergroups */ timerlist Timers; public: /** Constructor */ TimerManager(InspIRCd* Instance); /** Tick all pending InspTimers * @param TIME the current system time */ void TickTimers(time_t TIME); /** Add an InspTimer * @param T an InspTimer derived class to add * @param secs_from_now You may set this to the number of seconds * from the current time when the timer will tick, or you may just * leave this unset and the values set by the InspTimers constructor * will be used. This is used internally for re-triggering repeating * timers. */ void AddTimer(InspTimer* T, long secs_from_now = 0); /** Delete an InspTimer * @param T an InspTimer derived class to delete */ void DelTimer(InspTimer* T); /** Tick any timers that have been missed due to lag * @param TIME the current system time */ void TickMissedTimers(time_t TIME); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_TIMER_H
+#define INSPIRCD_TIMER_H
+
+class InspIRCd;
+
+/** Timer class for one-second resolution timers
+ * InspTimer provides a facility which allows module
+ * developers to create one-shot timers. The timer
+ * can be made to trigger at any time up to a one-second
+ * resolution. To use InspTimer, inherit a class from
+ * InspTimer, then insert your inherited class into the
+ * queue using Server::AddTimer(). The Tick() method of
+ * your object (which you should override) will be called
+ * at the given time.
+ */
+class CoreExport InspTimer : public Extensible
+{
+ private:
+ /** The triggering time
+ */
+ time_t trigger;
+ /** Number of seconds between triggers
+ */
+ long 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
+ */
+ InspTimer(long secs_from_now,time_t now, bool repeating = false)
+ {
+ trigger = now + secs_from_now;
+ secs = secs_from_now;
+ repeat = repeating;
+ }
+
+ /** Default destructor, does nothing.
+ */
+ virtual ~InspTimer() { }
+
+ /** Retrieve the current triggering time
+ */
+ virtual time_t GetTimer()
+ {
+ return trigger;
+ }
+
+ /** Called when the timer ticks.
+ * You should override this method with some useful code to
+ * handle the tick event.
+ */
+ virtual void Tick(time_t TIME) = 0;
+
+ /** Returns true if this timer is set to repeat
+ */
+ bool GetRepeat()
+ {
+ return repeat;
+ }
+
+ /** Returns the interval (number of seconds between ticks)
+ * of this timer object.
+ */
+ long GetSecs()
+ {
+ return secs;
+ }
+
+ /** 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 InspTimer::Tick() method is dangerous and may
+ * cause a segmentation fault. Calling CancelRepeat()
+ * is safe in this case.
+ */
+ void CancelRepeat()
+ {
+ repeat = false;
+ }
+};
+
+
+/** This class manages sets of InspTimers, 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 : public Extensible
+{
+ protected:
+ /** A group of timers all set to trigger at the same time
+ */
+ typedef std::vector<InspTimer*> timergroup;
+ /** A map of timergroups, each group has a specific trigger time
+ */
+ typedef std::map<time_t, timergroup*> timerlist;
+ /** Set when ticking timers, to prevent deletion while iterating
+ */
+ bool CantDeleteHere;
+ /** Creating server instance
+ */
+ InspIRCd* ServerInstance;
+ private:
+
+ /** The current timer set, a map of timergroups
+ */
+ timerlist Timers;
+
+ public:
+ /** Constructor
+ */
+ TimerManager(InspIRCd* Instance);
+ /** Tick all pending InspTimers
+ * @param TIME the current system time
+ */
+ void TickTimers(time_t TIME);
+ /** Add an InspTimer
+ * @param T an InspTimer derived class to add
+ * @param secs_from_now You may set this to the number of seconds
+ * from the current time when the timer will tick, or you may just
+ * leave this unset and the values set by the InspTimers constructor
+ * will be used. This is used internally for re-triggering repeating
+ * timers.
+ */
+ void AddTimer(InspTimer* T, long secs_from_now = 0);
+ /** Delete an InspTimer
+ * @param T an InspTimer derived class to delete
+ */
+ void DelTimer(InspTimer* T);
+ /** Tick any timers that have been missed due to lag
+ * @param TIME the current system time
+ */
+ void TickMissedTimers(time_t TIME);
+};
+
+#endif
+
diff --git a/include/typedefs.h b/include/typedefs.h
index 0c9ee3ed1..f101e1615 100644
--- a/include/typedefs.h
+++ b/include/typedefs.h
@@ -1 +1,53 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __TYPEDEF_H__ #define __TYPEDEF_H__ #include <string> #include "inspircd_config.h" #include "hash_map.h" #include "users.h" #include "channels.h" #include "hashcomp.h" #include "inspstring.h" #include "ctables.h" #include "modules.h" #include "globals.h" #ifndef WIN32 /** User hash (POSIX systems with GCC) */ typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash; /** Channel hash (POSIX systems with GCC) */ typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, irc::StrHashComp> chan_hash; #else /** User hash (windows systems with visual studio) */ typedef nspace::hash_map<std::string, userrec*, nspace::hash_compare<string, less<string> > > user_hash; /** Channel hash (windows systems with visual studio) */ typedef nspace::hash_map<std::string, chanrec*, nspace::hash_compare<string, less<string> > > chan_hash; #endif /** Server name cache */ typedef std::vector<std::string*> servernamelist; /** A cached text file stored line by line. */ typedef std::deque<std::string> file_cache; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __TYPEDEF_H__
+#define __TYPEDEF_H__
+
+#include <string>
+#include "inspircd_config.h"
+#include "hash_map.h"
+#include "users.h"
+#include "channels.h"
+#include "hashcomp.h"
+#include "inspstring.h"
+#include "ctables.h"
+#include "modules.h"
+#include "globals.h"
+
+#ifndef WIN32
+/** User hash (POSIX systems with GCC)
+ */
+typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
+/** Channel hash (POSIX systems with GCC)
+ */
+typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, irc::StrHashComp> chan_hash;
+#else
+/** User hash (windows systems with visual studio)
+ */
+typedef nspace::hash_map<std::string, userrec*, nspace::hash_compare<string, less<string> > > user_hash;
+/** Channel hash (windows systems with visual studio)
+ */
+typedef nspace::hash_map<std::string, chanrec*, nspace::hash_compare<string, less<string> > > chan_hash;
+#endif
+
+/** Server name cache
+ */
+typedef std::vector<std::string*> servernamelist;
+
+/** A cached text file stored line by line.
+ */
+typedef std::deque<std::string> file_cache;
+
+#endif
+
diff --git a/include/u_listmode.h b/include/u_listmode.h
index 3b2ff13d6..baf736745 100644
--- a/include/u_listmode.h
+++ b/include/u_listmode.h
@@ -1 +1,474 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_LISTMODE_PROVIDER #define INSPIRCD_LISTMODE_PROVIDER #include <stdio.h> #include <string> #include <sstream> #include <vector> #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" #include "inspircd.h" /* Updated to use the <banlist> config tag if it exists * Written by Om <omster@gmail.com>, December 2005. * Based on code previously written by Om - April 2005 * Updated to new API July 8th 2006 by Brain * Originally based on m_chanprotect and m_silence */ /** Get the time as a string */ inline std::string stringtime() { std::ostringstream TIME; TIME << time(NULL); return TIME.str(); } /** An item in a listmode's list */ class ListItem : public classbase { public: std::string nick; irc::string mask; std::string time; }; /** The number of items a listmode's list may contain */ class ListLimit : public classbase { public: std::string mask; unsigned int limit; }; /** Items stored in the channel's list */ typedef std::vector<ListItem> modelist; /** Max items per channel by name */ typedef std::vector<ListLimit> limitlist; /** A request used to check if a user is on a channel's list or not */ class ListModeRequest : public Request { public: userrec* user; chanrec* chan; /** Check if a user is on a channel's list. * The Event::Send() event returns true if the user is on the channel's list. * @param sender Sending module * @param target Target module * @param u User to check against * @param c Channel to check against */ ListModeRequest(Module* sender, Module* target, userrec* u, chanrec* c) : Request(sender, target, "LM_CHECKLIST"), user(u), chan(c) { } /** Destructor */ ~ListModeRequest() { } }; /** The base class for list modes, should be inherited. */ class ListModeBase : public ModeHandler { protected: /** Storage key */ std::string infokey; /** Numeric to use when outputting the list */ std::string listnumeric; /** Numeric to indicate end of list */ std::string 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: /** 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(InspIRCd* Instance, char modechar, const std::string &eolstr, const std::string &lnum, const std::string &eolnum, bool autotidy, const std::string &ctag = "banlist") : ModeHandler(Instance, modechar, 1, 1, true, MODETYPE_CHANNEL, false), listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy), configtag(ctag) { this->DoRehash(); infokey = "listbase_mode_" + std::string(1, mode) + "_list"; } /** See mode.h */ std::pair<bool,std::string> ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { modelist* el; channel->GetExt(infokey, el); 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(userrec* user, chanrec* channel) { modelist* el; channel->GetExt(infokey, el); if (el) { for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it) { user->WriteServ("%s %s %s %s %s %s", listnumeric.c_str(), user->nick, channel->name, it->mask.c_str(), it->nick.c_str(), it->time.c_str()); } } user->WriteServ("%s %s %s :%s", endoflistnumeric.c_str(), user->nick, channel->name, 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(chanrec* channel) { modelist* el; channel->GetExt(infokey, el); if (el) { irc::modestacker modestack(false); std::deque<std::string> stackresult; const char* mode_junk[MAXMODES+2]; mode_junk[0] = channel->name; userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); for (modelist::iterator it = el->begin(); it != el->end(); it++) { modestack.Push(this->GetModeChar(), assign(it->mask)); } while (modestack.GetStackedLine(stackresult)) { for (size_t j = 0; j < stackresult.size(); j++) { mode_junk[j+1] = stackresult[j].c_str(); } ServerInstance->SendMode(mode_junk, stackresult.size() + 1, n); } delete n; } } /** See mode.h */ virtual void RemoveMode(userrec* user) { /* Listmodes dont get set on users */ } /** Perform a rehash of this mode's configuration data */ virtual void DoRehash() { ConfigReader Conf(ServerInstance); chanlimits.clear(); for (int i = 0; i < Conf.Enumerate(configtag); i++) { // For each <banlist> tag ListLimit limit; limit.mask = Conf.ReadValue(configtag, "chan", i); limit.limit = Conf.ReadInteger(configtag, "limit", i, true); if (limit.mask.size() && limit.limit > 0) chanlimits.push_back(limit); } if (chanlimits.size() == 0) { 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(char* List) { List[I_OnChannelDelete] = List[I_OnSyncChannel] = List[I_OnCleanup] = List[I_OnRehash] = 1; } /** Handle the list mode. * See mode.h */ virtual ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { // Try and grab the list modelist* el; channel->GetExt(infokey, el); if (adding) { // If there was no list if (!el) { // Make one el = new modelist; channel->Extend(infokey, el); } // Clean the mask up if (this->tidy) ModeParser::CleanMask(parameter); // 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 (match(channel->name, it->mask.c_str())) { // We have a pattern matching the channel... maxsize = el->size(); if (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 = assign(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; } } } } /* List is full, give subclass a chance to send a custom message */ if (!TellListTooLong(source, channel, parameter)) { source->WriteServ("478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str()); } parameter = ""; 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->size() == 0) { channel->Shrink(infokey); delete el; } return MODEACTION_ALLOW; } } /* Tried to remove something that wasn't set */ TellNotSet(source, channel, parameter); parameter = ""; return MODEACTION_DENY; } else { /* Hmm, taking an exception off a non-existant list, DIE */ TellNotSet(source, channel, parameter); parameter = ""; return MODEACTION_DENY; } } return MODEACTION_DENY; } /** Get Extensible key for this mode */ virtual std::string& GetInfoKey() { return infokey; } /** Handle channel deletion. * See modules.h. * @param chan Channel being deleted */ virtual void DoChannelDelete(chanrec* chan) { modelist* list; chan->GetExt(infokey, list); if (list) { chan->Shrink(infokey); delete list; } } /** 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(chanrec* chan, Module* proto, void* opaque) { modelist* list; chan->GetExt(infokey, list); irc::modestacker modestack(true); std::deque<std::string> stackresult; if (list) { for (modelist::iterator it = list->begin(); it != list->end(); it++) { modestack.Push(std::string(1, mode)[0], assign(it->mask)); } } while (modestack.GetStackedLine(stackresult)) { irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1); std::string line = mode_join.GetJoined(); proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, line); } } /** Clean up module on unload * @param target_type Type of target to clean * @param item Item to clean */ virtual void DoCleanup(int target_type, void* item) { } /** 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(userrec* source, chanrec* channel, std::string &parameter) { 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(userrec* source, chanrec* channel, std::string &parameter) { 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(userrec* source, chanrec* 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(userrec* source, chanrec* channel, std::string &parameter) { } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_LISTMODE_PROVIDER
+#define INSPIRCD_LISTMODE_PROVIDER
+
+#include <stdio.h>
+#include <string>
+#include <sstream>
+#include <vector>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "inspircd.h"
+
+/* Updated to use the <banlist> config tag if it exists
+ * Written by Om <omster@gmail.com>, December 2005.
+ * Based on code previously written by Om - April 2005
+ * Updated to new API July 8th 2006 by Brain
+ * Originally based on m_chanprotect and m_silence
+ */
+
+/** Get the time as a string
+ */
+inline std::string stringtime()
+{
+ std::ostringstream TIME;
+ TIME << time(NULL);
+ return TIME.str();
+}
+
+/** An item in a listmode's list
+ */
+class ListItem : public classbase
+{
+public:
+ std::string nick;
+ irc::string mask;
+ std::string time;
+};
+
+/** The number of items a listmode's list may contain
+ */
+class ListLimit : public classbase
+{
+public:
+ std::string mask;
+ unsigned int limit;
+};
+
+/** Items stored in the channel's list
+ */
+typedef std::vector<ListItem> modelist;
+/** Max items per channel by name
+ */
+typedef std::vector<ListLimit> limitlist;
+
+/** A request used to check if a user is on a channel's list or not
+ */
+class ListModeRequest : public Request
+{
+ public:
+ userrec* user;
+ chanrec* chan;
+
+ /** Check if a user is on a channel's list.
+ * The Event::Send() event returns true if the user is on the channel's list.
+ * @param sender Sending module
+ * @param target Target module
+ * @param u User to check against
+ * @param c Channel to check against
+ */
+ ListModeRequest(Module* sender, Module* target, userrec* u, chanrec* c) : Request(sender, target, "LM_CHECKLIST"), user(u), chan(c)
+ {
+ }
+
+ /** Destructor
+ */
+ ~ListModeRequest()
+ {
+ }
+};
+
+/** The base class for list modes, should be inherited.
+ */
+class ListModeBase : public ModeHandler
+{
+ protected:
+ /** Storage key
+ */
+ std::string infokey;
+ /** Numeric to use when outputting the list
+ */
+ std::string listnumeric;
+ /** Numeric to indicate end of list
+ */
+ std::string 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:
+ /** 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(InspIRCd* Instance, char modechar, const std::string &eolstr, const std::string &lnum, const std::string &eolnum, bool autotidy, const std::string &ctag = "banlist")
+ : ModeHandler(Instance, modechar, 1, 1, true, MODETYPE_CHANNEL, false), listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy), configtag(ctag)
+ {
+ this->DoRehash();
+ infokey = "listbase_mode_" + std::string(1, mode) + "_list";
+ }
+
+ /** See mode.h
+ */
+ std::pair<bool,std::string> ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ modelist* el;
+ channel->GetExt(infokey, el);
+ 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(userrec* user, chanrec* channel)
+ {
+ modelist* el;
+ channel->GetExt(infokey, el);
+ if (el)
+ {
+ for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
+ {
+ user->WriteServ("%s %s %s %s %s %s", listnumeric.c_str(), user->nick, channel->name, it->mask.c_str(), it->nick.c_str(), it->time.c_str());
+ }
+ }
+ user->WriteServ("%s %s %s :%s", endoflistnumeric.c_str(), user->nick, channel->name, 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(chanrec* channel)
+ {
+ modelist* el;
+ channel->GetExt(infokey, el);
+ if (el)
+ {
+ irc::modestacker modestack(false);
+ std::deque<std::string> stackresult;
+ const char* mode_junk[MAXMODES+2];
+ mode_junk[0] = channel->name;
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+ for (modelist::iterator it = el->begin(); it != el->end(); it++)
+ {
+ modestack.Push(this->GetModeChar(), assign(it->mask));
+ }
+ while (modestack.GetStackedLine(stackresult))
+ {
+ for (size_t j = 0; j < stackresult.size(); j++)
+ {
+ mode_junk[j+1] = stackresult[j].c_str();
+ }
+ ServerInstance->SendMode(mode_junk, stackresult.size() + 1, n);
+ }
+
+ delete n;
+ }
+ }
+
+ /** See mode.h
+ */
+ virtual void RemoveMode(userrec* user)
+ {
+ /* Listmodes dont get set on users */
+ }
+
+ /** Perform a rehash of this mode's configuration data
+ */
+ virtual void DoRehash()
+ {
+ ConfigReader Conf(ServerInstance);
+
+ chanlimits.clear();
+
+ for (int i = 0; i < Conf.Enumerate(configtag); i++)
+ {
+ // For each <banlist> tag
+ ListLimit limit;
+ limit.mask = Conf.ReadValue(configtag, "chan", i);
+ limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
+
+ if (limit.mask.size() && limit.limit > 0)
+ chanlimits.push_back(limit);
+ }
+ if (chanlimits.size() == 0)
+ {
+ 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(char* List)
+ {
+ List[I_OnChannelDelete] = List[I_OnSyncChannel] = List[I_OnCleanup] = List[I_OnRehash] = 1;
+ }
+
+ /** Handle the list mode.
+ * See mode.h
+ */
+ virtual ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ // Try and grab the list
+ modelist* el;
+ channel->GetExt(infokey, el);
+
+ if (adding)
+ {
+ // If there was no list
+ if (!el)
+ {
+ // Make one
+ el = new modelist;
+ channel->Extend(infokey, el);
+ }
+
+ // Clean the mask up
+ if (this->tidy)
+ ModeParser::CleanMask(parameter);
+
+ // 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 (match(channel->name, it->mask.c_str()))
+ {
+ // We have a pattern matching the channel...
+ maxsize = el->size();
+ if (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 = assign(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;
+ }
+ }
+ }
+ }
+
+ /* List is full, give subclass a chance to send a custom message */
+ if (!TellListTooLong(source, channel, parameter))
+ {
+ source->WriteServ("478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str());
+ }
+
+ parameter = "";
+ 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->size() == 0)
+ {
+ channel->Shrink(infokey);
+ delete el;
+ }
+ return MODEACTION_ALLOW;
+ }
+ }
+ /* Tried to remove something that wasn't set */
+ TellNotSet(source, channel, parameter);
+ parameter = "";
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ /* Hmm, taking an exception off a non-existant list, DIE */
+ TellNotSet(source, channel, parameter);
+ parameter = "";
+ return MODEACTION_DENY;
+ }
+ }
+ return MODEACTION_DENY;
+ }
+
+ /** Get Extensible key for this mode
+ */
+ virtual std::string& GetInfoKey()
+ {
+ return infokey;
+ }
+
+ /** Handle channel deletion.
+ * See modules.h.
+ * @param chan Channel being deleted
+ */
+ virtual void DoChannelDelete(chanrec* chan)
+ {
+ modelist* list;
+ chan->GetExt(infokey, list);
+
+ if (list)
+ {
+ chan->Shrink(infokey);
+ delete list;
+ }
+ }
+
+ /** 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(chanrec* chan, Module* proto, void* opaque)
+ {
+ modelist* list;
+ chan->GetExt(infokey, list);
+ irc::modestacker modestack(true);
+ std::deque<std::string> stackresult;
+ if (list)
+ {
+ for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ {
+ modestack.Push(std::string(1, mode)[0], assign(it->mask));
+ }
+ }
+ while (modestack.GetStackedLine(stackresult))
+ {
+ irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
+ std::string line = mode_join.GetJoined();
+ proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, line);
+ }
+ }
+
+ /** Clean up module on unload
+ * @param target_type Type of target to clean
+ * @param item Item to clean
+ */
+ virtual void DoCleanup(int target_type, void* item)
+ {
+ }
+
+ /** 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(userrec* source, chanrec* channel, std::string &parameter)
+ {
+ 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(userrec* source, chanrec* channel, std::string &parameter)
+ {
+ 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(userrec* source, chanrec* 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(userrec* source, chanrec* channel, std::string &parameter)
+ {
+ }
+};
+
+#endif
+
diff --git a/include/users.h b/include/users.h
index 7420cc42f..0443b24d2 100644
--- a/include/users.h
+++ b/include/users.h
@@ -1 +1,1031 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __USERS_H__ #define __USERS_H__ #include <string> #include "inspircd_config.h" #include "socket.h" #include "channels.h" #include "inspstring.h" #include "connection.h" #include "hashcomp.h" #include "dns.h" /** Channel status for a user */ enum ChanStatus { /** Op */ STATUS_OP = 4, /** Halfop */ STATUS_HOP = 2, /** Voice */ STATUS_VOICE = 1, /** None */ STATUS_NORMAL = 0 }; /** connect class types */ enum ClassTypes { /** connect:allow */ CC_ALLOW = 0, /** connect:deny */ CC_DENY = 1 }; /** RFC1459 channel modes */ enum UserModes { /** +s: Server notices */ UM_SERVERNOTICE = 's' - 65, /** +w: WALLOPS */ UM_WALLOPS = 'w' - 65, /** +i: Invisible */ UM_INVISIBLE = 'i' - 65, /** +o: Operator */ UM_OPERATOR = 'o' - 65, /** +n: Server notice mask */ UM_SNOMASK = 'n' - 65 }; /** Registration state of a user, e.g. * have they sent USER, NICK, PASS yet? */ enum RegistrationState { #ifndef WIN32 // Burlex: This is already defined in win32, luckily it is still 0. REG_NONE = 0, /* Has sent nothing */ #endif REG_USER = 1, /* Has sent USER */ REG_NICK = 2, /* Has sent NICK */ REG_NICKUSER = 3, /* Bitwise combination of REG_NICK and REG_USER */ REG_ALL = 7 /* REG_NICKUSER plus next bit along */ }; /* Required forward declaration */ class InspIRCd; /** Derived from Resolver, and performs user forward/reverse lookups. */ class CoreExport UserResolver : public Resolver { private: /** User this class is 'attached' to. */ userrec* bound_user; /** File descriptor teh lookup is bound to */ int bound_fd; /** True if the lookup is forward, false if is a reverse lookup */ bool fwd; public: /** Create a resolver. * @param Instance The creating instance * @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(InspIRCd* Instance, userrec* 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); /** Called on failed lookup * @param e Error code * @param errormessage Error message string */ void OnError(ResolverError e, const std::string &errormessage); }; /** Holds information relevent to &lt;connect allow&gt; and &lt;connect deny&gt; tags in the config file. */ class CoreExport ConnectClass : public classbase { private: /** Type of line, either CC_ALLOW or CC_DENY */ char type; /** Max time to register the connection in seconds */ unsigned int registration_timeout; /** Number of lines in buffer before excess flood is triggered */ unsigned int flood; /** Host mask for this line */ std::string host; /** Number of seconds between pings for this line */ unsigned int pingtime; /** (Optional) Password for this line */ std::string pass; /** Threshold value for flood disconnect */ unsigned int threshold; /** Maximum size of sendq for users in this class (bytes) */ unsigned long sendqmax; /** Maximum size of recvq for users in this class (bytes) */ unsigned long recvqmax; /** Local max when connecting by this connection class */ unsigned long maxlocal; /** Global max when connecting by this connection class */ unsigned long maxglobal; /** Port number this connect class applies to */ int port; public: /** Create a new connect class with no settings. */ ConnectClass() : type(CC_DENY), registration_timeout(0), flood(0), host(""), pingtime(0), pass(""), threshold(0), sendqmax(0), recvqmax(0), maxlocal(0), maxglobal(0) { } /** Create a new connect class to ALLOW connections. * @param timeout The registration timeout * @param fld The flood value * @param hst The IP mask to allow * @param ping The ping frequency * @param pas The password to be used * @param thres The flooding threshold * @param sendq The maximum sendq value * @param recvq The maximum recvq value * @param maxl The maximum local sessions * @param maxg The maximum global sessions */ ConnectClass(unsigned int timeout, unsigned int fld, const std::string &hst, unsigned int ping, const std::string &pas, unsigned int thres, unsigned long sendq, unsigned long recvq, unsigned long maxl, unsigned long maxg, int p = 0) : type(CC_ALLOW), registration_timeout(timeout), flood(fld), host(hst), pingtime(ping), pass(pas), threshold(thres), sendqmax(sendq), recvqmax(recvq), maxlocal(maxl), maxglobal(maxg), port(p) { } /** Create a new connect class to DENY connections * @param hst The IP mask to deny */ ConnectClass(const std::string &hst) : type(CC_DENY), registration_timeout(0), flood(0), host(hst), pingtime(0), pass(""), threshold(0), sendqmax(0), recvqmax(0), maxlocal(0), maxglobal(0), port(0) { } /** Returns the type, CC_ALLOW or CC_DENY */ char GetType() { return (type == CC_ALLOW ? CC_ALLOW : CC_DENY); } /** Returns the registration timeout */ unsigned int GetRegTimeout() { return (registration_timeout ? registration_timeout : 90); } /** Returns the flood limit */ unsigned int GetFlood() { return (threshold ? flood : 999); } /** Returns the allowed or denied IP mask */ const std::string& GetHost() { return host; } int GetPort() { return port; } /** Returns the ping frequency */ unsigned int GetPingTime() { return (pingtime ? pingtime : 120); } /** Returns the password or an empty string */ const std::string& GetPass() { return pass; } /** Returns the flood threshold value */ unsigned int GetThreshold() { return (threshold ? threshold : 1); } /** Returns the maximum sendq value */ unsigned long GetSendqMax() { return (sendqmax ? sendqmax : 262114); } /** Returns the maximum recvq value */ unsigned long GetRecvqMax() { return (recvqmax ? recvqmax : 4096); } /** Returusn the maximum number of local sessions */ unsigned long GetMaxLocal() { return maxlocal; } /** Returns the maximum number of global sessions */ unsigned long GetMaxGlobal() { return maxglobal; } }; /** Holds a complete list of all channels to which a user has been invited and has not yet joined. */ typedef std::vector<irc::string> InvitedList; /** Holds a complete list of all allow and deny tags from the configuration file (connection classes) */ typedef std::vector<ConnectClass> ClassVector; /** Typedef for the list of user-channel records for a user */ typedef std::map<chanrec*, char> UserChanList; /** Shorthand for an iterator into a UserChanList */ typedef UserChanList::iterator UCListIter; /* Required forward declaration */ class userrec; /** Visibility data for a user. * If a user has a non-null instance of this class in their userrec, * then it is used to determine if this user is visible to other users * or not. */ class CoreExport VisData { public: /** Create a visdata */ VisData(); /** Destroy a visdata */ virtual ~VisData(); /** Is this user visible to some other user? * @param user The other user to compare to * @return true True if the user is visible to the other user, false if not */ virtual bool VisibleTo(userrec* user); }; /** Holds all information about a user * This class stores all information about a user connected to the irc server. Everything about a * connection is stored here primarily, from the user's socket ID (file descriptor) through to the * user's nickname and hostname. Use the FindNick method of the InspIRCd class to locate a specific user * by nickname, or the FindDescriptor method of the InspIRCd class to find a specific user by their * file descriptor value. */ class CoreExport userrec : public connection { private: /** Pointer to creator. * This is required to make use of core functions * from within the userrec class. */ InspIRCd* ServerInstance; /** A list of channels the user has a pending invite to. * Upon INVITE channels are added, and upon JOIN, the * channels are removed from this list. */ InvitedList invites; /** Number of channels this user is currently on */ unsigned int ChannelCount; /** Cached nick!ident@host value using the real hostname */ char* cached_fullhost; /** Cached nick!ident@ip value using the real IP address */ char* cached_hostip; /** Cached nick!ident@host value using the masked hostname */ char* cached_makehost; /** Cached nick!ident@realhost value using the real hostname */ char* cached_fullrealhost; /** When we erase the user (in the destructor), * we call this method to subtract one from all * mode characters this user is making use of. */ void DecrementModes(); /** Oper-only quit message for this user if non-null */ char* operquit; public: /** Resolvers for looking up this users IP address * This will occur if and when res_reverse completes. * When this class completes its lookup, userrec::dns_done * will be set from false to true. */ UserResolver* res_forward; /** Resolvers for looking up this users hostname * This is instantiated by userrec::StartDNSLookup(), * and on success, instantiates userrec::res_reverse. */ UserResolver* res_reverse; /** User visibility state, see definition of VisData. */ VisData* Visibility; /** Stored reverse lookup from res_forward */ 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 userrec::dns_done to true. */ void StartDNSLookup(); /** The users nickname. * An invalid nickname indicates an unregistered connection prior to the NICK command. * Use InspIRCd::IsNick() to validate nicknames. */ char nick[NICKMAX]; /** The users ident reply. * Two characters are added to the user-defined limit to compensate for the tilde etc. */ char ident[IDENTMAX+2]; /** The host displayed to non-opers (used for cloaking etc). * This usually matches the value of userrec::host. */ char dhost[65]; /** The users full name (GECOS). */ char fullname[MAXGECOS+1]; /** The user's mode list. * This is NOT a null terminated string! In the 1.1 version of InspIRCd * this is an array of values in a similar way to channel modes. * A value of 1 in field (modeletter-65) indicates that the mode is * set, for example, to work out if mode +s is set, we check the field * userrec::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. */ char modes[64]; /** What snomasks are set on this user. * This functions the same as the above modes. */ char snomasks[64]; /** Channels this user is on, and the permissions they have there */ UserChanList chans; /** The server the user is connected to. */ const char* server; /** The user's away message. * If this string is empty, the user is not marked as away. */ char awaymsg[MAXAWAY+1]; /** Number of lines the user can place into the buffer * (up to the global NetBufferSize bytes) before they * are disconnected for excess flood */ int flood; /** Timestamp of current time + connection class timeout. * This user must send USER/NICK before this timestamp is * reached or they will be disconnected. */ time_t timeout; /** The oper type they logged in as, if they are an oper. * This is used to check permissions in operclasses, so that * we can say 'yay' or 'nay' to any commands they issue. * The value of this is the value of a valid 'type name=' tag. */ char oper[NICKMAX]; /** True when DNS lookups are completed. * The UserResolver classes res_forward and res_reverse will * set this value once they complete. */ bool dns_done; /** Number of seconds between PINGs for this user (set from &lt;connect:allow&gt; tag */ unsigned int pingmax; /** Password specified by the user when they registered. * This is stored even if the <connect> block doesnt need a password, so that * modules may check it. */ char password[64]; /** User's receive queue. * Lines from the IRCd awaiting processing are stored here. * Upgraded april 2005, old system a bit hairy. */ std::string recvq; /** User's send queue. * Lines waiting to be sent are stored here until their buffer is flushed. */ std::string sendq; /** Flood counters - lines received */ int lines_in; /** Flood counters - time lines_in is due to be reset */ time_t reset_due; /** Flood counters - Highest value lines_in may reach before the user gets disconnected */ long threshold; /** If this is set to true, then all read operations for the user * are dropped into the bit-bucket. * This is used by the global CullList, but please note that setting this value * alone will NOT cause the user to quit. This means it can be used seperately, * for example by shun modules etc. */ bool muted; /** IPV4 or IPV6 ip address. Use SetSockAddr to set this and GetProtocolFamily/ * GetIPString/GetPort to obtain its values. */ sockaddr* ip; /** Initialize the clients sockaddr * @param protocol_family The protocol family of the IP address, AF_INET or AF_INET6 * @param ip A human-readable IP address for this user matching the protcol_family * @param port The port number of this user or zero for a remote user */ void SetSockAddr(int protocol_family, const char* ip, int port); /** Get port number from sockaddr * @return The port number of this user. */ int GetPort(); /** Get protocol family from sockaddr * @return The protocol family of this user, either AF_INET or AF_INET6 */ int GetProtocolFamily(); /** Get IP string from sockaddr, using static internal buffer * @return The IP string */ const char* GetIPString(); /** Get IP string from sockaddr, using caller-specified buffer * @param buf A buffer to use * @return The IP string */ const char* GetIPString(char* buf); /* Write error string */ std::string WriteError; /** Maximum size this user's sendq can become. * Copied from the connect class on connect. */ long sendqmax; /** Maximum size this user's recvq can become. * Copied from the connect class on connect. */ long recvqmax; /** This is true if the user matched an exception when they connected to the ircd. * It isnt valid after this point, and you should not attempt to do anything with it * after this point, because the eline might be removed at a later time, and/or no * longer be applicable to this user. It is only used to save doing the eline lookup * twice (instead we do it once and set this value). */ bool exempt; /** Default constructor * @throw Nothing at present */ userrec(InspIRCd* Instance); /** Returns the full displayed host of the user * This member function returns the hostname of the user as seen by other users * on the server, in nick!ident&at;host form. * @return The full masked host of the user */ virtual char* GetFullHost(); /** Returns the full real host of the user * This member function returns the hostname of the user as seen by other users * on the server, in nick!ident&at;host form. If any form of hostname cloaking is in operation, * e.g. through a module, then this method will ignore it and return the true hostname. * @return The full real host of the user */ virtual char* GetFullRealHost(); /** This clears any cached results that are used for GetFullRealHost() etc. * The results of these calls are cached as generating them can be generally expensive. */ void InvalidateCache(); /** Create a displayable mode string for this users snomasks * @return The notice mask character sequence */ const char* FormatNoticeMasks(); /** 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 */ std::string ProcessNoticeMasks(const char *sm); /** Returns true if a notice mask is set * @param sm A notice mask character to check * @return True if the notice mask is set */ 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 The mode string */ const char* FormatModes(); /** Returns true if a specific mode is set * @param m The user mode * @return True if the mode is set */ bool IsModeSet(unsigned char m); /** 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); /** 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 */ virtual 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 */ virtual void InviteTo(const irc::string &channel); /** 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 */ virtual void RemoveInvite(const irc::string &channel); /** Returns true or false for if a user can execute a privilaged oper command. * This is done by looking up their oper type from userrec::oper, then referencing * this to their oper classes and checking the commands they can execute. * @param command A command (should be all CAPS) * @return True if this user can execute the command */ bool HasPermission(const std::string &command); /** Calls read() to read some data for this user using their fd. * @param buffer The buffer to read into * @param size The size of data to read * @return The number of bytes read, or -1 if an error occured. */ int ReadData(void* buffer, size_t size); /** This method adds data to the read buffer of the user. * The buffer can grow to any size within limits of the available memory, * managed by the size of a std::string, however if any individual line in * the buffer grows over 600 bytes in length (which is 88 chars over the * RFC-specified limit per line) then the method will return false and the * text will not be inserted. * @param a The string to add to the users read buffer * @return True if the string was successfully added to the read buffer */ bool AddBuffer(std::string a); /** This method returns true if the buffer contains at least one carriage return * character (e.g. one complete line may be read) * @return True if there is at least one complete line in the users buffer */ bool BufferIsReady(); /** This function clears the entire buffer by setting it to an empty string. */ void ClearBuffer(); /** This method returns the first available string at the tail end of the buffer * and advances the tail end of the buffer past the string. This means it is * a one way operation in a similar way to strtok(), and multiple calls return * multiple lines if they are available. The results of this function if there * are no lines to be read are unknown, always use BufferIsReady() to check if * it is ok to read the buffer before calling GetBuffer(). * @return The string at the tail end of this users buffer */ std::string GetBuffer(); /** Sets the write error for a connection. This is done because the actual disconnect * of a client may occur at an inopportune time such as half way through /LIST output. * The WriteErrors of clients are checked at a more ideal time (in the mainloop) and * errored clients purged. * @param error The error string to set. */ void SetWriteError(const std::string &error); /** Returns the write error which last occured on this connection or an empty string * if none occured. * @return The error string which has occured for this user */ const char* GetWriteError(); /** Adds to the user's write buffer. * You may add any amount of text up to this users sendq value, if you exceed the * sendq value, SetWriteError() will be called to set the users error string to * "SendQ exceeded", and further buffer adds will be dropped. * @param data The data to add to the write buffer */ void AddWriteBuf(const std::string &data); /** Flushes as much of the user's buffer to the file descriptor as possible. * This function may not always flush the entire buffer, rather instead as much of it * as it possibly can. If the send() call fails to send the entire buffer, the buffer * position is advanced forwards and the rest of the data sent at the next call to * this method. */ void FlushWriteBuf(); /** 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 */ InvitedList* GetInviteList(); /** 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 */ char* MakeHost(); /** Creates a usermask with real ip. * Takes a buffer to use and fills the given buffer with the ipmask in the format user@ip * @return the usermask in the format user@ip */ char* MakeHostIP(); /** Shuts down and closes the user's socket * This will not cause the user to be deleted. Use InspIRCd::QuitUser for this, * which will call CloseSocket() for you. */ void CloseSocket(); /** Disconnect a user gracefully * @param user The user to remove * @param r The quit reason to show to normal users * @param oreason The quit reason to show to opers * @return Although this function has no return type, on exit the user provided will no longer exist. */ static void QuitUser(InspIRCd* Instance, userrec *user, const std::string &r, const char* oreason = ""); /** Add the user to WHOWAS system */ void AddToWhoWas(); /** Oper up the user using the given opertype. * This will also give the +o usermode. * @param opertype The oper type to oper as */ void Oper(const std::string &opertype); /** Call this method to find the matching <connect> for a user, and to check them against it. */ void CheckClass(); /** Use this method to fully connect a user. * This will send the message of the day, check G/K/E lines, etc. */ void FullConnect(); /** Change this users hash key to a new string. * You should not call this function directly. It is used by the core * to update the users hash entry on a nickchange. * @param New new user_hash key * @return Pointer to userrec in hash (usually 'this') */ userrec* UpdateNickHash(const char* New); /** Force a nickname change. * If the nickname change fails (for example, because the nick in question * already exists) this function will return false, and you must then either * output an error message, or quit the user for nickname collision. * @param newnick The nickname to change to * @return True if the nickchange was successful. */ bool ForceNickChange(const char* newnick); /** Add a client to the system. * This will create a new userrec, insert it into the user_hash, * initialize it as not yet registered, and add it to the socket engine. * @param Instance a pointer to the server instance * @param socket The socket id (file descriptor) this user is on * @param port The port number this user connected on * @param iscached This variable is reserved for future use * @param ip The IP address of the user * @return This function has no return value, but a call to AddClient may remove the user. */ static void AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip); /** Oper down. * This will clear the +o usermode and unset the user's oper type */ void UnOper(); /** Return the number of global clones of this user * @return The global clone count of this user */ unsigned long GlobalCloneCount(); /** Return the number of local clones of this user * @return The local clone count of this user */ unsigned long LocalCloneCount(); /** Remove all clone counts from the user, you should * use this if you change the user's IP address in * userrec::ip after they have registered. */ void RemoveCloneCounts(); /** Write text to this user, appending CR/LF. * @param text A std::string to send to the user */ void Write(std::string text); /** Write text to this user, appending CR/LF. * @param text The format string for text to send to the user * @param ... POD-type format arguments */ void Write(const char *text, ...); /** Write text to this user, appending CR/LF and prepending :server.name * @param text A std::string to send to the user */ void WriteServ(const std::string& text); /** Write text to this user, appending CR/LF and prepending :server.name * @param text The format string for text to send to the user * @param ... POD-type format arguments */ void WriteServ(const char* text, ...); /** Write text to this user, appending CR/LF and prepending :nick!user@host of the user provided in the first parameter. * @param user The user to prepend the :nick!user@host of * @param text A std::string to send to the user */ void WriteFrom(userrec *user, const std::string &text); /** Write text to this user, appending CR/LF and prepending :nick!user@host of the user provided in the first parameter. * @param user The user to prepend the :nick!user@host of * @param text The format string for text to send to the user * @param ... POD-type format arguments */ void WriteFrom(userrec *user, const char* text, ...); /** 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 text A std::string to send to the user */ void WriteTo(userrec *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 text The format string for text to send to the user * @param ... POD-type format arguments */ void WriteTo(userrec *dest, const char *data, ...); /** Write to all users that can see this user (including this user in the list), appending CR/LF * @param text A std::string to send to the users */ void WriteCommon(const std::string &text); /** Write to all users that can see this user (including this user in the list), appending CR/LF * @param text The format string for text to send to the users * @param ... POD-type format arguments */ void WriteCommon(const char* text, ...); /** 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, ...); /** Write to all users that can see this user (not including this user in the list), appending CR/LF * @param text A std::string to send to the users */ void WriteCommonExcept(const std::string &text); /** Write a quit message to all common users, as in userrec::WriteCommonExcept but with a specific * quit message for opers only. * @param normal_text Normal user quit message * @param oper_text Oper only quit message */ void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text); /** Write a WALLOPS message from this user to all local opers. * If this user is not opered, the function will return without doing anything. * @param text The format string to send in the WALLOPS message * @param ... Format arguments */ void WriteWallOps(const char* text, ...); /** Write a WALLOPS message from this user to all local opers. * If this user is not opered, the function will return without doing anything. * @param text The text to send in the WALLOPS message */ void WriteWallOps(const std::string &text); /** Return true if the user shares at least one channel with another user * @param other The other user to compare the channel list against * @return True if the given user shares at least one channel with this user */ bool SharesChannelWith(userrec *other); /** Change the displayed host of a user. * ALWAYS use this function, rather than writing userrec::dhost directly, * as this triggers module events allowing the change to be syncronized to * remote servers. This will also emulate a QUIT and rejoin (where configured) * before setting their host field. * @param host The new hostname to set * @return True if the change succeeded, false if it didn't */ bool ChangeDisplayedHost(const char* host); /** Change the ident (username) of a user. * ALWAYS use this function, rather than writing userrec::ident directly, * as this correctly causes the user to seem to quit (where configured) * before setting their ident field. * @param host The new ident to set * @return True if the change succeeded, false if it didn't */ bool ChangeIdent(const char* newident); /** Change a users realname field. * ALWAYS use this function, rather than writing userrec::fullname directly, * as this triggers module events allowing the change to be syncronized to * remote servers. * @param gecos The user's new realname * @return True if the change succeeded, false if otherwise */ bool ChangeName(const char* gecos); /** 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, char* text, ...); /** Compile a channel list for this user, and send it to the user 'source' * Used internally by WHOIS * @param The user to send the channel list to if it is not too long * @return This user's channel list */ std::string ChannelList(userrec* source); /** 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 userrec::ChannelList() */ void SplitChanList(userrec* dest, const std::string &cl); /** 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 matches this user's host or IP address * @return A reference to this user's connect class */ ConnectClass* GetClass(); /** Show the message of the day to this user */ void ShowMOTD(); /** Show the server RULES file to this user */ void ShowRULES(); /** Set oper-specific quit message shown to opers only when the user quits * (overrides any sent by QuitUser) */ void SetOperQuit(const std::string &oquit); /** Get oper-specific quit message shown only to opers when the user quits. * (overrides any sent by QuitUser) */ const char* GetOperQuit(); /** Handle socket event. * From EventHandler class. * @param et Event type * @param errornum Error number for EVENT_ERROR events */ void HandleEvent(EventType et, int errornum = 0); /** Default destructor */ virtual ~userrec(); }; /* Configuration callbacks */ class ServerConfig; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __USERS_H__
+#define __USERS_H__
+
+#include <string>
+#include "inspircd_config.h"
+#include "socket.h"
+#include "channels.h"
+#include "inspstring.h"
+#include "connection.h"
+#include "hashcomp.h"
+#include "dns.h"
+
+/** Channel status for a user
+ */
+enum ChanStatus {
+ /** Op */
+ STATUS_OP = 4,
+ /** Halfop */
+ STATUS_HOP = 2,
+ /** Voice */
+ STATUS_VOICE = 1,
+ /** None */
+ STATUS_NORMAL = 0
+};
+
+/** connect class types
+ */
+enum ClassTypes {
+ /** connect:allow */
+ CC_ALLOW = 0,
+ /** connect:deny */
+ CC_DENY = 1
+};
+
+/** RFC1459 channel modes
+ */
+enum UserModes {
+ /** +s: Server notices */
+ UM_SERVERNOTICE = 's' - 65,
+ /** +w: WALLOPS */
+ UM_WALLOPS = 'w' - 65,
+ /** +i: Invisible */
+ UM_INVISIBLE = 'i' - 65,
+ /** +o: Operator */
+ UM_OPERATOR = 'o' - 65,
+ /** +n: Server notice mask */
+ UM_SNOMASK = 'n' - 65
+};
+
+/** Registration state of a user, e.g.
+ * have they sent USER, NICK, PASS yet?
+ */
+enum RegistrationState {
+
+#ifndef WIN32 // Burlex: This is already defined in win32, luckily it is still 0.
+ REG_NONE = 0, /* Has sent nothing */
+#endif
+
+ REG_USER = 1, /* Has sent USER */
+ REG_NICK = 2, /* Has sent NICK */
+ REG_NICKUSER = 3, /* Bitwise combination of REG_NICK and REG_USER */
+ REG_ALL = 7 /* REG_NICKUSER plus next bit along */
+};
+
+/* Required forward declaration */
+class InspIRCd;
+
+/** Derived from Resolver, and performs user forward/reverse lookups.
+ */
+class CoreExport UserResolver : public Resolver
+{
+ private:
+ /** User this class is 'attached' to.
+ */
+ userrec* bound_user;
+ /** File descriptor teh lookup is bound to
+ */
+ int bound_fd;
+ /** True if the lookup is forward, false if is a reverse lookup
+ */
+ bool fwd;
+ public:
+ /** Create a resolver.
+ * @param Instance The creating instance
+ * @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(InspIRCd* Instance, userrec* 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);
+
+ /** Called on failed lookup
+ * @param e Error code
+ * @param errormessage Error message string
+ */
+ void OnError(ResolverError e, const std::string &errormessage);
+};
+
+
+/** Holds information relevent to &lt;connect allow&gt; and &lt;connect deny&gt; tags in the config file.
+ */
+class CoreExport ConnectClass : public classbase
+{
+ private:
+ /** Type of line, either CC_ALLOW or CC_DENY
+ */
+ char type;
+ /** Max time to register the connection in seconds
+ */
+ unsigned int registration_timeout;
+ /** Number of lines in buffer before excess flood is triggered
+ */
+ unsigned int flood;
+ /** Host mask for this line
+ */
+ std::string host;
+ /** Number of seconds between pings for this line
+ */
+ unsigned int pingtime;
+ /** (Optional) Password for this line
+ */
+ std::string pass;
+
+ /** Threshold value for flood disconnect
+ */
+ unsigned int threshold;
+
+ /** Maximum size of sendq for users in this class (bytes)
+ */
+ unsigned long sendqmax;
+
+ /** Maximum size of recvq for users in this class (bytes)
+ */
+ unsigned long recvqmax;
+
+ /** Local max when connecting by this connection class
+ */
+ unsigned long maxlocal;
+
+ /** Global max when connecting by this connection class
+ */
+ unsigned long maxglobal;
+ /** Port number this connect class applies to
+ */
+ int port;
+
+public:
+
+ /** Create a new connect class with no settings.
+ */
+ ConnectClass() : type(CC_DENY), registration_timeout(0), flood(0), host(""), pingtime(0), pass(""),
+ threshold(0), sendqmax(0), recvqmax(0), maxlocal(0), maxglobal(0) { }
+
+ /** Create a new connect class to ALLOW connections.
+ * @param timeout The registration timeout
+ * @param fld The flood value
+ * @param hst The IP mask to allow
+ * @param ping The ping frequency
+ * @param pas The password to be used
+ * @param thres The flooding threshold
+ * @param sendq The maximum sendq value
+ * @param recvq The maximum recvq value
+ * @param maxl The maximum local sessions
+ * @param maxg The maximum global sessions
+ */
+ ConnectClass(unsigned int timeout, unsigned int fld, const std::string &hst, unsigned int ping,
+ const std::string &pas, unsigned int thres, unsigned long sendq, unsigned long recvq,
+ unsigned long maxl, unsigned long maxg, int p = 0) :
+ type(CC_ALLOW), registration_timeout(timeout), flood(fld), host(hst), pingtime(ping), pass(pas),
+ threshold(thres), sendqmax(sendq), recvqmax(recvq), maxlocal(maxl), maxglobal(maxg), port(p) { }
+
+ /** Create a new connect class to DENY connections
+ * @param hst The IP mask to deny
+ */
+ ConnectClass(const std::string &hst) : type(CC_DENY), registration_timeout(0), flood(0), host(hst), pingtime(0),
+ pass(""), threshold(0), sendqmax(0), recvqmax(0), maxlocal(0), maxglobal(0), port(0) { }
+
+ /** Returns the type, CC_ALLOW or CC_DENY
+ */
+ char GetType()
+ {
+ return (type == CC_ALLOW ? CC_ALLOW : CC_DENY);
+ }
+
+ /** Returns the registration timeout
+ */
+ unsigned int GetRegTimeout()
+ {
+ return (registration_timeout ? registration_timeout : 90);
+ }
+
+ /** Returns the flood limit
+ */
+ unsigned int GetFlood()
+ {
+ return (threshold ? flood : 999);
+ }
+
+ /** Returns the allowed or denied IP mask
+ */
+ const std::string& GetHost()
+ {
+ return host;
+ }
+
+ int GetPort()
+ {
+ return port;
+ }
+
+ /** Returns the ping frequency
+ */
+ unsigned int GetPingTime()
+ {
+ return (pingtime ? pingtime : 120);
+ }
+
+ /** Returns the password or an empty string
+ */
+ const std::string& GetPass()
+ {
+ return pass;
+ }
+
+ /** Returns the flood threshold value
+ */
+ unsigned int GetThreshold()
+ {
+ return (threshold ? threshold : 1);
+ }
+
+ /** Returns the maximum sendq value
+ */
+ unsigned long GetSendqMax()
+ {
+ return (sendqmax ? sendqmax : 262114);
+ }
+
+ /** Returns the maximum recvq value
+ */
+ unsigned long GetRecvqMax()
+ {
+ return (recvqmax ? recvqmax : 4096);
+ }
+
+ /** Returusn the maximum number of local sessions
+ */
+ unsigned long GetMaxLocal()
+ {
+ return maxlocal;
+ }
+
+ /** Returns the maximum number of global sessions
+ */
+ unsigned long GetMaxGlobal()
+ {
+ return maxglobal;
+ }
+};
+
+/** Holds a complete list of all channels to which a user has been invited and has not yet joined.
+ */
+typedef std::vector<irc::string> InvitedList;
+
+/** Holds a complete list of all allow and deny tags from the configuration file (connection classes)
+ */
+typedef std::vector<ConnectClass> ClassVector;
+
+/** Typedef for the list of user-channel records for a user
+ */
+typedef std::map<chanrec*, char> UserChanList;
+
+/** Shorthand for an iterator into a UserChanList
+ */
+typedef UserChanList::iterator UCListIter;
+
+/* Required forward declaration
+ */
+class userrec;
+
+/** Visibility data for a user.
+ * If a user has a non-null instance of this class in their userrec,
+ * then it is used to determine if this user is visible to other users
+ * or not.
+ */
+class CoreExport VisData
+{
+ public:
+ /** Create a visdata
+ */
+ VisData();
+ /** Destroy a visdata
+ */
+ virtual ~VisData();
+ /** Is this user visible to some other user?
+ * @param user The other user to compare to
+ * @return true True if the user is visible to the other user, false if not
+ */
+ virtual bool VisibleTo(userrec* user);
+};
+
+/** Holds all information about a user
+ * This class stores all information about a user connected to the irc server. Everything about a
+ * connection is stored here primarily, from the user's socket ID (file descriptor) through to the
+ * user's nickname and hostname. Use the FindNick method of the InspIRCd class to locate a specific user
+ * by nickname, or the FindDescriptor method of the InspIRCd class to find a specific user by their
+ * file descriptor value.
+ */
+class CoreExport userrec : public connection
+{
+ private:
+ /** Pointer to creator.
+ * This is required to make use of core functions
+ * from within the userrec class.
+ */
+ InspIRCd* ServerInstance;
+
+ /** A list of channels the user has a pending invite to.
+ * Upon INVITE channels are added, and upon JOIN, the
+ * channels are removed from this list.
+ */
+ InvitedList invites;
+
+ /** Number of channels this user is currently on
+ */
+ unsigned int ChannelCount;
+
+ /** Cached nick!ident@host value using the real hostname
+ */
+ char* cached_fullhost;
+
+ /** Cached nick!ident@ip value using the real IP address
+ */
+ char* cached_hostip;
+
+ /** Cached nick!ident@host value using the masked hostname
+ */
+ char* cached_makehost;
+
+ /** Cached nick!ident@realhost value using the real hostname
+ */
+ char* cached_fullrealhost;
+
+ /** When we erase the user (in the destructor),
+ * we call this method to subtract one from all
+ * mode characters this user is making use of.
+ */
+ void DecrementModes();
+
+ /** Oper-only quit message for this user if non-null
+ */
+ char* operquit;
+
+ public:
+ /** Resolvers for looking up this users IP address
+ * This will occur if and when res_reverse completes.
+ * When this class completes its lookup, userrec::dns_done
+ * will be set from false to true.
+ */
+ UserResolver* res_forward;
+
+ /** Resolvers for looking up this users hostname
+ * This is instantiated by userrec::StartDNSLookup(),
+ * and on success, instantiates userrec::res_reverse.
+ */
+ UserResolver* res_reverse;
+
+ /** User visibility state, see definition of VisData.
+ */
+ VisData* Visibility;
+
+ /** Stored reverse lookup from res_forward
+ */
+ 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 userrec::dns_done to true.
+ */
+ void StartDNSLookup();
+
+ /** The users nickname.
+ * An invalid nickname indicates an unregistered connection prior to the NICK command.
+ * Use InspIRCd::IsNick() to validate nicknames.
+ */
+ char nick[NICKMAX];
+
+ /** The users ident reply.
+ * Two characters are added to the user-defined limit to compensate for the tilde etc.
+ */
+ char ident[IDENTMAX+2];
+
+ /** The host displayed to non-opers (used for cloaking etc).
+ * This usually matches the value of userrec::host.
+ */
+ char dhost[65];
+
+ /** The users full name (GECOS).
+ */
+ char fullname[MAXGECOS+1];
+
+ /** The user's mode list.
+ * This is NOT a null terminated string! In the 1.1 version of InspIRCd
+ * this is an array of values in a similar way to channel modes.
+ * A value of 1 in field (modeletter-65) indicates that the mode is
+ * set, for example, to work out if mode +s is set, we check the field
+ * userrec::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.
+ */
+ char modes[64];
+
+ /** What snomasks are set on this user.
+ * This functions the same as the above modes.
+ */
+ char snomasks[64];
+
+ /** Channels this user is on, and the permissions they have there
+ */
+ UserChanList chans;
+
+ /** The server the user is connected to.
+ */
+ const char* server;
+
+ /** The user's away message.
+ * If this string is empty, the user is not marked as away.
+ */
+ char awaymsg[MAXAWAY+1];
+
+ /** Number of lines the user can place into the buffer
+ * (up to the global NetBufferSize bytes) before they
+ * are disconnected for excess flood
+ */
+ int flood;
+
+ /** Timestamp of current time + connection class timeout.
+ * This user must send USER/NICK before this timestamp is
+ * reached or they will be disconnected.
+ */
+ time_t timeout;
+
+ /** The oper type they logged in as, if they are an oper.
+ * This is used to check permissions in operclasses, so that
+ * we can say 'yay' or 'nay' to any commands they issue.
+ * The value of this is the value of a valid 'type name=' tag.
+ */
+ char oper[NICKMAX];
+
+ /** True when DNS lookups are completed.
+ * The UserResolver classes res_forward and res_reverse will
+ * set this value once they complete.
+ */
+ bool dns_done;
+
+ /** Number of seconds between PINGs for this user (set from &lt;connect:allow&gt; tag
+ */
+ unsigned int pingmax;
+
+ /** Password specified by the user when they registered.
+ * This is stored even if the <connect> block doesnt need a password, so that
+ * modules may check it.
+ */
+ char password[64];
+
+ /** User's receive queue.
+ * Lines from the IRCd awaiting processing are stored here.
+ * Upgraded april 2005, old system a bit hairy.
+ */
+ std::string recvq;
+
+ /** User's send queue.
+ * Lines waiting to be sent are stored here until their buffer is flushed.
+ */
+ std::string sendq;
+
+ /** Flood counters - lines received
+ */
+ int lines_in;
+
+ /** Flood counters - time lines_in is due to be reset
+ */
+ time_t reset_due;
+
+ /** Flood counters - Highest value lines_in may reach before the user gets disconnected
+ */
+ long threshold;
+
+ /** If this is set to true, then all read operations for the user
+ * are dropped into the bit-bucket.
+ * This is used by the global CullList, but please note that setting this value
+ * alone will NOT cause the user to quit. This means it can be used seperately,
+ * for example by shun modules etc.
+ */
+ bool muted;
+
+ /** IPV4 or IPV6 ip address. Use SetSockAddr to set this and GetProtocolFamily/
+ * GetIPString/GetPort to obtain its values.
+ */
+ sockaddr* ip;
+
+ /** Initialize the clients sockaddr
+ * @param protocol_family The protocol family of the IP address, AF_INET or AF_INET6
+ * @param ip A human-readable IP address for this user matching the protcol_family
+ * @param port The port number of this user or zero for a remote user
+ */
+ void SetSockAddr(int protocol_family, const char* ip, int port);
+
+ /** Get port number from sockaddr
+ * @return The port number of this user.
+ */
+ int GetPort();
+
+ /** Get protocol family from sockaddr
+ * @return The protocol family of this user, either AF_INET or AF_INET6
+ */
+ int GetProtocolFamily();
+
+ /** Get IP string from sockaddr, using static internal buffer
+ * @return The IP string
+ */
+ const char* GetIPString();
+
+ /** Get IP string from sockaddr, using caller-specified buffer
+ * @param buf A buffer to use
+ * @return The IP string
+ */
+ const char* GetIPString(char* buf);
+
+ /* Write error string
+ */
+ std::string WriteError;
+
+ /** Maximum size this user's sendq can become.
+ * Copied from the connect class on connect.
+ */
+ long sendqmax;
+
+ /** Maximum size this user's recvq can become.
+ * Copied from the connect class on connect.
+ */
+ long recvqmax;
+
+ /** This is true if the user matched an exception when they connected to the ircd.
+ * It isnt valid after this point, and you should not attempt to do anything with it
+ * after this point, because the eline might be removed at a later time, and/or no
+ * longer be applicable to this user. It is only used to save doing the eline lookup
+ * twice (instead we do it once and set this value).
+ */
+ bool exempt;
+
+ /** Default constructor
+ * @throw Nothing at present
+ */
+ userrec(InspIRCd* Instance);
+
+ /** Returns the full displayed host of the user
+ * This member function returns the hostname of the user as seen by other users
+ * on the server, in nick!ident&at;host form.
+ * @return The full masked host of the user
+ */
+ virtual char* GetFullHost();
+
+ /** Returns the full real host of the user
+ * This member function returns the hostname of the user as seen by other users
+ * on the server, in nick!ident&at;host form. If any form of hostname cloaking is in operation,
+ * e.g. through a module, then this method will ignore it and return the true hostname.
+ * @return The full real host of the user
+ */
+ virtual char* GetFullRealHost();
+
+ /** This clears any cached results that are used for GetFullRealHost() etc.
+ * The results of these calls are cached as generating them can be generally expensive.
+ */
+ void InvalidateCache();
+
+ /** Create a displayable mode string for this users snomasks
+ * @return The notice mask character sequence
+ */
+ const char* FormatNoticeMasks();
+
+ /** 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
+ */
+ std::string ProcessNoticeMasks(const char *sm);
+
+ /** Returns true if a notice mask is set
+ * @param sm A notice mask character to check
+ * @return True if the notice mask is set
+ */
+ 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 The mode string
+ */
+ const char* FormatModes();
+
+ /** Returns true if a specific mode is set
+ * @param m The user mode
+ * @return True if the mode is set
+ */
+ bool IsModeSet(unsigned char m);
+
+ /** 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);
+
+ /** 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
+ */
+ virtual 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
+ */
+ virtual void InviteTo(const irc::string &channel);
+
+ /** 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
+ */
+ virtual void RemoveInvite(const irc::string &channel);
+
+ /** Returns true or false for if a user can execute a privilaged oper command.
+ * This is done by looking up their oper type from userrec::oper, then referencing
+ * this to their oper classes and checking the commands they can execute.
+ * @param command A command (should be all CAPS)
+ * @return True if this user can execute the command
+ */
+ bool HasPermission(const std::string &command);
+
+ /** Calls read() to read some data for this user using their fd.
+ * @param buffer The buffer to read into
+ * @param size The size of data to read
+ * @return The number of bytes read, or -1 if an error occured.
+ */
+ int ReadData(void* buffer, size_t size);
+
+ /** This method adds data to the read buffer of the user.
+ * The buffer can grow to any size within limits of the available memory,
+ * managed by the size of a std::string, however if any individual line in
+ * the buffer grows over 600 bytes in length (which is 88 chars over the
+ * RFC-specified limit per line) then the method will return false and the
+ * text will not be inserted.
+ * @param a The string to add to the users read buffer
+ * @return True if the string was successfully added to the read buffer
+ */
+ bool AddBuffer(std::string a);
+
+ /** This method returns true if the buffer contains at least one carriage return
+ * character (e.g. one complete line may be read)
+ * @return True if there is at least one complete line in the users buffer
+ */
+ bool BufferIsReady();
+
+ /** This function clears the entire buffer by setting it to an empty string.
+ */
+ void ClearBuffer();
+
+ /** This method returns the first available string at the tail end of the buffer
+ * and advances the tail end of the buffer past the string. This means it is
+ * a one way operation in a similar way to strtok(), and multiple calls return
+ * multiple lines if they are available. The results of this function if there
+ * are no lines to be read are unknown, always use BufferIsReady() to check if
+ * it is ok to read the buffer before calling GetBuffer().
+ * @return The string at the tail end of this users buffer
+ */
+ std::string GetBuffer();
+
+ /** Sets the write error for a connection. This is done because the actual disconnect
+ * of a client may occur at an inopportune time such as half way through /LIST output.
+ * The WriteErrors of clients are checked at a more ideal time (in the mainloop) and
+ * errored clients purged.
+ * @param error The error string to set.
+ */
+ void SetWriteError(const std::string &error);
+
+ /** Returns the write error which last occured on this connection or an empty string
+ * if none occured.
+ * @return The error string which has occured for this user
+ */
+ const char* GetWriteError();
+
+ /** Adds to the user's write buffer.
+ * You may add any amount of text up to this users sendq value, if you exceed the
+ * sendq value, SetWriteError() will be called to set the users error string to
+ * "SendQ exceeded", and further buffer adds will be dropped.
+ * @param data The data to add to the write buffer
+ */
+ void AddWriteBuf(const std::string &data);
+
+ /** Flushes as much of the user's buffer to the file descriptor as possible.
+ * This function may not always flush the entire buffer, rather instead as much of it
+ * as it possibly can. If the send() call fails to send the entire buffer, the buffer
+ * position is advanced forwards and the rest of the data sent at the next call to
+ * this method.
+ */
+ void FlushWriteBuf();
+
+ /** 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
+ */
+ InvitedList* GetInviteList();
+
+ /** 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
+ */
+ char* MakeHost();
+
+ /** Creates a usermask with real ip.
+ * Takes a buffer to use and fills the given buffer with the ipmask in the format user@ip
+ * @return the usermask in the format user@ip
+ */
+ char* MakeHostIP();
+
+ /** Shuts down and closes the user's socket
+ * This will not cause the user to be deleted. Use InspIRCd::QuitUser for this,
+ * which will call CloseSocket() for you.
+ */
+ void CloseSocket();
+
+ /** Disconnect a user gracefully
+ * @param user The user to remove
+ * @param r The quit reason to show to normal users
+ * @param oreason The quit reason to show to opers
+ * @return Although this function has no return type, on exit the user provided will no longer exist.
+ */
+ static void QuitUser(InspIRCd* Instance, userrec *user, const std::string &r, const char* oreason = "");
+
+ /** Add the user to WHOWAS system
+ */
+ void AddToWhoWas();
+
+ /** Oper up the user using the given opertype.
+ * This will also give the +o usermode.
+ * @param opertype The oper type to oper as
+ */
+ void Oper(const std::string &opertype);
+
+ /** Call this method to find the matching <connect> for a user, and to check them against it.
+ */
+ void CheckClass();
+
+ /** Use this method to fully connect a user.
+ * This will send the message of the day, check G/K/E lines, etc.
+ */
+ void FullConnect();
+
+ /** Change this users hash key to a new string.
+ * You should not call this function directly. It is used by the core
+ * to update the users hash entry on a nickchange.
+ * @param New new user_hash key
+ * @return Pointer to userrec in hash (usually 'this')
+ */
+ userrec* UpdateNickHash(const char* New);
+
+ /** Force a nickname change.
+ * If the nickname change fails (for example, because the nick in question
+ * already exists) this function will return false, and you must then either
+ * output an error message, or quit the user for nickname collision.
+ * @param newnick The nickname to change to
+ * @return True if the nickchange was successful.
+ */
+ bool ForceNickChange(const char* newnick);
+
+ /** Add a client to the system.
+ * This will create a new userrec, insert it into the user_hash,
+ * initialize it as not yet registered, and add it to the socket engine.
+ * @param Instance a pointer to the server instance
+ * @param socket The socket id (file descriptor) this user is on
+ * @param port The port number this user connected on
+ * @param iscached This variable is reserved for future use
+ * @param ip The IP address of the user
+ * @return This function has no return value, but a call to AddClient may remove the user.
+ */
+ static void AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip);
+
+ /** Oper down.
+ * This will clear the +o usermode and unset the user's oper type
+ */
+ void UnOper();
+
+ /** Return the number of global clones of this user
+ * @return The global clone count of this user
+ */
+ unsigned long GlobalCloneCount();
+
+ /** Return the number of local clones of this user
+ * @return The local clone count of this user
+ */
+ unsigned long LocalCloneCount();
+
+ /** Remove all clone counts from the user, you should
+ * use this if you change the user's IP address in
+ * userrec::ip after they have registered.
+ */
+ void RemoveCloneCounts();
+
+ /** Write text to this user, appending CR/LF.
+ * @param text A std::string to send to the user
+ */
+ void Write(std::string text);
+
+ /** Write text to this user, appending CR/LF.
+ * @param text The format string for text to send to the user
+ * @param ... POD-type format arguments
+ */
+ void Write(const char *text, ...);
+
+ /** Write text to this user, appending CR/LF and prepending :server.name
+ * @param text A std::string to send to the user
+ */
+ void WriteServ(const std::string& text);
+
+ /** Write text to this user, appending CR/LF and prepending :server.name
+ * @param text The format string for text to send to the user
+ * @param ... POD-type format arguments
+ */
+ void WriteServ(const char* text, ...);
+
+ /** Write text to this user, appending CR/LF and prepending :nick!user@host of the user provided in the first parameter.
+ * @param user The user to prepend the :nick!user@host of
+ * @param text A std::string to send to the user
+ */
+ void WriteFrom(userrec *user, const std::string &text);
+
+ /** Write text to this user, appending CR/LF and prepending :nick!user@host of the user provided in the first parameter.
+ * @param user The user to prepend the :nick!user@host of
+ * @param text The format string for text to send to the user
+ * @param ... POD-type format arguments
+ */
+ void WriteFrom(userrec *user, const char* text, ...);
+
+ /** 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 text A std::string to send to the user
+ */
+ void WriteTo(userrec *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 text The format string for text to send to the user
+ * @param ... POD-type format arguments
+ */
+ void WriteTo(userrec *dest, const char *data, ...);
+
+ /** Write to all users that can see this user (including this user in the list), appending CR/LF
+ * @param text A std::string to send to the users
+ */
+ void WriteCommon(const std::string &text);
+
+ /** Write to all users that can see this user (including this user in the list), appending CR/LF
+ * @param text The format string for text to send to the users
+ * @param ... POD-type format arguments
+ */
+ void WriteCommon(const char* text, ...);
+
+ /** 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, ...);
+
+ /** Write to all users that can see this user (not including this user in the list), appending CR/LF
+ * @param text A std::string to send to the users
+ */
+ void WriteCommonExcept(const std::string &text);
+
+ /** Write a quit message to all common users, as in userrec::WriteCommonExcept but with a specific
+ * quit message for opers only.
+ * @param normal_text Normal user quit message
+ * @param oper_text Oper only quit message
+ */
+ void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text);
+
+ /** Write a WALLOPS message from this user to all local opers.
+ * If this user is not opered, the function will return without doing anything.
+ * @param text The format string to send in the WALLOPS message
+ * @param ... Format arguments
+ */
+ void WriteWallOps(const char* text, ...);
+
+ /** Write a WALLOPS message from this user to all local opers.
+ * If this user is not opered, the function will return without doing anything.
+ * @param text The text to send in the WALLOPS message
+ */
+ void WriteWallOps(const std::string &text);
+
+ /** Return true if the user shares at least one channel with another user
+ * @param other The other user to compare the channel list against
+ * @return True if the given user shares at least one channel with this user
+ */
+ bool SharesChannelWith(userrec *other);
+
+ /** Change the displayed host of a user.
+ * ALWAYS use this function, rather than writing userrec::dhost directly,
+ * as this triggers module events allowing the change to be syncronized to
+ * remote servers. This will also emulate a QUIT and rejoin (where configured)
+ * before setting their host field.
+ * @param host The new hostname to set
+ * @return True if the change succeeded, false if it didn't
+ */
+ bool ChangeDisplayedHost(const char* host);
+
+ /** Change the ident (username) of a user.
+ * ALWAYS use this function, rather than writing userrec::ident directly,
+ * as this correctly causes the user to seem to quit (where configured)
+ * before setting their ident field.
+ * @param host The new ident to set
+ * @return True if the change succeeded, false if it didn't
+ */
+ bool ChangeIdent(const char* newident);
+
+ /** Change a users realname field.
+ * ALWAYS use this function, rather than writing userrec::fullname directly,
+ * as this triggers module events allowing the change to be syncronized to
+ * remote servers.
+ * @param gecos The user's new realname
+ * @return True if the change succeeded, false if otherwise
+ */
+ bool ChangeName(const char* gecos);
+
+ /** 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, char* text, ...);
+
+ /** Compile a channel list for this user, and send it to the user 'source'
+ * Used internally by WHOIS
+ * @param The user to send the channel list to if it is not too long
+ * @return This user's channel list
+ */
+ std::string ChannelList(userrec* source);
+
+ /** 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 userrec::ChannelList()
+ */
+ void SplitChanList(userrec* dest, const std::string &cl);
+
+ /** 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 matches this user's host or IP address
+ * @return A reference to this user's connect class
+ */
+ ConnectClass* GetClass();
+
+ /** Show the message of the day to this user
+ */
+ void ShowMOTD();
+
+ /** Show the server RULES file to this user
+ */
+ void ShowRULES();
+
+ /** Set oper-specific quit message shown to opers only when the user quits
+ * (overrides any sent by QuitUser)
+ */
+ void SetOperQuit(const std::string &oquit);
+
+ /** Get oper-specific quit message shown only to opers when the user quits.
+ * (overrides any sent by QuitUser)
+ */
+ const char* GetOperQuit();
+
+ /** Handle socket event.
+ * From EventHandler class.
+ * @param et Event type
+ * @param errornum Error number for EVENT_ERROR events
+ */
+ void HandleEvent(EventType et, int errornum = 0);
+
+ /** Default destructor
+ */
+ virtual ~userrec();
+};
+
+/* Configuration callbacks */
+class ServerConfig;
+
+#endif
+
diff --git a/include/wildcard.h b/include/wildcard.h
index c58d4bfc1..259d86184 100644
--- a/include/wildcard.h
+++ b/include/wildcard.h
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd_config.h" /** Match a string against a mask. * @param str The string to check * @param mask the mask to check against * @return true if the strings match */ CoreExport bool match(const char *str, const char *mask); /** Match a string against a mask, and define wether or not to use CIDR rules * @param str The string to check * @param mask the mask to check against * @param use_cidr_match True if CIDR matching rules should be applied first * @return true if the strings match */ CoreExport bool match(const char *str, const char *mask, bool use_cidr_match); /** Match a string against a mask, defining wether case sensitivity applies. * @param str The string to check * @param mask the mask to check against * @param case_sensitive True if the match is case sensitive * @return True if the strings match */ CoreExport bool match(bool case_sensitive, const char *str, const char *mask); /** Match a string against a mask, defining wether case sensitivity applies, * and defining wether or not to use CIDR rules first. * @param case_sensitive True if the match is case sensitive * @param str The string to check * @param mask the mask to check against * @param use_cidr_match True if CIDR matching rules should be applied first * @return true if the strings match */ CoreExport bool match(bool case_sensitive, const char *str, const char *mask, bool use_cidr_match); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd_config.h"
+
+/** Match a string against a mask.
+ * @param str The string to check
+ * @param mask the mask to check against
+ * @return true if the strings match
+ */
+CoreExport bool match(const char *str, const char *mask);
+/** Match a string against a mask, and define wether or not to use CIDR rules
+ * @param str The string to check
+ * @param mask the mask to check against
+ * @param use_cidr_match True if CIDR matching rules should be applied first
+ * @return true if the strings match
+ */
+CoreExport bool match(const char *str, const char *mask, bool use_cidr_match);
+/** Match a string against a mask, defining wether case sensitivity applies.
+ * @param str The string to check
+ * @param mask the mask to check against
+ * @param case_sensitive True if the match is case sensitive
+ * @return True if the strings match
+ */
+CoreExport bool match(bool case_sensitive, const char *str, const char *mask);
+/** Match a string against a mask, defining wether case sensitivity applies,
+ * and defining wether or not to use CIDR rules first.
+ * @param case_sensitive True if the match is case sensitive
+ * @param str The string to check
+ * @param mask the mask to check against
+ * @param use_cidr_match True if CIDR matching rules should be applied first
+ * @return true if the strings match
+ */
+CoreExport bool match(bool case_sensitive, const char *str, const char *mask, bool use_cidr_match);
+
diff --git a/include/xline.h b/include/xline.h
index 5695c3788..bb59a9735 100644
--- a/include/xline.h
+++ b/include/xline.h
@@ -1 +1,530 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __XLINE_H #define __XLINE_H // include the common header files #include <string> #include <deque> #include <vector> #include "users.h" #include "channels.h" const int APPLY_GLINES = 1; const int APPLY_KLINES = 2; const int APPLY_QLINES = 4; const int APPLY_ZLINES = 8; const int APPLY_PERM_ONLY = 16; const int APPLY_ALL = APPLY_GLINES | APPLY_KLINES | APPLY_QLINES | APPLY_ZLINES; /** XLine is the base class for ban lines such as G lines and K lines. */ class CoreExport XLine : public classbase { public: /** Create an XLine. * @param s_time The set time * @param d The duration of the xline * @param src The sender of the xline * @param re The reason of the xline */ XLine(time_t s_time, long d, const char* src, const char* re) : set_time(s_time), duration(d) { source = strdup(src); reason = strdup(re); expiry = set_time + duration; } /** Destructor */ virtual ~XLine() { free(reason); free(source); } /** The time the line was added. */ time_t set_time; /** The duration of the ban, or 0 if permenant */ long duration; /** Source of the ban. This can be a servername or an oper nickname */ char* source; /** Reason for the ban */ char* reason; /** Expiry time */ time_t expiry; }; /** KLine class */ class CoreExport KLine : public XLine { public: /** Create a K-Line. * @param s_time The set time * @param d The duration of the xline * @param src The sender of the xline * @param re The reason of the xline * @param ident Ident to match * @param host Host to match */ KLine(time_t s_time, long d, const char* src, const char* re, const char* ident, const char* host) : XLine(s_time, d, src, re) { identmask = strdup(ident); hostmask = strdup(host); } /** Destructor */ ~KLine() { free(identmask); free(hostmask); } /** Ident mask */ char* identmask; /** Host mask */ char* hostmask; }; /** GLine class */ class CoreExport GLine : public XLine { public: /** Create a G-Line. * @param s_time The set time * @param d The duration of the xline * @param src The sender of the xline * @param re The reason of the xline * @param ident Ident to match * @param host Host to match */ GLine(time_t s_time, long d, const char* src, const char* re, const char* ident, const char* host) : XLine(s_time, d, src, re) { identmask = strdup(ident); hostmask = strdup(host); } /** Destructor */ ~GLine() { free(identmask); free(hostmask); } /** Ident mask */ char* identmask; /** Host mask */ char* hostmask; }; /** ELine class */ class CoreExport ELine : public XLine { public: /** Create an E-Line. * @param s_time The set time * @param d The duration of the xline * @param src The sender of the xline * @param re The reason of the xline * @param ident Ident to match * @param host Host to match */ ELine(time_t s_time, long d, const char* src, const char* re, const char* ident, const char* host) : XLine(s_time, d, src, re) { identmask = strdup(ident); hostmask = strdup(host); } ~ELine() { free(identmask); free(hostmask); } /** Ident mask */ char* identmask; /** Host mask */ char* hostmask; }; /** ZLine class */ class CoreExport ZLine : public XLine { public: /** Create a Z-Line. * @param s_time The set time * @param d The duration of the xline * @param src The sender of the xline * @param re The reason of the xline * @param ip IP to match */ ZLine(time_t s_time, long d, const char* src, const char* re, const char* ip) : XLine(s_time, d, src, re) { ipaddr = strdup(ip); } /** Destructor */ ~ZLine() { free(ipaddr); } /** IP mask */ char* ipaddr; }; /** QLine class */ class CoreExport QLine : public XLine { public: /** Create a G-Line. * @param s_time The set time * @param d The duration of the xline * @param src The sender of the xline * @param re The reason of the xline * @param nickname Nickname to match */ QLine(time_t s_time, long d, const char* src, const char* re, const char* nickname) : XLine(s_time, d, src, re) { nick = strdup(nickname); } /** Destructor */ ~QLine() { free(nick); } /** Nickname mask */ char* nick; }; /* Required forward declarations */ class ServerConfig; class InspIRCd; /** Initialize x line */ bool InitXLine(ServerConfig* conf, const char* tag); /** Done adding zlines from the config */ bool DoneZLine(ServerConfig* conf, const char* tag); /** Done adding qlines from the config */ bool DoneQLine(ServerConfig* conf, const char* tag); /** Done adding klines from the config */ bool DoneKLine(ServerConfig* conf, const char* tag); /** Done adding elines from the config */ bool DoneELine(ServerConfig* conf, const char* tag); /** Add a config-defined zline */ bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types); /** Add a config-defined qline */ bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types); /** Add a config-defined kline */ bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types); /** Add a config-defined eline */ bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types); /** Contains an ident and host split into two strings */ typedef std::pair<std::string, std::string> IdentHostPair; /** XLineManager is a class used to manage glines, klines, elines, zlines and qlines. */ class CoreExport XLineManager { protected: /** The owner/creator of this class */ InspIRCd* ServerInstance; /** This functor is used by the std::sort() function to keep glines in order */ static bool GSortComparison ( const GLine* one, const GLine* two ); /** This functor is used by the std::sort() function to keep elines in order */ static bool ESortComparison ( const ELine* one, const ELine* two ); /** This functor is used by the std::sort() function to keep zlines in order */ static bool ZSortComparison ( const ZLine* one, const ZLine* two ); /** This functor is used by the std::sort() function to keep klines in order */ static bool KSortComparison ( const KLine* one, const KLine* two ); /** This functor is used by the std::sort() function to keep qlines in order */ static bool QSortComparison ( const QLine* one, const QLine* two ); public: /* Lists for temporary lines with an expiry time */ /** Temporary KLines */ std::vector<KLine*> klines; /** Temporary Glines */ std::vector<GLine*> glines; /** Temporary Zlines */ std::vector<ZLine*> zlines; /** Temporary QLines */ std::vector<QLine*> qlines; /** Temporary ELines */ std::vector<ELine*> elines; /* Seperate lists for perm XLines that isnt checked by expiry functions */ /** Permenant KLines */ std::vector<KLine*> pklines; /** Permenant GLines */ std::vector<GLine*> pglines; /** Permenant ZLines */ std::vector<ZLine*> pzlines; /** Permenant QLines */ std::vector<QLine*> pqlines; /** Permenant ELines */ std::vector<ELine*> pelines; /** Constructor * @param Instance A pointer to the creator object */ XLineManager(InspIRCd* Instance); /** Split an ident and host into two seperate strings. * This allows for faster matching. */ IdentHostPair IdentSplit(const std::string &ident_and_host); /** Add a new GLine * @param duration The duration of the line * @param source The source of the line * @param reason The reason for the line * @param hostmask The hostmask * @return True if the line was added successfully */ bool add_gline(long duration, const char* source, const char* reason, const char* hostmask); /** Add a new QLine * @param duration The duration of the line * @param source The source of the line * @param reason The reason for the line * @param nickname The nickmask * @return True if the line was added successfully */ bool add_qline(long duration, const char* source, const char* reason, const char* nickname); /** Add a new ZLine * @param duration The duration of the line * @param source The source of the line * @param reason The reason for the line * @param ipaddr The IP mask * @return True if the line was added successfully */ bool add_zline(long duration, const char* source, const char* reason, const char* ipaddr); /** Add a new KLine * @param duration The duration of the line * @param source The source of the line * @param reason The reason for the line * @param hostmask The hostmask * @return True if the line was added successfully */ bool add_kline(long duration, const char* source, const char* reason, const char* hostmask); /** Add a new ELine * @param duration The duration of the line * @param source The source of the line * @param reason The reason for the line * @param hostmask The hostmask * @return True if the line was added successfully */ bool add_eline(long duration, const char* source, const char* reason, const char* hostmask); /** Delete a GLine * @param hostmask The host to remove * @param simulate If this is true, don't actually remove the line, just return * @return True if the line was deleted successfully */ bool del_gline(const char* hostmask, bool simulate = false); /** Delete a QLine * @param nickname The nick to remove * @param simulate If this is true, don't actually remove the line, just return * @return True if the line was deleted successfully */ bool del_qline(const char* nickname, bool simulate = false); /** Delete a ZLine * @param ipaddr The IP to remove * @param simulate If this is true, don't actually remove the line, just return * @return True if the line was deleted successfully */ bool del_zline(const char* ipaddr, bool simulate = false); /** Delete a KLine * @param hostmask The host to remove * @param simulate If this is true, don't actually remove the line, just return * @return True if the line was deleted successfully */ bool del_kline(const char* hostmask, bool simulate = false); /** Delete a ELine * @param hostmask The host to remove * @param simulate If this is true, don't actually remove the line, just return * @return True if the line was deleted successfully */ bool del_eline(const char* hostmask, bool simulate = false); /** Check if a nickname matches a QLine * @return nick The nick to check against * @return The reason for the line if there is a match, or NULL if there is no match */ QLine* matches_qline(const char* nick, bool permonly = false); /** Check if a hostname matches a GLine * @param user The user to check against * @return The reason for the line if there is a match, or NULL if there is no match */ GLine* matches_gline(userrec* user, bool permonly = false); /** Check if a IP matches a ZLine * @param ipaddr The IP to check against * @return The reason for the line if there is a match, or NULL if there is no match */ ZLine* matches_zline(const char* ipaddr, bool permonly = false); /** Check if a hostname matches a KLine * @param user The user to check against * @return The reason for the line if there is a match, or NULL if there is no match */ KLine* matches_kline(userrec* user, bool permonly = false); /** Check if a hostname matches a ELine * @param user The user to check against * @return The reason for the line if there is a match, or NULL if there is no match */ ELine* matches_exception(userrec* user, bool permonly = false); /** Expire any pending non-permenant lines */ void expire_lines(); /** Apply any new lines * @param What The types of lines to apply, from the set * APPLY_GLINES | APPLY_KLINES | APPLY_QLINES | APPLY_ZLINES | APPLY_ALL * | APPLY_LOCAL_ONLY */ void apply_lines(const int What); /** Handle /STATS K * @param user The username making the query * @param results The string_list to receive the results */ void stats_k(userrec* user, string_list &results); /** Handle /STATS G * @param user The username making the query * @param results The string_list to receive the results */ void stats_g(userrec* user, string_list &results); /** Handle /STATS Q * @param user The username making the query * @param results The string_list to receive the results */ void stats_q(userrec* user, string_list &results); /** Handle /STATS Z * @param user The username making the query * @param results The string_list to receive the results */ void stats_z(userrec* user, string_list &results); /** Handle /STATS E * @param user The username making the query * @param results The string_list to receive the results */ void stats_e(userrec* user, string_list &results); /** Change creation time of a GLine * @param host The hostname to change * @param create_Time The new creation time */ void gline_set_creation_time(const char* host, time_t create_time); /** Change creation time of a QLine * @param nick The nickmask to change * @param create_Time The new creation time */ void qline_set_creation_time(const char* nick, time_t create_time); /** Change creation time of a ZLine * @param ip The ipmask to change * @param create_Time The new creation time */ void zline_set_creation_time(const char* ip, time_t create_time); /** Change creation time of a ELine * @param host The hostname to change * @param create_Time The new creation time */ void eline_set_creation_time(const char* host, time_t create_time); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __XLINE_H
+#define __XLINE_H
+
+// include the common header files
+
+#include <string>
+#include <deque>
+#include <vector>
+#include "users.h"
+#include "channels.h"
+
+const int APPLY_GLINES = 1;
+const int APPLY_KLINES = 2;
+const int APPLY_QLINES = 4;
+const int APPLY_ZLINES = 8;
+const int APPLY_PERM_ONLY = 16;
+const int APPLY_ALL = APPLY_GLINES | APPLY_KLINES | APPLY_QLINES | APPLY_ZLINES;
+
+/** XLine is the base class for ban lines such as G lines and K lines.
+ */
+class CoreExport XLine : public classbase
+{
+ public:
+
+ /** Create an XLine.
+ * @param s_time The set time
+ * @param d The duration of the xline
+ * @param src The sender of the xline
+ * @param re The reason of the xline
+ */
+ XLine(time_t s_time, long d, const char* src, const char* re)
+ : set_time(s_time), duration(d)
+ {
+ source = strdup(src);
+ reason = strdup(re);
+ expiry = set_time + duration;
+ }
+
+ /** Destructor
+ */
+ virtual ~XLine()
+ {
+ free(reason);
+ free(source);
+ }
+ /** The time the line was added.
+ */
+ time_t set_time;
+
+ /** The duration of the ban, or 0 if permenant
+ */
+ long duration;
+
+ /** Source of the ban. This can be a servername or an oper nickname
+ */
+ char* source;
+
+ /** Reason for the ban
+ */
+ char* reason;
+
+ /** Expiry time
+ */
+ time_t expiry;
+};
+
+/** KLine class
+ */
+class CoreExport KLine : public XLine
+{
+ public:
+ /** Create a K-Line.
+ * @param s_time The set time
+ * @param d The duration of the xline
+ * @param src The sender of the xline
+ * @param re The reason of the xline
+ * @param ident Ident to match
+ * @param host Host to match
+ */
+ KLine(time_t s_time, long d, const char* src, const char* re, const char* ident, const char* host) : XLine(s_time, d, src, re)
+ {
+ identmask = strdup(ident);
+ hostmask = strdup(host);
+ }
+
+ /** Destructor
+ */
+ ~KLine()
+ {
+ free(identmask);
+ free(hostmask);
+ }
+
+ /** Ident mask
+ */
+ char* identmask;
+ /** Host mask
+ */
+ char* hostmask;
+};
+
+/** GLine class
+ */
+class CoreExport GLine : public XLine
+{
+ public:
+ /** Create a G-Line.
+ * @param s_time The set time
+ * @param d The duration of the xline
+ * @param src The sender of the xline
+ * @param re The reason of the xline
+ * @param ident Ident to match
+ * @param host Host to match
+ */
+ GLine(time_t s_time, long d, const char* src, const char* re, const char* ident, const char* host) : XLine(s_time, d, src, re)
+ {
+ identmask = strdup(ident);
+ hostmask = strdup(host);
+ }
+
+ /** Destructor
+ */
+ ~GLine()
+ {
+ free(identmask);
+ free(hostmask);
+ }
+
+ /** Ident mask
+ */
+ char* identmask;
+ /** Host mask
+ */
+ char* hostmask;
+};
+
+/** ELine class
+ */
+class CoreExport ELine : public XLine
+{
+ public:
+ /** Create an E-Line.
+ * @param s_time The set time
+ * @param d The duration of the xline
+ * @param src The sender of the xline
+ * @param re The reason of the xline
+ * @param ident Ident to match
+ * @param host Host to match
+ */
+ ELine(time_t s_time, long d, const char* src, const char* re, const char* ident, const char* host) : XLine(s_time, d, src, re)
+ {
+ identmask = strdup(ident);
+ hostmask = strdup(host);
+ }
+
+ ~ELine()
+ {
+ free(identmask);
+ free(hostmask);
+ }
+
+ /** Ident mask
+ */
+ char* identmask;
+ /** Host mask
+ */
+ char* hostmask;
+};
+
+/** ZLine class
+ */
+class CoreExport ZLine : public XLine
+{
+ public:
+ /** Create a Z-Line.
+ * @param s_time The set time
+ * @param d The duration of the xline
+ * @param src The sender of the xline
+ * @param re The reason of the xline
+ * @param ip IP to match
+ */
+ ZLine(time_t s_time, long d, const char* src, const char* re, const char* ip) : XLine(s_time, d, src, re)
+ {
+ ipaddr = strdup(ip);
+ }
+
+ /** Destructor
+ */
+ ~ZLine()
+ {
+ free(ipaddr);
+ }
+
+ /** IP mask
+ */
+ char* ipaddr;
+};
+
+/** QLine class
+ */
+class CoreExport QLine : public XLine
+{
+ public:
+ /** Create a G-Line.
+ * @param s_time The set time
+ * @param d The duration of the xline
+ * @param src The sender of the xline
+ * @param re The reason of the xline
+ * @param nickname Nickname to match
+ */
+ QLine(time_t s_time, long d, const char* src, const char* re, const char* nickname) : XLine(s_time, d, src, re)
+ {
+ nick = strdup(nickname);
+ }
+
+ /** Destructor
+ */
+ ~QLine()
+ {
+ free(nick);
+ }
+
+ /** Nickname mask
+ */
+ char* nick;
+};
+
+/* Required forward declarations
+ */
+class ServerConfig;
+class InspIRCd;
+
+/** Initialize x line
+ */
+bool InitXLine(ServerConfig* conf, const char* tag);
+
+/** Done adding zlines from the config
+ */
+bool DoneZLine(ServerConfig* conf, const char* tag);
+/** Done adding qlines from the config
+ */
+bool DoneQLine(ServerConfig* conf, const char* tag);
+/** Done adding klines from the config
+ */
+bool DoneKLine(ServerConfig* conf, const char* tag);
+/** Done adding elines from the config
+ */
+bool DoneELine(ServerConfig* conf, const char* tag);
+
+/** Add a config-defined zline
+ */
+bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types);
+/** Add a config-defined qline
+ */
+bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types);
+/** Add a config-defined kline
+ */
+bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types);
+/** Add a config-defined eline
+ */
+bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types);
+
+/** Contains an ident and host split into two strings
+ */
+typedef std::pair<std::string, std::string> IdentHostPair;
+
+/** XLineManager is a class used to manage glines, klines, elines, zlines and qlines.
+ */
+class CoreExport XLineManager
+{
+ protected:
+ /** The owner/creator of this class
+ */
+ InspIRCd* ServerInstance;
+
+ /** This functor is used by the std::sort() function to keep glines in order
+ */
+ static bool GSortComparison ( const GLine* one, const GLine* two );
+
+ /** This functor is used by the std::sort() function to keep elines in order
+ */
+ static bool ESortComparison ( const ELine* one, const ELine* two );
+
+ /** This functor is used by the std::sort() function to keep zlines in order
+ */
+ static bool ZSortComparison ( const ZLine* one, const ZLine* two );
+
+ /** This functor is used by the std::sort() function to keep klines in order
+ */
+ static bool KSortComparison ( const KLine* one, const KLine* two );
+
+ /** This functor is used by the std::sort() function to keep qlines in order
+ */
+ static bool QSortComparison ( const QLine* one, const QLine* two );
+ public:
+ /* Lists for temporary lines with an expiry time */
+
+ /** Temporary KLines */
+ std::vector<KLine*> klines;
+
+ /** Temporary Glines */
+ std::vector<GLine*> glines;
+
+ /** Temporary Zlines */
+ std::vector<ZLine*> zlines;
+
+ /** Temporary QLines */
+ std::vector<QLine*> qlines;
+
+ /** Temporary ELines */
+ std::vector<ELine*> elines;
+
+ /* Seperate lists for perm XLines that isnt checked by expiry functions */
+
+ /** Permenant KLines */
+ std::vector<KLine*> pklines;
+
+ /** Permenant GLines */
+ std::vector<GLine*> pglines;
+
+ /** Permenant ZLines */
+ std::vector<ZLine*> pzlines;
+
+ /** Permenant QLines */
+ std::vector<QLine*> pqlines;
+
+ /** Permenant ELines */
+ std::vector<ELine*> pelines;
+
+ /** Constructor
+ * @param Instance A pointer to the creator object
+ */
+ XLineManager(InspIRCd* Instance);
+
+ /** Split an ident and host into two seperate strings.
+ * This allows for faster matching.
+ */
+ IdentHostPair IdentSplit(const std::string &ident_and_host);
+
+ /** Add a new GLine
+ * @param duration The duration of the line
+ * @param source The source of the line
+ * @param reason The reason for the line
+ * @param hostmask The hostmask
+ * @return True if the line was added successfully
+ */
+ bool add_gline(long duration, const char* source, const char* reason, const char* hostmask);
+
+ /** Add a new QLine
+ * @param duration The duration of the line
+ * @param source The source of the line
+ * @param reason The reason for the line
+ * @param nickname The nickmask
+ * @return True if the line was added successfully
+ */
+ bool add_qline(long duration, const char* source, const char* reason, const char* nickname);
+
+ /** Add a new ZLine
+ * @param duration The duration of the line
+ * @param source The source of the line
+ * @param reason The reason for the line
+ * @param ipaddr The IP mask
+ * @return True if the line was added successfully
+ */
+ bool add_zline(long duration, const char* source, const char* reason, const char* ipaddr);
+
+ /** Add a new KLine
+ * @param duration The duration of the line
+ * @param source The source of the line
+ * @param reason The reason for the line
+ * @param hostmask The hostmask
+ * @return True if the line was added successfully
+ */
+ bool add_kline(long duration, const char* source, const char* reason, const char* hostmask);
+
+ /** Add a new ELine
+ * @param duration The duration of the line
+ * @param source The source of the line
+ * @param reason The reason for the line
+ * @param hostmask The hostmask
+ * @return True if the line was added successfully
+ */
+ bool add_eline(long duration, const char* source, const char* reason, const char* hostmask);
+
+ /** Delete a GLine
+ * @param hostmask The host to remove
+ * @param simulate If this is true, don't actually remove the line, just return
+ * @return True if the line was deleted successfully
+ */
+ bool del_gline(const char* hostmask, bool simulate = false);
+
+ /** Delete a QLine
+ * @param nickname The nick to remove
+ * @param simulate If this is true, don't actually remove the line, just return
+ * @return True if the line was deleted successfully
+ */
+ bool del_qline(const char* nickname, bool simulate = false);
+
+ /** Delete a ZLine
+ * @param ipaddr The IP to remove
+ * @param simulate If this is true, don't actually remove the line, just return
+ * @return True if the line was deleted successfully
+ */
+ bool del_zline(const char* ipaddr, bool simulate = false);
+
+ /** Delete a KLine
+ * @param hostmask The host to remove
+ * @param simulate If this is true, don't actually remove the line, just return
+ * @return True if the line was deleted successfully
+ */
+ bool del_kline(const char* hostmask, bool simulate = false);
+
+ /** Delete a ELine
+ * @param hostmask The host to remove
+ * @param simulate If this is true, don't actually remove the line, just return
+ * @return True if the line was deleted successfully
+ */
+ bool del_eline(const char* hostmask, bool simulate = false);
+
+ /** Check if a nickname matches a QLine
+ * @return nick The nick to check against
+ * @return The reason for the line if there is a match, or NULL if there is no match
+ */
+ QLine* matches_qline(const char* nick, bool permonly = false);
+
+ /** Check if a hostname matches a GLine
+ * @param user The user to check against
+ * @return The reason for the line if there is a match, or NULL if there is no match
+ */
+ GLine* matches_gline(userrec* user, bool permonly = false);
+
+ /** Check if a IP matches a ZLine
+ * @param ipaddr The IP to check against
+ * @return The reason for the line if there is a match, or NULL if there is no match
+ */
+ ZLine* matches_zline(const char* ipaddr, bool permonly = false);
+
+ /** Check if a hostname matches a KLine
+ * @param user The user to check against
+ * @return The reason for the line if there is a match, or NULL if there is no match
+ */
+ KLine* matches_kline(userrec* user, bool permonly = false);
+
+ /** Check if a hostname matches a ELine
+ * @param user The user to check against
+ * @return The reason for the line if there is a match, or NULL if there is no match
+ */
+ ELine* matches_exception(userrec* user, bool permonly = false);
+
+ /** Expire any pending non-permenant lines
+ */
+ void expire_lines();
+
+ /** Apply any new lines
+ * @param What The types of lines to apply, from the set
+ * APPLY_GLINES | APPLY_KLINES | APPLY_QLINES | APPLY_ZLINES | APPLY_ALL
+ * | APPLY_LOCAL_ONLY
+ */
+ void apply_lines(const int What);
+
+ /** Handle /STATS K
+ * @param user The username making the query
+ * @param results The string_list to receive the results
+ */
+ void stats_k(userrec* user, string_list &results);
+
+ /** Handle /STATS G
+ * @param user The username making the query
+ * @param results The string_list to receive the results
+ */
+ void stats_g(userrec* user, string_list &results);
+
+ /** Handle /STATS Q
+ * @param user The username making the query
+ * @param results The string_list to receive the results
+ */
+ void stats_q(userrec* user, string_list &results);
+
+ /** Handle /STATS Z
+ * @param user The username making the query
+ * @param results The string_list to receive the results
+ */
+ void stats_z(userrec* user, string_list &results);
+
+ /** Handle /STATS E
+ * @param user The username making the query
+ * @param results The string_list to receive the results
+ */
+ void stats_e(userrec* user, string_list &results);
+
+ /** Change creation time of a GLine
+ * @param host The hostname to change
+ * @param create_Time The new creation time
+ */
+ void gline_set_creation_time(const char* host, time_t create_time);
+
+ /** Change creation time of a QLine
+ * @param nick The nickmask to change
+ * @param create_Time The new creation time
+ */
+ void qline_set_creation_time(const char* nick, time_t create_time);
+
+ /** Change creation time of a ZLine
+ * @param ip The ipmask to change
+ * @param create_Time The new creation time
+ */
+ void zline_set_creation_time(const char* ip, time_t create_time);
+
+ /** Change creation time of a ELine
+ * @param host The hostname to change
+ * @param create_Time The new creation time
+ */
+ void eline_set_creation_time(const char* host, time_t create_time);
+};
+
+#endif
+
diff --git a/make/configure.pm b/make/configure.pm
index 3e1a38333..b384ac109 100644
--- a/make/configure.pm
+++ b/make/configure.pm
@@ -1 +1,282 @@
-# # Copyright 2002-2007 The ChatSpike Development Team # <brain@chatspike.net> # <Craig@chatspike.net> # # Licensed under GPL, please see the COPYING file # for more information # package make::configure; use Exporter 'import'; use POSIX; use make::utilities; @EXPORT = qw(promptnumeric dumphash is_dir getmodules getrevision getcompilerflags getlinkerflags getdependencies resolve_directory yesno showhelp promptstring_s); my $no_svn = 0; sub yesno { my ($flag,$prompt) = @_; print "$prompt [\033[1;32m$main::config{$flag}\033[0m] -> "; chomp($tmp = <STDIN>); if ($tmp eq "") { $tmp = $main::config{$flag} } if (($tmp eq "") || ($tmp =~ /^y/i)) { $main::config{$flag} = "y"; } else { $main::config{$flag} = "n"; } return; } sub resolve_directory { my $ret = $_[0]; eval { use File::Spec; $ret = File::Spec->rel2abs($_[0]); }; return $ret; } sub getrevision { if ($no_svn) { return "0"; } my $data = `svn info`; if ($data eq "") { $no_svn = 1; $rev = "0"; return $rev; } $data =~ /Revision: (\d+)/; my $rev = $1; if (!defined($rev)) { $rev = "0"; } return $rev; } sub getcompilerflags { my ($file) = @_; open(FLAGS, $file); while (<FLAGS>) { if ($_ =~ /^\/\* \$CompileFlags: (.+) \*\/$/) { close(FLAGS); return translate_functions($1,$file); } } close(FLAGS); return undef; } sub getlinkerflags { my ($file) = @_; open(FLAGS, $file); while (<FLAGS>) { if ($_ =~ /^\/\* \$LinkerFlags: (.+) \*\/$/) { close(FLAGS); return translate_functions($1,$file); } } close(FLAGS); return undef; } sub getdependencies { my ($file) = @_; open(FLAGS, $file); while (<FLAGS>) { if ($_ =~ /^\/\* \$ModDep: (.+) \*\/$/) { close(FLAGS); return translate_functions($1,$file); } } close(FLAGS); return undef; } sub getmodules { my $i = 0; print "Detecting modules "; opendir(DIRHANDLE, "src/modules"); foreach $name (sort readdir(DIRHANDLE)) { if ($name =~ /^m_(.+)\.cpp$/) { $mod = $1; if ($mod !~ /_static$/) { $main::modlist[$i++] = $mod; print "."; } } } closedir(DIRHANDLE); print "\nOk, $i modules.\n"; } sub promptnumeric($$) { my $continue = 0; my ($prompt, $configitem) = @_; while (!$continue) { print "Please enter the maximum $prompt?\n"; print "[\033[1;32m$main::config{$configitem}\033[0m] -> "; chomp($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 promptstring_s($$) { my ($prompt,$default) = @_; my $var; print "$prompt\n"; print "[\033[1;32m$default\033[0m] -> "; chomp($var = <STDIN>); $var = $default if $var eq ""; print "\n"; return $var; } sub dumphash() { print "\n\033[1;32mPre-build configuration is complete!\033[0m\n\n"; print "\033[0mBase install path:\033[1;32m\t\t$main::config{BASE_DIR}\033[0m\n"; print "\033[0mConfig path:\033[1;32m\t\t\t$main::config{CONFIG_DIR}\033[0m\n"; print "\033[0mModule path:\033[1;32m\t\t\t$main::config{MODULE_DIR}\033[0m\n"; print "\033[0mLibrary path:\033[1;32m\t\t\t$main::config{LIBRARY_DIR}\033[0m\n"; print "\033[0mMax connections:\033[1;32m\t\t$main::config{MAX_CLIENT}\033[0m\n"; print "\033[0mMax nickname length:\033[1;32m\t\t$main::config{NICK_LENGT}\033[0m\n"; print "\033[0mMax channel length:\033[1;32m\t\t$main::config{CHAN_LENGT}\033[0m\n"; print "\033[0mMax mode length:\033[1;32m\t\t$main::config{MAXI_MODES}\033[0m\n"; print "\033[0mMax ident length:\033[1;32m\t\t$main::config{MAX_IDENT}\033[0m\n"; print "\033[0mMax quit length:\033[1;32m\t\t$main::config{MAX_QUIT}\033[0m\n"; print "\033[0mMax topic length:\033[1;32m\t\t$main::config{MAX_TOPIC}\033[0m\n"; print "\033[0mMax kick length:\033[1;32m\t\t$main::config{MAX_KICK}\033[0m\n"; print "\033[0mMax name length:\033[1;32m\t\t$main::config{MAX_GECOS}\033[0m\n"; print "\033[0mMax away length:\033[1;32m\t\t$main::config{MAX_AWAY}\033[0m\n"; print "\033[0mGCC Version Found:\033[1;32m\t\t$main::config{GCCVER}.x\033[0m\n"; print "\033[0mCompiler program:\033[1;32m\t\t$main::config{CC}\033[0m\n"; print "\033[0mStatic modules:\033[1;32m\t\t\t$main::config{STATIC_LINK}\033[0m\n"; print "\033[0mIPv6 Support:\033[1;32m\t\t\t$main::config{IPV6}\033[0m\n"; print "\033[0mIPv6 to IPv4 Links:\033[1;32m\t\t$main::config{SUPPORT_IP6LINKS}\033[0m\n"; print "\033[0mGnuTLS Support:\033[1;32m\t\t\t$main::config{USE_GNUTLS}\033[0m\n"; print "\033[0mOpenSSL Support:\033[1;32m\t\t$main::config{USE_OPENSSL}\033[0m\n\n"; } sub is_dir { my ($path) = @_; if (chdir($path)) { chdir($main::this); return 1; } else { # Just in case.. chdir($main::this); return 0; } } sub showhelp { chomp($PWD = `pwd`); print "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 intself, but will disable any interactive prompting. --update Update makefiles and dependencies --modupdate Detect new modules and write makefiles --svnupdate {--rebuild} Update working copy via subversion {and optionally rebuild if --rebuild is also specified} --clean Remove .config.cache file and go interactive --enable-gnutls Enable GnuTLS module [no] --enable-openssl Enable OpenSSL module [no] --with-nick-length=[n] Specify max. nick length [32] --with-channel-length=[n] Specify max. channel length [64] --with-max-clients=[n] Specify maximum number of users which may connect locally --enable-optimization=[n] Optimize using -O[n] gcc flag --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] --enable-ipv6 Build ipv6 native InspIRCd [no] --enable-remote-ipv6 Build with ipv6 support for remote servers on the network [yes] --disable-remote-ipv6 Do not allow remote ipv6 servers [not set] --with-cc=[filename] Use an alternative g++ binary to build InspIRCd [g++] --with-ident-length=[n] Specify max length of ident [12] --with-quit-length=[n] Specify max length of quit [200] --with-topic-length=[n] Specify max length of topic [350] --with-kick-length=[n] Specify max length of kick [200] --with-gecos-length=[n] Specify max length of gecos [150] --with-away-length=[n] Specify max length of away [150] --with-max-modes=[n] Specify max modes per line which have parameters [20] --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/conf] --module-dir=[directory] Modules directory for loadable modules [$PWD/modules] --binary-dir=[directory] Binaries directory for core binary [$PWD/bin] --library-dir=[directory] Library directory for core libraries [$PWD/lib] --help Show this help text and exit "; exit(0); } 1; \ No newline at end of file
+#
+# Copyright 2002-2007 The ChatSpike Development Team
+# <brain@chatspike.net>
+# <Craig@chatspike.net>
+#
+# Licensed under GPL, please see the COPYING file
+# for more information
+#
+
+package make::configure;
+use Exporter 'import';
+use POSIX;
+use make::utilities;
+@EXPORT = qw(promptnumeric dumphash is_dir getmodules getrevision getcompilerflags getlinkerflags getdependencies resolve_directory yesno showhelp promptstring_s);
+
+my $no_svn = 0;
+
+sub yesno {
+ my ($flag,$prompt) = @_;
+ print "$prompt [\033[1;32m$main::config{$flag}\033[0m] -> ";
+ chomp($tmp = <STDIN>);
+ if ($tmp eq "") { $tmp = $main::config{$flag} }
+ if (($tmp eq "") || ($tmp =~ /^y/i))
+ {
+ $main::config{$flag} = "y";
+ }
+ else
+ {
+ $main::config{$flag} = "n";
+ }
+ return;
+}
+
+sub resolve_directory
+{
+ my $ret = $_[0];
+ eval
+ {
+ use File::Spec;
+ $ret = File::Spec->rel2abs($_[0]);
+ };
+ return $ret;
+}
+
+sub getrevision {
+ if ($no_svn)
+ {
+ return "0";
+ }
+ my $data = `svn info`;
+ if ($data eq "")
+ {
+ $no_svn = 1;
+ $rev = "0";
+ return $rev;
+ }
+ $data =~ /Revision: (\d+)/;
+ my $rev = $1;
+ if (!defined($rev))
+ {
+ $rev = "0";
+ }
+ return $rev;
+}
+
+sub getcompilerflags {
+ my ($file) = @_;
+ open(FLAGS, $file);
+ while (<FLAGS>) {
+ if ($_ =~ /^\/\* \$CompileFlags: (.+) \*\/$/) {
+ close(FLAGS);
+ return translate_functions($1,$file);
+ }
+ }
+ close(FLAGS);
+ return undef;
+}
+
+sub getlinkerflags {
+ my ($file) = @_;
+ open(FLAGS, $file);
+ while (<FLAGS>) {
+ if ($_ =~ /^\/\* \$LinkerFlags: (.+) \*\/$/) {
+ close(FLAGS);
+ return translate_functions($1,$file);
+ }
+ }
+ close(FLAGS);
+ return undef;
+}
+
+sub getdependencies {
+ my ($file) = @_;
+ open(FLAGS, $file);
+ while (<FLAGS>) {
+ if ($_ =~ /^\/\* \$ModDep: (.+) \*\/$/) {
+ close(FLAGS);
+ return translate_functions($1,$file);
+ }
+ }
+ close(FLAGS);
+ return undef;
+}
+
+
+sub getmodules
+{
+ my $i = 0;
+ print "Detecting modules ";
+ opendir(DIRHANDLE, "src/modules");
+ foreach $name (sort readdir(DIRHANDLE))
+ {
+ if ($name =~ /^m_(.+)\.cpp$/)
+ {
+ $mod = $1;
+ if ($mod !~ /_static$/)
+ {
+ $main::modlist[$i++] = $mod;
+ print ".";
+ }
+ }
+ }
+ closedir(DIRHANDLE);
+ print "\nOk, $i modules.\n";
+}
+
+sub promptnumeric($$)
+{
+ my $continue = 0;
+ my ($prompt, $configitem) = @_;
+ while (!$continue)
+ {
+ print "Please enter the maximum $prompt?\n";
+ print "[\033[1;32m$main::config{$configitem}\033[0m] -> ";
+ chomp($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 promptstring_s($$)
+{
+ my ($prompt,$default) = @_;
+ my $var;
+ print "$prompt\n";
+ print "[\033[1;32m$default\033[0m] -> ";
+ chomp($var = <STDIN>);
+ $var = $default if $var eq "";
+ print "\n";
+ return $var;
+}
+
+sub dumphash()
+{
+ print "\n\033[1;32mPre-build configuration is complete!\033[0m\n\n";
+ print "\033[0mBase install path:\033[1;32m\t\t$main::config{BASE_DIR}\033[0m\n";
+ print "\033[0mConfig path:\033[1;32m\t\t\t$main::config{CONFIG_DIR}\033[0m\n";
+ print "\033[0mModule path:\033[1;32m\t\t\t$main::config{MODULE_DIR}\033[0m\n";
+ print "\033[0mLibrary path:\033[1;32m\t\t\t$main::config{LIBRARY_DIR}\033[0m\n";
+ print "\033[0mMax connections:\033[1;32m\t\t$main::config{MAX_CLIENT}\033[0m\n";
+ print "\033[0mMax nickname length:\033[1;32m\t\t$main::config{NICK_LENGT}\033[0m\n";
+ print "\033[0mMax channel length:\033[1;32m\t\t$main::config{CHAN_LENGT}\033[0m\n";
+ print "\033[0mMax mode length:\033[1;32m\t\t$main::config{MAXI_MODES}\033[0m\n";
+ print "\033[0mMax ident length:\033[1;32m\t\t$main::config{MAX_IDENT}\033[0m\n";
+ print "\033[0mMax quit length:\033[1;32m\t\t$main::config{MAX_QUIT}\033[0m\n";
+ print "\033[0mMax topic length:\033[1;32m\t\t$main::config{MAX_TOPIC}\033[0m\n";
+ print "\033[0mMax kick length:\033[1;32m\t\t$main::config{MAX_KICK}\033[0m\n";
+ print "\033[0mMax name length:\033[1;32m\t\t$main::config{MAX_GECOS}\033[0m\n";
+ print "\033[0mMax away length:\033[1;32m\t\t$main::config{MAX_AWAY}\033[0m\n";
+ print "\033[0mGCC Version Found:\033[1;32m\t\t$main::config{GCCVER}.x\033[0m\n";
+ print "\033[0mCompiler program:\033[1;32m\t\t$main::config{CC}\033[0m\n";
+ print "\033[0mStatic modules:\033[1;32m\t\t\t$main::config{STATIC_LINK}\033[0m\n";
+ print "\033[0mIPv6 Support:\033[1;32m\t\t\t$main::config{IPV6}\033[0m\n";
+ print "\033[0mIPv6 to IPv4 Links:\033[1;32m\t\t$main::config{SUPPORT_IP6LINKS}\033[0m\n";
+ print "\033[0mGnuTLS Support:\033[1;32m\t\t\t$main::config{USE_GNUTLS}\033[0m\n";
+ print "\033[0mOpenSSL Support:\033[1;32m\t\t$main::config{USE_OPENSSL}\033[0m\n\n";
+}
+
+sub is_dir
+{
+ my ($path) = @_;
+ if (chdir($path))
+ {
+ chdir($main::this);
+ return 1;
+ }
+ else
+ {
+ # Just in case..
+ chdir($main::this);
+ return 0;
+ }
+}
+
+sub showhelp
+{
+ chomp($PWD = `pwd`);
+ print "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 intself, but
+ will disable any interactive prompting.
+ --update Update makefiles and dependencies
+ --modupdate Detect new modules and write makefiles
+ --svnupdate {--rebuild} Update working copy via subversion
+ {and optionally rebuild if --rebuild
+ is also specified}
+ --clean Remove .config.cache file and go interactive
+ --enable-gnutls Enable GnuTLS module [no]
+ --enable-openssl Enable OpenSSL module [no]
+ --with-nick-length=[n] Specify max. nick length [32]
+ --with-channel-length=[n] Specify max. channel length [64]
+ --with-max-clients=[n] Specify maximum number of users
+ which may connect locally
+ --enable-optimization=[n] Optimize using -O[n] gcc flag
+ --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]
+ --enable-ipv6 Build ipv6 native InspIRCd [no]
+ --enable-remote-ipv6 Build with ipv6 support for remote
+ servers on the network [yes]
+ --disable-remote-ipv6 Do not allow remote ipv6 servers [not set]
+ --with-cc=[filename] Use an alternative g++ binary to
+ build InspIRCd [g++]
+ --with-ident-length=[n] Specify max length of ident [12]
+ --with-quit-length=[n] Specify max length of quit [200]
+ --with-topic-length=[n] Specify max length of topic [350]
+ --with-kick-length=[n] Specify max length of kick [200]
+ --with-gecos-length=[n] Specify max length of gecos [150]
+ --with-away-length=[n] Specify max length of away [150]
+ --with-max-modes=[n] Specify max modes per line which
+ have parameters [20]
+ --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/conf]
+ --module-dir=[directory] Modules directory for loadable modules
+ [$PWD/modules]
+ --binary-dir=[directory] Binaries directory for core binary
+ [$PWD/bin]
+ --library-dir=[directory] Library directory for core libraries
+ [$PWD/lib]
+ --help Show this help text and exit
+
+";
+ exit(0);
+}
+
+1;
+
diff --git a/make/gnutlscert.pm b/make/gnutlscert.pm
index 885a30603..517e08b1f 100644
--- a/make/gnutlscert.pm
+++ b/make/gnutlscert.pm
@@ -1 +1,115 @@
-package make::gnutlscert; use Exporter 'import'; use make::configure; @EXPORT = qw(make_gnutls_cert); sub make_gnutls_cert() { open (FH, ">certtool.template"); my $timestr = time(); my $org = promptstring_s("Please enter the organization name", "My IRC Network"); my $unit = promptstring_s("Please enter the unit Name", "Server Admins"); my $state = promptstring_s("Pleae enter your state (two letter code)", "CA"); my $country = promptstring_s("Please enter your country", "Oompa Loompa Land"); my $commonname = promptstring_s("Please enter the certificate common name (hostname)", "irc.mynetwork.com"); my $email = promptstring_s("Please enter a contact email address", "oompa\@loompa.com"); 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 = # 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 = 700 # 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); system("certtool --generate-privkey --outfile key.pem") or return 1; system("certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template certtool.template") or return 1; unlink("certtool.template"); } 1; \ No newline at end of file
+package make::gnutlscert;
+
+use Exporter 'import';
+use make::configure;
+@EXPORT = qw(make_gnutls_cert);
+
+
+sub make_gnutls_cert()
+{
+ open (FH, ">certtool.template");
+ my $timestr = time();
+ my $org = promptstring_s("Please enter the organization name", "My IRC Network");
+ my $unit = promptstring_s("Please enter the unit Name", "Server Admins");
+ my $state = promptstring_s("Pleae enter your state (two letter code)", "CA");
+ my $country = promptstring_s("Please enter your country", "Oompa Loompa Land");
+ my $commonname = promptstring_s("Please enter the certificate common name (hostname)", "irc.mynetwork.com");
+ my $email = promptstring_s("Please enter a contact email address", "oompa\@loompa.com");
+ 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 =
+
+# 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 = 700
+
+# 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);
+system("certtool --generate-privkey --outfile key.pem") or return 1;
+system("certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template certtool.template") or return 1;
+unlink("certtool.template");
+}
+
+1;
+
diff --git a/make/opensslcert.pm b/make/opensslcert.pm
index cb94869f9..a4954e0be 100644
--- a/make/opensslcert.pm
+++ b/make/opensslcert.pm
@@ -1 +1,36 @@
-package make::opensslcert; use Exporter 'import'; use make::configure; @EXPORT = qw(make_openssl_cert); sub make_openssl_cert() { open (FH, ">openssl.template"); my $org = promptstring_s("Please enter the organization name", "My IRC Network"); my $unit = promptstring_s("Please enter the unit Name", "Server Admins"); my $country = promptstring_s("Please enter your country (two letter code)", "US"); my $state = promptstring_s("Please enter your state or locality name", "Alaska"); my $city = promptstring_s("Please enter your city", "Factory Town"); my $email = promptstring_s("Please enter a contact email address", "oompa\@loompa.com"); my $commonname = promptstring_s("Please enter the common name (domain name) of the irc server", "example.inspircd.org"); print FH <<__END__; $country $state $city $org $unit $commonname $email __END__ close(FH); my $time = promptstring_s("Please enter the number of days that this certificate is valid for","365"); system("cat openssl.template | openssl req -x509 -nodes -newkey rsa:1024 -keyout key.pem -out cert.pem -days $time 2>/dev/null"); system("openssl dhparam -out dhparams.pem 1024"); unlink("openssl.template"); } 1; \ No newline at end of file
+package make::opensslcert;
+
+use Exporter 'import';
+use make::configure;
+@EXPORT = qw(make_openssl_cert);
+
+
+sub make_openssl_cert()
+{
+ open (FH, ">openssl.template");
+ my $org = promptstring_s("Please enter the organization name", "My IRC Network");
+ my $unit = promptstring_s("Please enter the unit Name", "Server Admins");
+ my $country = promptstring_s("Please enter your country (two letter code)", "US");
+ my $state = promptstring_s("Please enter your state or locality name", "Alaska");
+ my $city = promptstring_s("Please enter your city", "Factory Town");
+ my $email = promptstring_s("Please enter a contact email address", "oompa\@loompa.com");
+ my $commonname = promptstring_s("Please enter the common name (domain name) of the irc server", "example.inspircd.org");
+ print FH <<__END__;
+$country
+$state
+$city
+$org
+$unit
+$commonname
+$email
+__END__
+close(FH);
+
+my $time = promptstring_s("Please enter the number of days that this certificate is valid for","365");
+
+system("cat openssl.template | openssl req -x509 -nodes -newkey rsa:1024 -keyout key.pem -out cert.pem -days $time 2>/dev/null");
+system("openssl dhparam -out dhparams.pem 1024");
+unlink("openssl.template");
+}
+
+1;
diff --git a/make/utilities.pm b/make/utilities.pm
index 873797f80..c00a541b3 100644
--- a/make/utilities.pm
+++ b/make/utilities.pm
@@ -1 +1,383 @@
-# # Copyright 2002-2007 The ChatSpike Development Team # <brain@chatspike.net> # <Craig@chatspike.net> # # Licensed under GPL, please see the COPYING file # for more information # package make::utilities; use Exporter 'import'; use POSIX; use Getopt::Long; @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring vcheck); # Parse the output of a *_config program, # such as pcre_config, take out the -L # directive and return an rpath for it. # \033[1;32msrc/Makefile\033[0m my %already_added = (); sub promptstring($$$$$) { my ($prompt, $configitem, $default, $package, $commandlineswitch) = @_; my $var; if (!$main::interactive) { undef $opt_commandlineswitch; GetOptions ("$commandlineswitch=s" => \$opt_commandlineswitch); if (defined $opt_commandlineswitch) { print "\033[1;32m$opt_commandlineswitch\033[0m\n"; $var = $opt_commandlineswitch; } else { die "Could not detect $package! Please specify the $prompt via the command line option \033[1;32m--$commandlineswitch=\"/path/to/file\"\033[0m"; } } else { print "\nPlease enter the $prompt?\n"; print "[\033[1;32m$default\033[0m] -> "; chomp($var = <STDIN>); } if ($var eq "") { $var = $default; } $main::config{$configitem} = $var; } sub make_rpath($;$) { my ($executable, $module) = @_; chomp($data = `$executable`); my $output = ""; while ($data =~ /-L(\S+)/) { $libpath = $1; if (!exists $already_added{$libpath}) { print "Adding extra library path to \033[1;32m$module\033[0m ... \033[1;32m$libpath\033[0m\n"; $already_added{$libpath} = 1; } $output .= "-Wl,--rpath -Wl,$libpath -L$libpath "; $data =~ s/-L(\S+)//; } return $output; } sub extend_pkg_path() { if (!exists $ENV{PKG_CONFIG_PATH}) { $ENV{PKG_CONFIG_PATH} = "/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig"; } else { $ENV{PKG_CONFIG_PATH} .= ":/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig"; } } sub pkgconfig_get_include_dirs($$$;$) { my ($packagename, $headername, $defaults, $module) = @_; my $key = "default_includedir_$packagename"; if (exists $main::config{$key}) { print "Locating include directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... "; $ret = $main::config{$key}; print "\033[1;32m$ret\033[0m (cached)\n"; return $ret; } extend_pkg_path(); print "Locating include directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... "; $v = `pkg-config --modversion $packagename 2>/dev/null`; $ret = `pkg-config --cflags $packagename 2>/dev/null`; if ((!defined $v) || ($v eq "")) { $foo = `locate "$headername" | head -n 1`; $foo =~ /(.+)\Q$headername\E/; $find = $1; chomp($find); if ((defined $find) && ($find ne "") && ($find ne $packagename)) { print "(\033[1;32mFound via search\033[0m) "; $foo = "-I$1"; } else { $foo = " "; undef $v; } $ret = "$foo"; } if (($defaults ne "") && (($ret eq "") || (!defined $ret))) { $ret = "$foo " . $defaults; } chomp($ret); if ((($ret eq " ") || (!defined $ret)) && ((!defined $v) || ($v eq ""))) { my $key = "default_includedir_$packagename"; if (exists $main::config{$key}) { $ret = $main::config{$key}; } else { $headername =~ s/^\///; promptstring("path to the directory containing $headername", $key, "/usr/include",$packagename,"$packagename-includes"); $packagename =~ tr/a-z/A-Z/; $main::config{$key} = "-I$main::config{$key}" . " $defaults -DVERSION_$packagename=\"$v\""; $main::config{$key} =~ s/^\s+//g; $ret = $main::config{$key}; return $ret; } } else { chomp($v); my $key = "default_includedir_$packagename"; $packagename =~ tr/a-z/A-Z/; $main::config{$key} = "$ret -DVERSION_$packagename=\"$v\""; $main::config{$key} =~ s/^\s+//g; $ret = $main::config{$key}; print "\033[1;32m$ret\033[0m (version $v)\n"; } $ret =~ s/^\s+//g; return $ret; } sub vcheck($$) { my ($version1, $version2) = @_; $version1 =~ s/\-r(\d+)/\.\1/g; # minor revs/patchlevels $version2 =~ s/\-r(\d+)/\.\1/g; $version1 =~ s/p(\d+)/\.\1/g; $version2 =~ s/p(\d+)/\.\1/g; $version1 =~ s/\-//g; $version2 =~ s/\-//g; $version1 =~ s/a-z//g; $version2 =~ s/a-z//g; my @v1 = split('\.', $version1); my @v2 = split('\.', $version2); for ($curr = 0; $curr < scalar(@v1); $curr++) { if ($v1[$curr] < $v2[$curr]) { return 0; } } return 1; } sub pkgconfig_check_version($$;$) { my ($packagename, $version, $module) = @_; extend_pkg_path(); print "Checking version of package \033[1;32m$packagename\033[0m is >= \033[1;32m$version\033[0m... "; $v = `pkg-config --modversion $packagename 2>/dev/null`; if (defined $v) { chomp($v); } if ((defined $v) && ($v ne "")) { if (vcheck($v,$version) == 1) { print "\033[1;32mYes (version $v)\033[0m\n"; return 1; } else { print "\033[1;32mNo (version $v)\033[0m\n"; return 0; } } # If we didnt find it, we cant definitively say its too old. # Return ok, and let pkgconflibs() or pkgconfincludes() pick up # the missing library later on. print "\033[1;32mNo (not found)\033[0m\n"; return 1; } sub pkgconfig_get_lib_dirs($$$;$) { my ($packagename, $libname, $defaults, $module) = @_; my $key = "default_libdir_$packagename"; if (exists $main::config{$key}) { print "Locating library directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... "; $ret = $main::config{$key}; print "\033[1;32m$ret\033[0m (cached)\n"; return $ret; } extend_pkg_path(); print "Locating library directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... "; $v = `pkg-config --modversion $packagename 2>/dev/null`; $ret = `pkg-config --libs $packagename 2>/dev/null`; if ((!defined $v) || ($v eq "")) { $foo = `locate "$libname" | head -n 1`; $foo =~ /(.+)\Q$libname\E/; $find = $1; chomp($find); if ((defined $find) && ($find ne "") && ($find ne $packagename)) { print "(\033[1;32mFound via search\033[0m) "; $foo = "-L$1"; } else { $foo = " "; undef $v; } $ret = "$foo"; } if (($defaults ne "") && (($ret eq "") || (!defined $ret))) { $ret = "$foo " . $defaults; } chomp($ret); if ((($ret eq " ") || (!defined $ret)) && ((!defined $v) || ($v eq ""))) { my $key = "default_libdir_$packagename"; if (exists $main::config{$key}) { $ret = $main::config{$key}; } else { $libname =~ s/^\///; promptstring("path to the directory containing $libname", $key, "/usr/lib",$packagename,"$packagename-libs"); $main::config{$key} = "-L$main::config{$key}" . " $defaults"; $main::config{$key} =~ s/^\s+//g; $ret = $main::config{$key}; return $ret; } } else { chomp($v); print "\033[1;32m$ret\033[0m (version $v)\n"; my $key = "default_libdir_$packagename"; $main::config{$key} = $ret; $main::config{$key} =~ s/^\s+//g; $ret =~ s/^\s+//g; } $ret =~ s/^\s+//g; return $ret; } # Translate a $CompileFlags etc line and parse out function calls # to functions within these modules at configure time. sub translate_functions($$) { my ($line,$module) = @_; eval { $module =~ /modules*\/(.+?)$/; $module = $1; # This is only a cursory check, just designed to catch casual accidental use of backticks. # There are pleanty of ways around it, but its not supposed to be for security, just checking # that people are using the new configuration api as theyre supposed to and not just using # backticks instead of eval(), being as eval has accountability. People wanting to get around # the accountability will do so anyway. if (($line =~ /`/) && ($line !~ /eval\(.+?`.+?\)/)) { die "Developers should no longer use backticks in configuration macros. Please use exec() and eval() macros instead. Offending line: $line (In module: $module)"; } while ($line =~ /exec\("(.+?)"\)/) { print "Executing program for module \033[1;32m$module\033[0m ... \033[1;32m$1\033[0m\n"; my $replace = `$1`; chomp($replace); $line =~ s/exec\("(.+?)"\)/$replace/; } while ($line =~ /eval\("(.+?)"\)/) { print "Evaluating perl code for module \033[1;32m$module\033[0m ... "; my $tmpfile; do { $tmpfile = tmpnam(); } until sysopen(TF, $tmpfile, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0700); print "(Created and executed \033[1;32m$tmpfile\033[0m)\n"; print TF $1; close TF; my $replace = `perl $tmpfile`; chomp($replace); $line =~ s/eval\("(.+?)"\)/$replace/; } while ($line =~ /pkgconflibs\("(.+?)","(.+?)","(.+?)"\)/) { my $replace = pkgconfig_get_lib_dirs($1, $2, $3, $module); $line =~ s/pkgconflibs\("(.+?)","(.+?)","(.+?)"\)/$replace/; } while ($line =~ /pkgconfversion\("(.+?)","(.+?)"\)/) { if (pkgconfig_check_version($1, $2, $module) != 1) { die "Version of package $1 is too old. Please upgrade it to version \033[1;32m$2\033[0m or greater and try again."; } # This doesnt actually get replaced with anything $line =~ s/pkgconfversion\("(.+?)","(.+?)"\)//; } while ($line =~ /pkgconflibs\("(.+?)","(.+?)",""\)/) { my $replace = pkgconfig_get_lib_dirs($1, $2, "", $module); $line =~ s/pkgconflibs\("(.+?)","(.+?)",""\)/$replace/; } while ($line =~ /pkgconfincludes\("(.+?)","(.+?)",""\)/) { my $replace = pkgconfig_get_include_dirs($1, $2, "", $module); $line =~ s/pkgconfincludes\("(.+?)","(.+?)",""\)/$replace/; } while ($line =~ /pkgconfincludes\("(.+?)","(.+?)","(.+?)"\)/) { my $replace = pkgconfig_get_include_dirs($1, $2, $3, $module); $line =~ s/pkgconfincludes\("(.+?)","(.+?)","(.+?)"\)/$replace/; } while ($line =~ /rpath\("(.+?)"\)/) { my $replace = make_rpath($1,$module); $replace = "" if ($^O =~ /darwin/i); $line =~ s/rpath\("(.+?)"\)/$replace/; } }; if ($@) { $err = $@; $err =~ s/at .+? line \d+.*//g; print "\n\nConfiguration failed. The following error occured:\n\n$err\n"; exit; } else { return $line; } } 1; \ No newline at end of file
+#
+# Copyright 2002-2007 The ChatSpike Development Team
+# <brain@chatspike.net>
+# <Craig@chatspike.net>
+#
+# Licensed under GPL, please see the COPYING file
+# for more information
+#
+
+package make::utilities;
+use Exporter 'import';
+use POSIX;
+use Getopt::Long;
+@EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring vcheck);
+
+# Parse the output of a *_config program,
+# such as pcre_config, take out the -L
+# directive and return an rpath for it.
+
+# \033[1;32msrc/Makefile\033[0m
+
+my %already_added = ();
+
+sub promptstring($$$$$)
+{
+ my ($prompt, $configitem, $default, $package, $commandlineswitch) = @_;
+ my $var;
+ if (!$main::interactive)
+ {
+ undef $opt_commandlineswitch;
+ GetOptions ("$commandlineswitch=s" => \$opt_commandlineswitch);
+ if (defined $opt_commandlineswitch)
+ {
+ print "\033[1;32m$opt_commandlineswitch\033[0m\n";
+ $var = $opt_commandlineswitch;
+ }
+ else
+ {
+ die "Could not detect $package! Please specify the $prompt via the command line option \033[1;32m--$commandlineswitch=\"/path/to/file\"\033[0m";
+ }
+ }
+ else
+ {
+ print "\nPlease enter the $prompt?\n";
+ print "[\033[1;32m$default\033[0m] -> ";
+ chomp($var = <STDIN>);
+ }
+ if ($var eq "")
+ {
+ $var = $default;
+ }
+ $main::config{$configitem} = $var;
+}
+
+sub make_rpath($;$)
+{
+ my ($executable, $module) = @_;
+ chomp($data = `$executable`);
+ my $output = "";
+ while ($data =~ /-L(\S+)/)
+ {
+ $libpath = $1;
+ if (!exists $already_added{$libpath})
+ {
+ print "Adding extra library path to \033[1;32m$module\033[0m ... \033[1;32m$libpath\033[0m\n";
+ $already_added{$libpath} = 1;
+ }
+ $output .= "-Wl,--rpath -Wl,$libpath -L$libpath ";
+ $data =~ s/-L(\S+)//;
+ }
+ return $output;
+}
+
+sub extend_pkg_path()
+{
+ if (!exists $ENV{PKG_CONFIG_PATH})
+ {
+ $ENV{PKG_CONFIG_PATH} = "/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig";
+ }
+ else
+ {
+ $ENV{PKG_CONFIG_PATH} .= ":/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig";
+ }
+}
+
+sub pkgconfig_get_include_dirs($$$;$)
+{
+ my ($packagename, $headername, $defaults, $module) = @_;
+
+ my $key = "default_includedir_$packagename";
+ if (exists $main::config{$key})
+ {
+ print "Locating include directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... ";
+ $ret = $main::config{$key};
+ print "\033[1;32m$ret\033[0m (cached)\n";
+ return $ret;
+ }
+
+ extend_pkg_path();
+
+ print "Locating include directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... ";
+
+ $v = `pkg-config --modversion $packagename 2>/dev/null`;
+ $ret = `pkg-config --cflags $packagename 2>/dev/null`;
+
+ if ((!defined $v) || ($v eq ""))
+ {
+ $foo = `locate "$headername" | head -n 1`;
+ $foo =~ /(.+)\Q$headername\E/;
+ $find = $1;
+ chomp($find);
+ if ((defined $find) && ($find ne "") && ($find ne $packagename))
+ {
+ print "(\033[1;32mFound via search\033[0m) ";
+ $foo = "-I$1";
+ }
+ else
+ {
+ $foo = " ";
+ undef $v;
+ }
+ $ret = "$foo";
+ }
+ if (($defaults ne "") && (($ret eq "") || (!defined $ret)))
+ {
+ $ret = "$foo " . $defaults;
+ }
+ chomp($ret);
+ if ((($ret eq " ") || (!defined $ret)) && ((!defined $v) || ($v eq "")))
+ {
+ my $key = "default_includedir_$packagename";
+ if (exists $main::config{$key})
+ {
+ $ret = $main::config{$key};
+ }
+ else
+ {
+ $headername =~ s/^\///;
+ promptstring("path to the directory containing $headername", $key, "/usr/include",$packagename,"$packagename-includes");
+ $packagename =~ tr/a-z/A-Z/;
+ $main::config{$key} = "-I$main::config{$key}" . " $defaults -DVERSION_$packagename=\"$v\"";
+ $main::config{$key} =~ s/^\s+//g;
+ $ret = $main::config{$key};
+ return $ret;
+ }
+ }
+ else
+ {
+ chomp($v);
+ my $key = "default_includedir_$packagename";
+ $packagename =~ tr/a-z/A-Z/;
+ $main::config{$key} = "$ret -DVERSION_$packagename=\"$v\"";
+ $main::config{$key} =~ s/^\s+//g;
+ $ret = $main::config{$key};
+ print "\033[1;32m$ret\033[0m (version $v)\n";
+ }
+ $ret =~ s/^\s+//g;
+ return $ret;
+}
+
+sub vcheck($$)
+{
+ my ($version1, $version2) = @_;
+ $version1 =~ s/\-r(\d+)/\.\1/g; # minor revs/patchlevels
+ $version2 =~ s/\-r(\d+)/\.\1/g;
+ $version1 =~ s/p(\d+)/\.\1/g;
+ $version2 =~ s/p(\d+)/\.\1/g;
+ $version1 =~ s/\-//g;
+ $version2 =~ s/\-//g;
+ $version1 =~ s/a-z//g;
+ $version2 =~ s/a-z//g;
+ my @v1 = split('\.', $version1);
+ my @v2 = split('\.', $version2);
+ for ($curr = 0; $curr < scalar(@v1); $curr++)
+ {
+ if ($v1[$curr] < $v2[$curr])
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+sub pkgconfig_check_version($$;$)
+{
+ my ($packagename, $version, $module) = @_;
+
+ extend_pkg_path();
+
+ print "Checking version of package \033[1;32m$packagename\033[0m is >= \033[1;32m$version\033[0m... ";
+
+ $v = `pkg-config --modversion $packagename 2>/dev/null`;
+ if (defined $v)
+ {
+ chomp($v);
+ }
+ if ((defined $v) && ($v ne ""))
+ {
+ if (vcheck($v,$version) == 1)
+ {
+ print "\033[1;32mYes (version $v)\033[0m\n";
+ return 1;
+ }
+ else
+ {
+ print "\033[1;32mNo (version $v)\033[0m\n";
+ return 0;
+ }
+ }
+ # If we didnt find it, we cant definitively say its too old.
+ # Return ok, and let pkgconflibs() or pkgconfincludes() pick up
+ # the missing library later on.
+ print "\033[1;32mNo (not found)\033[0m\n";
+ return 1;
+}
+
+sub pkgconfig_get_lib_dirs($$$;$)
+{
+ my ($packagename, $libname, $defaults, $module) = @_;
+
+ my $key = "default_libdir_$packagename";
+ if (exists $main::config{$key})
+ {
+ print "Locating library directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... ";
+ $ret = $main::config{$key};
+ print "\033[1;32m$ret\033[0m (cached)\n";
+ return $ret;
+ }
+
+ extend_pkg_path();
+
+ print "Locating library directory for package \033[1;32m$packagename\033[0m for module \033[1;32m$module\033[0m... ";
+
+ $v = `pkg-config --modversion $packagename 2>/dev/null`;
+ $ret = `pkg-config --libs $packagename 2>/dev/null`;
+
+ if ((!defined $v) || ($v eq ""))
+ {
+ $foo = `locate "$libname" | head -n 1`;
+ $foo =~ /(.+)\Q$libname\E/;
+ $find = $1;
+ chomp($find);
+ if ((defined $find) && ($find ne "") && ($find ne $packagename))
+ {
+ print "(\033[1;32mFound via search\033[0m) ";
+ $foo = "-L$1";
+ }
+ else
+ {
+ $foo = " ";
+ undef $v;
+ }
+ $ret = "$foo";
+ }
+
+ if (($defaults ne "") && (($ret eq "") || (!defined $ret)))
+ {
+ $ret = "$foo " . $defaults;
+ }
+ chomp($ret);
+ if ((($ret eq " ") || (!defined $ret)) && ((!defined $v) || ($v eq "")))
+ {
+ my $key = "default_libdir_$packagename";
+ if (exists $main::config{$key})
+ {
+ $ret = $main::config{$key};
+ }
+ else
+ {
+ $libname =~ s/^\///;
+ promptstring("path to the directory containing $libname", $key, "/usr/lib",$packagename,"$packagename-libs");
+ $main::config{$key} = "-L$main::config{$key}" . " $defaults";
+ $main::config{$key} =~ s/^\s+//g;
+ $ret = $main::config{$key};
+ return $ret;
+ }
+ }
+ else
+ {
+ chomp($v);
+ print "\033[1;32m$ret\033[0m (version $v)\n";
+ my $key = "default_libdir_$packagename";
+ $main::config{$key} = $ret;
+ $main::config{$key} =~ s/^\s+//g;
+ $ret =~ s/^\s+//g;
+ }
+ $ret =~ s/^\s+//g;
+ return $ret;
+}
+
+# Translate a $CompileFlags etc line and parse out function calls
+# to functions within these modules at configure time.
+sub translate_functions($$)
+{
+ my ($line,$module) = @_;
+
+ eval
+ {
+ $module =~ /modules*\/(.+?)$/;
+ $module = $1;
+
+ # This is only a cursory check, just designed to catch casual accidental use of backticks.
+ # There are pleanty of ways around it, but its not supposed to be for security, just checking
+ # that people are using the new configuration api as theyre supposed to and not just using
+ # backticks instead of eval(), being as eval has accountability. People wanting to get around
+ # the accountability will do so anyway.
+ if (($line =~ /`/) && ($line !~ /eval\(.+?`.+?\)/))
+ {
+ die "Developers should no longer use backticks in configuration macros. Please use exec() and eval() macros instead. Offending line: $line (In module: $module)";
+ }
+ while ($line =~ /exec\("(.+?)"\)/)
+ {
+ print "Executing program for module \033[1;32m$module\033[0m ... \033[1;32m$1\033[0m\n";
+ my $replace = `$1`;
+ chomp($replace);
+ $line =~ s/exec\("(.+?)"\)/$replace/;
+ }
+ while ($line =~ /eval\("(.+?)"\)/)
+ {
+ print "Evaluating perl code for module \033[1;32m$module\033[0m ... ";
+ my $tmpfile;
+ do
+ {
+ $tmpfile = tmpnam();
+ } until sysopen(TF, $tmpfile, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0700);
+ print "(Created and executed \033[1;32m$tmpfile\033[0m)\n";
+ print TF $1;
+ close TF;
+ my $replace = `perl $tmpfile`;
+ chomp($replace);
+ $line =~ s/eval\("(.+?)"\)/$replace/;
+ }
+ while ($line =~ /pkgconflibs\("(.+?)","(.+?)","(.+?)"\)/)
+ {
+ my $replace = pkgconfig_get_lib_dirs($1, $2, $3, $module);
+ $line =~ s/pkgconflibs\("(.+?)","(.+?)","(.+?)"\)/$replace/;
+ }
+ while ($line =~ /pkgconfversion\("(.+?)","(.+?)"\)/)
+ {
+ if (pkgconfig_check_version($1, $2, $module) != 1)
+ {
+ die "Version of package $1 is too old. Please upgrade it to version \033[1;32m$2\033[0m or greater and try again.";
+ }
+ # This doesnt actually get replaced with anything
+ $line =~ s/pkgconfversion\("(.+?)","(.+?)"\)//;
+ }
+ while ($line =~ /pkgconflibs\("(.+?)","(.+?)",""\)/)
+ {
+ my $replace = pkgconfig_get_lib_dirs($1, $2, "", $module);
+ $line =~ s/pkgconflibs\("(.+?)","(.+?)",""\)/$replace/;
+ }
+ while ($line =~ /pkgconfincludes\("(.+?)","(.+?)",""\)/)
+ {
+ my $replace = pkgconfig_get_include_dirs($1, $2, "", $module);
+ $line =~ s/pkgconfincludes\("(.+?)","(.+?)",""\)/$replace/;
+ }
+ while ($line =~ /pkgconfincludes\("(.+?)","(.+?)","(.+?)"\)/)
+ {
+ my $replace = pkgconfig_get_include_dirs($1, $2, $3, $module);
+ $line =~ s/pkgconfincludes\("(.+?)","(.+?)","(.+?)"\)/$replace/;
+ }
+ while ($line =~ /rpath\("(.+?)"\)/)
+ {
+ my $replace = make_rpath($1,$module);
+ $replace = "" if ($^O =~ /darwin/i);
+ $line =~ s/rpath\("(.+?)"\)/$replace/;
+ }
+ };
+ if ($@)
+ {
+ $err = $@;
+ $err =~ s/at .+? line \d+.*//g;
+ print "\n\nConfiguration failed. The following error occured:\n\n$err\n";
+ exit;
+ }
+ else
+ {
+ return $line;
+ }
+}
+
+1;
+
diff --git a/src/base.cpp b/src/base.cpp
index ef6ff5ccf..9c002773b 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -1 +1,95 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd_config.h" #include "base.h" #include <time.h> #include "inspircd.h" const int bitfields[] = {1,2,4,8,16,32,64,128}; const int inverted_bitfields[] = {~1,~2,~4,~8,~16,~32,~64,~128}; classbase::classbase() { this->age = time(NULL); } bool Extensible::Shrink(const std::string &key) { /* map::size_type map::erase( const key_type& key ); * returns the number of elements removed, std::map * is single-associative so this should only be 0 or 1 */ return this->Extension_Items.erase(key); } void Extensible::GetExtList(std::deque<std::string> &list) { for (ExtensibleStore::iterator u = Extension_Items.begin(); u != Extension_Items.end(); u++) { list.push_back(u->first); } } void BoolSet::Set(int number) { this->bits |= bitfields[number]; } void BoolSet::Unset(int number) { this->bits &= inverted_bitfields[number]; } void BoolSet::Invert(int number) { this->bits ^= bitfields[number]; } bool BoolSet::Get(int number) { return ((this->bits | bitfields[number]) > 0); } bool BoolSet::operator==(BoolSet other) { return (this->bits == other.bits); } BoolSet BoolSet::operator|(BoolSet other) { BoolSet x(this->bits | other.bits); return x; } BoolSet BoolSet::operator&(BoolSet other) { BoolSet x(this->bits & other.bits); return x; } BoolSet::BoolSet() { this->bits = 0; } BoolSet::BoolSet(char bitmask) { this->bits = bitmask; } bool BoolSet::operator=(BoolSet other) { this->bits = other.bits; return true; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd_config.h"
+#include "base.h"
+#include <time.h>
+#include "inspircd.h"
+
+const int bitfields[] = {1,2,4,8,16,32,64,128};
+const int inverted_bitfields[] = {~1,~2,~4,~8,~16,~32,~64,~128};
+
+classbase::classbase()
+{
+ this->age = time(NULL);
+}
+
+bool Extensible::Shrink(const std::string &key)
+{
+ /* map::size_type map::erase( const key_type& key );
+ * returns the number of elements removed, std::map
+ * is single-associative so this should only be 0 or 1
+ */
+ return this->Extension_Items.erase(key);
+}
+
+void Extensible::GetExtList(std::deque<std::string> &list)
+{
+ for (ExtensibleStore::iterator u = Extension_Items.begin(); u != Extension_Items.end(); u++)
+ {
+ list.push_back(u->first);
+ }
+}
+
+void BoolSet::Set(int number)
+{
+ this->bits |= bitfields[number];
+}
+
+void BoolSet::Unset(int number)
+{
+ this->bits &= inverted_bitfields[number];
+}
+
+void BoolSet::Invert(int number)
+{
+ this->bits ^= bitfields[number];
+}
+
+bool BoolSet::Get(int number)
+{
+ return ((this->bits | bitfields[number]) > 0);
+}
+
+bool BoolSet::operator==(BoolSet other)
+{
+ return (this->bits == other.bits);
+}
+
+BoolSet BoolSet::operator|(BoolSet other)
+{
+ BoolSet x(this->bits | other.bits);
+ return x;
+}
+
+BoolSet BoolSet::operator&(BoolSet other)
+{
+ BoolSet x(this->bits & other.bits);
+ return x;
+}
+
+BoolSet::BoolSet()
+{
+ this->bits = 0;
+}
+
+BoolSet::BoolSet(char bitmask)
+{
+ this->bits = bitmask;
+}
+
+bool BoolSet::operator=(BoolSet other)
+{
+ this->bits = other.bits;
+ return true;
+}
diff --git a/src/channels.cpp b/src/channels.cpp
index 74543925e..b44a863b4 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -1 +1,1067 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <stdarg.h> #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "mode.h" chanrec::chanrec(InspIRCd* Instance) : ServerInstance(Instance) { *name = *topic = *setby = *key = 0; maxbans = created = topicset = limit = 0; memset(&modes,0,64); age = ServerInstance->Time(true); } void chanrec::SetMode(char mode,bool mode_on) { modes[mode-65] = mode_on; if (!mode_on) this->SetModeParam(mode,"",false); } void chanrec::SetModeParam(char mode,const char* parameter,bool mode_on) { CustomModeList::iterator n = custom_mode_params.find(mode); if (mode_on) { if (n == custom_mode_params.end()) custom_mode_params[mode] = strdup(parameter); } else { if (n != custom_mode_params.end()) { free(n->second); custom_mode_params.erase(n); } } } bool chanrec::IsModeSet(char mode) { return modes[mode-65]; } std::string chanrec::GetModeParameter(char mode) { switch (mode) { case 'k': return this->key; case 'l': return ConvToStr(this->limit); default: CustomModeList::iterator n = custom_mode_params.find(mode); if (n != custom_mode_params.end()) return n->second; return ""; break; } } long chanrec::GetUserCounter() { return (this->internal_userlist.size()); } void chanrec::AddUser(userrec* user) { internal_userlist[user] = user->nick; } unsigned long chanrec::DelUser(userrec* user) { CUListIter a = internal_userlist.find(user); if (a != internal_userlist.end()) { internal_userlist.erase(a); /* And tidy any others... */ DelOppedUser(user); DelHalfoppedUser(user); DelVoicedUser(user); } return internal_userlist.size(); } bool chanrec::HasUser(userrec* user) { return (internal_userlist.find(user) != internal_userlist.end()); } void chanrec::AddOppedUser(userrec* user) { internal_op_userlist[user] = user->nick; } void chanrec::DelOppedUser(userrec* user) { CUListIter a = internal_op_userlist.find(user); if (a != internal_op_userlist.end()) { internal_op_userlist.erase(a); return; } } void chanrec::AddHalfoppedUser(userrec* user) { internal_halfop_userlist[user] = user->nick; } void chanrec::DelHalfoppedUser(userrec* user) { CUListIter a = internal_halfop_userlist.find(user); if (a != internal_halfop_userlist.end()) { internal_halfop_userlist.erase(a); } } void chanrec::AddVoicedUser(userrec* user) { internal_voice_userlist[user] = user->nick; } void chanrec::DelVoicedUser(userrec* user) { CUListIter a = internal_voice_userlist.find(user); if (a != internal_voice_userlist.end()) { internal_voice_userlist.erase(a); } } CUList* chanrec::GetUsers() { return &internal_userlist; } CUList* chanrec::GetOppedUsers() { return &internal_op_userlist; } CUList* chanrec::GetHalfoppedUsers() { return &internal_halfop_userlist; } CUList* chanrec::GetVoicedUsers() { return &internal_voice_userlist; } void chanrec::SetDefaultModes() { irc::spacesepstream list(ServerInstance->Config->DefaultModes); std::string modeseq = list.GetToken(); std::string parameter; userrec* dummyuser = new userrec(ServerInstance); dummyuser->SetFd(FD_MAGIC_NUMBER); for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n) { ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); if (mode) { if (mode->GetNumParams(true)) parameter = list.GetToken().c_str(); else parameter.clear(); mode->OnModeChange(dummyuser, dummyuser, this, parameter, true); } } delete dummyuser; } /* * add a channel to a user, creating the record for it if needed and linking * it to the user record */ chanrec* chanrec::JoinUser(InspIRCd* Instance, userrec *user, const char* cn, bool override, const char* key, time_t TS) { if (!user || !cn) return NULL; bool new_channel = false; char cname[MAXBUF]; int MOD_RESULT = 0; strlcpy(cname,cn,CHANMAX); std::string privs; chanrec* Ptr = Instance->FindChan(cname); if (!Ptr) { if ((!IS_LOCAL(user)) && (!TS)) Instance->Log(DEBUG,"*** BUG *** chanrec::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick, cn); privs = "@"; if (IS_LOCAL(user) && override == false) { MOD_RESULT = 0; FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,NULL,cname,privs)); if (MOD_RESULT == 1) return NULL; } /* create a new one */ Ptr = new chanrec(Instance); (*(Instance->chanlist))[cname] = Ptr; strlcpy(Ptr->name, cname,CHANMAX); /* As spotted by jilles, dont bother to set this on remote users */ if (IS_LOCAL(user)) Ptr->SetDefaultModes(); Ptr->created = TS ? TS : Instance->Time(); Ptr->age = Ptr->created; *Ptr->topic = 0; *Ptr->setby = 0; Ptr->topicset = 0; new_channel = true; } else { /* Already on the channel */ if (Ptr->HasUser(user)) return NULL; /* * remote users are allowed us to bypass channel modes * and bans (used by servers) */ if (IS_LOCAL(user) && override == false) { MOD_RESULT = 0; FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,Ptr,cname,privs)); if (MOD_RESULT == 1) { return NULL; } else if (MOD_RESULT == 0) { if (*Ptr->key) { MOD_RESULT = 0; FOREACH_RESULT_I(Instance,I_OnCheckKey,OnCheckKey(user, Ptr, key ? key : "")); if (!MOD_RESULT) { if ((!key) || strcmp(key,Ptr->key)) { user->WriteServ("475 %s %s :Cannot join channel (Incorrect channel key)",user->nick, Ptr->name); return NULL; } } } if (Ptr->modes[CM_INVITEONLY]) { MOD_RESULT = 0; FOREACH_RESULT_I(Instance,I_OnCheckInvite,OnCheckInvite(user, Ptr)); if (!MOD_RESULT) { if (user->IsInvited(Ptr->name)) { /* user was invited to channel */ /* there may be an optional channel NOTICE here */ } else { user->WriteServ("473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name); return NULL; } } user->RemoveInvite(Ptr->name); } if (Ptr->limit) { MOD_RESULT = 0; FOREACH_RESULT_I(Instance,I_OnCheckLimit,OnCheckLimit(user, Ptr)); if (!MOD_RESULT) { if (Ptr->GetUserCounter() >= Ptr->limit) { user->WriteServ("471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name); return NULL; } } } if (Ptr->bans.size()) { if (Ptr->IsBanned(user)) { user->WriteServ("474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name); return NULL; } } } } } /* NOTE: If the user is an oper here, we can extend their user->chans by up to * OperMaxchans. For remote users which are not bound by the channel limits, * we can extend infinitely. Otherwise, nope, youre restricted to MaxChans. */ if (!IS_LOCAL(user) || override == true) { return chanrec::ForceChan(Instance, Ptr, user, privs); } else if (IS_OPER(user)) { /* Oper allows extension up to the OperMaxchans value */ if (user->chans.size() < Instance->Config->OperMaxChans) { return chanrec::ForceChan(Instance, Ptr, user, privs); } } else if (user->chans.size() < Instance->Config->MaxChans) { return chanrec::ForceChan(Instance, Ptr, user, privs); } user->WriteServ("405 %s %s :You are on too many channels",user->nick, cname); if (new_channel) { /* Things went seriously pear shaped, so take this away. bwahaha. */ chan_hash::iterator n = Instance->chanlist->find(cname); if (n != Instance->chanlist->end()) { Ptr->DelUser(user); DELETE(Ptr); Instance->chanlist->erase(n); } } return NULL; } chanrec* chanrec::ForceChan(InspIRCd* Instance, chanrec* Ptr, userrec* user, const std::string &privs) { userrec* dummyuser = new userrec(Instance); std::string nick = user->nick; bool silent = false; dummyuser->SetFd(FD_MAGIC_NUMBER); Ptr->AddUser(user); /* Just in case they have no permissions */ user->chans[Ptr] = 0; for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++) { const char status = *x; ModeHandler* mh = Instance->Modes->FindPrefix(status); if (mh) { Ptr->SetPrefix(user, status, mh->GetPrefixRank(), true); /* Make sure that the mode handler knows this mode was now set */ mh->OnModeChange(dummyuser, dummyuser, Ptr, nick, true); switch (mh->GetPrefix()) { /* These logic ops are SAFE IN THIS CASE * because if the entry doesnt exist, * addressing operator[] creates it. * If they do exist, it points to it. * At all other times where we dont want * to create an item if it doesnt exist, we * must stick to ::find(). */ case '@': user->chans[Ptr] |= UCMODE_OP; break; case '%': user->chans[Ptr] |= UCMODE_HOP; break; case '+': user->chans[Ptr] |= UCMODE_VOICE; break; } } } delete dummyuser; FOREACH_MOD_I(Instance,I_OnUserJoin,OnUserJoin(user, Ptr, silent)); if (!silent) Ptr->WriteChannel(user,"JOIN :%s",Ptr->name); /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */ std::string ms = Instance->Modes->ModeString(user, Ptr); if ((Ptr->GetUserCounter() > 1) && (ms.length())) Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name, ms.c_str()); /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */ if (IS_LOCAL(user)) { if (Ptr->topicset) { user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic); user->WriteServ("333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset); } Ptr->UserList(user); } FOREACH_MOD_I(Instance,I_OnPostJoin,OnPostJoin(user, Ptr)); return Ptr; } bool chanrec::IsBanned(userrec* user) { char mask[MAXBUF]; int MOD_RESULT = 0; FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, this)); if (!MOD_RESULT) { snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString()); for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++) { /* This allows CIDR ban matching * * Full masked host Full unmasked host IP with/without CIDR */ if ((match(user->GetFullHost(),i->data)) || (match(user->GetFullRealHost(),i->data)) || (match(mask, i->data, true))) { return true; } } } return false; } /* chanrec::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 chanrec. */ long chanrec::PartUser(userrec *user, const char* reason) { bool silent = false; if (!user) return this->GetUserCounter(); UCListIter i = user->chans.find(this); if (i != user->chans.end()) { FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason ? reason : "", silent)); if (!silent) this->WriteChannel(user, "PART %s%s%s", this->name, reason ? " :" : "", reason ? reason : ""); user->chans.erase(i); this->RemoveAllPrefixes(user); } if (!this->DelUser(user)) /* if there are no users left on the channel... */ { 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); } return 0; } return this->GetUserCounter(); } long chanrec::ServerKickUser(userrec* user, const char* reason, bool triggerevents) { bool silent = false; if (!user || !reason) return this->GetUserCounter(); if (IS_LOCAL(user)) { if (!this->HasUser(user)) { /* Not on channel */ return this->GetUserCounter(); } } if (triggerevents) { FOREACH_MOD(I_OnUserKick,OnUserKick(NULL, user, this, reason, silent)); } UCListIter i = user->chans.find(this); if (i != user->chans.end()) { if (!silent) this->WriteChannelWithServ(ServerInstance->Config->ServerName, "KICK %s %s :%s", this->name, user->nick, reason); user->chans.erase(i); this->RemoveAllPrefixes(user); } if (!this->DelUser(user)) { 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); } return 0; } return this->GetUserCounter(); } long chanrec::KickUser(userrec *src, userrec *user, const char* reason) { bool silent = false; if (!src || !user || !reason) return this->GetUserCounter(); if (IS_LOCAL(src)) { if (!this->HasUser(user)) { src->WriteServ("441 %s %s %s :They are not on that channel",src->nick, user->nick, this->name); return this->GetUserCounter(); } if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server))) { src->WriteServ("482 %s %s :Only a u-line may kick a u-line from a channel.",src->nick, this->name); return this->GetUserCounter(); } int MOD_RESULT = 0; if (!ServerInstance->ULine(src->server)) { MOD_RESULT = 0; FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason)); if (MOD_RESULT == 1) return this->GetUserCounter(); } /* Set to -1 by OnUserPreKick if explicit allow was set */ if (MOD_RESULT != -1) { FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK)); if ((MOD_RESULT == ACR_DENY) && (!ServerInstance->ULine(src->server))) return this->GetUserCounter(); if ((MOD_RESULT == ACR_DEFAULT) || (!ServerInstance->ULine(src->server))) { int them = this->GetStatus(src); int us = this->GetStatus(user); if ((them < STATUS_HOP) || (them < us)) { src->WriteServ("482 %s %s :You must be a channel %soperator",src->nick, this->name, them == STATUS_HOP ? "" : "half-"); return this->GetUserCounter(); } } } } FOREACH_MOD(I_OnUserKick,OnUserKick(src, user, this, reason, silent)); UCListIter i = user->chans.find(this); if (i != user->chans.end()) { /* zap it from the channel list of the user */ if (!silent) this->WriteChannel(src, "KICK %s %s :%s", this->name, user->nick, reason); user->chans.erase(i); this->RemoveAllPrefixes(user); } if (!this->DelUser(user)) /* if there are no users left on the channel */ { 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); } return 0; } return this->GetUserCounter(); } void chanrec::WriteChannel(userrec* user, char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; if (!user || !text) return; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteChannel(user, std::string(textbuffer)); } void chanrec::WriteChannel(userrec* user, const std::string &text) { CUList *ulist = this->GetUsers(); char tb[MAXBUF]; if (!user) return; snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); std::string out = tb; for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (IS_LOCAL(i->first)) i->first->Write(out); } } void chanrec::WriteChannelWithServ(const char* ServName, const char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; if (!text) return; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteChannelWithServ(ServName, std::string(textbuffer)); } void chanrec::WriteChannelWithServ(const char* ServName, const std::string &text) { CUList *ulist = this->GetUsers(); char tb[MAXBUF]; snprintf(tb,MAXBUF,":%s %s",ServName ? ServName : ServerInstance->Config->ServerName, text.c_str()); std::string out = tb; for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (IS_LOCAL(i->first)) i->first->Write(out); } } /* write formatted text from a source user to all users on a channel except * for the sender (for privmsg etc) */ void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, 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)); } void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; if (!text) return; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteAllExcept(user, serversource, status, except_list, std::string(textbuffer)); } void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, const std::string &text) { CUList *ulist; char tb[MAXBUF]; switch (status) { case '@': ulist = this->GetOppedUsers(); break; case '%': ulist = this->GetHalfoppedUsers(); break; case '+': ulist = this->GetVoicedUsers(); break; default: ulist = this->GetUsers(); break; } snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); std::string out = tb; for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end())) { if (serversource) i->first->WriteServ(text); else i->first->Write(out); } } } void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, const std::string& text) { CUList except_list; except_list[user] = user->nick; 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 chanrec::CountInvisible() { int count = 0; CUList *ulist= this->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (!(i->first->IsModeSet('i'))) count++; } return count; } char* chanrec::ChanModes(bool showkey) { static char scratch[MAXBUF]; static char sparam[MAXBUF]; char* offset = scratch; std::string extparam; *scratch = '\0'; *sparam = '\0'; /* This was still iterating up to 190, chanrec::modes is only 64 elements -- Om */ for(int n = 0; n < 64; n++) { if(this->modes[n]) { *offset++ = n + 65; extparam.clear(); switch (n) { case CM_KEY: extparam = (showkey ? this->key : "<key>"); break; case CM_LIMIT: extparam = ConvToStr(this->limit); break; case CM_NOEXTERNAL: case CM_TOPICLOCK: case CM_INVITEONLY: case CM_MODERATED: case CM_SECRET: case CM_PRIVATE: /* We know these have no parameters */ break; default: extparam = this->GetModeParameter(n + 65); break; } if (!extparam.empty()) { charlcat(sparam,' ',MAXBUF); strlcat(sparam,extparam.c_str(),MAXBUF); } } } /* 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 chanrec::UserList(userrec *user, CUList *ulist) { char list[MAXBUF]; size_t dlen, curlen; int MOD_RESULT = 0; if (!IS_LOCAL(user)) return; FOREACH_RESULT(I_OnUserList,OnUserList(user, this, ulist)); if (MOD_RESULT == 1) return; dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name); int numusers = 0; char* ptr = list + dlen; if (!ulist) ulist = this->GetUsers(); /* Improvement by Brain - this doesnt change in value, so why was it inside * the loop? */ bool has_user = this->HasUser(user); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if ((!has_user) && (i->first->modes[UM_INVISIBLE])) { /* * user is +i, and source not on the channel, does not show * nick in NAMES list */ continue; } if (i->first->Visibility && !i->first->Visibility->VisibleTo(user)) continue; size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", this->GetPrefixChar(i->first), i->second.c_str()); /* OnUserList can change this - reset it back to normal */ i->second = i->first->nick; curlen += ptrlen; ptr += ptrlen; numusers++; if (curlen > (480-NICKMAX)) { /* list overflowed into multiple numerics */ user->WriteServ(std::string(list)); /* reset our lengths */ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name); ptr = list + dlen; ptrlen = 0; numusers = 0; } } /* if whats left in the list isnt empty, send it */ if (numusers) { user->WriteServ(std::string(list)); } user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, this->name); } long chanrec::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 (match(this->name,n->first.c_str())) { this->maxbans = n->second; return n->second; } } /* Screw it, just return the default of 64 */ this->maxbans = 64; return this->maxbans; } void chanrec::ResetMaxBans() { this->maxbans = 0; } /* 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* chanrec::GetPrefixChar(userrec *user) { static char pf[2] = {0, 0}; prefixlist::iterator n = prefixes.find(user); if (n != prefixes.end()) { if (n->second.size()) { /* If the user has any prefixes, their highest prefix * will always be at the head of the list, as the list is * sorted in rank order highest first (see SetPrefix() * for reasons why) */ *pf = n->second.begin()->first; return pf; } } *pf = 0; return pf; } const char* chanrec::GetAllPrefixChars(userrec* user) { static char prefix[MAXBUF]; int ctr = 0; *prefix = 0; prefixlist::iterator n = prefixes.find(user); if (n != prefixes.end()) { for (std::vector<prefixtype>::iterator x = n->second.begin(); x != n->second.end(); x++) { prefix[ctr++] = x->first; } } prefix[ctr] = 0; return prefix; } unsigned int chanrec::GetPrefixValue(userrec* user) { prefixlist::iterator n = prefixes.find(user); if (n != prefixes.end()) { if (n->second.size()) return n->second.begin()->second; } return 0; } int chanrec::GetStatusFlags(userrec *user) { UCListIter i = user->chans.find(this); if (i != user->chans.end()) { return i->second; } return 0; } int chanrec::GetStatus(userrec *user) { if (ServerInstance->ULine(user->server)) return STATUS_OP; UCListIter i = user->chans.find(this); if (i != user->chans.end()) { if ((i->second & UCMODE_OP) > 0) { return STATUS_OP; } if ((i->second & UCMODE_HOP) > 0) { return STATUS_HOP; } if ((i->second & UCMODE_VOICE) > 0) { return STATUS_VOICE; } return STATUS_NORMAL; } return STATUS_NORMAL; } void chanrec::SetPrefix(userrec* user, char prefix, unsigned int prefix_value, bool adding) { prefixlist::iterator n = prefixes.find(user); prefixtype pfx = std::make_pair(prefix,prefix_value); if (adding) { if (n != prefixes.end()) { if (std::find(n->second.begin(), n->second.end(), pfx) == n->second.end()) { n->second.push_back(pfx); /* We must keep prefixes in rank order, largest first. * This is for two reasons, firstly because x-chat *ass-u-me's* this * state, and secondly it turns out to be a benefit to us later. * See above in GetPrefix(). */ std::sort(n->second.begin(), n->second.end(), ModeParser::PrefixComparison); } } else { pfxcontainer one; one.push_back(pfx); prefixes.insert(std::make_pair<userrec*,pfxcontainer>(user, one)); } } else { if (n != prefixes.end()) { pfxcontainer::iterator x = std::find(n->second.begin(), n->second.end(), pfx); if (x != n->second.end()) n->second.erase(x); } } } void chanrec::RemoveAllPrefixes(userrec* user) { prefixlist::iterator n = prefixes.find(user); if (n != prefixes.end()) { prefixes.erase(n); } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <stdarg.h>
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "mode.h"
+
+chanrec::chanrec(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ *name = *topic = *setby = *key = 0;
+ maxbans = created = topicset = limit = 0;
+ memset(&modes,0,64);
+ age = ServerInstance->Time(true);
+}
+
+void chanrec::SetMode(char mode,bool mode_on)
+{
+ modes[mode-65] = mode_on;
+ if (!mode_on)
+ this->SetModeParam(mode,"",false);
+}
+
+
+void chanrec::SetModeParam(char mode,const char* parameter,bool mode_on)
+{
+ CustomModeList::iterator n = custom_mode_params.find(mode);
+
+ if (mode_on)
+ {
+ if (n == custom_mode_params.end())
+ custom_mode_params[mode] = strdup(parameter);
+ }
+ else
+ {
+ if (n != custom_mode_params.end())
+ {
+ free(n->second);
+ custom_mode_params.erase(n);
+ }
+ }
+}
+
+bool chanrec::IsModeSet(char mode)
+{
+ return modes[mode-65];
+}
+
+std::string chanrec::GetModeParameter(char mode)
+{
+ switch (mode)
+ {
+ case 'k':
+ return this->key;
+ case 'l':
+ return ConvToStr(this->limit);
+ default:
+ CustomModeList::iterator n = custom_mode_params.find(mode);
+ if (n != custom_mode_params.end())
+ return n->second;
+ return "";
+ break;
+ }
+}
+
+long chanrec::GetUserCounter()
+{
+ return (this->internal_userlist.size());
+}
+
+void chanrec::AddUser(userrec* user)
+{
+ internal_userlist[user] = user->nick;
+}
+
+unsigned long chanrec::DelUser(userrec* user)
+{
+ CUListIter a = internal_userlist.find(user);
+
+ if (a != internal_userlist.end())
+ {
+ internal_userlist.erase(a);
+ /* And tidy any others... */
+ DelOppedUser(user);
+ DelHalfoppedUser(user);
+ DelVoicedUser(user);
+ }
+
+ return internal_userlist.size();
+}
+
+bool chanrec::HasUser(userrec* user)
+{
+ return (internal_userlist.find(user) != internal_userlist.end());
+}
+
+void chanrec::AddOppedUser(userrec* user)
+{
+ internal_op_userlist[user] = user->nick;
+}
+
+void chanrec::DelOppedUser(userrec* user)
+{
+ CUListIter a = internal_op_userlist.find(user);
+ if (a != internal_op_userlist.end())
+ {
+ internal_op_userlist.erase(a);
+ return;
+ }
+}
+
+void chanrec::AddHalfoppedUser(userrec* user)
+{
+ internal_halfop_userlist[user] = user->nick;
+}
+
+void chanrec::DelHalfoppedUser(userrec* user)
+{
+ CUListIter a = internal_halfop_userlist.find(user);
+
+ if (a != internal_halfop_userlist.end())
+ {
+ internal_halfop_userlist.erase(a);
+ }
+}
+
+void chanrec::AddVoicedUser(userrec* user)
+{
+ internal_voice_userlist[user] = user->nick;
+}
+
+void chanrec::DelVoicedUser(userrec* user)
+{
+ CUListIter a = internal_voice_userlist.find(user);
+
+ if (a != internal_voice_userlist.end())
+ {
+ internal_voice_userlist.erase(a);
+ }
+}
+
+CUList* chanrec::GetUsers()
+{
+ return &internal_userlist;
+}
+
+CUList* chanrec::GetOppedUsers()
+{
+ return &internal_op_userlist;
+}
+
+CUList* chanrec::GetHalfoppedUsers()
+{
+ return &internal_halfop_userlist;
+}
+
+CUList* chanrec::GetVoicedUsers()
+{
+ return &internal_voice_userlist;
+}
+
+void chanrec::SetDefaultModes()
+{
+ irc::spacesepstream list(ServerInstance->Config->DefaultModes);
+ std::string modeseq = list.GetToken();
+ std::string parameter;
+ userrec* dummyuser = new userrec(ServerInstance);
+ dummyuser->SetFd(FD_MAGIC_NUMBER);
+
+ for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
+ {
+ ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
+ if (mode)
+ {
+ if (mode->GetNumParams(true))
+ parameter = list.GetToken().c_str();
+ else
+ parameter.clear();
+
+ mode->OnModeChange(dummyuser, dummyuser, this, parameter, true);
+ }
+ }
+
+ delete dummyuser;
+}
+
+/*
+ * add a channel to a user, creating the record for it if needed and linking
+ * it to the user record
+ */
+chanrec* chanrec::JoinUser(InspIRCd* Instance, userrec *user, const char* cn, bool override, const char* key, time_t TS)
+{
+ if (!user || !cn)
+ return NULL;
+
+ bool new_channel = false;
+ char cname[MAXBUF];
+ int MOD_RESULT = 0;
+ strlcpy(cname,cn,CHANMAX);
+
+ std::string privs;
+
+ chanrec* Ptr = Instance->FindChan(cname);
+
+ if (!Ptr)
+ {
+ if ((!IS_LOCAL(user)) && (!TS))
+ Instance->Log(DEBUG,"*** BUG *** chanrec::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick, cn);
+
+ privs = "@";
+
+ if (IS_LOCAL(user) && override == false)
+ {
+ MOD_RESULT = 0;
+ FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,NULL,cname,privs));
+ if (MOD_RESULT == 1)
+ return NULL;
+ }
+
+ /* create a new one */
+ Ptr = new chanrec(Instance);
+ (*(Instance->chanlist))[cname] = Ptr;
+
+ strlcpy(Ptr->name, cname,CHANMAX);
+
+ /* As spotted by jilles, dont bother to set this on remote users */
+ if (IS_LOCAL(user))
+ Ptr->SetDefaultModes();
+
+ Ptr->created = TS ? TS : Instance->Time();
+ Ptr->age = Ptr->created;
+ *Ptr->topic = 0;
+ *Ptr->setby = 0;
+ Ptr->topicset = 0;
+ new_channel = true;
+ }
+ else
+ {
+ /* Already on the channel */
+ if (Ptr->HasUser(user))
+ return NULL;
+
+ /*
+ * remote users are allowed us to bypass channel modes
+ * and bans (used by servers)
+ */
+ if (IS_LOCAL(user) && override == false)
+ {
+ MOD_RESULT = 0;
+ FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,Ptr,cname,privs));
+ if (MOD_RESULT == 1)
+ {
+ return NULL;
+ }
+ else if (MOD_RESULT == 0)
+ {
+ if (*Ptr->key)
+ {
+ MOD_RESULT = 0;
+ FOREACH_RESULT_I(Instance,I_OnCheckKey,OnCheckKey(user, Ptr, key ? key : ""));
+ if (!MOD_RESULT)
+ {
+ if ((!key) || strcmp(key,Ptr->key))
+ {
+ user->WriteServ("475 %s %s :Cannot join channel (Incorrect channel key)",user->nick, Ptr->name);
+ return NULL;
+ }
+ }
+ }
+ if (Ptr->modes[CM_INVITEONLY])
+ {
+ MOD_RESULT = 0;
+ FOREACH_RESULT_I(Instance,I_OnCheckInvite,OnCheckInvite(user, Ptr));
+ if (!MOD_RESULT)
+ {
+ if (user->IsInvited(Ptr->name))
+ {
+ /* user was invited to channel */
+ /* there may be an optional channel NOTICE here */
+ }
+ else
+ {
+ user->WriteServ("473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
+ return NULL;
+ }
+ }
+ user->RemoveInvite(Ptr->name);
+ }
+ if (Ptr->limit)
+ {
+ MOD_RESULT = 0;
+ FOREACH_RESULT_I(Instance,I_OnCheckLimit,OnCheckLimit(user, Ptr));
+ if (!MOD_RESULT)
+ {
+ if (Ptr->GetUserCounter() >= Ptr->limit)
+ {
+ user->WriteServ("471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
+ return NULL;
+ }
+ }
+ }
+ if (Ptr->bans.size())
+ {
+ if (Ptr->IsBanned(user))
+ {
+ user->WriteServ("474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
+ return NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /* NOTE: If the user is an oper here, we can extend their user->chans by up to
+ * OperMaxchans. For remote users which are not bound by the channel limits,
+ * we can extend infinitely. Otherwise, nope, youre restricted to MaxChans.
+ */
+ if (!IS_LOCAL(user) || override == true)
+ {
+ return chanrec::ForceChan(Instance, Ptr, user, privs);
+ }
+ else if (IS_OPER(user))
+ {
+ /* Oper allows extension up to the OperMaxchans value */
+ if (user->chans.size() < Instance->Config->OperMaxChans)
+ {
+ return chanrec::ForceChan(Instance, Ptr, user, privs);
+ }
+ }
+ else if (user->chans.size() < Instance->Config->MaxChans)
+ {
+ return chanrec::ForceChan(Instance, Ptr, user, privs);
+ }
+
+
+ user->WriteServ("405 %s %s :You are on too many channels",user->nick, cname);
+
+ if (new_channel)
+ {
+ /* Things went seriously pear shaped, so take this away. bwahaha. */
+ chan_hash::iterator n = Instance->chanlist->find(cname);
+ if (n != Instance->chanlist->end())
+ {
+ Ptr->DelUser(user);
+ DELETE(Ptr);
+ Instance->chanlist->erase(n);
+ }
+ }
+
+ return NULL;
+}
+
+chanrec* chanrec::ForceChan(InspIRCd* Instance, chanrec* Ptr, userrec* user, const std::string &privs)
+{
+ userrec* dummyuser = new userrec(Instance);
+ std::string nick = user->nick;
+ bool silent = false;
+
+ dummyuser->SetFd(FD_MAGIC_NUMBER);
+ Ptr->AddUser(user);
+
+ /* Just in case they have no permissions */
+ user->chans[Ptr] = 0;
+
+ for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
+ {
+ const char status = *x;
+ ModeHandler* mh = Instance->Modes->FindPrefix(status);
+ if (mh)
+ {
+ Ptr->SetPrefix(user, status, mh->GetPrefixRank(), true);
+ /* Make sure that the mode handler knows this mode was now set */
+ mh->OnModeChange(dummyuser, dummyuser, Ptr, nick, true);
+
+ switch (mh->GetPrefix())
+ {
+ /* These logic ops are SAFE IN THIS CASE
+ * because if the entry doesnt exist,
+ * addressing operator[] creates it.
+ * If they do exist, it points to it.
+ * At all other times where we dont want
+ * to create an item if it doesnt exist, we
+ * must stick to ::find().
+ */
+ case '@':
+ user->chans[Ptr] |= UCMODE_OP;
+ break;
+ case '%':
+ user->chans[Ptr] |= UCMODE_HOP;
+ break;
+ case '+':
+ user->chans[Ptr] |= UCMODE_VOICE;
+ break;
+ }
+ }
+ }
+
+ delete dummyuser;
+
+ FOREACH_MOD_I(Instance,I_OnUserJoin,OnUserJoin(user, Ptr, silent));
+
+ if (!silent)
+ Ptr->WriteChannel(user,"JOIN :%s",Ptr->name);
+
+ /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
+ std::string ms = Instance->Modes->ModeString(user, Ptr);
+ if ((Ptr->GetUserCounter() > 1) && (ms.length()))
+ Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name, ms.c_str());
+
+ /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
+ if (IS_LOCAL(user))
+ {
+ if (Ptr->topicset)
+ {
+ user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
+ user->WriteServ("333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset);
+ }
+ Ptr->UserList(user);
+ }
+ FOREACH_MOD_I(Instance,I_OnPostJoin,OnPostJoin(user, Ptr));
+ return Ptr;
+}
+
+bool chanrec::IsBanned(userrec* user)
+{
+ char mask[MAXBUF];
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, this));
+ if (!MOD_RESULT)
+ {
+ snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString());
+ for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+ {
+ /* This allows CIDR ban matching
+ *
+ * Full masked host Full unmasked host IP with/without CIDR
+ */
+ if ((match(user->GetFullHost(),i->data)) || (match(user->GetFullRealHost(),i->data)) || (match(mask, i->data, true)))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/* chanrec::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 chanrec.
+ */
+long chanrec::PartUser(userrec *user, const char* reason)
+{
+ bool silent = false;
+
+ if (!user)
+ return this->GetUserCounter();
+
+ UCListIter i = user->chans.find(this);
+ if (i != user->chans.end())
+ {
+ FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason ? reason : "", silent));
+
+ if (!silent)
+ this->WriteChannel(user, "PART %s%s%s", this->name, reason ? " :" : "", reason ? reason : "");
+
+ user->chans.erase(i);
+ this->RemoveAllPrefixes(user);
+ }
+
+ if (!this->DelUser(user)) /* if there are no users left on the channel... */
+ {
+ 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);
+ }
+ return 0;
+ }
+
+ return this->GetUserCounter();
+}
+
+long chanrec::ServerKickUser(userrec* user, const char* reason, bool triggerevents)
+{
+ bool silent = false;
+
+ if (!user || !reason)
+ return this->GetUserCounter();
+
+ if (IS_LOCAL(user))
+ {
+ if (!this->HasUser(user))
+ {
+ /* Not on channel */
+ return this->GetUserCounter();
+ }
+ }
+
+ if (triggerevents)
+ {
+ FOREACH_MOD(I_OnUserKick,OnUserKick(NULL, user, this, reason, silent));
+ }
+
+ UCListIter i = user->chans.find(this);
+ if (i != user->chans.end())
+ {
+ if (!silent)
+ this->WriteChannelWithServ(ServerInstance->Config->ServerName, "KICK %s %s :%s", this->name, user->nick, reason);
+
+ user->chans.erase(i);
+ this->RemoveAllPrefixes(user);
+ }
+
+ if (!this->DelUser(user))
+ {
+ 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);
+ }
+ return 0;
+ }
+
+ return this->GetUserCounter();
+}
+
+long chanrec::KickUser(userrec *src, userrec *user, const char* reason)
+{
+ bool silent = false;
+
+ if (!src || !user || !reason)
+ return this->GetUserCounter();
+
+ if (IS_LOCAL(src))
+ {
+ if (!this->HasUser(user))
+ {
+ src->WriteServ("441 %s %s %s :They are not on that channel",src->nick, user->nick, this->name);
+ return this->GetUserCounter();
+ }
+ if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
+ {
+ src->WriteServ("482 %s %s :Only a u-line may kick a u-line from a channel.",src->nick, this->name);
+ return this->GetUserCounter();
+ }
+ int MOD_RESULT = 0;
+
+ if (!ServerInstance->ULine(src->server))
+ {
+ MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason));
+ if (MOD_RESULT == 1)
+ return this->GetUserCounter();
+ }
+ /* Set to -1 by OnUserPreKick if explicit allow was set */
+ if (MOD_RESULT != -1)
+ {
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK));
+ if ((MOD_RESULT == ACR_DENY) && (!ServerInstance->ULine(src->server)))
+ return this->GetUserCounter();
+
+ if ((MOD_RESULT == ACR_DEFAULT) || (!ServerInstance->ULine(src->server)))
+ {
+ int them = this->GetStatus(src);
+ int us = this->GetStatus(user);
+ if ((them < STATUS_HOP) || (them < us))
+ {
+ src->WriteServ("482 %s %s :You must be a channel %soperator",src->nick, this->name, them == STATUS_HOP ? "" : "half-");
+ return this->GetUserCounter();
+ }
+ }
+ }
+ }
+
+ FOREACH_MOD(I_OnUserKick,OnUserKick(src, user, this, reason, silent));
+
+ UCListIter i = user->chans.find(this);
+ if (i != user->chans.end())
+ {
+ /* zap it from the channel list of the user */
+ if (!silent)
+ this->WriteChannel(src, "KICK %s %s :%s", this->name, user->nick, reason);
+
+ user->chans.erase(i);
+ this->RemoveAllPrefixes(user);
+ }
+
+ if (!this->DelUser(user))
+ /* if there are no users left on the channel */
+ {
+ 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);
+ }
+ return 0;
+ }
+
+ return this->GetUserCounter();
+}
+
+void chanrec::WriteChannel(userrec* user, char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ if (!user || !text)
+ return;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteChannel(user, std::string(textbuffer));
+}
+
+void chanrec::WriteChannel(userrec* user, const std::string &text)
+{
+ CUList *ulist = this->GetUsers();
+ char tb[MAXBUF];
+
+ if (!user)
+ return;
+
+ snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
+ std::string out = tb;
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (IS_LOCAL(i->first))
+ i->first->Write(out);
+ }
+}
+
+void chanrec::WriteChannelWithServ(const char* ServName, const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ if (!text)
+ return;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteChannelWithServ(ServName, std::string(textbuffer));
+}
+
+void chanrec::WriteChannelWithServ(const char* ServName, const std::string &text)
+{
+ CUList *ulist = this->GetUsers();
+ char tb[MAXBUF];
+
+ snprintf(tb,MAXBUF,":%s %s",ServName ? ServName : ServerInstance->Config->ServerName, text.c_str());
+ std::string out = tb;
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (IS_LOCAL(i->first))
+ i->first->Write(out);
+ }
+}
+
+/* write formatted text from a source user to all users on a channel except
+ * for the sender (for privmsg etc) */
+void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, 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));
+}
+
+void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ if (!text)
+ return;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
+}
+
+void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, const std::string &text)
+{
+ CUList *ulist;
+ char tb[MAXBUF];
+
+ switch (status)
+ {
+ case '@':
+ ulist = this->GetOppedUsers();
+ break;
+ case '%':
+ ulist = this->GetHalfoppedUsers();
+ break;
+ case '+':
+ ulist = this->GetVoicedUsers();
+ break;
+ default:
+ ulist = this->GetUsers();
+ break;
+ }
+
+ snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
+ std::string out = tb;
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end()))
+ {
+ if (serversource)
+ i->first->WriteServ(text);
+ else
+ i->first->Write(out);
+ }
+ }
+}
+
+void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, const std::string& text)
+{
+ CUList except_list;
+ except_list[user] = user->nick;
+ 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 chanrec::CountInvisible()
+{
+ int count = 0;
+ CUList *ulist= this->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (!(i->first->IsModeSet('i')))
+ count++;
+ }
+
+ return count;
+}
+
+char* chanrec::ChanModes(bool showkey)
+{
+ static char scratch[MAXBUF];
+ static char sparam[MAXBUF];
+ char* offset = scratch;
+ std::string extparam;
+
+ *scratch = '\0';
+ *sparam = '\0';
+
+ /* This was still iterating up to 190, chanrec::modes is only 64 elements -- Om */
+ for(int n = 0; n < 64; n++)
+ {
+ if(this->modes[n])
+ {
+ *offset++ = n + 65;
+ extparam.clear();
+ switch (n)
+ {
+ case CM_KEY:
+ extparam = (showkey ? this->key : "<key>");
+ break;
+ case CM_LIMIT:
+ extparam = ConvToStr(this->limit);
+ break;
+ case CM_NOEXTERNAL:
+ case CM_TOPICLOCK:
+ case CM_INVITEONLY:
+ case CM_MODERATED:
+ case CM_SECRET:
+ case CM_PRIVATE:
+ /* We know these have no parameters */
+ break;
+ default:
+ extparam = this->GetModeParameter(n + 65);
+ break;
+ }
+ if (!extparam.empty())
+ {
+ charlcat(sparam,' ',MAXBUF);
+ strlcat(sparam,extparam.c_str(),MAXBUF);
+ }
+ }
+ }
+
+ /* 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 chanrec::UserList(userrec *user, CUList *ulist)
+{
+ char list[MAXBUF];
+ size_t dlen, curlen;
+ int MOD_RESULT = 0;
+
+ if (!IS_LOCAL(user))
+ return;
+
+ FOREACH_RESULT(I_OnUserList,OnUserList(user, this, ulist));
+ if (MOD_RESULT == 1)
+ return;
+
+ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name);
+
+ int numusers = 0;
+ char* ptr = list + dlen;
+
+ if (!ulist)
+ ulist = this->GetUsers();
+
+ /* Improvement by Brain - this doesnt change in value, so why was it inside
+ * the loop?
+ */
+ bool has_user = this->HasUser(user);
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if ((!has_user) && (i->first->modes[UM_INVISIBLE]))
+ {
+ /*
+ * user is +i, and source not on the channel, does not show
+ * nick in NAMES list
+ */
+ continue;
+ }
+
+ if (i->first->Visibility && !i->first->Visibility->VisibleTo(user))
+ continue;
+
+ size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", this->GetPrefixChar(i->first), i->second.c_str());
+ /* OnUserList can change this - reset it back to normal */
+ i->second = i->first->nick;
+
+ curlen += ptrlen;
+ ptr += ptrlen;
+
+ numusers++;
+
+ if (curlen > (480-NICKMAX))
+ {
+ /* list overflowed into multiple numerics */
+ user->WriteServ(std::string(list));
+
+ /* reset our lengths */
+ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name);
+ ptr = list + dlen;
+
+ ptrlen = 0;
+ numusers = 0;
+ }
+ }
+
+ /* if whats left in the list isnt empty, send it */
+ if (numusers)
+ {
+ user->WriteServ(std::string(list));
+ }
+
+ user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, this->name);
+}
+
+long chanrec::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 (match(this->name,n->first.c_str()))
+ {
+ this->maxbans = n->second;
+ return n->second;
+ }
+ }
+
+ /* Screw it, just return the default of 64 */
+ this->maxbans = 64;
+ return this->maxbans;
+}
+
+void chanrec::ResetMaxBans()
+{
+ this->maxbans = 0;
+}
+
+/* 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* chanrec::GetPrefixChar(userrec *user)
+{
+ static char pf[2] = {0, 0};
+
+ prefixlist::iterator n = prefixes.find(user);
+ if (n != prefixes.end())
+ {
+ if (n->second.size())
+ {
+ /* If the user has any prefixes, their highest prefix
+ * will always be at the head of the list, as the list is
+ * sorted in rank order highest first (see SetPrefix()
+ * for reasons why)
+ */
+ *pf = n->second.begin()->first;
+ return pf;
+ }
+ }
+
+ *pf = 0;
+ return pf;
+}
+
+const char* chanrec::GetAllPrefixChars(userrec* user)
+{
+ static char prefix[MAXBUF];
+ int ctr = 0;
+ *prefix = 0;
+
+ prefixlist::iterator n = prefixes.find(user);
+ if (n != prefixes.end())
+ {
+ for (std::vector<prefixtype>::iterator x = n->second.begin(); x != n->second.end(); x++)
+ {
+ prefix[ctr++] = x->first;
+ }
+ }
+
+ prefix[ctr] = 0;
+
+ return prefix;
+}
+
+unsigned int chanrec::GetPrefixValue(userrec* user)
+{
+ prefixlist::iterator n = prefixes.find(user);
+ if (n != prefixes.end())
+ {
+ if (n->second.size())
+ return n->second.begin()->second;
+ }
+ return 0;
+}
+
+int chanrec::GetStatusFlags(userrec *user)
+{
+ UCListIter i = user->chans.find(this);
+ if (i != user->chans.end())
+ {
+ return i->second;
+ }
+ return 0;
+}
+
+int chanrec::GetStatus(userrec *user)
+{
+ if (ServerInstance->ULine(user->server))
+ return STATUS_OP;
+
+ UCListIter i = user->chans.find(this);
+ if (i != user->chans.end())
+ {
+ if ((i->second & UCMODE_OP) > 0)
+ {
+ return STATUS_OP;
+ }
+ if ((i->second & UCMODE_HOP) > 0)
+ {
+ return STATUS_HOP;
+ }
+ if ((i->second & UCMODE_VOICE) > 0)
+ {
+ return STATUS_VOICE;
+ }
+ return STATUS_NORMAL;
+ }
+ return STATUS_NORMAL;
+}
+
+void chanrec::SetPrefix(userrec* user, char prefix, unsigned int prefix_value, bool adding)
+{
+ prefixlist::iterator n = prefixes.find(user);
+ prefixtype pfx = std::make_pair(prefix,prefix_value);
+ if (adding)
+ {
+ if (n != prefixes.end())
+ {
+ if (std::find(n->second.begin(), n->second.end(), pfx) == n->second.end())
+ {
+ n->second.push_back(pfx);
+ /* We must keep prefixes in rank order, largest first.
+ * This is for two reasons, firstly because x-chat *ass-u-me's* this
+ * state, and secondly it turns out to be a benefit to us later.
+ * See above in GetPrefix().
+ */
+ std::sort(n->second.begin(), n->second.end(), ModeParser::PrefixComparison);
+ }
+ }
+ else
+ {
+ pfxcontainer one;
+ one.push_back(pfx);
+ prefixes.insert(std::make_pair<userrec*,pfxcontainer>(user, one));
+ }
+ }
+ else
+ {
+ if (n != prefixes.end())
+ {
+ pfxcontainer::iterator x = std::find(n->second.begin(), n->second.end(), pfx);
+ if (x != n->second.end())
+ n->second.erase(x);
+ }
+ }
+}
+
+void chanrec::RemoveAllPrefixes(userrec* user)
+{
+ prefixlist::iterator n = prefixes.find(user);
+ if (n != prefixes.end())
+ {
+ prefixes.erase(n);
+ }
+}
+
diff --git a/src/cmd_admin.cpp b/src/cmd_admin.cpp
index acfb8da43..698468d0c 100644
--- a/src/cmd_admin.cpp
+++ b/src/cmd_admin.cpp
@@ -1 +1,35 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_admin.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_admin(Instance); } /** Handle /ADMIN */ CmdResult cmd_admin::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("256 %s :Administrative info for %s",user->nick,ServerInstance->Config->ServerName); if (*ServerInstance->Config->AdminName) user->WriteServ("257 %s :Name - %s",user->nick,ServerInstance->Config->AdminName); user->WriteServ("258 %s :Nickname - %s",user->nick,ServerInstance->Config->AdminNick); user->WriteServ("258 %s :E-Mail - %s",user->nick,ServerInstance->Config->AdminEmail); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_admin.h"
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_admin(Instance);
+}
+
+/** Handle /ADMIN
+ */
+CmdResult cmd_admin::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("256 %s :Administrative info for %s",user->nick,ServerInstance->Config->ServerName);
+ if (*ServerInstance->Config->AdminName)
+ user->WriteServ("257 %s :Name - %s",user->nick,ServerInstance->Config->AdminName);
+ user->WriteServ("258 %s :Nickname - %s",user->nick,ServerInstance->Config->AdminNick);
+ user->WriteServ("258 %s :E-Mail - %s",user->nick,ServerInstance->Config->AdminEmail);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_away.cpp b/src/cmd_away.cpp
index 894924938..10f96c80b 100644
--- a/src/cmd_away.cpp
+++ b/src/cmd_away.cpp
@@ -1 +1,42 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_away.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_away(Instance); } /** Handle /AWAY */ CmdResult cmd_away::Handle (const char** parameters, int pcnt, userrec *user) { if ((pcnt) && (*parameters[0])) { strlcpy(user->awaymsg,parameters[0],MAXAWAY); user->WriteServ("306 %s :You have been marked as being away",user->nick); FOREACH_MOD(I_OnSetAway,OnSetAway(user)); } else { *user->awaymsg = 0; user->WriteServ("305 %s :You are no longer marked as being away",user->nick); FOREACH_MOD(I_OnCancelAway,OnCancelAway(user)); } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_away.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_away(Instance);
+}
+
+/** Handle /AWAY
+ */
+CmdResult cmd_away::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if ((pcnt) && (*parameters[0]))
+ {
+ strlcpy(user->awaymsg,parameters[0],MAXAWAY);
+ user->WriteServ("306 %s :You have been marked as being away",user->nick);
+ FOREACH_MOD(I_OnSetAway,OnSetAway(user));
+ }
+ else
+ {
+ *user->awaymsg = 0;
+ user->WriteServ("305 %s :You are no longer marked as being away",user->nick);
+ FOREACH_MOD(I_OnCancelAway,OnCancelAway(user));
+ }
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_clearcache.cpp b/src/cmd_clearcache.cpp
index cdc4b987c..daf8fa78b 100644
--- a/src/cmd_clearcache.cpp
+++ b/src/cmd_clearcache.cpp
@@ -1 +1,31 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_clearcache.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_clearcache(Instance); } /** Handle /CLEARCACHE */ CmdResult cmd_clearcache::Handle (const char** parameters, int pcnt, userrec *user) { int n = ServerInstance->Res->ClearCache(); user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick, n); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_clearcache.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_clearcache(Instance);
+}
+
+/** Handle /CLEARCACHE
+ */
+CmdResult cmd_clearcache::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ int n = ServerInstance->Res->ClearCache();
+ user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick, n);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_commands.cpp b/src/cmd_commands.cpp
index 9eff6ed50..fc95de5f1 100644
--- a/src/cmd_commands.cpp
+++ b/src/cmd_commands.cpp
@@ -1 +1,33 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_commands.h" /** Handle /COMMANDS */ extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_commands(Instance); } CmdResult cmd_commands::Handle (const char** parameters, int pcnt, userrec *user) { for (command_table::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++) { user->WriteServ("902 %s :%s %s %d",user->nick,i->second->command.c_str(),i->second->source.c_str(),i->second->min_params); } user->WriteServ("903 %s :End of COMMANDS list",user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_commands.h"
+
+/** Handle /COMMANDS
+ */
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_commands(Instance);
+}
+
+CmdResult cmd_commands::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ for (command_table::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
+ {
+ user->WriteServ("902 %s :%s %s %d",user->nick,i->second->command.c_str(),i->second->source.c_str(),i->second->min_params);
+ }
+ user->WriteServ("903 %s :End of COMMANDS list",user->nick);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_connect.cpp b/src/cmd_connect.cpp
index da4a11565..fc3ae9d7a 100644
--- a/src/cmd_connect.cpp
+++ b/src/cmd_connect.cpp
@@ -1 +1,33 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_connect.h" /* * This is handled by the server linking module, if necessary. Do not remove this stub. */ extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_connect(Instance); } /** Handle /CONNECT */ CmdResult cmd_connect::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_connect.h"
+
+/*
+ * This is handled by the server linking module, if necessary. Do not remove this stub.
+ */
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_connect(Instance);
+}
+
+/** Handle /CONNECT
+ */
+CmdResult cmd_connect::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_die.cpp b/src/cmd_die.cpp
index c8195a41c..7f0a81df8 100644
--- a/src/cmd_die.cpp
+++ b/src/cmd_die.cpp
@@ -1 +1,47 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_die.h" #include "exitcodes.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_die(Instance); } /** Handle /DIE */ CmdResult cmd_die::Handle (const char** parameters, int pcnt, userrec *user) { if (!strcmp(parameters[0],ServerInstance->Config->diepass)) { std::string diebuf = std::string("*** DIE command from ") + user->nick + "!" + user->ident + "@" + user->dhost + ". Terminating in " + ConvToStr(ServerInstance->Config->DieDelay) + " seconds."; ServerInstance->Log(SPARSE, diebuf); ServerInstance->SendError(diebuf); if (ServerInstance->Config->DieDelay) sleep(ServerInstance->Config->DieDelay); InspIRCd::Exit(EXIT_STATUS_DIE); } else { ServerInstance->Log(SPARSE, "Failed /DIE command from %s!%s@%s", user->nick, user->ident, user->host); ServerInstance->WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_die.h"
+#include "exitcodes.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_die(Instance);
+}
+
+/** Handle /DIE
+ */
+CmdResult cmd_die::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (!strcmp(parameters[0],ServerInstance->Config->diepass))
+ {
+ std::string diebuf = std::string("*** DIE command from ") + user->nick + "!" + user->ident + "@" + user->dhost + ". Terminating in " + ConvToStr(ServerInstance->Config->DieDelay) + " seconds.";
+ ServerInstance->Log(SPARSE, diebuf);
+ ServerInstance->SendError(diebuf);
+
+ if (ServerInstance->Config->DieDelay)
+ sleep(ServerInstance->Config->DieDelay);
+
+ InspIRCd::Exit(EXIT_STATUS_DIE);
+ }
+ else
+ {
+ ServerInstance->Log(SPARSE, "Failed /DIE command from %s!%s@%s", user->nick, user->ident, user->host);
+ ServerInstance->WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host);
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_eline.cpp b/src/cmd_eline.cpp
index 0282369c1..793f7a413 100644
--- a/src/cmd_eline.cpp
+++ b/src/cmd_eline.cpp
@@ -1 +1,77 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_eline.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_eline(Instance); } /** Handle /ELINE */ CmdResult cmd_eline::Handle (const char** parameters, int pcnt, userrec *user) { if (pcnt >= 3) { IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]); if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) return CMD_FAILURE; if (!strchr(parameters[0],'@')) { user->WriteServ("NOTICE %s :*** E-Line must contain a username, e.g. *@%s",user->nick,parameters[0]); return CMD_FAILURE; } long duration = ServerInstance->Duration(parameters[1]); if (ServerInstance->XLines->add_eline(duration,user->nick,parameters[2],parameters[0])) { FOREACH_MOD(I_OnAddELine,OnAddELine(duration, user, parameters[2], parameters[0])); if (!duration) { ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s.",user->nick,parameters[0]); } else { time_t c_requires_crap = duration + ServerInstance->Time(); ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s",user->nick,parameters[0], ServerInstance->TimeString(c_requires_crap).c_str()); } } else { user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick,parameters[0]); } } else { if (ServerInstance->XLines->del_eline(parameters[0])) { FOREACH_MOD(I_OnDelELine,OnDelELine(user, parameters[0])); ServerInstance->SNO->WriteToSnoMask('x',"%s Removed E-line on %s.",user->nick,parameters[0]); } else { user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick,parameters[0]); } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_eline.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_eline(Instance);
+}
+
+/** Handle /ELINE
+ */
+CmdResult cmd_eline::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (pcnt >= 3)
+ {
+ IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]);
+ if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
+ return CMD_FAILURE;
+
+ if (!strchr(parameters[0],'@'))
+ {
+ user->WriteServ("NOTICE %s :*** E-Line must contain a username, e.g. *@%s",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ long duration = ServerInstance->Duration(parameters[1]);
+ if (ServerInstance->XLines->add_eline(duration,user->nick,parameters[2],parameters[0]))
+ {
+ FOREACH_MOD(I_OnAddELine,OnAddELine(duration, user, parameters[2], parameters[0]));
+
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s",user->nick,parameters[0],
+ ServerInstance->TimeString(c_requires_crap).c_str());
+ }
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick,parameters[0]);
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->del_eline(parameters[0]))
+ {
+ FOREACH_MOD(I_OnDelELine,OnDelELine(user, parameters[0]));
+ ServerInstance->SNO->WriteToSnoMask('x',"%s Removed E-line on %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick,parameters[0]);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_gline.cpp b/src/cmd_gline.cpp
index c1632df82..f208d40bf 100644
--- a/src/cmd_gline.cpp
+++ b/src/cmd_gline.cpp
@@ -1 +1,89 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_gline.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_gline(Instance); } /** Handle /GLINE */ CmdResult cmd_gline::Handle (const char** parameters, int pcnt, userrec *user) { if (pcnt >= 3) { IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]); if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) return CMD_FAILURE; if (!strchr(parameters[0],'@')) { user->WriteServ("NOTICE %s :*** G-Line must contain a username, e.g. *@%s",user->nick,parameters[0]); return CMD_FAILURE; } else if (strchr(parameters[0],'!')) { user->WriteServ("NOTICE %s :*** G-Line cannot contain a nickname!",user->nick); return CMD_FAILURE; } long duration = ServerInstance->Duration(parameters[1]); if (ServerInstance->XLines->add_gline(duration,user->nick,parameters[2],parameters[0])) { int to_apply = APPLY_GLINES; FOREACH_MOD(I_OnAddGLine,OnAddGLine(duration, user, parameters[2], parameters[0])); if (!duration) { ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s.",user->nick,parameters[0]); to_apply |= APPLY_PERM_ONLY; } else { time_t c_requires_crap = duration + ServerInstance->Time(); ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s",user->nick,parameters[0], ServerInstance->TimeString(c_requires_crap).c_str()); } ServerInstance->XLines->apply_lines(to_apply); } else { user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick,parameters[0]); } } else { if (ServerInstance->XLines->del_gline(parameters[0])) { FOREACH_MOD(I_OnDelGLine,OnDelGLine(user, parameters[0])); ServerInstance->SNO->WriteToSnoMask('x',"%s Removed G-line on %s.",user->nick,parameters[0]); } else { user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick,parameters[0]); } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_gline.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_gline(Instance);
+}
+
+/** Handle /GLINE
+ */
+CmdResult cmd_gline::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (pcnt >= 3)
+ {
+ IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]);
+ if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
+ return CMD_FAILURE;
+
+ if (!strchr(parameters[0],'@'))
+ {
+ user->WriteServ("NOTICE %s :*** G-Line must contain a username, e.g. *@%s",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+ else if (strchr(parameters[0],'!'))
+ {
+ user->WriteServ("NOTICE %s :*** G-Line cannot contain a nickname!",user->nick);
+ return CMD_FAILURE;
+ }
+
+ long duration = ServerInstance->Duration(parameters[1]);
+ if (ServerInstance->XLines->add_gline(duration,user->nick,parameters[2],parameters[0]))
+ {
+ int to_apply = APPLY_GLINES;
+
+ FOREACH_MOD(I_OnAddGLine,OnAddGLine(duration, user, parameters[2], parameters[0]));
+
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s.",user->nick,parameters[0]);
+ to_apply |= APPLY_PERM_ONLY;
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s",user->nick,parameters[0],
+ ServerInstance->TimeString(c_requires_crap).c_str());
+ }
+
+ ServerInstance->XLines->apply_lines(to_apply);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick,parameters[0]);
+ }
+
+ }
+ else
+ {
+ if (ServerInstance->XLines->del_gline(parameters[0]))
+ {
+ FOREACH_MOD(I_OnDelGLine,OnDelGLine(user, parameters[0]));
+ ServerInstance->SNO->WriteToSnoMask('x',"%s Removed G-line on %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick,parameters[0]);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_info.cpp b/src/cmd_info.cpp
index de6c18d82..3564b0c6a 100644
--- a/src/cmd_info.cpp
+++ b/src/cmd_info.cpp
@@ -1 +1,75 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_info.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_info(Instance); } /** Handle /INFO */ CmdResult cmd_info::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ( "371 %s :. o O ( \2The Inspire Internet Relay Chat Server\2 ) O o .", user->nick); user->WriteServ( "371 %s : ( \2Putting the ricer into ircer since 2007\2 )", user->nick); user->WriteServ( "371 %s : ", user->nick); user->WriteServ( "371 %s :\2Core Developers\2:", user->nick); user->WriteServ( "371 %s : Craig Edwards (Brain)", user->nick); user->WriteServ( "371 %s : Craig McLure", user->nick); user->WriteServ( "371 %s : w00t", user->nick); user->WriteServ( "371 %s : Om", user->nick); user->WriteServ( "371 %s : Special", user->nick); user->WriteServ( "371 %s : pippijn", user->nick); user->WriteServ( "371 %s : peavey", user->nick); user->WriteServ( "371 %s : Burlex", user->nick); user->WriteServ( "371 %s : ", user->nick); user->WriteServ( "371 %s :\2Contributors\2:", user->nick); user->WriteServ( "371 %s : typobox43 Jazza", user->nick); user->WriteServ( "371 %s : jamie LeaChim", user->nick); user->WriteServ( "371 %s : satmd nenolod", user->nick); user->WriteServ( "371 %s : HiroP BuildSmart", user->nick); user->WriteServ( "371 %s : ", user->nick); user->WriteServ( "371 %s :\2Quality Assurance\2:", user->nick); user->WriteServ( "371 %s : Bricker owine", user->nick); user->WriteServ( "371 %s : dmb Adremelech", user->nick); user->WriteServ( "371 %s : ThePopeSVCD satmd", user->nick); user->WriteServ( "371 %s :\2Testers\2:", user->nick); user->WriteServ( "371 %s : CC Piggles", user->nick); user->WriteServ( "371 %s : Foamy Hart", user->nick); user->WriteServ( "371 %s : RageD [ed]", user->nick); user->WriteServ( "371 %s : Azhrarn luigiman", user->nick); user->WriteServ( "371 %s : Chu aquanight", user->nick); user->WriteServ( "371 %s : xptek Grantlinks", user->nick); user->WriteServ( "371 %s : Rob angelic", user->nick); user->WriteServ( "371 %s : Jason ThaPrince", user->nick); user->WriteServ( "371 %s : eggy skenmy", user->nick); user->WriteServ( "371 %s : ", user->nick); user->WriteServ( "371 %s :Contains portions of \2FireDNS\2 written by", user->nick); user->WriteServ( "371 %s :Ian Gulliver, (c) 2002.", user->nick); user->WriteServ( "371 %s : ", user->nick); user->WriteServ( "371 %s :Thanks to \2irc-junkie\2 and \2SearchIRC\2", user->nick); user->WriteServ( "371 %s :for the nice comments and the help", user->nick); user->WriteServ( "371 %s :you gave us in attracting users to", user->nick); user->WriteServ( "371 %s :this software.", user->nick); user->WriteServ( "371 %s : ", user->nick); user->WriteServ( "371 %s :Best experienced with: \2An IRC client\2.", user->nick); FOREACH_MOD(I_OnInfo,OnInfo(user)); user->WriteServ( "374 %s :End of /INFO list", user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_info.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_info(Instance);
+}
+
+/** Handle /INFO
+ */
+CmdResult cmd_info::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ( "371 %s :. o O ( \2The Inspire Internet Relay Chat Server\2 ) O o .", user->nick);
+ user->WriteServ( "371 %s : ( \2Putting the ricer into ircer since 2007\2 )", user->nick);
+ user->WriteServ( "371 %s : ", user->nick);
+ user->WriteServ( "371 %s :\2Core Developers\2:", user->nick);
+ user->WriteServ( "371 %s : Craig Edwards (Brain)", user->nick);
+ user->WriteServ( "371 %s : Craig McLure", user->nick);
+ user->WriteServ( "371 %s : w00t", user->nick);
+ user->WriteServ( "371 %s : Om", user->nick);
+ user->WriteServ( "371 %s : Special", user->nick);
+ user->WriteServ( "371 %s : pippijn", user->nick);
+ user->WriteServ( "371 %s : peavey", user->nick);
+ user->WriteServ( "371 %s : Burlex", user->nick);
+ user->WriteServ( "371 %s : ", user->nick);
+ user->WriteServ( "371 %s :\2Contributors\2:", user->nick);
+ user->WriteServ( "371 %s : typobox43 Jazza", user->nick);
+ user->WriteServ( "371 %s : jamie LeaChim", user->nick);
+ user->WriteServ( "371 %s : satmd nenolod", user->nick);
+ user->WriteServ( "371 %s : HiroP BuildSmart", user->nick);
+ user->WriteServ( "371 %s : ", user->nick);
+ user->WriteServ( "371 %s :\2Quality Assurance\2:", user->nick);
+ user->WriteServ( "371 %s : Bricker owine", user->nick);
+ user->WriteServ( "371 %s : dmb Adremelech", user->nick);
+ user->WriteServ( "371 %s : ThePopeSVCD satmd", user->nick);
+ user->WriteServ( "371 %s :\2Testers\2:", user->nick);
+ user->WriteServ( "371 %s : CC Piggles", user->nick);
+ user->WriteServ( "371 %s : Foamy Hart", user->nick);
+ user->WriteServ( "371 %s : RageD [ed]", user->nick);
+ user->WriteServ( "371 %s : Azhrarn luigiman", user->nick);
+ user->WriteServ( "371 %s : Chu aquanight", user->nick);
+ user->WriteServ( "371 %s : xptek Grantlinks", user->nick);
+ user->WriteServ( "371 %s : Rob angelic", user->nick);
+ user->WriteServ( "371 %s : Jason ThaPrince", user->nick);
+ user->WriteServ( "371 %s : eggy skenmy", user->nick);
+ user->WriteServ( "371 %s : ", user->nick);
+ user->WriteServ( "371 %s :Contains portions of \2FireDNS\2 written by", user->nick);
+ user->WriteServ( "371 %s :Ian Gulliver, (c) 2002.", user->nick);
+ user->WriteServ( "371 %s : ", user->nick);
+ user->WriteServ( "371 %s :Thanks to \2irc-junkie\2 and \2SearchIRC\2", user->nick);
+ user->WriteServ( "371 %s :for the nice comments and the help", user->nick);
+ user->WriteServ( "371 %s :you gave us in attracting users to", user->nick);
+ user->WriteServ( "371 %s :this software.", user->nick);
+ user->WriteServ( "371 %s : ", user->nick);
+ user->WriteServ( "371 %s :Best experienced with: \2An IRC client\2.", user->nick);
+ FOREACH_MOD(I_OnInfo,OnInfo(user));
+ user->WriteServ( "374 %s :End of /INFO list", user->nick);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_invite.cpp b/src/cmd_invite.cpp
index 19330783e..0eb3a2354 100644
--- a/src/cmd_invite.cpp
+++ b/src/cmd_invite.cpp
@@ -1 +1,98 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_invite.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_invite(Instance); } /** Handle /INVITE */ CmdResult cmd_invite::Handle (const char** parameters, int pcnt, userrec *user) { int MOD_RESULT = 0; if (pcnt == 2) { userrec* u = ServerInstance->FindNick(parameters[0]); chanrec* c = ServerInstance->FindChan(parameters[1]); if ((!c) || (!u)) { if (!c) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]); } else { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); } return CMD_FAILURE; } if ((c->modes[CM_INVITEONLY]) && (IS_LOCAL(user))) { if (c->GetStatus(user) < STATUS_HOP) { user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name); return CMD_FAILURE; } } if (c->HasUser(u)) { user->WriteServ("443 %s %s %s :is already on channel",user->nick,u->nick,c->name); return CMD_FAILURE; } if ((IS_LOCAL(user)) && (!c->HasUser(user))) { user->WriteServ("442 %s %s :You're not on that channel!",user->nick, c->name); return CMD_FAILURE; } FOREACH_RESULT(I_OnUserPreInvite,OnUserPreInvite(user,u,c)); if (MOD_RESULT == 1) { return CMD_FAILURE; } u->InviteTo(c->name); u->WriteFrom(user,"INVITE %s :%s",u->nick,c->name); user->WriteServ("341 %s %s %s",user->nick,u->nick,c->name); if (ServerInstance->Config->AnnounceInvites) c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s invited %s into the channel", c->name, user->nick, u->nick); FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c)); } else { // pinched from ircu - invite with not enough parameters shows channels // youve been invited to but haven't joined yet. InvitedList* il = user->GetInviteList(); for (InvitedList::iterator i = il->begin(); i != il->end(); i++) { user->WriteServ("346 %s :%s",user->nick,i->c_str()); } user->WriteServ("347 %s :End of INVITE list",user->nick); } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_invite.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_invite(Instance);
+}
+
+/** Handle /INVITE
+ */
+CmdResult cmd_invite::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ int MOD_RESULT = 0;
+
+ if (pcnt == 2)
+ {
+ userrec* u = ServerInstance->FindNick(parameters[0]);
+ chanrec* c = ServerInstance->FindChan(parameters[1]);
+
+ if ((!c) || (!u))
+ {
+ if (!c)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ }
+
+ return CMD_FAILURE;
+ }
+
+ if ((c->modes[CM_INVITEONLY]) && (IS_LOCAL(user)))
+ {
+ if (c->GetStatus(user) < STATUS_HOP)
+ {
+ user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+ }
+
+ if (c->HasUser(u))
+ {
+ user->WriteServ("443 %s %s %s :is already on channel",user->nick,u->nick,c->name);
+ return CMD_FAILURE;
+ }
+
+ if ((IS_LOCAL(user)) && (!c->HasUser(user)))
+ {
+ user->WriteServ("442 %s %s :You're not on that channel!",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+
+ FOREACH_RESULT(I_OnUserPreInvite,OnUserPreInvite(user,u,c));
+
+ if (MOD_RESULT == 1)
+ {
+ return CMD_FAILURE;
+ }
+
+ u->InviteTo(c->name);
+ u->WriteFrom(user,"INVITE %s :%s",u->nick,c->name);
+ user->WriteServ("341 %s %s %s",user->nick,u->nick,c->name);
+ if (ServerInstance->Config->AnnounceInvites)
+ c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s invited %s into the channel", c->name, user->nick, u->nick);
+ FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c));
+ }
+ else
+ {
+ // pinched from ircu - invite with not enough parameters shows channels
+ // youve been invited to but haven't joined yet.
+ InvitedList* il = user->GetInviteList();
+ for (InvitedList::iterator i = il->begin(); i != il->end(); i++)
+ {
+ user->WriteServ("346 %s :%s",user->nick,i->c_str());
+ }
+ user->WriteServ("347 %s :End of INVITE list",user->nick);
+ }
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_ison.cpp b/src/cmd_ison.cpp
index f63019427..0cee160c7 100644
--- a/src/cmd_ison.cpp
+++ b/src/cmd_ison.cpp
@@ -1 +1,86 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_ison.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_ison(Instance); } /** Handle /ISON */ CmdResult cmd_ison::Handle (const char** parameters, int pcnt, userrec *user) { std::map<userrec*,userrec*> ison_already; userrec *u; std::string reply = std::string("303 ") + user->nick + " :"; for (int i = 0; i < pcnt; i++) { u = ServerInstance->FindNick(parameters[i]); if (ison_already.find(u) != ison_already.end()) continue; if (u) { reply.append(u->nick).append(" "); if (reply.length() > 450) { user->WriteServ(reply); reply = std::string("303 ") + user->nick + " :"; } ison_already[u] = u; } else { if ((i == pcnt-1) && (strchr(parameters[i],' '))) { /* Its a space seperated list of nicks (RFC1459 says to support this) */ irc::spacesepstream list(parameters[i]); std::string item("*"); while (((item = list.GetToken()) != "")) { u = ServerInstance->FindNick(item); if (ison_already.find(u) != ison_already.end()) continue; if (u) { if (u->Visibility && !u->Visibility->VisibleTo(user)) continue; reply.append(u->nick).append(" "); if (reply.length() > 450) { user->WriteServ(reply); reply = std::string("303 ") + user->nick + " :"; } ison_already[u] = u; } } } /* There will only be one of these, we can bail after. */ break; } } if (!reply.empty()) user->WriteServ(reply); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_ison.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_ison(Instance);
+}
+
+/** Handle /ISON
+ */
+CmdResult cmd_ison::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ std::map<userrec*,userrec*> ison_already;
+ userrec *u;
+ std::string reply = std::string("303 ") + user->nick + " :";
+
+ for (int i = 0; i < pcnt; i++)
+ {
+ u = ServerInstance->FindNick(parameters[i]);
+ if (ison_already.find(u) != ison_already.end())
+ continue;
+
+ if (u)
+ {
+ reply.append(u->nick).append(" ");
+ if (reply.length() > 450)
+ {
+ user->WriteServ(reply);
+ reply = std::string("303 ") + user->nick + " :";
+ }
+ ison_already[u] = u;
+ }
+ else
+ {
+ if ((i == pcnt-1) && (strchr(parameters[i],' ')))
+ {
+ /* Its a space seperated list of nicks (RFC1459 says to support this)
+ */
+ irc::spacesepstream list(parameters[i]);
+ std::string item("*");
+ while (((item = list.GetToken()) != ""))
+ {
+ u = ServerInstance->FindNick(item);
+ if (ison_already.find(u) != ison_already.end())
+ continue;
+
+ if (u)
+ {
+ if (u->Visibility && !u->Visibility->VisibleTo(user))
+ continue;
+
+ reply.append(u->nick).append(" ");
+ if (reply.length() > 450)
+ {
+ user->WriteServ(reply);
+ reply = std::string("303 ") + user->nick + " :";
+ }
+ ison_already[u] = u;
+ }
+ }
+ }
+ /* There will only be one of these, we can bail after. */
+ break;
+ }
+ }
+
+ if (!reply.empty())
+ user->WriteServ(reply);
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_join.cpp b/src/cmd_join.cpp
index 6780ac4be..218e0f7b8 100644
--- a/src/cmd_join.cpp
+++ b/src/cmd_join.cpp
@@ -1 +1,52 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_join.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_join(Instance); } /** Handle /JOIN */ CmdResult cmd_join::Handle (const char** parameters, int pcnt, userrec *user) { if (pcnt > 1) { if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0, 1)) return CMD_SUCCESS; if (ServerInstance->IsChannel(parameters[0])) { chanrec::JoinUser(ServerInstance, user, parameters[0], false, parameters[1]); return CMD_SUCCESS; } } else { if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_SUCCESS; if (ServerInstance->IsChannel(parameters[0])) { chanrec::JoinUser(ServerInstance, user, parameters[0], false, ""); return CMD_SUCCESS; } } user->WriteServ("403 %s %s :Invalid channel name",user->nick, parameters[0]); return CMD_FAILURE; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_join.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_join(Instance);
+}
+
+/** Handle /JOIN
+ */
+CmdResult cmd_join::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (pcnt > 1)
+ {
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0, 1))
+ return CMD_SUCCESS;
+
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ chanrec::JoinUser(ServerInstance, user, parameters[0], false, parameters[1]);
+ return CMD_SUCCESS;
+ }
+ }
+ else
+ {
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_SUCCESS;
+
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ chanrec::JoinUser(ServerInstance, user, parameters[0], false, "");
+ return CMD_SUCCESS;
+ }
+ }
+
+ user->WriteServ("403 %s %s :Invalid channel name",user->nick, parameters[0]);
+ return CMD_FAILURE;
+}
diff --git a/src/cmd_kick.cpp b/src/cmd_kick.cpp
index 263cf35c5..17933a43f 100644
--- a/src/cmd_kick.cpp
+++ b/src/cmd_kick.cpp
@@ -1 +1,58 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "inspircd.h" #include "commands/cmd_kick.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_kick(Instance); } /** Handle /KICK */ CmdResult cmd_kick::Handle (const char** parameters, int pcnt, userrec *user) { char reason[MAXKICK]; chanrec* c = ServerInstance->FindChan(parameters[0]); userrec* u = ServerInstance->FindNick(parameters[1]); if (!u || !c) { user->WriteServ( "401 %s %s :No such nick/channel", user->nick, u ? parameters[0] : parameters[1]); 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, parameters[0]); return CMD_FAILURE; } if (pcnt > 2) { strlcpy(reason, parameters[2], MAXKICK - 1); } else { strlcpy(reason, user->nick, MAXKICK - 1); } if (!c->KickUser(user, u, reason)) /* Nobody left here, delete the chanrec */ delete c; return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "inspircd.h"
+#include "commands/cmd_kick.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_kick(Instance);
+}
+
+/** Handle /KICK
+ */
+CmdResult cmd_kick::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ char reason[MAXKICK];
+ chanrec* c = ServerInstance->FindChan(parameters[0]);
+ userrec* u = ServerInstance->FindNick(parameters[1]);
+
+ if (!u || !c)
+ {
+ user->WriteServ( "401 %s %s :No such nick/channel", user->nick, u ? parameters[0] : parameters[1]);
+ 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, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ if (pcnt > 2)
+ {
+ strlcpy(reason, parameters[2], MAXKICK - 1);
+ }
+ else
+ {
+ strlcpy(reason, user->nick, MAXKICK - 1);
+ }
+
+ if (!c->KickUser(user, u, reason))
+ /* Nobody left here, delete the chanrec */
+ delete c;
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_kill.cpp b/src/cmd_kill.cpp
index b8dbfc345..26f354b8f 100644
--- a/src/cmd_kill.cpp
+++ b/src/cmd_kill.cpp
@@ -1 +1,117 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "commands/cmd_kill.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_kill(Instance); } /** Handle /KILL */ CmdResult cmd_kill::Handle (const char** parameters, int pcnt, userrec *user) { /* Allow comma seperated lists of users for /KILL (thanks w00t) */ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_SUCCESS; userrec *u = ServerInstance->FindNick(parameters[0]); char killreason[MAXBUF]; char killoperreason[MAXBUF]; int MOD_RESULT = 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 */ FOREACH_RESULT(I_OnKill, OnKill(user, u, parameters[1])); if (MOD_RESULT) return CMD_FAILURE; if (*ServerInstance->Config->HideKillsServer) { // hidekills is on, use it snprintf(killreason, MAXQUIT, "Killed (%s (%s))", ServerInstance->Config->HideKillsServer, parameters[1]); } else { // hidekills is off, do nothing snprintf(killreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]); } // opers are lucky ducks, they always see the real reason snprintf(killoperreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]); } else { snprintf(killreason, MAXQUIT, "%s", parameters[1]); /* * XXX - yes, this means opers will probably see a censored kill remotely. this needs fixing. * maybe a version of QuitUser that doesn't take nor propegate an oper reason? -- w00t */ snprintf(killoperreason, MAXQUIT, "%s", parameters[1]); } /* * 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@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]); FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killoperreason)); } 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 */ ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s!%s@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]); ServerInstance->Log(DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost, user->nick, parameters[1]); user->WriteTo(u, "KILL %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost, *ServerInstance->Config->HideKillsServer ? ServerInstance->Config->HideKillsServer : user->nick, parameters[1]); } // send the quit out userrec::QuitUser(ServerInstance, u, killreason, killoperreason); } else { user->WriteServ( "401 %s %s :No such nick/channel", user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_kill.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_kill(Instance);
+}
+
+/** Handle /KILL
+ */
+CmdResult cmd_kill::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ /* Allow comma seperated lists of users for /KILL (thanks w00t) */
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_SUCCESS;
+
+ userrec *u = ServerInstance->FindNick(parameters[0]);
+ char killreason[MAXBUF];
+ char killoperreason[MAXBUF];
+ int MOD_RESULT = 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
+ */
+ FOREACH_RESULT(I_OnKill, OnKill(user, u, parameters[1]));
+
+ if (MOD_RESULT)
+ return CMD_FAILURE;
+
+ if (*ServerInstance->Config->HideKillsServer)
+ {
+ // hidekills is on, use it
+ snprintf(killreason, MAXQUIT, "Killed (%s (%s))", ServerInstance->Config->HideKillsServer, parameters[1]);
+ }
+ else
+ {
+ // hidekills is off, do nothing
+ snprintf(killreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]);
+ }
+
+ // opers are lucky ducks, they always see the real reason
+ snprintf(killoperreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]);
+ }
+ else
+ {
+ snprintf(killreason, MAXQUIT, "%s", parameters[1]);
+ /*
+ * XXX - yes, this means opers will probably see a censored kill remotely. this needs fixing.
+ * maybe a version of QuitUser that doesn't take nor propegate an oper reason? -- w00t
+ */
+ snprintf(killoperreason, MAXQUIT, "%s", parameters[1]);
+ }
+
+ /*
+ * 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@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]);
+ FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killoperreason));
+ }
+ 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
+ */
+ ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s!%s@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]);
+ ServerInstance->Log(DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost, user->nick, parameters[1]);
+ user->WriteTo(u, "KILL %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost,
+ *ServerInstance->Config->HideKillsServer ? ServerInstance->Config->HideKillsServer : user->nick, parameters[1]);
+ }
+
+ // send the quit out
+ userrec::QuitUser(ServerInstance, u, killreason, killoperreason);
+ }
+ else
+ {
+ user->WriteServ( "401 %s %s :No such nick/channel", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_kline.cpp b/src/cmd_kline.cpp
index ac1aafe15..05856893a 100644
--- a/src/cmd_kline.cpp
+++ b/src/cmd_kline.cpp
@@ -1 +1,88 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_kline.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_kline(Instance); } /** Handle /KLINE */ CmdResult cmd_kline::Handle (const char** parameters, int pcnt, userrec *user) { if (pcnt >= 3) { IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]); if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) return CMD_FAILURE; if (!strchr(parameters[0],'@')) { user->WriteServ("NOTICE %s :*** K-Line must contain a username, e.g. *@%s",user->nick,parameters[0]); return CMD_FAILURE; } else if (strchr(parameters[0],'!')) { user->WriteServ("NOTICE %s :*** K-Line cannot contain a nickname!",user->nick); return CMD_FAILURE; } long duration = ServerInstance->Duration(parameters[1]); if (ServerInstance->XLines->add_kline(duration,user->nick,parameters[2],parameters[0])) { int to_apply = APPLY_KLINES; FOREACH_MOD(I_OnAddKLine,OnAddKLine(duration, user, parameters[2], parameters[0])); if (!duration) { ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s.",user->nick,parameters[0]); to_apply |= APPLY_PERM_ONLY; } else { time_t c_requires_crap = duration + ServerInstance->Time(); ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s",user->nick,parameters[0], ServerInstance->TimeString(c_requires_crap).c_str()); } ServerInstance->XLines->apply_lines(to_apply); } else { user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick,parameters[0]); } } else { if (ServerInstance->XLines->del_kline(parameters[0])) { FOREACH_MOD(I_OnDelKLine,OnDelKLine(user, parameters[0])); ServerInstance->SNO->WriteToSnoMask('x',"%s Removed K-line on %s.",user->nick,parameters[0]); } else { user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick,parameters[0]); } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_kline.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_kline(Instance);
+}
+
+/** Handle /KLINE
+ */
+CmdResult cmd_kline::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (pcnt >= 3)
+ {
+ IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]);
+ if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
+ return CMD_FAILURE;
+
+ if (!strchr(parameters[0],'@'))
+ {
+ user->WriteServ("NOTICE %s :*** K-Line must contain a username, e.g. *@%s",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+ else if (strchr(parameters[0],'!'))
+ {
+ user->WriteServ("NOTICE %s :*** K-Line cannot contain a nickname!",user->nick);
+ return CMD_FAILURE;
+ }
+
+ long duration = ServerInstance->Duration(parameters[1]);
+ if (ServerInstance->XLines->add_kline(duration,user->nick,parameters[2],parameters[0]))
+ {
+ int to_apply = APPLY_KLINES;
+
+ FOREACH_MOD(I_OnAddKLine,OnAddKLine(duration, user, parameters[2], parameters[0]));
+
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s.",user->nick,parameters[0]);
+ to_apply |= APPLY_PERM_ONLY;
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s",user->nick,parameters[0],
+ ServerInstance->TimeString(c_requires_crap).c_str());
+ }
+
+ ServerInstance->XLines->apply_lines(to_apply);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick,parameters[0]);
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->del_kline(parameters[0]))
+ {
+ FOREACH_MOD(I_OnDelKLine,OnDelKLine(user, parameters[0]));
+ ServerInstance->SNO->WriteToSnoMask('x',"%s Removed K-line on %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick,parameters[0]);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_links.cpp b/src/cmd_links.cpp
index 39f01383e..83e558d5d 100644
--- a/src/cmd_links.cpp
+++ b/src/cmd_links.cpp
@@ -1 +1,32 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_links.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_links(Instance); } /** Handle /LINKS */ CmdResult cmd_links::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("364 %s %s %s :0 %s",user->nick,ServerInstance->Config->ServerName,ServerInstance->Config->ServerName,ServerInstance->Config->ServerDesc); user->WriteServ("365 %s * :End of /LINKS list.",user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_links.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_links(Instance);
+}
+
+/** Handle /LINKS
+ */
+CmdResult cmd_links::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("364 %s %s %s :0 %s",user->nick,ServerInstance->Config->ServerName,ServerInstance->Config->ServerName,ServerInstance->Config->ServerDesc);
+ user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_list.cpp b/src/cmd_list.cpp
index 14cf45272..ed956e7f1 100644
--- a/src/cmd_list.cpp
+++ b/src/cmd_list.cpp
@@ -1 +1,85 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "inspircd.h" #include "commands/cmd_list.h" #include "wildcard.h" /** Handle /LIST */ extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_list(Instance); } CmdResult cmd_list::Handle (const char** parameters, int pcnt, userrec *user) { int minusers = 0, maxusers = 0; user->WriteServ("321 %s Channel :Users Name",user->nick); /* Work around mIRC suckyness. YOU SUCK, KHALED! */ if (pcnt == 1) { if (*parameters[0] == '<') { maxusers = atoi(parameters[0]+1); pcnt = 0; } else if (*parameters[0] == '>') { minusers = atoi(parameters[0]+1); pcnt = 0; } } 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 (pcnt) { if (!match(i->second->name, parameters[0]) && !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); if ((i->second->modes[CM_PRIVATE]) && (!n)) { if (users) user->WriteServ("322 %s *",user->nick,i->second->name); } else { if (((!(i->second->modes[CM_PRIVATE])) && (!(i->second->modes[CM_SECRET]))) || (n)) { long users = i->second->GetUserCounter(); if (users) user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,users,i->second->ChanModes(n),i->second->topic); } } } user->WriteServ("323 %s :End of channel list.",user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "inspircd.h"
+#include "commands/cmd_list.h"
+#include "wildcard.h"
+
+/** Handle /LIST
+ */
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_list(Instance);
+}
+
+CmdResult cmd_list::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ int minusers = 0, maxusers = 0;
+
+ user->WriteServ("321 %s Channel :Users Name",user->nick);
+
+ /* Work around mIRC suckyness. YOU SUCK, KHALED! */
+ if (pcnt == 1)
+ {
+ if (*parameters[0] == '<')
+ {
+ maxusers = atoi(parameters[0]+1);
+ pcnt = 0;
+ }
+ else if (*parameters[0] == '>')
+ {
+ minusers = atoi(parameters[0]+1);
+ pcnt = 0;
+ }
+ }
+
+ 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 (pcnt)
+ {
+ if (!match(i->second->name, parameters[0]) && !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);
+ if ((i->second->modes[CM_PRIVATE]) && (!n))
+ {
+ if (users)
+ user->WriteServ("322 %s *",user->nick,i->second->name);
+ }
+ else
+ {
+ if (((!(i->second->modes[CM_PRIVATE])) && (!(i->second->modes[CM_SECRET]))) || (n))
+ {
+ long users = i->second->GetUserCounter();
+ if (users)
+ user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,users,i->second->ChanModes(n),i->second->topic);
+ }
+ }
+ }
+ user->WriteServ("323 %s :End of channel list.",user->nick);
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_loadmodule.cpp b/src/cmd_loadmodule.cpp
index 07776c1a8..08179d120 100644
--- a/src/cmd_loadmodule.cpp
+++ b/src/cmd_loadmodule.cpp
@@ -1 +1,39 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_loadmodule.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_loadmodule(Instance); } /** Handle /LOADMODULE */ CmdResult cmd_loadmodule::Handle (const char** parameters, int pcnt, userrec *user) { if (ServerInstance->LoadModule(parameters[0])) { ServerInstance->WriteOpers("*** NEW MODULE: %s loaded %s",user->nick, parameters[0]); user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]); return CMD_SUCCESS; } else { user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); return CMD_FAILURE; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_loadmodule.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_loadmodule(Instance);
+}
+
+/** Handle /LOADMODULE
+ */
+CmdResult cmd_loadmodule::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (ServerInstance->LoadModule(parameters[0]))
+ {
+ ServerInstance->WriteOpers("*** NEW MODULE: %s loaded %s",user->nick, parameters[0]);
+ user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]);
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
+ return CMD_FAILURE;
+ }
+}
+
diff --git a/src/cmd_lusers.cpp b/src/cmd_lusers.cpp
index 87194d9e2..fa58ca076 100644
--- a/src/cmd_lusers.cpp
+++ b/src/cmd_lusers.cpp
@@ -1 +1,42 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "inspircd.h" #include "commands/cmd_lusers.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_lusers(Instance); } /** Handle /LUSERS */ CmdResult cmd_lusers::Handle (const char** parameters, int pcnt, userrec *user) { // this lusers command shows one server at all times because // a protocol module must override it to show those stats. user->WriteServ("251 %s :There are %d users and %d invisible on 1 server",user->nick,ServerInstance->UserCount()-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount()); if (ServerInstance->OperCount()) user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount()); if (ServerInstance->UnregisteredUserCount()) user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount()); if (ServerInstance->ChannelCount()) user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount()); if (ServerInstance->LocalUserCount()) user->WriteServ("255 %s :I have %d clients and 0 servers",user->nick,ServerInstance->LocalUserCount()); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "inspircd.h"
+#include "commands/cmd_lusers.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_lusers(Instance);
+}
+
+/** Handle /LUSERS
+ */
+CmdResult cmd_lusers::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ // this lusers command shows one server at all times because
+ // a protocol module must override it to show those stats.
+ user->WriteServ("251 %s :There are %d users and %d invisible on 1 server",user->nick,ServerInstance->UserCount()-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount());
+ if (ServerInstance->OperCount())
+ user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
+ if (ServerInstance->UnregisteredUserCount())
+ user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
+ if (ServerInstance->ChannelCount())
+ user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
+ if (ServerInstance->LocalUserCount())
+ user->WriteServ("255 %s :I have %d clients and 0 servers",user->nick,ServerInstance->LocalUserCount());
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_map.cpp b/src/cmd_map.cpp
index 270374cbf..c75a18b3f 100644
--- a/src/cmd_map.cpp
+++ b/src/cmd_map.cpp
@@ -1 +1,35 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_map.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_map(Instance); } /** Handle /MAP */ CmdResult cmd_map::Handle (const char** parameters, int pcnt, userrec *user) { // as with /LUSERS this does nothing without a linking // module to override its behaviour and display something // better. user->WriteServ("006 %s :%s",user->nick,ServerInstance->Config->ServerName); user->WriteServ("007 %s :End of /MAP",user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_map.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_map(Instance);
+}
+
+/** Handle /MAP
+ */
+CmdResult cmd_map::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ // as with /LUSERS this does nothing without a linking
+ // module to override its behaviour and display something
+ // better.
+ user->WriteServ("006 %s :%s",user->nick,ServerInstance->Config->ServerName);
+ user->WriteServ("007 %s :End of /MAP",user->nick);
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_mode.cpp b/src/cmd_mode.cpp
index 563fa75db..18c1e69ad 100644
--- a/src/cmd_mode.cpp
+++ b/src/cmd_mode.cpp
@@ -1 +1,31 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_mode.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_mode(Instance); } /** Handle /MODE */ CmdResult cmd_mode::Handle (const char** parameters, int pcnt, userrec *user) { ServerInstance->Modes->Process(parameters, pcnt, user, false); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_mode.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_mode(Instance);
+}
+
+/** Handle /MODE
+ */
+CmdResult cmd_mode::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ ServerInstance->Modes->Process(parameters, pcnt, user, false);
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_modules.cpp b/src/cmd_modules.cpp
index b8812c22d..be236fcd9 100644
--- a/src/cmd_modules.cpp
+++ b/src/cmd_modules.cpp
@@ -1 +1,75 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "commands/cmd_modules.h" char* itab[] = { "OnUserConnect", "OnUserQuit", "OnUserDisconnect", "OnUserJoin", "OnUserPart", "OnRehash", "OnServerRaw", "OnUserPreJoin", "OnUserPreKick", "OnUserKick", "OnOper", "OnInfo", "OnWhois", "OnUserPreInvite", "OnUserInvite", "OnUserPreMessage", "OnUserPreNotice", "OnUserPreNick", "OnUserMessage", "OnUserNotice", "OnMode", "OnGetServerDescription", "OnSyncUser", "OnSyncChannel", "OnSyncChannelMetaData", "OnSyncUserMetaData", "OnDecodeMetaData", "ProtoSendMode", "ProtoSendMetaData", "OnWallops", "OnChangeHost", "OnChangeName", "OnAddGLine", "OnAddZLine", "OnAddQLine", "OnAddKLine", "OnAddELine", "OnDelGLine", "OnDelZLine", "OnDelKLine", "OnDelELine", "OnDelQLine", "OnCleanup", "OnUserPostNick", "OnAccessCheck", "On005Numeric", "OnKill", "OnRemoteKill", "OnLoadModule", "OnUnloadModule", "OnBackgroundTimer", "OnSendList", "OnPreCommand", "OnCheckReady", "OnUserRegister", "OnCheckInvite", "OnCheckKey", "OnCheckLimit", "OnCheckBan", "OnStats", "OnChangeLocalUserHost", "OnChangeLocalUserGecos", "OnLocalTopicChange", "OnPostLocalTopicChange", "OnEvent", "OnRequest", "OnOperCompre", "OnGlobalOper", "OnPostConnect", "OnAddBan", "OnDelBan", "OnRawSocketAccept", "OnRawSocketClose", "OnRawSocketWrite", "OnRawSocketRead", "OnChangeLocalUserGECOS", "OnUserRegister", "OnOperCompare", "OnChannelDelete", "OnPostOper", "OnSyncOtherMetaData", "OnSetAway", "OnCancelAway", "OnNamesList", "OnPostCommand", "OnPostJoin", "OnWhoisLine", "OnBuildExemptList", "OnRawSocketConnect", "OnGarbageCollect", NULL }; extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_modules(Instance); } /** Handle /MODULES */ CmdResult cmd_modules::Handle (const char** parameters, int pcnt, userrec *user) { for (unsigned int i = 0; i < ServerInstance->Config->module_names.size(); i++) { Version V = ServerInstance->modules[i]->GetVersion(); char modulename[MAXBUF]; char flagstate[MAXBUF]; *flagstate = 0; if (V.Flags & VF_STATIC) strlcat(flagstate,", static",MAXBUF); if (V.Flags & VF_VENDOR) strlcat(flagstate,", vendor",MAXBUF); if (V.Flags & VF_COMMON) strlcat(flagstate,", common",MAXBUF); if (V.Flags & VF_SERVICEPROVIDER) strlcat(flagstate,", service provider",MAXBUF); if (!flagstate[0]) strcpy(flagstate," <no flags>"); strlcpy(modulename,ServerInstance->Config->module_names[i].c_str(),256); if (IS_OPER(user)) { user->WriteServ("900 %s :0x%08lx %d.%d.%d.%d %s (%s)",user->nick,ServerInstance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2); } else { user->WriteServ("900 %s :%s",user->nick,ServerConfig::CleanFilename(modulename)); } } user->WriteServ("901 %s :End of MODULES list",user->nick); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_modules.h"
+
+char* itab[] = {
+ "OnUserConnect", "OnUserQuit", "OnUserDisconnect", "OnUserJoin", "OnUserPart", "OnRehash", "OnServerRaw",
+ "OnUserPreJoin", "OnUserPreKick", "OnUserKick", "OnOper", "OnInfo", "OnWhois", "OnUserPreInvite",
+ "OnUserInvite", "OnUserPreMessage", "OnUserPreNotice", "OnUserPreNick", "OnUserMessage", "OnUserNotice", "OnMode",
+ "OnGetServerDescription", "OnSyncUser", "OnSyncChannel", "OnSyncChannelMetaData", "OnSyncUserMetaData",
+ "OnDecodeMetaData", "ProtoSendMode", "ProtoSendMetaData", "OnWallops", "OnChangeHost", "OnChangeName", "OnAddGLine",
+ "OnAddZLine", "OnAddQLine", "OnAddKLine", "OnAddELine", "OnDelGLine", "OnDelZLine", "OnDelKLine", "OnDelELine", "OnDelQLine",
+ "OnCleanup", "OnUserPostNick", "OnAccessCheck", "On005Numeric", "OnKill", "OnRemoteKill", "OnLoadModule", "OnUnloadModule",
+ "OnBackgroundTimer", "OnSendList", "OnPreCommand", "OnCheckReady", "OnUserRegister", "OnCheckInvite",
+ "OnCheckKey", "OnCheckLimit", "OnCheckBan", "OnStats", "OnChangeLocalUserHost", "OnChangeLocalUserGecos", "OnLocalTopicChange",
+ "OnPostLocalTopicChange", "OnEvent", "OnRequest", "OnOperCompre", "OnGlobalOper", "OnPostConnect", "OnAddBan", "OnDelBan",
+ "OnRawSocketAccept", "OnRawSocketClose", "OnRawSocketWrite", "OnRawSocketRead", "OnChangeLocalUserGECOS", "OnUserRegister",
+ "OnOperCompare", "OnChannelDelete", "OnPostOper", "OnSyncOtherMetaData", "OnSetAway", "OnCancelAway", "OnNamesList",
+ "OnPostCommand", "OnPostJoin", "OnWhoisLine", "OnBuildExemptList", "OnRawSocketConnect", "OnGarbageCollect", NULL
+};
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_modules(Instance);
+}
+
+/** Handle /MODULES
+ */
+CmdResult cmd_modules::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ for (unsigned int i = 0; i < ServerInstance->Config->module_names.size(); i++)
+ {
+ Version V = ServerInstance->modules[i]->GetVersion();
+ char modulename[MAXBUF];
+ char flagstate[MAXBUF];
+ *flagstate = 0;
+ if (V.Flags & VF_STATIC)
+ strlcat(flagstate,", static",MAXBUF);
+ if (V.Flags & VF_VENDOR)
+ strlcat(flagstate,", vendor",MAXBUF);
+ if (V.Flags & VF_COMMON)
+ strlcat(flagstate,", common",MAXBUF);
+ if (V.Flags & VF_SERVICEPROVIDER)
+ strlcat(flagstate,", service provider",MAXBUF);
+ if (!flagstate[0])
+ strcpy(flagstate," <no flags>");
+ strlcpy(modulename,ServerInstance->Config->module_names[i].c_str(),256);
+ if (IS_OPER(user))
+ {
+ user->WriteServ("900 %s :0x%08lx %d.%d.%d.%d %s (%s)",user->nick,ServerInstance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2);
+ }
+ else
+ {
+ user->WriteServ("900 %s :%s",user->nick,ServerConfig::CleanFilename(modulename));
+ }
+ }
+ user->WriteServ("901 %s :End of MODULES list",user->nick);
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_motd.cpp b/src/cmd_motd.cpp
index f14af0bd1..6aaf1a9af 100644
--- a/src/cmd_motd.cpp
+++ b/src/cmd_motd.cpp
@@ -1 +1,29 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_motd.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_motd(Instance); } /** Handle /MOTD */ CmdResult cmd_motd::Handle (const char** parameters, int pcnt, userrec *user) { user->ShowMOTD(); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_motd.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_motd(Instance);
+}
+
+/** Handle /MOTD
+ */
+CmdResult cmd_motd::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->ShowMOTD();
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_names.cpp b/src/cmd_names.cpp
index c157c3c04..fe61c4dea 100644
--- a/src/cmd_names.cpp
+++ b/src/cmd_names.cpp
@@ -1 +1,54 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_names.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_names(Instance); } /** Handle /NAMES */ CmdResult cmd_names::Handle (const char** parameters, int pcnt, userrec *user) { chanrec* c; if (!pcnt) { user->WriteServ("366 %s * :End of /NAMES list.",user->nick); return CMD_SUCCESS; } if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_SUCCESS; c = ServerInstance->FindChan(parameters[0]); if (c) { if ((c->modes[CM_SECRET]) && (!c->HasUser(user))) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, c->name); return CMD_FAILURE; } c->UserList(user); } else { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_names.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_names(Instance);
+}
+
+/** Handle /NAMES
+ */
+CmdResult cmd_names::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ chanrec* c;
+
+ if (!pcnt)
+ {
+ user->WriteServ("366 %s * :End of /NAMES list.",user->nick);
+ return CMD_SUCCESS;
+ }
+
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_SUCCESS;
+
+ c = ServerInstance->FindChan(parameters[0]);
+ if (c)
+ {
+ if ((c->modes[CM_SECRET]) && (!c->HasUser(user)))
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+ c->UserList(user);
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_nick.cpp b/src/cmd_nick.cpp
index 5bc9ce6d1..4e2ebcf6e 100644
--- a/src/cmd_nick.cpp
+++ b/src/cmd_nick.cpp
@@ -1 +1,189 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_nick.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_nick(Instance); } /** 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 cmd_nick::Handle (const char** parameters, int pcnt, userrec *user) { char oldnick[NICKMAX]; if (!*parameters[0] || !*user->nick) { /* We cant put blanks in the parameters, so for this (extremely rare) issue we just put '*' here. */ user->WriteServ("432 %s * :Erroneous Nickname", *user->nick ? user->nick : "*"); return CMD_FAILURE; } if (irc::string(user->nick) == irc::string(parameters[0])) { /* If its exactly the same, even case, dont do anything. */ if (!strcmp(user->nick,parameters[0])) return CMD_SUCCESS; /* Its a change of case. People insisted that they should be * able to do silly things like this even though the RFC says * the nick AAA is the same as the nick aaa. */ strlcpy(oldnick, user->nick, NICKMAX - 1); int MOD_RESULT = 0; FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0])); if (MOD_RESULT) return CMD_FAILURE; if (user->registered == REG_ALL) user->WriteCommon("NICK %s",parameters[0]); strlcpy(user->nick, parameters[0], NICKMAX - 1); user->InvalidateCache(); FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick)); return CMD_SUCCESS; } else { QLine* mq = ServerInstance->XLines->matches_qline(parameters[0]); if (mq) { ServerInstance->SNO->WriteToSnoMask('x', "Q-Lined nickname %s from %s!%s@%s: %s", parameters[0], user->nick, user->ident, user->host, mq->reason); user->WriteServ("432 %s %s :Invalid nickname: %s",user->nick,parameters[0], mq->reason); return CMD_FAILURE; } /* Check for nickname overruled - * This happens when one user has connected and sent only NICK, and is essentially * "camping" upon a nickname. To give the new user connecting a fair chance of having * the nickname too, we force a nickchange on the older user (Simply the one who was * here first, no TS checks need to take place here) */ userrec* InUse = ServerInstance->FindNick(parameters[0]); if (InUse && (InUse != user) && (ServerInstance->IsNick(parameters[0]))) { if (InUse->registered != REG_ALL) { /* change the nick of the older user to nnn-overruled, * where nnn is their file descriptor. We know this to be unique. * NOTE: We must do this and not quit the user, even though we do * not have UID support yet. This is because if we set this user * as quitting and then introduce the new user before the old one * has quit, then the user hash gets totally buggered. * (Yes, that is a technical term). -- Brain */ std::string changeback = ConvToStr(InUse->GetFd()) + "-overruled"; InUse->WriteTo(InUse, "NICK %s", changeback.c_str()); InUse->WriteServ("433 %s %s :Nickname overruled.", InUse->nick, InUse->nick); InUse->UpdateNickHash(changeback.c_str()); strlcpy(InUse->nick, changeback.c_str(), NICKMAX - 1); InUse->InvalidateCache(); /* Take away their nickname-sent state forcing them to send a nick again */ InUse->registered &= ~REG_NICK; } else { user->WriteServ("433 %s %s :Nickname is already in use.", user->registered >= REG_NICK ? user->nick : "*", parameters[0]); return CMD_FAILURE; } } } if ((!ServerInstance->IsNick(parameters[0])) && (IS_LOCAL(user))) { user->WriteServ("432 %s %s :Erroneous Nickname",user->nick,parameters[0]); return CMD_FAILURE; } if (user->registered == REG_ALL) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0])); if (MOD_RESULT) { // if a module returns true, the nick change is silently forbidden. return CMD_FAILURE; } user->WriteCommon("NICK %s",parameters[0]); } strlcpy(oldnick, user->nick, NICKMAX - 1); /* change the nick of the user in the users_hash */ user = user->UpdateNickHash(parameters[0]); /* actually change the nick within the record */ if (!user) return CMD_FAILURE; if (!*user->nick) return CMD_FAILURE; strlcpy(user->nick, parameters[0], NICKMAX - 1); user->InvalidateCache(); /* Update display nicks */ for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++) { CUList* ulist = v->first->GetUsers(); CUList::iterator i = ulist->find(user); if (i != ulist->end()) i->second = user->nick; } if (user->registered < REG_NICKUSER) { user->registered = (user->registered | REG_NICK); if (ServerInstance->Config->NoUserDns) { user->dns_done = true; ServerInstance->next_call = ServerInstance->Time(); } else { user->StartDNSLookup(); if (user->dns_done) { /* Cached result or instant failure - fall right through if possible */ ServerInstance->next_call = ServerInstance->Time(); } else { if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout) ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout; } } } if (user->registered == REG_NICKUSER) { /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ int MOD_RESULT = 0; FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user)); if (MOD_RESULT > 0) return CMD_FAILURE; } if (user->registered == REG_ALL) { FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick)); } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_nick.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_nick(Instance);
+}
+
+/** 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 cmd_nick::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ char oldnick[NICKMAX];
+
+ if (!*parameters[0] || !*user->nick)
+ {
+ /* We cant put blanks in the parameters, so for this (extremely rare) issue we just put '*' here. */
+ user->WriteServ("432 %s * :Erroneous Nickname", *user->nick ? user->nick : "*");
+ return CMD_FAILURE;
+ }
+
+ if (irc::string(user->nick) == irc::string(parameters[0]))
+ {
+ /* If its exactly the same, even case, dont do anything. */
+ if (!strcmp(user->nick,parameters[0]))
+ return CMD_SUCCESS;
+
+ /* Its a change of case. People insisted that they should be
+ * able to do silly things like this even though the RFC says
+ * the nick AAA is the same as the nick aaa.
+ */
+ strlcpy(oldnick, user->nick, NICKMAX - 1);
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0]));
+ if (MOD_RESULT)
+ return CMD_FAILURE;
+ if (user->registered == REG_ALL)
+ user->WriteCommon("NICK %s",parameters[0]);
+ strlcpy(user->nick, parameters[0], NICKMAX - 1);
+ user->InvalidateCache();
+ FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick));
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ QLine* mq = ServerInstance->XLines->matches_qline(parameters[0]);
+ if (mq)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x', "Q-Lined nickname %s from %s!%s@%s: %s", parameters[0], user->nick, user->ident, user->host, mq->reason);
+ user->WriteServ("432 %s %s :Invalid nickname: %s",user->nick,parameters[0], mq->reason);
+ return CMD_FAILURE;
+ }
+ /* Check for nickname overruled -
+ * This happens when one user has connected and sent only NICK, and is essentially
+ * "camping" upon a nickname. To give the new user connecting a fair chance of having
+ * the nickname too, we force a nickchange on the older user (Simply the one who was
+ * here first, no TS checks need to take place here)
+ */
+ userrec* InUse = ServerInstance->FindNick(parameters[0]);
+ if (InUse && (InUse != user) && (ServerInstance->IsNick(parameters[0])))
+ {
+ if (InUse->registered != REG_ALL)
+ {
+ /* change the nick of the older user to nnn-overruled,
+ * where nnn is their file descriptor. We know this to be unique.
+ * NOTE: We must do this and not quit the user, even though we do
+ * not have UID support yet. This is because if we set this user
+ * as quitting and then introduce the new user before the old one
+ * has quit, then the user hash gets totally buggered.
+ * (Yes, that is a technical term). -- Brain
+ */
+ std::string changeback = ConvToStr(InUse->GetFd()) + "-overruled";
+ InUse->WriteTo(InUse, "NICK %s", changeback.c_str());
+ InUse->WriteServ("433 %s %s :Nickname overruled.", InUse->nick, InUse->nick);
+ InUse->UpdateNickHash(changeback.c_str());
+ strlcpy(InUse->nick, changeback.c_str(), NICKMAX - 1);
+ InUse->InvalidateCache();
+ /* Take away their nickname-sent state forcing them to send a nick again */
+ InUse->registered &= ~REG_NICK;
+ }
+ else
+ {
+ user->WriteServ("433 %s %s :Nickname is already in use.", user->registered >= REG_NICK ? user->nick : "*", parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+ }
+ if ((!ServerInstance->IsNick(parameters[0])) && (IS_LOCAL(user)))
+ {
+ user->WriteServ("432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ if (user->registered == REG_ALL)
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0]));
+ if (MOD_RESULT) {
+ // if a module returns true, the nick change is silently forbidden.
+ return CMD_FAILURE;
+ }
+
+ user->WriteCommon("NICK %s",parameters[0]);
+
+ }
+
+ strlcpy(oldnick, user->nick, NICKMAX - 1);
+
+ /* change the nick of the user in the users_hash */
+ user = user->UpdateNickHash(parameters[0]);
+
+ /* actually change the nick within the record */
+ if (!user) return CMD_FAILURE;
+ if (!*user->nick) return CMD_FAILURE;
+
+ strlcpy(user->nick, parameters[0], NICKMAX - 1);
+
+ user->InvalidateCache();
+
+ /* Update display nicks */
+ for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
+ {
+ CUList* ulist = v->first->GetUsers();
+ CUList::iterator i = ulist->find(user);
+ if (i != ulist->end())
+ i->second = user->nick;
+ }
+
+ if (user->registered < REG_NICKUSER)
+ {
+ user->registered = (user->registered | REG_NICK);
+
+ if (ServerInstance->Config->NoUserDns)
+ {
+ user->dns_done = true;
+ ServerInstance->next_call = ServerInstance->Time();
+ }
+ else
+ {
+ user->StartDNSLookup();
+ if (user->dns_done)
+ {
+ /* Cached result or instant failure - fall right through if possible */
+ ServerInstance->next_call = ServerInstance->Time();
+ }
+ else
+ {
+ if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout)
+ ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
+ }
+ }
+ }
+ if (user->registered == REG_NICKUSER)
+ {
+ /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user));
+ if (MOD_RESULT > 0)
+ return CMD_FAILURE;
+ }
+ if (user->registered == REG_ALL)
+ {
+ FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick));
+ }
+
+ return CMD_SUCCESS;
+
+}
+
diff --git a/src/cmd_notice.cpp b/src/cmd_notice.cpp
index 27fdd5279..a797fefab 100644
--- a/src/cmd_notice.cpp
+++ b/src/cmd_notice.cpp
@@ -1 +1,158 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "commands/cmd_notice.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_notice(Instance); } CmdResult cmd_notice::Handle (const char** parameters, int pcnt, userrec *user) { userrec *dest; chanrec *chan; CUList exempt_list; user->idle_lastmsg = ServerInstance->Time(); if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_SUCCESS; if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server))) { int MOD_RESULT = 0; std::string temp = parameters[1]; FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,(void*)parameters[0],TYPE_SERVER,temp,0,exempt_list)); if (MOD_RESULT) return CMD_FAILURE; parameters[1] = temp.c_str(); // notice to server mask const char* servermask = parameters[0] + 1; if (match(ServerInstance->Config->ServerName,servermask)) { user->SendAll("NOTICE", "%s", parameters[1]); } FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,exempt_list)); return CMD_SUCCESS; } char status = 0; if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+')) { status = *parameters[0]; parameters[0]++; } if (*parameters[0] == '#') { chan = ServerInstance->FindChan(parameters[0]); exempt_list[user] = user->nick; if (chan) { if (IS_LOCAL(user)) { if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user))) { user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); return CMD_FAILURE; } if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE)) { user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); return CMD_FAILURE; } } int MOD_RESULT = 0; std::string temp = parameters[1]; FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,chan,TYPE_CHANNEL,temp,status, exempt_list)); if (MOD_RESULT) { return CMD_FAILURE; } parameters[1] = temp.c_str(); if (temp.empty()) { user->WriteServ("412 %s :No text to send", user->nick); return CMD_FAILURE; } if (status) { if (ServerInstance->Config->UndernetMsgPrefix) { chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name, status, parameters[1]); } else { chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name, parameters[1]); } } else { chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name, parameters[1]); } FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,parameters[1],status,exempt_list)); } else { /* no such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } dest = ServerInstance->FindNick(parameters[0]); if (dest) { if (!*parameters[1]) { user->WriteServ("412 %s :No text to send", user->nick); return CMD_FAILURE; } int MOD_RESULT = 0; std::string temp = parameters[1]; FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,dest,TYPE_USER,temp,0,exempt_list)); if (MOD_RESULT) { return CMD_FAILURE; } parameters[1] = (char*)temp.c_str(); if (IS_LOCAL(dest)) { // direct write, same server user->WriteTo(dest, "NOTICE %s :%s", dest->nick, parameters[1]); } FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,parameters[1],0,exempt_list)); } else { /* no such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_notice.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_notice(Instance);
+}
+
+CmdResult cmd_notice::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ userrec *dest;
+ chanrec *chan;
+
+ CUList exempt_list;
+
+ user->idle_lastmsg = ServerInstance->Time();
+
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_SUCCESS;
+ if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server)))
+ {
+ int MOD_RESULT = 0;
+ std::string temp = parameters[1];
+ FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,(void*)parameters[0],TYPE_SERVER,temp,0,exempt_list));
+ if (MOD_RESULT)
+ return CMD_FAILURE;
+ parameters[1] = temp.c_str();
+ // notice to server mask
+ const char* servermask = parameters[0] + 1;
+ if (match(ServerInstance->Config->ServerName,servermask))
+ {
+ user->SendAll("NOTICE", "%s", parameters[1]);
+ }
+ FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,exempt_list));
+ return CMD_SUCCESS;
+ }
+ char status = 0;
+ if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+'))
+ {
+ status = *parameters[0];
+ parameters[0]++;
+ }
+ if (*parameters[0] == '#')
+ {
+ chan = ServerInstance->FindChan(parameters[0]);
+
+ exempt_list[user] = user->nick;
+
+ if (chan)
+ {
+ if (IS_LOCAL(user))
+ {
+ if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user)))
+ {
+ user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
+ return CMD_FAILURE;
+ }
+ if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE))
+ {
+ user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
+ return CMD_FAILURE;
+ }
+ }
+ int MOD_RESULT = 0;
+
+ std::string temp = parameters[1];
+ FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,chan,TYPE_CHANNEL,temp,status, exempt_list));
+ if (MOD_RESULT) {
+ return CMD_FAILURE;
+ }
+ parameters[1] = temp.c_str();
+
+ if (temp.empty())
+ {
+ user->WriteServ("412 %s :No text to send", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (status)
+ {
+ if (ServerInstance->Config->UndernetMsgPrefix)
+ {
+ chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name, status, parameters[1]);
+ }
+ else
+ {
+ chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name, parameters[1]);
+ }
+ }
+ else
+ {
+ chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name, parameters[1]);
+ }
+
+ FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,parameters[1],status,exempt_list));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+ }
+
+ dest = ServerInstance->FindNick(parameters[0]);
+ if (dest)
+ {
+ if (!*parameters[1])
+ {
+ user->WriteServ("412 %s :No text to send", user->nick);
+ return CMD_FAILURE;
+ }
+
+ int MOD_RESULT = 0;
+ std::string temp = parameters[1];
+ FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,dest,TYPE_USER,temp,0,exempt_list));
+ if (MOD_RESULT) {
+ return CMD_FAILURE;
+ }
+ parameters[1] = (char*)temp.c_str();
+
+ if (IS_LOCAL(dest))
+ {
+ // direct write, same server
+ user->WriteTo(dest, "NOTICE %s :%s", dest->nick, parameters[1]);
+ }
+
+ FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,parameters[1],0,exempt_list));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+
+}
+
diff --git a/src/cmd_oper.cpp b/src/cmd_oper.cpp
index af811115d..686182876 100644
--- a/src/cmd_oper.cpp
+++ b/src/cmd_oper.cpp
@@ -1 +1,153 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "typedefs.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "commands/cmd_oper.h" #include "hashcomp.h" bool OneOfMatches(const char* host, const char* ip, const char* hostlist) { std::stringstream hl(hostlist); std::string xhost; while (hl >> xhost) { if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true)) { return true; } } return false; } extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_oper(Instance); } CmdResult cmd_oper::Handle (const char** parameters, int pcnt, userrec *user) { char LoginName[MAXBUF]; char Password[MAXBUF]; char OperType[MAXBUF]; char TypeName[MAXBUF]; char HostName[MAXBUF]; char TheHost[MAXBUF]; char TheIP[MAXBUF]; int j; bool found = false; bool type_invalid = false; bool match_login = false; bool match_pass = false; bool match_hosts = false; snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host); snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString()); for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++) { ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "name", i, LoginName, MAXBUF); ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "password", i, Password, MAXBUF); ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "type", i, OperType, MAXBUF); ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "host", i, HostName, MAXBUF); match_login = !strcmp(LoginName,parameters[0]); match_pass = !ServerInstance->OperPassCompare(Password,parameters[1], i); match_hosts = OneOfMatches(TheHost,TheIP,HostName); if (match_login && match_pass && match_hosts) { type_invalid = true; for (j =0; j < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "type"); j++) { ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","name", j, TypeName, MAXBUF); if (!strcmp(TypeName,OperType)) { /* found this oper's opertype */ if (!ServerInstance->IsNick(TypeName)) { user->WriteServ("491 %s :Invalid oper type (oper types must follow the same syntax as nicknames)",user->nick); ServerInstance->SNO->WriteToSnoMask('o',"CONFIGURATION ERROR! Oper type '%s' contains invalid characters",OperType); ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but oper type erroneous.",user->nick,user->ident,user->host); return CMD_FAILURE; } ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","host", j, HostName, MAXBUF); if (*HostName) user->ChangeDisplayedHost(HostName); found = true; type_invalid = false; break; } } } if (match_login || found) break; } if (found) { /* correct oper credentials */ ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",user->nick,user->ident,user->host,irc::Spacify(OperType),parameters[0]); user->WriteServ("381 %s :You are now an IRC operator of type %s",user->nick,irc::Spacify(OperType)); if (!user->modes[UM_OPERATOR]) user->Oper(OperType); } else { std::deque<std::string> n; n.push_back("o"); char broadcast[MAXBUF]; if (!type_invalid) { std::string fields; if (!match_login) fields.append("login "); if (!match_pass) fields.append("password "); if (!match_hosts) fields.append("hosts"); user->WriteServ("491 %s :Invalid oper credentials",user->nick); snprintf(broadcast, MAXBUF, "WARNING! Failed oper attempt by %s!%s@%s using login '%s': The following fields do not match: %s",user->nick,user->ident,user->host, parameters[0], fields.c_str()); ServerInstance->SNO->WriteToSnoMask('o',std::string(broadcast)); n.push_back(broadcast); Event rmode2((char *)&n, NULL, "send_snoset"); rmode2.Send(ServerInstance); ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': The following fields did not match: %s",user->nick,user->ident,user->host,parameters[0],fields.c_str()); return CMD_FAILURE; } else { user->WriteServ("491 %s :Your oper block does not have a valid opertype associated with it",user->nick); snprintf(broadcast, MAXBUF, "CONFIGURATION ERROR! Oper block '%s': missing OperType %s",parameters[0],OperType); ServerInstance->SNO->WriteToSnoMask('o', std::string(broadcast)); n.push_back(broadcast); Event rmode2((char *)&n, NULL, "send_snoset"); rmode2.Send(ServerInstance); ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': credentials valid, but oper type nonexistent.",user->nick,user->ident,user->host,parameters[0]); return CMD_FAILURE; } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "typedefs.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_oper.h"
+#include "hashcomp.h"
+
+bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
+{
+ std::stringstream hl(hostlist);
+ std::string xhost;
+ while (hl >> xhost)
+ {
+ if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_oper(Instance);
+}
+
+CmdResult cmd_oper::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ char LoginName[MAXBUF];
+ char Password[MAXBUF];
+ char OperType[MAXBUF];
+ char TypeName[MAXBUF];
+ char HostName[MAXBUF];
+ char TheHost[MAXBUF];
+ char TheIP[MAXBUF];
+ int j;
+ bool found = false;
+ bool type_invalid = false;
+
+ bool match_login = false;
+ bool match_pass = false;
+ bool match_hosts = false;
+
+ snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host);
+ snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString());
+
+ for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++)
+ {
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "name", i, LoginName, MAXBUF);
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "password", i, Password, MAXBUF);
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "type", i, OperType, MAXBUF);
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "host", i, HostName, MAXBUF);
+
+ match_login = !strcmp(LoginName,parameters[0]);
+ match_pass = !ServerInstance->OperPassCompare(Password,parameters[1], i);
+ match_hosts = OneOfMatches(TheHost,TheIP,HostName);
+
+ if (match_login && match_pass && match_hosts)
+ {
+ type_invalid = true;
+ for (j =0; j < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "type"); j++)
+ {
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","name", j, TypeName, MAXBUF);
+
+ if (!strcmp(TypeName,OperType))
+ {
+ /* found this oper's opertype */
+ if (!ServerInstance->IsNick(TypeName))
+ {
+ user->WriteServ("491 %s :Invalid oper type (oper types must follow the same syntax as nicknames)",user->nick);
+ ServerInstance->SNO->WriteToSnoMask('o',"CONFIGURATION ERROR! Oper type '%s' contains invalid characters",OperType);
+ ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but oper type erroneous.",user->nick,user->ident,user->host);
+ return CMD_FAILURE;
+ }
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","host", j, HostName, MAXBUF);
+ if (*HostName)
+ user->ChangeDisplayedHost(HostName);
+ found = true;
+ type_invalid = false;
+ break;
+ }
+ }
+ }
+ if (match_login || found)
+ break;
+ }
+ if (found)
+ {
+ /* correct oper credentials */
+ ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",user->nick,user->ident,user->host,irc::Spacify(OperType),parameters[0]);
+ user->WriteServ("381 %s :You are now an IRC operator of type %s",user->nick,irc::Spacify(OperType));
+ if (!user->modes[UM_OPERATOR])
+ user->Oper(OperType);
+ }
+ else
+ {
+ std::deque<std::string> n;
+ n.push_back("o");
+ char broadcast[MAXBUF];
+
+ if (!type_invalid)
+ {
+ std::string fields;
+ if (!match_login)
+ fields.append("login ");
+ if (!match_pass)
+ fields.append("password ");
+ if (!match_hosts)
+ fields.append("hosts");
+ user->WriteServ("491 %s :Invalid oper credentials",user->nick);
+
+ snprintf(broadcast, MAXBUF, "WARNING! Failed oper attempt by %s!%s@%s using login '%s': The following fields do not match: %s",user->nick,user->ident,user->host, parameters[0], fields.c_str());
+ ServerInstance->SNO->WriteToSnoMask('o',std::string(broadcast));
+ n.push_back(broadcast);
+ Event rmode2((char *)&n, NULL, "send_snoset");
+ rmode2.Send(ServerInstance);
+
+ ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': The following fields did not match: %s",user->nick,user->ident,user->host,parameters[0],fields.c_str());
+ return CMD_FAILURE;
+ }
+ else
+ {
+ user->WriteServ("491 %s :Your oper block does not have a valid opertype associated with it",user->nick);
+
+ snprintf(broadcast, MAXBUF, "CONFIGURATION ERROR! Oper block '%s': missing OperType %s",parameters[0],OperType);
+
+ ServerInstance->SNO->WriteToSnoMask('o', std::string(broadcast));
+ n.push_back(broadcast);
+ Event rmode2((char *)&n, NULL, "send_snoset");
+ rmode2.Send(ServerInstance);
+
+ ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': credentials valid, but oper type nonexistent.",user->nick,user->ident,user->host,parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_part.cpp b/src/cmd_part.cpp
index 3bf22ac5d..347466c22 100644
--- a/src/cmd_part.cpp
+++ b/src/cmd_part.cpp
@@ -1 +1,43 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_part.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_part(Instance); } CmdResult cmd_part::Handle (const char** parameters, int pcnt, userrec *user) { if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_SUCCESS; chanrec* c = ServerInstance->FindChan(parameters[0]); if (c) { if (!c->PartUser(user, pcnt > 1 ? parameters[1] : NULL)) /* Arse, who stole our channel! :/ */ delete c; } else { user->WriteServ( "401 %s %s :No such channel", user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_part.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_part(Instance);
+}
+
+CmdResult cmd_part::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_SUCCESS;
+
+ chanrec* c = ServerInstance->FindChan(parameters[0]);
+
+ if (c)
+ {
+ if (!c->PartUser(user, pcnt > 1 ? parameters[1] : NULL))
+ /* Arse, who stole our channel! :/ */
+ delete c;
+ }
+ else
+ {
+ user->WriteServ( "401 %s %s :No such channel", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_pass.cpp b/src/cmd_pass.cpp
index 0a836ccdf..29c83c3bf 100644
--- a/src/cmd_pass.cpp
+++ b/src/cmd_pass.cpp
@@ -1 +1,42 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_pass.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_pass(Instance); } CmdResult cmd_pass::Handle (const char** parameters, int pcnt, userrec *user) { // Check to make sure they havnt registered -- Fix by FCS if (user->registered == REG_ALL) { user->WriteServ("462 %s :You may not reregister",user->nick); return CMD_FAILURE; } ConnectClass* a = user->GetClass(); if (!a) return CMD_FAILURE; strlcpy(user->password,parameters[0],63); if (a->GetPass() == parameters[0]) { user->haspassed = true; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_pass.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_pass(Instance);
+}
+
+CmdResult cmd_pass::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ // Check to make sure they havnt registered -- Fix by FCS
+ if (user->registered == REG_ALL)
+ {
+ user->WriteServ("462 %s :You may not reregister",user->nick);
+ return CMD_FAILURE;
+ }
+ ConnectClass* a = user->GetClass();
+ if (!a)
+ return CMD_FAILURE;
+
+ strlcpy(user->password,parameters[0],63);
+ if (a->GetPass() == parameters[0])
+ {
+ user->haspassed = true;
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_ping.cpp b/src/cmd_ping.cpp
index c36415d12..afb708d70 100644
--- a/src/cmd_ping.cpp
+++ b/src/cmd_ping.cpp
@@ -1 +1,28 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_ping.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_ping(Instance); } CmdResult cmd_ping::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("PONG %s :%s",ServerInstance->Config->ServerName,parameters[0]); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_ping.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_ping(Instance);
+}
+
+CmdResult cmd_ping::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("PONG %s :%s",ServerInstance->Config->ServerName,parameters[0]);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_pong.cpp b/src/cmd_pong.cpp
index 16b42355b..c89542240 100644
--- a/src/cmd_pong.cpp
+++ b/src/cmd_pong.cpp
@@ -1 +1,28 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_pong.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_pong(Instance); } CmdResult cmd_pong::Handle (const char** parameters, int pcnt, userrec *user) { // set the user as alive so they survive to next ping user->lastping = 1; return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_pong.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_pong(Instance);
+}
+
+CmdResult cmd_pong::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ // set the user as alive so they survive to next ping
+ user->lastping = 1;
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_privmsg.cpp b/src/cmd_privmsg.cpp
index 3fdc12090..f3295df07 100644
--- a/src/cmd_privmsg.cpp
+++ b/src/cmd_privmsg.cpp
@@ -1 +1,164 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "commands/cmd_privmsg.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_privmsg(Instance); } CmdResult cmd_privmsg::Handle (const char** parameters, int pcnt, userrec *user) { userrec *dest; chanrec *chan; CUList except_list; user->idle_lastmsg = ServerInstance->Time(); if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_SUCCESS; if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server))) { int MOD_RESULT = 0; std::string temp = parameters[1]; FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,(void*)parameters[0],TYPE_SERVER,temp,0,except_list)); if (MOD_RESULT) return CMD_FAILURE; parameters[1] = temp.c_str(); // notice to server mask const char* servermask = parameters[0] + 1; if (match(ServerInstance->Config->ServerName,servermask)) { user->SendAll("PRIVMSG", "%s", parameters[1]); } FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,except_list)); return CMD_SUCCESS; } char status = 0; if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+')) { status = *parameters[0]; parameters[0]++; } if (parameters[0][0] == '#') { chan = ServerInstance->FindChan(parameters[0]); except_list[user] = user->nick; if (chan) { if (IS_LOCAL(user)) { if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user))) { user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); return CMD_FAILURE; } if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE)) { user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); return CMD_FAILURE; } } int MOD_RESULT = 0; std::string temp = parameters[1]; FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,chan,TYPE_CHANNEL,temp,status,except_list)); if (MOD_RESULT) { return CMD_FAILURE; } parameters[1] = temp.c_str(); /* Check again, a module may have zapped the input string */ if (temp.empty()) { user->WriteServ("412 %s :No text to send", user->nick); return CMD_FAILURE; } if (status) { if (ServerInstance->Config->UndernetMsgPrefix) { chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name, status, parameters[1]); } else { chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name, parameters[1]); } } else { chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name, parameters[1]); } FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,parameters[1],status,except_list)); } else { /* no such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } dest = ServerInstance->FindNick(parameters[0]); if (dest) { if (!*parameters[1]) { user->WriteServ("412 %s :No text to send", user->nick); return CMD_FAILURE; } if (IS_AWAY(dest)) { /* auto respond with aweh msg */ user->WriteServ("301 %s %s :%s",user->nick,dest->nick,dest->awaymsg); } int MOD_RESULT = 0; std::string temp = parameters[1]; FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,dest,TYPE_USER,temp,0,except_list)); if (MOD_RESULT) { return CMD_FAILURE; } parameters[1] = (char*)temp.c_str(); if (IS_LOCAL(dest)) { // direct write, same server user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick, parameters[1]); } FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,dest,TYPE_USER,parameters[1],0,except_list)); } else { /* no such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_privmsg.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_privmsg(Instance);
+}
+
+CmdResult cmd_privmsg::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ userrec *dest;
+ chanrec *chan;
+ CUList except_list;
+
+ user->idle_lastmsg = ServerInstance->Time();
+
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_SUCCESS;
+
+ if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server)))
+ {
+ int MOD_RESULT = 0;
+ std::string temp = parameters[1];
+ FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,(void*)parameters[0],TYPE_SERVER,temp,0,except_list));
+ if (MOD_RESULT)
+ return CMD_FAILURE;
+ parameters[1] = temp.c_str();
+ // notice to server mask
+ const char* servermask = parameters[0] + 1;
+ if (match(ServerInstance->Config->ServerName,servermask))
+ {
+ user->SendAll("PRIVMSG", "%s", parameters[1]);
+ }
+ FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,except_list));
+ return CMD_SUCCESS;
+ }
+ char status = 0;
+ if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+'))
+ {
+ status = *parameters[0];
+ parameters[0]++;
+ }
+ if (parameters[0][0] == '#')
+ {
+ chan = ServerInstance->FindChan(parameters[0]);
+
+ except_list[user] = user->nick;
+
+ if (chan)
+ {
+ if (IS_LOCAL(user))
+ {
+ if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user)))
+ {
+ user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
+ return CMD_FAILURE;
+ }
+ if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE))
+ {
+ user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
+ return CMD_FAILURE;
+ }
+ }
+ int MOD_RESULT = 0;
+
+ std::string temp = parameters[1];
+ FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,chan,TYPE_CHANNEL,temp,status,except_list));
+ if (MOD_RESULT) {
+ return CMD_FAILURE;
+ }
+ parameters[1] = temp.c_str();
+
+ /* Check again, a module may have zapped the input string */
+ if (temp.empty())
+ {
+ user->WriteServ("412 %s :No text to send", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (status)
+ {
+ if (ServerInstance->Config->UndernetMsgPrefix)
+ {
+ chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name, status, parameters[1]);
+ }
+ else
+ {
+ chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name, parameters[1]);
+ }
+ }
+ else
+ {
+ chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name, parameters[1]);
+ }
+
+ FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,parameters[1],status,except_list));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+ }
+
+ dest = ServerInstance->FindNick(parameters[0]);
+ if (dest)
+ {
+ if (!*parameters[1])
+ {
+ user->WriteServ("412 %s :No text to send", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (IS_AWAY(dest))
+ {
+ /* auto respond with aweh msg */
+ user->WriteServ("301 %s %s :%s",user->nick,dest->nick,dest->awaymsg);
+ }
+
+ int MOD_RESULT = 0;
+
+ std::string temp = parameters[1];
+ FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,dest,TYPE_USER,temp,0,except_list));
+ if (MOD_RESULT) {
+ return CMD_FAILURE;
+ }
+ parameters[1] = (char*)temp.c_str();
+
+ if (IS_LOCAL(dest))
+ {
+ // direct write, same server
+ user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick, parameters[1]);
+ }
+
+ FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,dest,TYPE_USER,parameters[1],0,except_list));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_qline.cpp b/src/cmd_qline.cpp
index 892f6480c..bb1122854 100644
--- a/src/cmd_qline.cpp
+++ b/src/cmd_qline.cpp
@@ -1 +1,80 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_qline.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_qline(Instance); } CmdResult cmd_qline::Handle (const char** parameters, int pcnt, userrec *user) { if (pcnt >= 3) { if (ServerInstance->NickMatchesEveryone(parameters[0],user)) return CMD_FAILURE; if (strchr(parameters[0],'@') || strchr(parameters[0],'!') || strchr(parameters[0],'.')) { user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick); return CMD_FAILURE; } long duration = ServerInstance->Duration(parameters[1]); if (ServerInstance->XLines->add_qline(duration,user->nick,parameters[2],parameters[0])) { int to_apply = APPLY_QLINES; FOREACH_MOD(I_OnAddQLine,OnAddQLine(duration, user, parameters[2], parameters[0])); if (!duration) { to_apply |= APPLY_PERM_ONLY; ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s.",user->nick,parameters[0]); } else { time_t c_requires_crap = duration + ServerInstance->Time(); ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s",user->nick,parameters[0], ServerInstance->TimeString(c_requires_crap).c_str()); } ServerInstance->XLines->apply_lines(to_apply); } else { user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick,parameters[0]); } } else { if (ServerInstance->XLines->del_qline(parameters[0])) { FOREACH_MOD(I_OnDelQLine,OnDelQLine(user, parameters[0])); ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Q-line on %s.",user->nick,parameters[0]); } else { user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick,parameters[0]); return CMD_FAILURE; } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_qline.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_qline(Instance);
+}
+
+CmdResult cmd_qline::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (pcnt >= 3)
+ {
+ if (ServerInstance->NickMatchesEveryone(parameters[0],user))
+ return CMD_FAILURE;
+
+ if (strchr(parameters[0],'@') || strchr(parameters[0],'!') || strchr(parameters[0],'.'))
+ {
+ user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick);
+ return CMD_FAILURE;
+ }
+
+ long duration = ServerInstance->Duration(parameters[1]);
+ if (ServerInstance->XLines->add_qline(duration,user->nick,parameters[2],parameters[0]))
+ {
+ int to_apply = APPLY_QLINES;
+ FOREACH_MOD(I_OnAddQLine,OnAddQLine(duration, user, parameters[2], parameters[0]));
+ if (!duration)
+ {
+ to_apply |= APPLY_PERM_ONLY;
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s",user->nick,parameters[0],
+ ServerInstance->TimeString(c_requires_crap).c_str());
+ }
+ ServerInstance->XLines->apply_lines(to_apply);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick,parameters[0]);
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->del_qline(parameters[0]))
+ {
+ FOREACH_MOD(I_OnDelQLine,OnDelQLine(user, parameters[0]));
+ ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Q-line on %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_quit.cpp b/src/cmd_quit.cpp
index 45e970207..a859c1790 100644
--- a/src/cmd_quit.cpp
+++ b/src/cmd_quit.cpp
@@ -1 +1,48 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_quit.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_quit(Instance); } CmdResult cmd_quit::Handle (const char** parameters, int pcnt, userrec *user) { std::string quitmsg; if (IS_LOCAL(user)) { if (*ServerInstance->Config->FixedQuit) quitmsg = ServerInstance->Config->FixedQuit; else quitmsg = pcnt ? ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit : "Client exited"; } else quitmsg = pcnt ? parameters[0] : "Client exited"; userrec::QuitUser(ServerInstance, user, quitmsg); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_quit.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_quit(Instance);
+}
+
+CmdResult cmd_quit::Handle (const char** parameters, int pcnt, userrec *user)
+{
+
+ std::string quitmsg;
+
+ if (IS_LOCAL(user))
+ {
+ if (*ServerInstance->Config->FixedQuit)
+ quitmsg = ServerInstance->Config->FixedQuit;
+ else
+ quitmsg = pcnt ?
+ ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit
+ : "Client exited";
+ }
+ else
+ quitmsg = pcnt ? parameters[0] : "Client exited";
+
+ userrec::QuitUser(ServerInstance, user, quitmsg);
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_rehash.cpp b/src/cmd_rehash.cpp
index c8415f313..34789b0ea 100644
--- a/src/cmd_rehash.cpp
+++ b/src/cmd_rehash.cpp
@@ -1 +1,56 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_rehash.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_rehash(Instance); } CmdResult cmd_rehash::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("382 %s %s :Rehashing",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName)); std::string parameter; std::string old_disabled = ServerInstance->Config->DisabledCommands; if (pcnt) { parameter = parameters[0]; } else { ServerInstance->WriteOpers("*** %s is rehashing config file %s",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName)); ServerInstance->CloseLog(); ServerInstance->OpenLog(ServerInstance->Config->argv, ServerInstance->Config->argc); ServerInstance->RehashUsersAndChans(); FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect()); ServerInstance->Config->Read(false,user); ServerInstance->Res->Rehash(); ServerInstance->ResetMaxBans(); } if (old_disabled != ServerInstance->Config->DisabledCommands) InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance); FOREACH_MOD(I_OnRehash,OnRehash(user, parameter)); ServerInstance->BuildISupport(); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_rehash.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_rehash(Instance);
+}
+
+CmdResult cmd_rehash::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("382 %s %s :Rehashing",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName));
+ std::string parameter;
+ std::string old_disabled = ServerInstance->Config->DisabledCommands;
+ if (pcnt)
+ {
+ parameter = parameters[0];
+ }
+ else
+ {
+ ServerInstance->WriteOpers("*** %s is rehashing config file %s",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName));
+ ServerInstance->CloseLog();
+ ServerInstance->OpenLog(ServerInstance->Config->argv, ServerInstance->Config->argc);
+ ServerInstance->RehashUsersAndChans();
+ FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
+ ServerInstance->Config->Read(false,user);
+ ServerInstance->Res->Rehash();
+ ServerInstance->ResetMaxBans();
+ }
+ if (old_disabled != ServerInstance->Config->DisabledCommands)
+ InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
+
+ FOREACH_MOD(I_OnRehash,OnRehash(user, parameter));
+
+ ServerInstance->BuildISupport();
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_reloadmodule.cpp b/src/cmd_reloadmodule.cpp
index 0eb6f1ec4..41da0c40d 100644
--- a/src/cmd_reloadmodule.cpp
+++ b/src/cmd_reloadmodule.cpp
@@ -1 +1,39 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_reloadmodule.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_reloadmodule(Instance); } CmdResult cmd_reloadmodule::Handle (const char** parameters, int pcnt, userrec *user) { if (ServerInstance->UnloadModule(parameters[0])) { ServerInstance->WriteOpers("*** RELOAD MODULE: %s unloaded %s",user->nick, parameters[0]); if (ServerInstance->LoadModule(parameters[0])) { ServerInstance->WriteOpers("*** RELOAD MODULE: %s reloaded %s",user->nick, parameters[0]); user->WriteServ("975 %s %s :Module successfully reloaded.",user->nick, parameters[0]); return CMD_SUCCESS; } } ServerInstance->WriteOpers("*** RELOAD MODULE: %s unsuccessfully reloaded %s",user->nick, parameters[0]); user->WriteServ("975 %s %s :Module failed to reload.",user->nick, parameters[0]); return CMD_FAILURE; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_reloadmodule.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_reloadmodule(Instance);
+}
+
+CmdResult cmd_reloadmodule::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (ServerInstance->UnloadModule(parameters[0]))
+ {
+ ServerInstance->WriteOpers("*** RELOAD MODULE: %s unloaded %s",user->nick, parameters[0]);
+ if (ServerInstance->LoadModule(parameters[0]))
+ {
+ ServerInstance->WriteOpers("*** RELOAD MODULE: %s reloaded %s",user->nick, parameters[0]);
+ user->WriteServ("975 %s %s :Module successfully reloaded.",user->nick, parameters[0]);
+ return CMD_SUCCESS;
+ }
+ }
+
+ ServerInstance->WriteOpers("*** RELOAD MODULE: %s unsuccessfully reloaded %s",user->nick, parameters[0]);
+ user->WriteServ("975 %s %s :Module failed to reload.",user->nick, parameters[0]);
+ return CMD_FAILURE;
+}
diff --git a/src/cmd_restart.cpp b/src/cmd_restart.cpp
index 1dbf5b121..c910df8ba 100644
--- a/src/cmd_restart.cpp
+++ b/src/cmd_restart.cpp
@@ -1 +1,49 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_restart.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_restart(Instance); } CmdResult cmd_restart::Handle (const char** parameters, int pcnt, userrec *user) { ServerInstance->Log(DEFAULT,"Restart: %s",user->nick); if (!strcmp(parameters[0],ServerInstance->Config->restartpass)) { ServerInstance->WriteOpers("*** RESTART command from %s!%s@%s, restarting server.",user->nick,user->ident,user->host); try { ServerInstance->Restart("Server restarting."); } catch (...) { /* We dont actually get here unless theres some fatal and unrecoverable error. */ exit(0); } } else { ServerInstance->WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_restart.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_restart(Instance);
+}
+
+CmdResult cmd_restart::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ ServerInstance->Log(DEFAULT,"Restart: %s",user->nick);
+ if (!strcmp(parameters[0],ServerInstance->Config->restartpass))
+ {
+ ServerInstance->WriteOpers("*** RESTART command from %s!%s@%s, restarting server.",user->nick,user->ident,user->host);
+
+ try
+ {
+ ServerInstance->Restart("Server restarting.");
+ }
+ catch (...)
+ {
+ /* We dont actually get here unless theres some fatal and unrecoverable error. */
+ exit(0);
+ }
+ }
+ else
+ {
+ ServerInstance->WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host);
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_rules.cpp b/src/cmd_rules.cpp
index ef531732d..95b744412 100644
--- a/src/cmd_rules.cpp
+++ b/src/cmd_rules.cpp
@@ -1 +1,27 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_rules.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_rules(Instance); } CmdResult cmd_rules::Handle (const char** parameters, int pcnt, userrec *user) { user->ShowRULES(); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_rules.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_rules(Instance);
+}
+
+CmdResult cmd_rules::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->ShowRULES();
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_server.cpp b/src/cmd_server.cpp
index acad55b1b..cace13c38 100644
--- a/src/cmd_server.cpp
+++ b/src/cmd_server.cpp
@@ -1 +1,30 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_server.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_server(Instance); } CmdResult cmd_server::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("666 %s :You cannot identify as a server, you are a USER. IRC Operators informed.",user->nick); ServerInstance->WriteOpers("*** WARNING: %s attempted to issue a SERVER command and is registered as a user!",user->nick); return CMD_FAILURE; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_server.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_server(Instance);
+}
+
+CmdResult cmd_server::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("666 %s :You cannot identify as a server, you are a USER. IRC Operators informed.",user->nick);
+ ServerInstance->WriteOpers("*** WARNING: %s attempted to issue a SERVER command and is registered as a user!",user->nick);
+ return CMD_FAILURE;
+}
diff --git a/src/cmd_squit.cpp b/src/cmd_squit.cpp
index d6fe63c94..57105109b 100644
--- a/src/cmd_squit.cpp
+++ b/src/cmd_squit.cpp
@@ -1 +1,32 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_squit.h" /* * This is handled by the server linking module, if necessary. Do not remove this stub. */ extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_squit(Instance); } CmdResult cmd_squit::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick); return CMD_FAILURE; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_squit.h"
+
+/*
+ * This is handled by the server linking module, if necessary. Do not remove this stub.
+ */
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_squit(Instance);
+}
+
+CmdResult cmd_squit::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick);
+ return CMD_FAILURE;
+}
diff --git a/src/cmd_stats.cpp b/src/cmd_stats.cpp
index 4bf872981..98b2c63dd 100644
--- a/src/cmd_stats.cpp
+++ b/src/cmd_stats.cpp
@@ -1 +1,318 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #ifndef WIN32 #include <sys/resource.h> /* This is just to be completely certain that the change which fixed getrusage on RH7 doesn't break anything else -- Om */ #ifndef RUSAGE_SELF #define RUSAGE_SELF 0 #endif #endif #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_stats.h" #include "commands/cmd_whowas.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_stats(Instance); } CmdResult cmd_stats::Handle (const char** parameters, int pcnt, userrec *user) { if (IS_LOCAL(user)) { string_list values; DoStats(this->ServerInstance, *parameters[0], user, values); for (size_t i = 0; i < values.size(); i++) user->Write(":%s", values[i].c_str()); } return CMD_SUCCESS; } DllExport void DoStats(InspIRCd* ServerInstance, char statschar, userrec* user, string_list &results) { std::string sn = ServerInstance->Config->ServerName; if ((*ServerInstance->Config->UserStats) && !IS_OPER(user) && !strchr(ServerInstance->Config->UserStats,statschar)) { results.push_back(sn+std::string(" 481 ")+user->nick+" :Permission denied - STATS "+statschar+" is oper-only"); return; } int MOD_RESULT = 0; FOREACH_RESULT(I_OnStats,OnStats(statschar,user,results)); if (MOD_RESULT) return; switch (statschar) { /* stats p (show listening ports and registered clients on each) */ case 'p': { for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) { std::string ip = ServerInstance->Config->ports[i]->GetIP(); if (ip.empty()) ip.assign("*"); results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ServerInstance->Config->ports[i]->GetPort())+" (client, " + ServerInstance->Config->ports[i]->GetDescription() + ")"); } } break; case 'n': case 'c': { /* This stats symbol must be handled by a linking module */ } break; case 'i': { int idx = 0; for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { results.push_back(sn+" 215 "+user->nick+" I NOMATCH * "+i->GetHost()+" "+ConvToStr(MAXCLIENTS)+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *"); idx++; } } break; case 'Y': { int idx = 0; for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(i->GetPingTime())+" 0 "+ConvToStr(i->GetSendqMax())+" :"+ ConvToStr(i->GetFlood())+" "+ConvToStr(i->GetRegTimeout())); idx++; } } break; case 'U': { char ulined[MAXBUF]; for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "uline"); i++) { ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "uline","server", i, ulined, MAXBUF); results.push_back(sn+" 248 "+user->nick+" U "+std::string(ulined)); } } break; case 'P': { int idx = 0; for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) { if (IS_OPER(i->second) && !ServerInstance->ULine(i->second->server)) { results.push_back(sn+" 249 "+user->nick+" :"+i->second->nick+" ("+i->second->ident+"@"+i->second->dhost+") Idle: "+ (IS_LOCAL(i->second) ? ConvToStr(ServerInstance->Time() - i->second->idle_lastmsg) + " secs" : "unavailable")); idx++; } } results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)"); } break; case 'k': ServerInstance->XLines->stats_k(user,results); break; case 'g': ServerInstance->XLines->stats_g(user,results); break; case 'q': ServerInstance->XLines->stats_q(user,results); break; case 'Z': ServerInstance->XLines->stats_z(user,results); break; case 'e': ServerInstance->XLines->stats_e(user,results); break; /* stats m (list number of times each command has been used, plus bytecount) */ case 'm': for (command_table::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->command+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes)); } } break; /* stats z (debug and memory info) */ case 'z': { results.push_back(sn+" 240 "+user->nick+" :InspIRCd(CLASS) "+ConvToStr(sizeof(InspIRCd))+" bytes"); results.push_back(sn+" 249 "+user->nick+" :Users(HASH_MAP) "+ConvToStr(ServerInstance->clientlist->size())+" ("+ConvToStr(ServerInstance->clientlist->size()*sizeof(userrec))+" bytes)"); results.push_back(sn+" 249 "+user->nick+" :Channels(HASH_MAP) "+ConvToStr(ServerInstance->chanlist->size())+" ("+ConvToStr(ServerInstance->chanlist->size()*sizeof(chanrec))+" bytes)"); results.push_back(sn+" 249 "+user->nick+" :Commands(VECTOR) "+ConvToStr(ServerInstance->Parser->cmdlist.size())+" ("+ConvToStr(ServerInstance->Parser->cmdlist.size()*sizeof(command_t))+" bytes)"); if (!ServerInstance->Config->WhoWasGroupSize == 0 && !ServerInstance->Config->WhoWasMaxGroups == 0) { command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS"); if (whowas_command) { std::deque<classbase*> params; Extensible whowas_stats; params.push_back(&whowas_stats); whowas_command->HandleInternal(WHOWAS_STATS, params); if (whowas_stats.GetExt("stats")) { char* stats; whowas_stats.GetExt("stats", stats); results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(stats)); } } } results.push_back(sn+" 249 "+user->nick+" :MOTD(VECTOR) "+ConvToStr(ServerInstance->Config->MOTD.size())+", RULES(VECTOR) "+ConvToStr(ServerInstance->Config->RULES.size())); results.push_back(sn+" 249 "+user->nick+" :Modules(VECTOR) "+ConvToStr(ServerInstance->modules.size())+" ("+ConvToStr(ServerInstance->modules.size()*sizeof(Module))+" bytes)"); results.push_back(sn+" 249 "+user->nick+" :ClassFactories(VECTOR) "+ConvToStr(ServerInstance->factory.size())+" ("+ConvToStr(ServerInstance->factory.size()*sizeof(ircd_module))+" bytes)"); #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)); timeval tv; char percent[30]; gettimeofday(&tv, NULL); float n_elapsed = ((tv.tv_sec - ServerInstance->stats->LastSampled.tv_sec) * 1000000 + tv.tv_usec - ServerInstance->stats->LastSampled.tv_usec); 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 Usage: "+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,ServerInstance->stats->statsSent / 1024,ServerInstance->stats->statsRecv / 1024); results.push_back(sn+buffer); } break; /* stats o */ case 'o': for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++) { char LoginName[MAXBUF]; char HostName[MAXBUF]; char OperType[MAXBUF]; ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","name", i, LoginName, MAXBUF); ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","host", i, HostName, MAXBUF); ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","type", i, OperType, MAXBUF); results.push_back(sn+" 243 "+user->nick+" O "+HostName+" * "+LoginName+" "+OperType+" 0"); } 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 (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++) { userrec* i = *n; if (ServerInstance->IsNick(i->nick)) { results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age)); } } break; /* stats L (show user I/O stats with IP addresses) */ case 'L': results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open"); for (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++) { userrec* i = *n; if (ServerInstance->IsNick(i->nick)) { results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age)); } } 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,(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,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)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); return; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#ifndef WIN32
+#include <sys/resource.h>
+
+/* This is just to be completely certain that the change which fixed getrusage on RH7 doesn't break anything else -- Om */
+#ifndef RUSAGE_SELF
+#define RUSAGE_SELF 0
+#endif
+
+#endif
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_stats.h"
+#include "commands/cmd_whowas.h"
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_stats(Instance);
+}
+
+CmdResult cmd_stats::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (IS_LOCAL(user))
+ {
+ string_list values;
+ DoStats(this->ServerInstance, *parameters[0], user, values);
+ for (size_t i = 0; i < values.size(); i++)
+ user->Write(":%s", values[i].c_str());
+ }
+
+ return CMD_SUCCESS;
+}
+
+DllExport void DoStats(InspIRCd* ServerInstance, char statschar, userrec* user, string_list &results)
+{
+ std::string sn = ServerInstance->Config->ServerName;
+
+ if ((*ServerInstance->Config->UserStats) && !IS_OPER(user) && !strchr(ServerInstance->Config->UserStats,statschar))
+ {
+ results.push_back(sn+std::string(" 481 ")+user->nick+" :Permission denied - STATS "+statschar+" is oper-only");
+ return;
+ }
+
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnStats,OnStats(statschar,user,results));
+ if (MOD_RESULT)
+ return;
+
+ switch (statschar)
+ {
+ /* stats p (show listening ports and registered clients on each) */
+ case 'p':
+ {
+ for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
+ {
+ std::string ip = ServerInstance->Config->ports[i]->GetIP();
+ if (ip.empty())
+ ip.assign("*");
+
+ results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ServerInstance->Config->ports[i]->GetPort())+" (client, " +
+ ServerInstance->Config->ports[i]->GetDescription() + ")");
+ }
+ }
+ break;
+
+ case 'n':
+ case 'c':
+ {
+ /* This stats symbol must be handled by a linking module */
+ }
+ break;
+
+ case 'i':
+ {
+ int idx = 0;
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ results.push_back(sn+" 215 "+user->nick+" I NOMATCH * "+i->GetHost()+" "+ConvToStr(MAXCLIENTS)+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
+ idx++;
+ }
+ }
+ break;
+
+ case 'Y':
+ {
+ int idx = 0;
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(i->GetPingTime())+" 0 "+ConvToStr(i->GetSendqMax())+" :"+
+ ConvToStr(i->GetFlood())+" "+ConvToStr(i->GetRegTimeout()));
+ idx++;
+ }
+ }
+ break;
+
+ case 'U':
+ {
+ char ulined[MAXBUF];
+ for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "uline"); i++)
+ {
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "uline","server", i, ulined, MAXBUF);
+ results.push_back(sn+" 248 "+user->nick+" U "+std::string(ulined));
+ }
+ }
+ break;
+
+ case 'P':
+ {
+ int idx = 0;
+ for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
+ {
+ if (IS_OPER(i->second) && !ServerInstance->ULine(i->second->server))
+ {
+ results.push_back(sn+" 249 "+user->nick+" :"+i->second->nick+" ("+i->second->ident+"@"+i->second->dhost+") Idle: "+
+ (IS_LOCAL(i->second) ? ConvToStr(ServerInstance->Time() - i->second->idle_lastmsg) + " secs" : "unavailable"));
+ idx++;
+ }
+ }
+ results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
+ }
+ break;
+
+ case 'k':
+ ServerInstance->XLines->stats_k(user,results);
+ break;
+
+ case 'g':
+ ServerInstance->XLines->stats_g(user,results);
+ break;
+
+ case 'q':
+ ServerInstance->XLines->stats_q(user,results);
+ break;
+
+ case 'Z':
+ ServerInstance->XLines->stats_z(user,results);
+ break;
+
+ case 'e':
+ ServerInstance->XLines->stats_e(user,results);
+ break;
+
+ /* stats m (list number of times each command has been used, plus bytecount) */
+ case 'm':
+ for (command_table::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->command+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
+ }
+ }
+ break;
+
+ /* stats z (debug and memory info) */
+ case 'z':
+ {
+ results.push_back(sn+" 240 "+user->nick+" :InspIRCd(CLASS) "+ConvToStr(sizeof(InspIRCd))+" bytes");
+ results.push_back(sn+" 249 "+user->nick+" :Users(HASH_MAP) "+ConvToStr(ServerInstance->clientlist->size())+" ("+ConvToStr(ServerInstance->clientlist->size()*sizeof(userrec))+" bytes)");
+ results.push_back(sn+" 249 "+user->nick+" :Channels(HASH_MAP) "+ConvToStr(ServerInstance->chanlist->size())+" ("+ConvToStr(ServerInstance->chanlist->size()*sizeof(chanrec))+" bytes)");
+ results.push_back(sn+" 249 "+user->nick+" :Commands(VECTOR) "+ConvToStr(ServerInstance->Parser->cmdlist.size())+" ("+ConvToStr(ServerInstance->Parser->cmdlist.size()*sizeof(command_t))+" bytes)");
+
+ if (!ServerInstance->Config->WhoWasGroupSize == 0 && !ServerInstance->Config->WhoWasMaxGroups == 0)
+ {
+ command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
+ if (whowas_command)
+ {
+ std::deque<classbase*> params;
+ Extensible whowas_stats;
+ params.push_back(&whowas_stats);
+ whowas_command->HandleInternal(WHOWAS_STATS, params);
+ if (whowas_stats.GetExt("stats"))
+ {
+ char* stats;
+ whowas_stats.GetExt("stats", stats);
+ results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(stats));
+ }
+ }
+ }
+
+ results.push_back(sn+" 249 "+user->nick+" :MOTD(VECTOR) "+ConvToStr(ServerInstance->Config->MOTD.size())+", RULES(VECTOR) "+ConvToStr(ServerInstance->Config->RULES.size()));
+ results.push_back(sn+" 249 "+user->nick+" :Modules(VECTOR) "+ConvToStr(ServerInstance->modules.size())+" ("+ConvToStr(ServerInstance->modules.size()*sizeof(Module))+" bytes)");
+ results.push_back(sn+" 249 "+user->nick+" :ClassFactories(VECTOR) "+ConvToStr(ServerInstance->factory.size())+" ("+ConvToStr(ServerInstance->factory.size()*sizeof(ircd_module))+" bytes)");
+
+#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));
+
+ timeval tv;
+ char percent[30];
+ gettimeofday(&tv, NULL);
+
+ float n_elapsed = ((tv.tv_sec - ServerInstance->stats->LastSampled.tv_sec) * 1000000 + tv.tv_usec - ServerInstance->stats->LastSampled.tv_usec);
+ 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 Usage: "+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,ServerInstance->stats->statsSent / 1024,ServerInstance->stats->statsRecv / 1024);
+ results.push_back(sn+buffer);
+ }
+ break;
+
+ /* stats o */
+ case 'o':
+ for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++)
+ {
+ char LoginName[MAXBUF];
+ char HostName[MAXBUF];
+ char OperType[MAXBUF];
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","name", i, LoginName, MAXBUF);
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","host", i, HostName, MAXBUF);
+ ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","type", i, OperType, MAXBUF);
+ results.push_back(sn+" 243 "+user->nick+" O "+HostName+" * "+LoginName+" "+OperType+" 0");
+ }
+ 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 (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++)
+ {
+ userrec* i = *n;
+ if (ServerInstance->IsNick(i->nick))
+ {
+ results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
+ }
+ }
+ break;
+
+ /* stats L (show user I/O stats with IP addresses) */
+ case 'L':
+ results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open");
+ for (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++)
+ {
+ userrec* i = *n;
+ if (ServerInstance->IsNick(i->nick))
+ {
+ results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
+ }
+ }
+ 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,(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,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)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
+
+ return;
+}
+
diff --git a/src/cmd_summon.cpp b/src/cmd_summon.cpp
index 134ac9f2f..520bdf090 100644
--- a/src/cmd_summon.cpp
+++ b/src/cmd_summon.cpp
@@ -1 +1,27 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_summon.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_summon(Instance); } CmdResult cmd_summon::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("445 %s :SUMMON has been disabled (depreciated command)",user->nick); return CMD_FAILURE; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_summon.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_summon(Instance);
+}
+
+CmdResult cmd_summon::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("445 %s :SUMMON has been disabled (depreciated command)",user->nick);
+ return CMD_FAILURE;
+}
diff --git a/src/cmd_time.cpp b/src/cmd_time.cpp
index 272a0ce82..cd0f0e1c7 100644
--- a/src/cmd_time.cpp
+++ b/src/cmd_time.cpp
@@ -1 +1,40 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_time.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_time(Instance); } CmdResult cmd_time::Handle (const char** parameters, int pcnt, userrec *user) { struct tm* timeinfo; time_t local = ServerInstance->Time(); timeinfo = localtime(&local); char tms[26]; snprintf(tms,26,"%s",asctime(timeinfo)); tms[24] = 0; user->WriteServ("391 %s %s :%s",user->nick,ServerInstance->Config->ServerName,tms); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_time.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_time(Instance);
+}
+
+CmdResult cmd_time::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ struct tm* timeinfo;
+ time_t local = ServerInstance->Time();
+
+ timeinfo = localtime(&local);
+
+ char tms[26];
+ snprintf(tms,26,"%s",asctime(timeinfo));
+ tms[24] = 0;
+
+ user->WriteServ("391 %s %s :%s",user->nick,ServerInstance->Config->ServerName,tms);
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_topic.cpp b/src/cmd_topic.cpp
index 82f21b24e..741558282 100644
--- a/src/cmd_topic.cpp
+++ b/src/cmd_topic.cpp
@@ -1 +1,118 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_topic.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_topic(Instance); } CmdResult cmd_topic::Handle (const char** parameters, int pcnt, userrec *user) { chanrec* Ptr; if (pcnt == 1) { Ptr = ServerInstance->FindChan(parameters[0]); if (Ptr) { if ((Ptr->modes[CM_SECRET]) && (!Ptr->HasUser(user))) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, Ptr->name); return CMD_FAILURE; } if (Ptr->topicset) { user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic); user->WriteServ("333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset); } else { user->WriteServ("331 %s %s :No topic is set.", user->nick, Ptr->name); } } else { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return CMD_FAILURE; } return CMD_SUCCESS; } else if (pcnt>1) { Ptr = ServerInstance->FindChan(parameters[0]); if (Ptr) { if (IS_LOCAL(user)) { if (!Ptr->HasUser(user)) { user->WriteServ("442 %s %s :You're not on that channel!",user->nick, Ptr->name); return CMD_FAILURE; } if ((Ptr->modes[CM_TOPICLOCK]) && (Ptr->GetStatus(user) < STATUS_HOP)) { user->WriteServ("482 %s %s :You must be at least a half-operator to change the topic on this channel", user->nick, Ptr->name); return CMD_FAILURE; } } char topic[MAXTOPIC]; if (IS_LOCAL(user)) { /* XXX: we need two string copies for a local topic, because we cant * let a module see the topic as longer than it actually is */ int MOD_RESULT = 0; strlcpy(topic,parameters[1],MAXTOPIC-1); FOREACH_RESULT(I_OnLocalTopicChange,OnLocalTopicChange(user,Ptr,topic)); if (MOD_RESULT) return CMD_FAILURE; strlcpy(Ptr->topic,topic,MAXTOPIC-1); } else { /* Sneaky shortcut, one string copy for a remote topic */ strlcpy(Ptr->topic, parameters[1], MAXTOPIC-1); } if (ServerInstance->Config->FullHostInTopic) strlcpy(Ptr->setby,user->GetFullHost(),127); else strlcpy(Ptr->setby,user->nick,127); Ptr->topicset = ServerInstance->Time(); Ptr->WriteChannel(user, "TOPIC %s :%s", Ptr->name, Ptr->topic); if (IS_LOCAL(user)) /* We know 'topic' will contain valid data here */ FOREACH_MOD(I_OnPostLocalTopicChange,OnPostLocalTopicChange(user, Ptr, topic)); } else { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return CMD_FAILURE; } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_topic.h"
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_topic(Instance);
+}
+
+CmdResult cmd_topic::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ chanrec* Ptr;
+
+ if (pcnt == 1)
+ {
+ Ptr = ServerInstance->FindChan(parameters[0]);
+ if (Ptr)
+ {
+ if ((Ptr->modes[CM_SECRET]) && (!Ptr->HasUser(user)))
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, Ptr->name);
+ return CMD_FAILURE;
+ }
+ if (Ptr->topicset)
+ {
+ user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
+ user->WriteServ("333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
+ }
+ else
+ {
+ user->WriteServ("331 %s %s :No topic is set.", user->nick, Ptr->name);
+ }
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+ }
+ else if (pcnt>1)
+ {
+ Ptr = ServerInstance->FindChan(parameters[0]);
+ if (Ptr)
+ {
+ if (IS_LOCAL(user))
+ {
+ if (!Ptr->HasUser(user))
+ {
+ user->WriteServ("442 %s %s :You're not on that channel!",user->nick, Ptr->name);
+ return CMD_FAILURE;
+ }
+ if ((Ptr->modes[CM_TOPICLOCK]) && (Ptr->GetStatus(user) < STATUS_HOP))
+ {
+ user->WriteServ("482 %s %s :You must be at least a half-operator to change the topic on this channel", user->nick, Ptr->name);
+ return CMD_FAILURE;
+ }
+ }
+
+ char topic[MAXTOPIC];
+
+ if (IS_LOCAL(user))
+ {
+ /* XXX: we need two string copies for a local topic, because we cant
+ * let a module see the topic as longer than it actually is
+ */
+ int MOD_RESULT = 0;
+
+ strlcpy(topic,parameters[1],MAXTOPIC-1);
+ FOREACH_RESULT(I_OnLocalTopicChange,OnLocalTopicChange(user,Ptr,topic));
+ if (MOD_RESULT)
+ return CMD_FAILURE;
+
+ strlcpy(Ptr->topic,topic,MAXTOPIC-1);
+ }
+ else
+ {
+ /* Sneaky shortcut, one string copy for a remote topic */
+ strlcpy(Ptr->topic, parameters[1], MAXTOPIC-1);
+ }
+
+ if (ServerInstance->Config->FullHostInTopic)
+ strlcpy(Ptr->setby,user->GetFullHost(),127);
+ else
+ strlcpy(Ptr->setby,user->nick,127);
+
+ Ptr->topicset = ServerInstance->Time();
+ Ptr->WriteChannel(user, "TOPIC %s :%s", Ptr->name, Ptr->topic);
+
+ if (IS_LOCAL(user))
+ /* We know 'topic' will contain valid data here */
+ FOREACH_MOD(I_OnPostLocalTopicChange,OnPostLocalTopicChange(user, Ptr, topic));
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_trace.cpp b/src/cmd_trace.cpp
index 9cb58e8ee..42105df98 100644
--- a/src/cmd_trace.cpp
+++ b/src/cmd_trace.cpp
@@ -1 +1,46 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_trace.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_trace(Instance); } /** XXX: This is crap. someone fix this when you have time, to be more useful. */ CmdResult cmd_trace::Handle (const char** parameters, int pcnt, userrec *user) { for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) { if (i->second->registered == REG_ALL) { if (IS_OPER(i->second)) { user->WriteServ("205 %s :Oper 0 %s",user->nick,i->second->nick); } else { user->WriteServ("204 %s :User 0 %s",user->nick,i->second->nick); } } else { user->WriteServ("203 %s :???? 0 [%s]",user->nick,i->second->host); } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_trace.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_trace(Instance);
+}
+
+/** XXX: This is crap. someone fix this when you have time, to be more useful.
+ */
+CmdResult cmd_trace::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
+ {
+ if (i->second->registered == REG_ALL)
+ {
+ if (IS_OPER(i->second))
+ {
+ user->WriteServ("205 %s :Oper 0 %s",user->nick,i->second->nick);
+ }
+ else
+ {
+ user->WriteServ("204 %s :User 0 %s",user->nick,i->second->nick);
+ }
+ }
+ else
+ {
+ user->WriteServ("203 %s :???? 0 [%s]",user->nick,i->second->host);
+ }
+ }
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_unloadmodule.cpp b/src/cmd_unloadmodule.cpp
index 51192aa7a..44c2133e7 100644
--- a/src/cmd_unloadmodule.cpp
+++ b/src/cmd_unloadmodule.cpp
@@ -1 +1,39 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_unloadmodule.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_unloadmodule(Instance); } CmdResult cmd_unloadmodule::Handle (const char** parameters, int pcnt, userrec *user) { if (ServerInstance->UnloadModule(parameters[0])) { ServerInstance->WriteOpers("*** MODULE UNLOADED: %s unloaded %s", user->nick, parameters[0]); user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]); } else { user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_unloadmodule.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_unloadmodule(Instance);
+}
+
+CmdResult cmd_unloadmodule::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (ServerInstance->UnloadModule(parameters[0]))
+ {
+ ServerInstance->WriteOpers("*** MODULE UNLOADED: %s unloaded %s", user->nick, parameters[0]);
+ user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]);
+ }
+ else
+ {
+ user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_user.cpp b/src/cmd_user.cpp
index aa9e3d321..dc224db76 100644
--- a/src/cmd_user.cpp
+++ b/src/cmd_user.cpp
@@ -1 +1,69 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_user.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_user(Instance); } CmdResult cmd_user::Handle (const char** parameters, int pcnt, userrec *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->WriteServ("461 %s USER :Your username is not valid",user->nick); return CMD_FAILURE; } else { /* We're not checking ident, but I'm not sure I like the idea of '~' prefixing.. */ /* XXX - 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. */ strlcpy(user->ident, parameters[0], IDENTMAX); strlcpy(user->fullname, *parameters[3] ? parameters[3] : "No info", MAXGECOS); user->registered = (user->registered | REG_USER); } } else { user->WriteServ("462 %s :You may not reregister",user->nick); return CMD_FAILURE; } /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */ if (user->registered == REG_NICKUSER) { int MOD_RESULT = 0; /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout) ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout; FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user)); if (MOD_RESULT > 0) return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_user.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_user(Instance);
+}
+
+CmdResult cmd_user::Handle (const char** parameters, int pcnt, userrec *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->WriteServ("461 %s USER :Your username is not valid",user->nick);
+ return CMD_FAILURE;
+ }
+ else
+ {
+ /* We're not checking ident, but I'm not sure I like the idea of '~' prefixing.. */
+ /* XXX - 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.
+ */
+ strlcpy(user->ident, parameters[0], IDENTMAX);
+ strlcpy(user->fullname, *parameters[3] ? parameters[3] : "No info", MAXGECOS);
+ user->registered = (user->registered | REG_USER);
+ }
+ }
+ else
+ {
+ user->WriteServ("462 %s :You may not reregister",user->nick);
+ return CMD_FAILURE;
+ }
+ /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
+ if (user->registered == REG_NICKUSER)
+ {
+ int MOD_RESULT = 0;
+ /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
+ if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout)
+ ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
+ FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user));
+ if (MOD_RESULT > 0)
+ return CMD_FAILURE;
+
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_userhost.cpp b/src/cmd_userhost.cpp
index c0a2362cd..9e644bdd1 100644
--- a/src/cmd_userhost.cpp
+++ b/src/cmd_userhost.cpp
@@ -1 +1,63 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_userhost.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_userhost(Instance); } CmdResult cmd_userhost::Handle (const char** parameters, int pcnt, userrec *user) { std::string retbuf = std::string("302 ") + user->nick + " :"; for (int i = 0; i < pcnt; i++) { userrec *u = ServerInstance->FindNick(parameters[i]); if ((u) && (u->registered == REG_ALL)) { retbuf = retbuf + u->nick; if (IS_OPER(u)) { retbuf = retbuf + "*=+"; } else { retbuf = retbuf + "=+"; } retbuf = retbuf + u->ident + "@"; if (IS_OPER(user)) { retbuf = retbuf + u->host; } else { retbuf = retbuf + u->dhost; } retbuf = retbuf + " "; } } user->WriteServ(retbuf); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_userhost.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_userhost(Instance);
+}
+
+CmdResult cmd_userhost::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ std::string retbuf = std::string("302 ") + user->nick + " :";
+
+
+ for (int i = 0; i < pcnt; i++)
+ {
+ userrec *u = ServerInstance->FindNick(parameters[i]);
+
+ if ((u) && (u->registered == REG_ALL))
+ {
+ retbuf = retbuf + u->nick;
+
+ if (IS_OPER(u))
+ {
+ retbuf = retbuf + "*=+";
+ }
+ else
+ {
+ retbuf = retbuf + "=+";
+ }
+
+ retbuf = retbuf + u->ident + "@";
+
+ if (IS_OPER(user))
+ {
+ retbuf = retbuf + u->host;
+ }
+ else
+ {
+ retbuf = retbuf + u->dhost;
+ }
+
+ retbuf = retbuf + " ";
+ }
+ }
+
+ user->WriteServ(retbuf);
+
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_users.cpp b/src/cmd_users.cpp
index c3cb02075..97b9b247d 100644
--- a/src/cmd_users.cpp
+++ b/src/cmd_users.cpp
@@ -1 +1,27 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "commands/cmd_users.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_users(Instance); } CmdResult cmd_users::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("445 %s :USERS has been disabled (depreciated command)",user->nick); return CMD_FAILURE; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "commands/cmd_users.h"
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_users(Instance);
+}
+
+CmdResult cmd_users::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("445 %s :USERS has been disabled (depreciated command)",user->nick);
+ return CMD_FAILURE;
+}
diff --git a/src/cmd_version.cpp b/src/cmd_version.cpp
index a33dd5bd0..599b7bf75 100644
--- a/src/cmd_version.cpp
+++ b/src/cmd_version.cpp
@@ -1 +1,31 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_version.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_version(Instance); } CmdResult cmd_version::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("351 %s :%s",user->nick,ServerInstance->GetVersionString().c_str()); ServerInstance->Config->Send005(user); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_version.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_version(Instance);
+}
+
+CmdResult cmd_version::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("351 %s :%s",user->nick,ServerInstance->GetVersionString().c_str());
+ ServerInstance->Config->Send005(user);
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_wallops.cpp b/src/cmd_wallops.cpp
index 88c8fccf5..d32b19ebd 100644
--- a/src/cmd_wallops.cpp
+++ b/src/cmd_wallops.cpp
@@ -1 +1,31 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "modules.h" #include "commands/cmd_wallops.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_wallops(Instance); } CmdResult cmd_wallops::Handle (const char** parameters, int pcnt, userrec *user) { user->WriteWallOps(std::string(parameters[0])); FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0])); return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "modules.h"
+#include "commands/cmd_wallops.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_wallops(Instance);
+}
+
+CmdResult cmd_wallops::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteWallOps(std::string(parameters[0]));
+ FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0]));
+ return CMD_SUCCESS;
+}
diff --git a/src/cmd_who.cpp b/src/cmd_who.cpp
index 6054351d9..31e8030f5 100644
--- a/src/cmd_who.cpp
+++ b/src/cmd_who.cpp
@@ -1 +1,328 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "commands/cmd_who.h" /* get the last 'visible' chan of a user */ static char *getlastchanname(userrec *u) { UCListIter i = u->chans.begin(); if (i != u->chans.end()) { if (!i->first->IsModeSet('s')) return i->first->name; } return "*"; } bool cmd_who::whomatch(userrec* user, const char* matchtext) { bool realhost = false; bool realname = false; bool positive = true; bool metadata = false; bool ident = false; bool away = false; bool port = false; char* dummy = NULL; 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 { if (opt_metadata) metadata = user->GetExt(matchtext, dummy); else { if (opt_realname) realname = match(user->fullname, matchtext); else { if (opt_showrealhost) realhost = match(user->host, matchtext); else { if (opt_ident) ident = match(user->ident, matchtext); else { if (opt_port) { irc::portparser portrange(matchtext, false); long portno = -1; while ((portno = portrange.GetToken())) if (portno == user->GetPort()) port = true; } else { if (opt_away) away = match(user->awaymsg, matchtext); } } } } } return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext))); } } extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_who(Instance); } bool cmd_who::CanView(chanrec* chan, userrec* user) { if (!user || !chan) return false; /* Execute items in fastest-to-execute first order */ /* Opers see all */ if (IS_OPER(user)) return true; else if (!chan->IsModeSet('s') && !chan->IsModeSet('p')) return true; else if (chan->HasUser(user)) return true; return false; } void cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults) { std::string lcn = getlastchanname(u); chanrec* chlast = ServerInstance->FindChan(lcn); /* Not visible to this user */ if (u->Visibility && !u->Visibility->VisibleTo(user)) return; std::string wholine = initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " + ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) + " " + u->nick + " "; /* away? */ if (IS_AWAY(u)) { wholine.append("G"); } else { wholine.append("H"); } /* oper? */ if (IS_OPER(u)) { wholine.append("*"); } wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname; whoresults.push_back(wholine); } CmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *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_unlimit = 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; chanrec *ch = NULL; std::vector<std::string> whoresults; std::string initial = "352 " + std::string(user->nick) + " "; const char* matchtext = NULL; /* Change '0' into '*' so the wildcard matcher can grok it */ matchtext = parameters[0]; if (!strcmp(matchtext,"0")) matchtext = "*"; if (pcnt > 1) { /* parse flags */ const char *iter = parameters[1]; while (*iter) { switch (*iter) { case 'o': opt_viewopersonly = true; break; case 'h': if (IS_OPER(user)) opt_showrealhost = true; break; case 'u': if (IS_OPER(user)) opt_unlimit = true; break; case 'r': opt_realname = true; break; case 'm': opt_mode = true; break; case 'M': opt_metadata = true; break; case 'i': opt_ident = true; break; case 'p': opt_port = true; break; case 'a': opt_away = true; break; case 'l': opt_local = true; break; case 'f': opt_far = true; break; } *iter++; } } /* who on a channel? */ ch = ServerInstance->FindChan(matchtext); if (ch) { if (CanView(ch,user)) { bool inside = ch->HasUser(user); /* who on a channel. */ CUList *cu = ch->GetUsers(); for (CUList::iterator i = cu->begin(); i != cu->end(); i++) { /* opers only, please */ if (opt_viewopersonly && !IS_OPER(i->first)) continue; /* If we're not inside the channel, hide +i users */ if (i->first->IsModeSet('i') && !inside) continue; SendWhoLine(user, initial, ch, i->first, whoresults); } } } else { /* Match against wildcard of nick, server or host */ if (opt_viewopersonly) { /* Showing only opers */ for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++) { userrec* oper = *i; if (whomatch(oper, matchtext)) { if ((!oper->IsModeSet('i')) && (!IS_OPER(user))) continue; SendWhoLine(user, initial, NULL, oper, whoresults); } } } else { for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) { if (whomatch(i->second, matchtext)) { if ((i->second->IsModeSet('i')) && (!IS_OPER(user))) continue; SendWhoLine(user, initial, NULL, i->second, whoresults); } } } } /* Send the results out */ if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit) { for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++) user->WriteServ(*n); user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*"); return CMD_SUCCESS; } else { /* BZZT! Too many results. */ user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]); return CMD_FAILURE; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_who.h"
+
+/* get the last 'visible' chan of a user */
+static char *getlastchanname(userrec *u)
+{
+ UCListIter i = u->chans.begin();
+ if (i != u->chans.end())
+ {
+ if (!i->first->IsModeSet('s'))
+ return i->first->name;
+ }
+
+ return "*";
+}
+
+bool cmd_who::whomatch(userrec* user, const char* matchtext)
+{
+ bool realhost = false;
+ bool realname = false;
+ bool positive = true;
+ bool metadata = false;
+ bool ident = false;
+ bool away = false;
+ bool port = false;
+ char* dummy = NULL;
+
+ 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
+ {
+
+ if (opt_metadata)
+ metadata = user->GetExt(matchtext, dummy);
+ else
+ {
+ if (opt_realname)
+ realname = match(user->fullname, matchtext);
+ else
+ {
+ if (opt_showrealhost)
+ realhost = match(user->host, matchtext);
+ else
+ {
+ if (opt_ident)
+ ident = match(user->ident, matchtext);
+ else
+ {
+ if (opt_port)
+ {
+ irc::portparser portrange(matchtext, false);
+ long portno = -1;
+ while ((portno = portrange.GetToken()))
+ if (portno == user->GetPort())
+ port = true;
+ }
+ else
+ {
+ if (opt_away)
+ away = match(user->awaymsg, matchtext);
+ }
+ }
+ }
+ }
+ }
+ return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));
+ }
+}
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_who(Instance);
+}
+
+bool cmd_who::CanView(chanrec* chan, userrec* user)
+{
+ if (!user || !chan)
+ return false;
+
+ /* Execute items in fastest-to-execute first order */
+
+ /* Opers see all */
+ if (IS_OPER(user))
+ return true;
+ else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
+ return true;
+ else if (chan->HasUser(user))
+ return true;
+
+ return false;
+}
+
+void cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults)
+{
+ std::string lcn = getlastchanname(u);
+ chanrec* chlast = ServerInstance->FindChan(lcn);
+
+ /* Not visible to this user */
+ if (u->Visibility && !u->Visibility->VisibleTo(user))
+ return;
+
+ std::string wholine = initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
+ ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +
+ " " + u->nick + " ";
+
+ /* away? */
+ if (IS_AWAY(u))
+ {
+ wholine.append("G");
+ }
+ else
+ {
+ wholine.append("H");
+ }
+
+ /* oper? */
+ if (IS_OPER(u))
+ {
+ wholine.append("*");
+ }
+
+ wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
+ whoresults.push_back(wholine);
+}
+
+CmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *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_unlimit = 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;
+
+ chanrec *ch = NULL;
+ std::vector<std::string> whoresults;
+ std::string initial = "352 " + std::string(user->nick) + " ";
+
+ const char* matchtext = NULL;
+
+ /* Change '0' into '*' so the wildcard matcher can grok it */
+ matchtext = parameters[0];
+ if (!strcmp(matchtext,"0"))
+ matchtext = "*";
+
+ if (pcnt > 1)
+ {
+ /* parse flags */
+ const char *iter = parameters[1];
+
+ while (*iter)
+ {
+ switch (*iter)
+ {
+ case 'o':
+ opt_viewopersonly = true;
+ break;
+ case 'h':
+ if (IS_OPER(user))
+ opt_showrealhost = true;
+ break;
+ case 'u':
+ if (IS_OPER(user))
+ opt_unlimit = true;
+ break;
+ case 'r':
+ opt_realname = true;
+ break;
+ case 'm':
+ opt_mode = true;
+ break;
+ case 'M':
+ opt_metadata = true;
+ break;
+ case 'i':
+ opt_ident = true;
+ break;
+ case 'p':
+ opt_port = true;
+ break;
+ case 'a':
+ opt_away = true;
+ break;
+ case 'l':
+ opt_local = true;
+ break;
+ case 'f':
+ opt_far = true;
+ break;
+ }
+
+ *iter++;
+ }
+ }
+
+
+ /* who on a channel? */
+ ch = ServerInstance->FindChan(matchtext);
+
+ if (ch)
+ {
+ if (CanView(ch,user))
+ {
+ bool inside = ch->HasUser(user);
+
+ /* who on a channel. */
+ CUList *cu = ch->GetUsers();
+
+ for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
+ {
+ /* opers only, please */
+ if (opt_viewopersonly && !IS_OPER(i->first))
+ continue;
+
+ /* If we're not inside the channel, hide +i users */
+ if (i->first->IsModeSet('i') && !inside)
+ continue;
+
+ SendWhoLine(user, initial, ch, i->first, whoresults);
+ }
+ }
+ }
+ else
+ {
+ /* Match against wildcard of nick, server or host */
+
+ if (opt_viewopersonly)
+ {
+ /* Showing only opers */
+ for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
+ {
+ userrec* oper = *i;
+
+ if (whomatch(oper, matchtext))
+ {
+ if ((!oper->IsModeSet('i')) && (!IS_OPER(user)))
+ continue;
+
+ SendWhoLine(user, initial, NULL, oper, whoresults);
+ }
+ }
+ }
+ else
+ {
+ for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
+ {
+ if (whomatch(i->second, matchtext))
+ {
+ if ((i->second->IsModeSet('i')) && (!IS_OPER(user)))
+ continue;
+
+ SendWhoLine(user, initial, NULL, i->second, whoresults);
+ }
+ }
+ }
+ }
+ /* Send the results out */
+ if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
+ {
+ for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
+ user->WriteServ(*n);
+ user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ /* BZZT! Too many results. */
+ user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+}
diff --git a/src/cmd_whois.cpp b/src/cmd_whois.cpp
index 3797efeaa..897ec60ac 100644
--- a/src/cmd_whois.cpp
+++ b/src/cmd_whois.cpp
@@ -1 +1,144 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "commands/cmd_whois.h" #include "hashcomp.h" void do_whois(InspIRCd* ServerInstance, userrec* user, userrec* dest,unsigned long signon, unsigned long idle, const char* nick) { if (dest->Visibility && !dest->Visibility->VisibleTo(user)) { ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*"); ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*"); return; } if (dest->registered == REG_ALL) { ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname); if (user == dest || IS_OPER(user)) { ServerInstance->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick, dest->nick, dest->ident, dest->host, dest->GetIPString()); } std::string cl = dest->ChannelList(user); if (cl.length()) { if (cl.length() > 400) { user->SplitChanList(dest,cl); } else { ServerInstance->SendWhoisLine(user, dest, 319, "%s %s :%s",user->nick, dest->nick, cl.c_str()); } } if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) { ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, ServerInstance->Config->HideWhoisServer, ServerInstance->Config->Network); } else { ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, dest->server, ServerInstance->GetServerDescription(dest->server).c_str()); } if (IS_AWAY(dest)) { ServerInstance->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick, dest->nick, dest->awaymsg); } if (IS_OPER(dest)) { ServerInstance->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick, dest->nick, (strchr("AEIOUaeiou",*dest->oper) ? "an" : "a"),irc::Spacify(dest->oper), ServerInstance->Config->Network); } 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)) { ServerInstance->SendWhoisLine(user, dest, 317, "%s %s %d %d :seconds idle, signon time",user->nick, dest->nick, idle, signon); } ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, dest->nick); } else { ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*"); ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*"); } } extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_whois(Instance); } CmdResult cmd_whois::Handle (const char** parameters, int pcnt, userrec *user) { userrec *dest; int userindex = 0; unsigned long idle = 0, signon = 0; if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 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 (pcnt > 1) userindex = 1; dest = ServerInstance->FindNick(parameters[userindex]); if (dest) { /* * 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 pcnt > 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 || pcnt > 1)) { idle = abs((dest->idle_lastmsg)-ServerInstance->Time()); signon = dest->signon; } do_whois(this->ServerInstance, user,dest,signon,idle,parameters[userindex]); } else { /* no such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, *parameters[userindex] ? parameters[userindex] : "*"); user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, *parameters[userindex] ? parameters[userindex] : "*"); return CMD_FAILURE; } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "hashcomp.h"
+
+void do_whois(InspIRCd* ServerInstance, userrec* user, userrec* dest,unsigned long signon, unsigned long idle, const char* nick)
+{
+ if (dest->Visibility && !dest->Visibility->VisibleTo(user))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*");
+ ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*");
+ return;
+ }
+
+ if (dest->registered == REG_ALL)
+ {
+ ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname);
+ if (user == dest || IS_OPER(user))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick, dest->nick, dest->ident, dest->host, dest->GetIPString());
+ }
+
+ std::string cl = dest->ChannelList(user);
+
+ if (cl.length())
+ {
+ if (cl.length() > 400)
+ {
+ user->SplitChanList(dest,cl);
+ }
+ else
+ {
+ ServerInstance->SendWhoisLine(user, dest, 319, "%s %s :%s",user->nick, dest->nick, cl.c_str());
+ }
+ }
+ if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, ServerInstance->Config->HideWhoisServer, ServerInstance->Config->Network);
+ }
+ else
+ {
+ ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, dest->server, ServerInstance->GetServerDescription(dest->server).c_str());
+ }
+
+ if (IS_AWAY(dest))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick, dest->nick, dest->awaymsg);
+ }
+
+ if (IS_OPER(dest))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick, dest->nick, (strchr("AEIOUaeiou",*dest->oper) ? "an" : "a"),irc::Spacify(dest->oper), ServerInstance->Config->Network);
+ }
+
+ 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))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 317, "%s %s %d %d :seconds idle, signon time",user->nick, dest->nick, idle, signon);
+ }
+
+ ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, dest->nick);
+ }
+ else
+ {
+ ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*");
+ ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*");
+ }
+}
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_whois(Instance);
+}
+
+CmdResult cmd_whois::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ userrec *dest;
+ int userindex = 0;
+ unsigned long idle = 0, signon = 0;
+
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 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 (pcnt > 1)
+ userindex = 1;
+
+ dest = ServerInstance->FindNick(parameters[userindex]);
+
+ if (dest)
+ {
+ /*
+ * 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 pcnt > 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 || pcnt > 1))
+ {
+ idle = abs((dest->idle_lastmsg)-ServerInstance->Time());
+ signon = dest->signon;
+ }
+
+ do_whois(this->ServerInstance, user,dest,signon,idle,parameters[userindex]);
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, *parameters[userindex] ? parameters[userindex] : "*");
+ user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, *parameters[userindex] ? parameters[userindex] : "*");
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
diff --git a/src/cmd_whowas.cpp b/src/cmd_whowas.cpp
index aab457243..2d504c47c 100644
--- a/src/cmd_whowas.cpp
+++ b/src/cmd_whowas.cpp
@@ -1 +1,341 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "commands/cmd_whowas.h" WhoWasMaintainTimer * timer; extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_whowas(Instance); } cmd_whowas::cmd_whowas(InspIRCd* Instance) : command_t(Instance, "WHOWAS", 0, 1) { syntax = "<nick>{,<nick>}"; timer = new WhoWasMaintainTimer(Instance, 3600); Instance->Timers->AddTimer(timer); } CmdResult cmd_whowas::Handle (const char** parameters, int pcnt, userrec* user) { /* if whowas disabled in config */ if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0) { user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str()); return CMD_FAILURE; } whowas_users::iterator i = whowas.find(parameters[0]); if (i == whowas.end()) { user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]); user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]); 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; time_t rawtime = u->signon; tm *timeinfo; char b[MAXBUF]; timeinfo = localtime(&rawtime); /* XXX - 'b' could be only 25 chars long and then strlcpy() would terminate it for us too? */ strlcpy(b,asctime(timeinfo),MAXBUF); b[24] = 0; user->WriteServ("314 %s %s %s %s * :%s",user->nick,parameters[0],u->ident,u->dhost,u->gecos); if (IS_OPER(user)) user->WriteServ("379 %s %s :was connecting from *@%s", user->nick, parameters[0], u->host); if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], ServerInstance->Config->HideWhoisServer, b); else user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], u->server, b); } } else { user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]); user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]); return CMD_FAILURE; } } user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]); return CMD_SUCCESS; } CmdResult cmd_whowas::HandleInternal(const unsigned int id, const std::deque<classbase*> &parameters) { switch (id) { case WHOWAS_ADD: AddToWhoWas((userrec*)parameters[0]); break; case WHOWAS_STATS: GetStats((Extensible*)parameters[0]); break; case WHOWAS_PRUNE: PruneWhoWas(ServerInstance->Time()); break; case WHOWAS_MAINTAIN: MaintainWhoWas(ServerInstance->Time()); break; default: break; } return CMD_SUCCESS; } void cmd_whowas::GetStats(Extensible* ext) { 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() ) ); } } stats.assign("Whowas(MAPSETS) " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)"); ext->Extend("stats", stats.c_str()); } void cmd_whowas::AddToWhoWas(userrec* user) { /* if whowas disabled */ if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0) { return; } whowas_users::iterator iter = whowas.find(user->nick); if (iter == whowas.end()) { whowas_set* n = new whowas_set; WhoWasGroup *a = new WhoWasGroup(user); n->push_back(a); whowas[user->nick] = n; whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick)); if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups) { whowas_users::iterator iter = whowas.find(whowas_fifo[0].second); if (iter != whowas.end()) { 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 { whowas_set* group = (whowas_set*)iter->second; WhoWasGroup *a = new WhoWasGroup(user); group->push_back(a); if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize) { WhoWasGroup *a = (WhoWasGroup*)*(group->begin()); DELETE(a); group->pop_front(); } } } /* on rehash, refactor maps according to new conf values */ void cmd_whowas::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->Log(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->Log(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 cmd_whowas::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()); } } } } cmd_whowas::~cmd_whowas() { 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->Log(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(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon) { this->host = strdup(user->host); this->dhost = strdup(user->dhost); this->ident = strdup(user->ident); this->server = user->server; this->gecos = strdup(user->fullname); } WhoWasGroup::~WhoWasGroup() { if (host) free(host); if (dhost) free(dhost); if (ident) free(ident); if (gecos) free(gecos); } /* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */ void WhoWasMaintainTimer::Tick(time_t t) { command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS"); if (whowas_command) { std::deque<classbase*> params; whowas_command->HandleInternal(WHOWAS_MAINTAIN, params); } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "commands/cmd_whowas.h"
+
+WhoWasMaintainTimer * timer;
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_whowas(Instance);
+}
+
+cmd_whowas::cmd_whowas(InspIRCd* Instance)
+: command_t(Instance, "WHOWAS", 0, 1)
+{
+ syntax = "<nick>{,<nick>}";
+ timer = new WhoWasMaintainTimer(Instance, 3600);
+ Instance->Timers->AddTimer(timer);
+}
+
+CmdResult cmd_whowas::Handle (const char** parameters, int pcnt, userrec* user)
+{
+ /* if whowas disabled in config */
+ if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
+ {
+ user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str());
+ return CMD_FAILURE;
+ }
+
+ whowas_users::iterator i = whowas.find(parameters[0]);
+
+ if (i == whowas.end())
+ {
+ user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]);
+ user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]);
+ 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;
+ time_t rawtime = u->signon;
+ tm *timeinfo;
+ char b[MAXBUF];
+
+ timeinfo = localtime(&rawtime);
+
+ /* XXX - 'b' could be only 25 chars long and then strlcpy() would terminate it for us too? */
+ strlcpy(b,asctime(timeinfo),MAXBUF);
+ b[24] = 0;
+
+ user->WriteServ("314 %s %s %s %s * :%s",user->nick,parameters[0],u->ident,u->dhost,u->gecos);
+
+ if (IS_OPER(user))
+ user->WriteServ("379 %s %s :was connecting from *@%s", user->nick, parameters[0], u->host);
+
+ if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user))
+ user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], ServerInstance->Config->HideWhoisServer, b);
+ else
+ user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], u->server, b);
+ }
+ }
+ else
+ {
+ user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]);
+ user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+
+ user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]);
+ return CMD_SUCCESS;
+}
+
+CmdResult cmd_whowas::HandleInternal(const unsigned int id, const std::deque<classbase*> &parameters)
+{
+ switch (id)
+ {
+ case WHOWAS_ADD:
+ AddToWhoWas((userrec*)parameters[0]);
+ break;
+
+ case WHOWAS_STATS:
+ GetStats((Extensible*)parameters[0]);
+ break;
+
+ case WHOWAS_PRUNE:
+ PruneWhoWas(ServerInstance->Time());
+ break;
+
+ case WHOWAS_MAINTAIN:
+ MaintainWhoWas(ServerInstance->Time());
+ break;
+
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+void cmd_whowas::GetStats(Extensible* ext)
+{
+ 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() ) );
+ }
+ }
+ stats.assign("Whowas(MAPSETS) " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)");
+ ext->Extend("stats", stats.c_str());
+}
+
+void cmd_whowas::AddToWhoWas(userrec* user)
+{
+ /* if whowas disabled */
+ if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
+ {
+ return;
+ }
+
+ whowas_users::iterator iter = whowas.find(user->nick);
+
+ if (iter == whowas.end())
+ {
+ whowas_set* n = new whowas_set;
+ WhoWasGroup *a = new WhoWasGroup(user);
+ n->push_back(a);
+ whowas[user->nick] = n;
+ whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick));
+
+ if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
+ {
+ whowas_users::iterator iter = whowas.find(whowas_fifo[0].second);
+ if (iter != whowas.end())
+ {
+ 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
+ {
+ whowas_set* group = (whowas_set*)iter->second;
+ WhoWasGroup *a = new WhoWasGroup(user);
+ group->push_back(a);
+
+ if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
+ {
+ WhoWasGroup *a = (WhoWasGroup*)*(group->begin());
+ DELETE(a);
+ group->pop_front();
+ }
+ }
+}
+
+/* on rehash, refactor maps according to new conf values */
+void cmd_whowas::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->Log(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->Log(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 cmd_whowas::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());
+ }
+ }
+ }
+}
+
+cmd_whowas::~cmd_whowas()
+{
+ 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->Log(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(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon)
+{
+ this->host = strdup(user->host);
+ this->dhost = strdup(user->dhost);
+ this->ident = strdup(user->ident);
+ this->server = user->server;
+ this->gecos = strdup(user->fullname);
+}
+
+WhoWasGroup::~WhoWasGroup()
+{
+ if (host)
+ free(host);
+ if (dhost)
+ free(dhost);
+ if (ident)
+ free(ident);
+ if (gecos)
+ free(gecos);
+}
+
+/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
+void WhoWasMaintainTimer::Tick(time_t t)
+{
+ command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
+ if (whowas_command)
+ {
+ std::deque<classbase*> params;
+ whowas_command->HandleInternal(WHOWAS_MAINTAIN, params);
+ }
+}
diff --git a/src/cmd_zline.cpp b/src/cmd_zline.cpp
index 43a0fd042..8fea70656 100644
--- a/src/cmd_zline.cpp
+++ b/src/cmd_zline.cpp
@@ -1 +1,80 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "xline.h" #include "commands/cmd_zline.h" extern "C" DllExport command_t* init_command(InspIRCd* Instance) { return new cmd_zline(Instance); } CmdResult cmd_zline::Handle (const char** parameters, int pcnt, userrec *user) { if (pcnt >= 3) { if (strchr(parameters[0],'@') || strchr(parameters[0],'!')) { user->WriteServ("NOTICE %s :*** You cannot include a username or nickname in a zline, a zline must ban only an IP mask",user->nick); return CMD_FAILURE; } if (ServerInstance->IPMatchesEveryone(parameters[0],user)) return CMD_FAILURE; long duration = ServerInstance->Duration(parameters[1]); if (ServerInstance->XLines->add_zline(duration,user->nick,parameters[2],parameters[0])) { int to_apply = APPLY_ZLINES; FOREACH_MOD(I_OnAddZLine,OnAddZLine(duration, user, parameters[2], parameters[0])); if (!duration) { to_apply |= APPLY_PERM_ONLY; ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s.",user->nick,parameters[0]); } else { time_t c_requires_crap = duration + ServerInstance->Time(); ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s",user->nick,parameters[0], ServerInstance->TimeString(c_requires_crap).c_str()); } ServerInstance->XLines->apply_lines(to_apply); } else { user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick,parameters[0]); } } else { if (ServerInstance->XLines->del_zline(parameters[0])) { FOREACH_MOD(I_OnDelZLine,OnDelZLine(user, parameters[0])); ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Z-line on %s.",user->nick,parameters[0]); } else { user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick,parameters[0]); return CMD_FAILURE; } } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "xline.h"
+#include "commands/cmd_zline.h"
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+ return new cmd_zline(Instance);
+}
+
+CmdResult cmd_zline::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (pcnt >= 3)
+ {
+ if (strchr(parameters[0],'@') || strchr(parameters[0],'!'))
+ {
+ user->WriteServ("NOTICE %s :*** You cannot include a username or nickname in a zline, a zline must ban only an IP mask",user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (ServerInstance->IPMatchesEveryone(parameters[0],user))
+ return CMD_FAILURE;
+
+ long duration = ServerInstance->Duration(parameters[1]);
+ if (ServerInstance->XLines->add_zline(duration,user->nick,parameters[2],parameters[0]))
+ {
+ int to_apply = APPLY_ZLINES;
+
+ FOREACH_MOD(I_OnAddZLine,OnAddZLine(duration, user, parameters[2], parameters[0]));
+ if (!duration)
+ {
+ to_apply |= APPLY_PERM_ONLY;
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s",user->nick,parameters[0],
+ ServerInstance->TimeString(c_requires_crap).c_str());
+ }
+ ServerInstance->XLines->apply_lines(to_apply);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick,parameters[0]);
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->del_zline(parameters[0]))
+ {
+ FOREACH_MOD(I_OnDelZLine,OnDelZLine(user, parameters[0]));
+ ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Z-line on %s.",user->nick,parameters[0]);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick,parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/command_parse.cpp b/src/command_parse.cpp
index bf2e61ce9..52c58ef99 100644
--- a/src/command_parse.cpp
+++ b/src/command_parse.cpp
@@ -1 +1,563 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include <algorithm> #include "users.h" #include "modules.h" #include "wildcard.h" #include "xline.h" #include "socketengine.h" #include "socket.h" #include "command_parse.h" /* Directory Searching for Unix-Only */ #ifndef WIN32 #include <dirent.h> #include <dlfcn.h> #endif bool InspIRCd::ULine(const char* server) { if (!server) return false; if (!*server) return true; return (Config->ulines.find(server) != Config->ulines.end()); } bool InspIRCd::SilentULine(const char* server) { std::map<irc::string,bool>::iterator n = Config->ulines.find(server); if (n != Config->ulines.end()) return n->second; else return false; } int InspIRCd::OperPassCompare(const char* data,const char* input, int tagnumber) { int MOD_RESULT = 0; FOREACH_RESULT_I(this,I_OnOperCompare,OnOperCompare(data, input, tagnumber)) if (MOD_RESULT == 1) return 0; if (MOD_RESULT == -1) return 1; return strcmp(data,input); } std::string InspIRCd::TimeString(time_t curtime) { return std::string(ctime(&curtime),24); } /** Refactored by Brain, Jun 2007. Much faster with some clever O(1) array * lookups and pointer maths. */ long InspIRCd::Duration(const std::string &str) { unsigned char multiplier = 0; long total = 0; long times = 1; long subtotal = 0; /* Iterate each item in the string, looking for number or multiplier */ for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i) { /* Found a number, queue it onto the current number */ if ((*i >= '0') && (*i <= '9')) { subtotal = subtotal + ((*i - '0') * times); times = times * 10; } else { /* Found something thats not a number, find out how much * it multiplies the built up number by, multiply the total * and reset the built up number. */ if (subtotal) total += subtotal * duration_multi[multiplier]; /* Next subtotal please */ subtotal = 0; multiplier = *i; times = 1; } } if (multiplier) { total += subtotal * duration_multi[multiplier]; subtotal = 0; } /* Any trailing values built up are treated as raw seconds */ return total + subtotal; } /* 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(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra) { /* First check if we have more than one item in the list, if we don't we return zero here and the handler * which called us just carries on as it was. */ if (!strchr(parameters[splithere],',')) return 0; /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm. * By using std::map (thanks for the idea w00t) we can cut this down a ton. * ...VOOODOOOO! */ std::map<irc::string, bool> dupes; /* Create two lists, one for channel names, one for keys */ irc::commasepstream items1(parameters[splithere]); irc::commasepstream items2(parameters[extra]); std::string item("*"); unsigned int max = 0; /* 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. */ while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets)) { if (dupes.find(item.c_str()) == dupes.end()) { const char* new_parameters[127]; for (int t = 0; (t < pcnt) && (t < 127); t++) new_parameters[t] = parameters[t]; std::string extrastuff = items2.GetToken(); new_parameters[splithere] = item.c_str(); new_parameters[extra] = extrastuff.c_str(); CommandObj->Handle(new_parameters,pcnt,user); dupes[item.c_str()] = true; } } return 1; } int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere) { /* First check if we have more than one item in the list, if we don't we return zero here and the handler * which called us just carries on as it was. */ if (!strchr(parameters[splithere],',')) return 0; std::map<irc::string, bool> dupes; /* Only one commasepstream here */ irc::commasepstream items1(parameters[splithere]); std::string item("*"); unsigned int max = 0; /* Parse the commasepstream until there are no tokens remaining. * Each token we parse out, call the command handler that called us * with it */ while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets)) { if (dupes.find(item.c_str()) == dupes.end()) { const char* new_parameters[127]; for (int t = 0; (t < pcnt) && (t < 127); t++) new_parameters[t] = parameters[t]; new_parameters[splithere] = item.c_str(); parameters[splithere] = item.c_str(); /* Execute the command handler over and over. If someone pulls our user * record out from under us (e.g. if we /kill a comma sep list, and we're * in that list ourselves) abort if we're gone. */ CommandObj->Handle(new_parameters,pcnt,user); dupes[item.c_str()] = true; } } /* By returning 1 we tell our caller that nothing is to be done, * as all the previous calls handled the data. This makes the parent * return without doing any processing. */ return 1; } bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user) { command_table::iterator n = cmdlist.find(commandname); if (n != cmdlist.end()) { if ((pcnt>=n->second->min_params) && (n->second->source != "<core>")) { if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65])) { if (n->second->flags_needed) { return ((user->HasPermission(commandname)) || (ServerInstance->ULine(user->server))); } return true; } } } return false; } command_t* CommandParser::GetHandler(const std::string &commandname) { command_table::iterator n = cmdlist.find(commandname); if (n != cmdlist.end()) return n->second; return NULL; } // calls a handler function for a command CmdResult CommandParser::CallHandler(const std::string &commandname,const char** parameters, int pcnt, userrec *user) { command_table::iterator n = cmdlist.find(commandname); if (n != cmdlist.end()) { if (pcnt >= n->second->min_params) { if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65])) { if (n->second->flags_needed) { if ((user->HasPermission(commandname)) || (!IS_LOCAL(user))) { return n->second->Handle(parameters,pcnt,user); } } else { return n->second->Handle(parameters,pcnt,user); } } } } return CMD_INVALID; } void CommandParser::ProcessCommand(userrec *user, std::string &cmd) { const char *command_p[127]; int items = 0; irc::tokenstream tokens(cmd); std::string command; tokens.GetToken(command); /* A client sent a nick prefix on their command (ick) * rhapsody and some braindead bouncers do this -- * the rfc says they shouldnt but also says the ircd should * discard it if they do. */ if (*command.c_str() == ':') tokens.GetToken(command); while (tokens.GetToken(para[items]) && (items < 127)) { command_p[items] = para[items].c_str(); items++; } std::transform(command.begin(), command.end(), command.begin(), ::toupper); int MOD_RESULT = 0; FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false,cmd)); if (MOD_RESULT == 1) { return; } command_table::iterator cm = cmdlist.find(command); if (cm != cmdlist.end()) { if (user) { /* activity resets the ping pending timer */ user->nping = ServerInstance->Time() + user->pingmax; if (cm->second->flags_needed) { if (!user->IsModeSet(cm->second->flags_needed)) { user->WriteServ("481 %s :Permission Denied - You do not have the required operator privileges",user->nick); return; } if (!user->HasPermission(command)) { user->WriteServ("481 %s :Permission Denied - Oper type %s does not have access to command %s",user->nick,user->oper,command.c_str()); return; } } if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled())) { /* command is disabled! */ user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str()); return; } if (items < cm->second->min_params) { user->WriteServ("461 %s %s :Not enough parameters.", user->nick, command.c_str()); /* If syntax is given, display this as the 461 reply */ if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length())) user->WriteServ("304 %s :SYNTAX %s %s", user->nick, cm->second->command.c_str(), cm->second->syntax.c_str()); return; } if ((user->registered == REG_ALL) || (cm->second->WorksBeforeReg())) { /* ikky /stats counters */ cm->second->use_count++; cm->second->total_bytes += cmd.length(); int MOD_RESULT = 0; FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true,cmd)); if (MOD_RESULT == 1) return; /* * WARNING: nothing may come after the * command handler call, as the handler * may free the user structure! */ CmdResult result = cm->second->Handle(command_p,items,user); FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, items, user, result,cmd)); return; } else { user->WriteServ("451 %s :You have not registered",command.c_str()); return; } } } else if (user) { ServerInstance->stats->statsUnknown++; user->WriteServ("421 %s %s :Unknown command",user->nick,command.c_str()); } } bool CommandParser::RemoveCommands(const char* source) { command_table::iterator i,safei; for (i = cmdlist.begin(); i != cmdlist.end(); i++) { safei = i; safei++; if (safei != cmdlist.end()) { RemoveCommand(safei, source); } } safei = cmdlist.begin(); if (safei != cmdlist.end()) { RemoveCommand(safei, source); } return true; } void CommandParser::RemoveCommand(command_table::iterator safei, const char* source) { command_t* x = safei->second; if (x->source == std::string(source)) { cmdlist.erase(safei); delete x; } } void CommandParser::ProcessBuffer(std::string &buffer,userrec *user) { std::string::size_type a; if (!user) return; while ((a = buffer.rfind("\n")) != std::string::npos) buffer.erase(a); while ((a = buffer.rfind("\r")) != std::string::npos) buffer.erase(a); if (buffer.length()) { if (!user->muted) { ServerInstance->Log(DEBUG,"C[%d] -> :%s %s",user->GetFd(), user->nick, buffer.c_str()); this->ProcessCommand(user,buffer); } } } bool CommandParser::CreateCommand(command_t *f, void* so_handle) { if (so_handle) { if (RFCCommands.find(f->command) == RFCCommands.end()) RFCCommands[f->command] = so_handle; else { ServerInstance->Log(DEFAULT,"ERK! Somehow, we loaded a cmd_*.so file twice! Only the first instance is being recorded."); return false; } } /* create the command and push it onto the table */ if (cmdlist.find(f->command) == cmdlist.end()) { cmdlist[f->command] = f; return true; } else return false; } CommandParser::CommandParser(InspIRCd* Instance) : ServerInstance(Instance) { para.resize(128); this->SetupCommandTable(); } bool CommandParser::FindSym(void** v, void* h) { *v = dlsym(h, "init_command"); const char* err = dlerror(); if (err && !(*v)) { ServerInstance->Log(SPARSE, "Error loading core command: %s\n", err); return false; } return true; } bool CommandParser::ReloadCommand(const char* cmd) { char filename[MAXBUF]; char commandname[MAXBUF]; int y = 0; for (const char* x = cmd; *x; x++, y++) commandname[y] = toupper(*x); commandname[y] = 0; SharedObjectList::iterator command = RFCCommands.find(commandname); if (command != RFCCommands.end()) { command_t* cmdptr = cmdlist.find(commandname)->second; cmdlist.erase(cmdlist.find(commandname)); for (char* x = commandname; *x; x++) *x = tolower(*x); delete cmdptr; dlclose(command->second); RFCCommands.erase(command); snprintf(filename, MAXBUF, "cmd_%s.so", commandname); this->LoadCommand(filename); return true; } return false; } CmdResult cmd_reload::Handle(const char** parameters, int pcnt, userrec *user) { user->WriteServ("NOTICE %s :*** Reloading command '%s'",user->nick, parameters[0]); if (ServerInstance->Parser->ReloadCommand(parameters[0])) { user->WriteServ("NOTICE %s :*** Successfully reloaded command '%s'", user->nick, parameters[0]); ServerInstance->WriteOpers("*** RELOAD: %s reloaded the '%s' command.", user->nick, parameters[0]); return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :*** Could not reload command '%s'", user->nick, parameters[0]); return CMD_FAILURE; } } void CommandParser::LoadCommand(const char* name) { char filename[MAXBUF]; void* h; command_t* (*cmd_factory_func)(InspIRCd*); snprintf(filename, MAXBUF, "%s/%s", LIBRARYDIR, name); h = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); if (!h) { ServerInstance->Log(SPARSE, "Error loading core command: %s", dlerror()); return; } if (this->FindSym((void **)&cmd_factory_func, h)) { command_t* newcommand = cmd_factory_func(ServerInstance); this->CreateCommand(newcommand, h); } } void CommandParser::SetupCommandTable() { RFCCommands.clear(); printf("\nLoading core commands"); fflush(stdout); DIR* library = opendir(LIBRARYDIR); if (library) { dirent* entry = NULL; while ((entry = readdir(library))) { if (match(entry->d_name, "cmd_*.so")) { printf("."); fflush(stdout); this->LoadCommand(entry->d_name); } } closedir(library); printf("\n"); } this->CreateCommand(new cmd_reload(ServerInstance)); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include <algorithm>
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "socketengine.h"
+#include "socket.h"
+#include "command_parse.h"
+
+/* Directory Searching for Unix-Only */
+#ifndef WIN32
+#include <dirent.h>
+#include <dlfcn.h>
+#endif
+
+bool InspIRCd::ULine(const char* server)
+{
+ if (!server)
+ return false;
+ if (!*server)
+ return true;
+
+ return (Config->ulines.find(server) != Config->ulines.end());
+}
+
+bool InspIRCd::SilentULine(const char* server)
+{
+ std::map<irc::string,bool>::iterator n = Config->ulines.find(server);
+ if (n != Config->ulines.end())
+ return n->second;
+ else return false;
+}
+
+int InspIRCd::OperPassCompare(const char* data,const char* input, int tagnumber)
+{
+ int MOD_RESULT = 0;
+ FOREACH_RESULT_I(this,I_OnOperCompare,OnOperCompare(data, input, tagnumber))
+ if (MOD_RESULT == 1)
+ return 0;
+ if (MOD_RESULT == -1)
+ return 1;
+ return strcmp(data,input);
+}
+
+std::string InspIRCd::TimeString(time_t curtime)
+{
+ return std::string(ctime(&curtime),24);
+}
+
+/** Refactored by Brain, Jun 2007. Much faster with some clever O(1) array
+ * lookups and pointer maths.
+ */
+long InspIRCd::Duration(const std::string &str)
+{
+ unsigned char multiplier = 0;
+ long total = 0;
+ long times = 1;
+ long subtotal = 0;
+
+ /* Iterate each item in the string, looking for number or multiplier */
+ for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i)
+ {
+ /* Found a number, queue it onto the current number */
+ if ((*i >= '0') && (*i <= '9'))
+ {
+ subtotal = subtotal + ((*i - '0') * times);
+ times = times * 10;
+ }
+ else
+ {
+ /* Found something thats not a number, find out how much
+ * it multiplies the built up number by, multiply the total
+ * and reset the built up number.
+ */
+ if (subtotal)
+ total += subtotal * duration_multi[multiplier];
+
+ /* Next subtotal please */
+ subtotal = 0;
+ multiplier = *i;
+ times = 1;
+ }
+ }
+ if (multiplier)
+ {
+ total += subtotal * duration_multi[multiplier];
+ subtotal = 0;
+ }
+ /* Any trailing values built up are treated as raw seconds */
+ return total + subtotal;
+}
+
+/* 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(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra)
+{
+ /* First check if we have more than one item in the list, if we don't we return zero here and the handler
+ * which called us just carries on as it was.
+ */
+ if (!strchr(parameters[splithere],','))
+ return 0;
+
+ /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
+ * By using std::map (thanks for the idea w00t) we can cut this down a ton.
+ * ...VOOODOOOO!
+ */
+ std::map<irc::string, bool> dupes;
+
+ /* Create two lists, one for channel names, one for keys
+ */
+ irc::commasepstream items1(parameters[splithere]);
+ irc::commasepstream items2(parameters[extra]);
+ std::string item("*");
+ unsigned int max = 0;
+
+ /* 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.
+ */
+ while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
+ {
+ if (dupes.find(item.c_str()) == dupes.end())
+ {
+ const char* new_parameters[127];
+
+ for (int t = 0; (t < pcnt) && (t < 127); t++)
+ new_parameters[t] = parameters[t];
+
+ std::string extrastuff = items2.GetToken();
+
+ new_parameters[splithere] = item.c_str();
+ new_parameters[extra] = extrastuff.c_str();
+
+ CommandObj->Handle(new_parameters,pcnt,user);
+
+ dupes[item.c_str()] = true;
+ }
+ }
+ return 1;
+}
+
+int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere)
+{
+ /* First check if we have more than one item in the list, if we don't we return zero here and the handler
+ * which called us just carries on as it was.
+ */
+ if (!strchr(parameters[splithere],','))
+ return 0;
+
+ std::map<irc::string, bool> dupes;
+
+ /* Only one commasepstream here */
+ irc::commasepstream items1(parameters[splithere]);
+ std::string item("*");
+ unsigned int max = 0;
+
+ /* Parse the commasepstream until there are no tokens remaining.
+ * Each token we parse out, call the command handler that called us
+ * with it
+ */
+ while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
+ {
+ if (dupes.find(item.c_str()) == dupes.end())
+ {
+ const char* new_parameters[127];
+
+ for (int t = 0; (t < pcnt) && (t < 127); t++)
+ new_parameters[t] = parameters[t];
+
+ new_parameters[splithere] = item.c_str();
+
+ parameters[splithere] = item.c_str();
+
+ /* Execute the command handler over and over. If someone pulls our user
+ * record out from under us (e.g. if we /kill a comma sep list, and we're
+ * in that list ourselves) abort if we're gone.
+ */
+ CommandObj->Handle(new_parameters,pcnt,user);
+
+ dupes[item.c_str()] = true;
+ }
+ }
+ /* By returning 1 we tell our caller that nothing is to be done,
+ * as all the previous calls handled the data. This makes the parent
+ * return without doing any processing.
+ */
+ return 1;
+}
+
+bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user)
+{
+ command_table::iterator n = cmdlist.find(commandname);
+
+ if (n != cmdlist.end())
+ {
+ if ((pcnt>=n->second->min_params) && (n->second->source != "<core>"))
+ {
+ if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
+ {
+ if (n->second->flags_needed)
+ {
+ return ((user->HasPermission(commandname)) || (ServerInstance->ULine(user->server)));
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+command_t* CommandParser::GetHandler(const std::string &commandname)
+{
+ command_table::iterator n = cmdlist.find(commandname);
+ if (n != cmdlist.end())
+ return n->second;
+
+ return NULL;
+}
+
+// calls a handler function for a command
+
+CmdResult CommandParser::CallHandler(const std::string &commandname,const char** parameters, int pcnt, userrec *user)
+{
+ command_table::iterator n = cmdlist.find(commandname);
+
+ if (n != cmdlist.end())
+ {
+ if (pcnt >= n->second->min_params)
+ {
+ if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
+ {
+ if (n->second->flags_needed)
+ {
+ if ((user->HasPermission(commandname)) || (!IS_LOCAL(user)))
+ {
+ return n->second->Handle(parameters,pcnt,user);
+ }
+ }
+ else
+ {
+ return n->second->Handle(parameters,pcnt,user);
+ }
+ }
+ }
+ }
+ return CMD_INVALID;
+}
+
+void CommandParser::ProcessCommand(userrec *user, std::string &cmd)
+{
+ const char *command_p[127];
+ int items = 0;
+ irc::tokenstream tokens(cmd);
+ std::string command;
+ tokens.GetToken(command);
+
+ /* A client sent a nick prefix on their command (ick)
+ * rhapsody and some braindead bouncers do this --
+ * the rfc says they shouldnt but also says the ircd should
+ * discard it if they do.
+ */
+ if (*command.c_str() == ':')
+ tokens.GetToken(command);
+
+ while (tokens.GetToken(para[items]) && (items < 127))
+ {
+ command_p[items] = para[items].c_str();
+ items++;
+ }
+
+ std::transform(command.begin(), command.end(), command.begin(), ::toupper);
+
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false,cmd));
+ if (MOD_RESULT == 1) {
+ return;
+ }
+
+ command_table::iterator cm = cmdlist.find(command);
+
+ if (cm != cmdlist.end())
+ {
+ if (user)
+ {
+ /* activity resets the ping pending timer */
+ user->nping = ServerInstance->Time() + user->pingmax;
+ if (cm->second->flags_needed)
+ {
+ if (!user->IsModeSet(cm->second->flags_needed))
+ {
+ user->WriteServ("481 %s :Permission Denied - You do not have the required operator privileges",user->nick);
+ return;
+ }
+ if (!user->HasPermission(command))
+ {
+ user->WriteServ("481 %s :Permission Denied - Oper type %s does not have access to command %s",user->nick,user->oper,command.c_str());
+ return;
+ }
+ }
+ if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
+ {
+ /* command is disabled! */
+ user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str());
+ return;
+ }
+ if (items < cm->second->min_params)
+ {
+ user->WriteServ("461 %s %s :Not enough parameters.", user->nick, command.c_str());
+ /* If syntax is given, display this as the 461 reply */
+ if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
+ user->WriteServ("304 %s :SYNTAX %s %s", user->nick, cm->second->command.c_str(), cm->second->syntax.c_str());
+ return;
+ }
+ if ((user->registered == REG_ALL) || (cm->second->WorksBeforeReg()))
+ {
+ /* ikky /stats counters */
+ cm->second->use_count++;
+ cm->second->total_bytes += cmd.length();
+
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true,cmd));
+ if (MOD_RESULT == 1)
+ return;
+
+ /*
+ * WARNING: nothing may come after the
+ * command handler call, as the handler
+ * may free the user structure!
+ */
+ CmdResult result = cm->second->Handle(command_p,items,user);
+
+ FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, items, user, result,cmd));
+ return;
+ }
+ else
+ {
+ user->WriteServ("451 %s :You have not registered",command.c_str());
+ return;
+ }
+ }
+ }
+ else if (user)
+ {
+ ServerInstance->stats->statsUnknown++;
+ user->WriteServ("421 %s %s :Unknown command",user->nick,command.c_str());
+ }
+}
+
+bool CommandParser::RemoveCommands(const char* source)
+{
+ command_table::iterator i,safei;
+ for (i = cmdlist.begin(); i != cmdlist.end(); i++)
+ {
+ safei = i;
+ safei++;
+ if (safei != cmdlist.end())
+ {
+ RemoveCommand(safei, source);
+ }
+ }
+ safei = cmdlist.begin();
+ if (safei != cmdlist.end())
+ {
+ RemoveCommand(safei, source);
+ }
+ return true;
+}
+
+void CommandParser::RemoveCommand(command_table::iterator safei, const char* source)
+{
+ command_t* x = safei->second;
+ if (x->source == std::string(source))
+ {
+ cmdlist.erase(safei);
+ delete x;
+ }
+}
+
+void CommandParser::ProcessBuffer(std::string &buffer,userrec *user)
+{
+ std::string::size_type a;
+
+ if (!user)
+ return;
+
+ while ((a = buffer.rfind("\n")) != std::string::npos)
+ buffer.erase(a);
+ while ((a = buffer.rfind("\r")) != std::string::npos)
+ buffer.erase(a);
+
+ if (buffer.length())
+ {
+ if (!user->muted)
+ {
+ ServerInstance->Log(DEBUG,"C[%d] -> :%s %s",user->GetFd(), user->nick, buffer.c_str());
+ this->ProcessCommand(user,buffer);
+ }
+ }
+}
+
+bool CommandParser::CreateCommand(command_t *f, void* so_handle)
+{
+ if (so_handle)
+ {
+ if (RFCCommands.find(f->command) == RFCCommands.end())
+ RFCCommands[f->command] = so_handle;
+ else
+ {
+ ServerInstance->Log(DEFAULT,"ERK! Somehow, we loaded a cmd_*.so file twice! Only the first instance is being recorded.");
+ return false;
+ }
+ }
+
+ /* create the command and push it onto the table */
+ if (cmdlist.find(f->command) == cmdlist.end())
+ {
+ cmdlist[f->command] = f;
+ return true;
+ }
+ else return false;
+}
+
+CommandParser::CommandParser(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ para.resize(128);
+ this->SetupCommandTable();
+}
+
+bool CommandParser::FindSym(void** v, void* h)
+{
+ *v = dlsym(h, "init_command");
+ const char* err = dlerror();
+ if (err && !(*v))
+ {
+ ServerInstance->Log(SPARSE, "Error loading core command: %s\n", err);
+ return false;
+ }
+ return true;
+}
+
+bool CommandParser::ReloadCommand(const char* cmd)
+{
+ char filename[MAXBUF];
+ char commandname[MAXBUF];
+ int y = 0;
+
+ for (const char* x = cmd; *x; x++, y++)
+ commandname[y] = toupper(*x);
+
+ commandname[y] = 0;
+
+ SharedObjectList::iterator command = RFCCommands.find(commandname);
+
+ if (command != RFCCommands.end())
+ {
+ command_t* cmdptr = cmdlist.find(commandname)->second;
+ cmdlist.erase(cmdlist.find(commandname));
+
+ for (char* x = commandname; *x; x++)
+ *x = tolower(*x);
+
+
+ delete cmdptr;
+ dlclose(command->second);
+ RFCCommands.erase(command);
+
+ snprintf(filename, MAXBUF, "cmd_%s.so", commandname);
+ this->LoadCommand(filename);
+
+ return true;
+ }
+
+ return false;
+}
+
+CmdResult cmd_reload::Handle(const char** parameters, int pcnt, userrec *user)
+{
+ user->WriteServ("NOTICE %s :*** Reloading command '%s'",user->nick, parameters[0]);
+ if (ServerInstance->Parser->ReloadCommand(parameters[0]))
+ {
+ user->WriteServ("NOTICE %s :*** Successfully reloaded command '%s'", user->nick, parameters[0]);
+ ServerInstance->WriteOpers("*** RELOAD: %s reloaded the '%s' command.", user->nick, parameters[0]);
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Could not reload command '%s'", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+}
+
+void CommandParser::LoadCommand(const char* name)
+{
+ char filename[MAXBUF];
+ void* h;
+ command_t* (*cmd_factory_func)(InspIRCd*);
+
+ snprintf(filename, MAXBUF, "%s/%s", LIBRARYDIR, name);
+ h = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
+
+ if (!h)
+ {
+ ServerInstance->Log(SPARSE, "Error loading core command: %s", dlerror());
+ return;
+ }
+
+ if (this->FindSym((void **)&cmd_factory_func, h))
+ {
+ command_t* newcommand = cmd_factory_func(ServerInstance);
+ this->CreateCommand(newcommand, h);
+ }
+}
+
+void CommandParser::SetupCommandTable()
+{
+ RFCCommands.clear();
+
+ printf("\nLoading core commands");
+ fflush(stdout);
+
+ DIR* library = opendir(LIBRARYDIR);
+ if (library)
+ {
+ dirent* entry = NULL;
+ while ((entry = readdir(library)))
+ {
+ if (match(entry->d_name, "cmd_*.so"))
+ {
+ printf(".");
+ fflush(stdout);
+ this->LoadCommand(entry->d_name);
+ }
+ }
+ closedir(library);
+ printf("\n");
+ }
+
+ this->CreateCommand(new cmd_reload(ServerInstance));
+}
+
diff --git a/src/commands.cpp b/src/commands.cpp
index 469b4ed74..65b11e5a0 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -1 +1,111 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.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, userrec* user) { char itrigger[MAXBUF]; long matches = 0; if (!Config->ConfValue(Config->config_data, "insane","trigger", 0, itrigger, MAXBUF)) strlcpy(itrigger,"95.5",MAXBUF); if (Config->ConfValueBool(Config->config_data, "insane","hostmasks", 0)) return false; for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++) { if ((match(u->second->MakeHost(),mask.c_str(),true)) || (match(u->second->MakeHostIP(),mask.c_str(),true))) { matches++; } } if (!matches) return false; float percent = ((float)matches / (float)clientlist->size()) * 100; if (percent > (float)atof(itrigger)) { WriteOpers("*** \2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick,mask.c_str(),percent); return true; } return false; } bool InspIRCd::IPMatchesEveryone(const std::string &ip, userrec* user) { char itrigger[MAXBUF]; long matches = 0; if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF)) strlcpy(itrigger,"95.5",MAXBUF); if (Config->ConfValueBool(Config->config_data, "insane","ipmasks",0)) return false; for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++) { if (match(u->second->GetIPString(),ip.c_str(),true)) matches++; } if (!matches) return false; float percent = ((float)matches / (float)clientlist->size()) * 100; if (percent > (float)atof(itrigger)) { WriteOpers("*** \2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick,ip.c_str(),percent); return true; } return false; } bool InspIRCd::NickMatchesEveryone(const std::string &nick, userrec* user) { char itrigger[MAXBUF]; long matches = 0; if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF)) strlcpy(itrigger,"95.5",MAXBUF); if (Config->ConfValueBool(Config->config_data, "insane","nickmasks",0)) return false; for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++) { if (match(u->second->nick,nick.c_str())) matches++; } if (!matches) return false; float percent = ((float)matches / (float)clientlist->size()) * 100; if (percent > (float)atof(itrigger)) { WriteOpers("*** \2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick,nick.c_str(),percent); return true; } return false; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.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, userrec* user)
+{
+ char itrigger[MAXBUF];
+ long matches = 0;
+
+ if (!Config->ConfValue(Config->config_data, "insane","trigger", 0, itrigger, MAXBUF))
+ strlcpy(itrigger,"95.5",MAXBUF);
+
+ if (Config->ConfValueBool(Config->config_data, "insane","hostmasks", 0))
+ return false;
+
+ for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++)
+ {
+ if ((match(u->second->MakeHost(),mask.c_str(),true)) || (match(u->second->MakeHostIP(),mask.c_str(),true)))
+ {
+ matches++;
+ }
+ }
+
+ if (!matches)
+ return false;
+
+ float percent = ((float)matches / (float)clientlist->size()) * 100;
+ if (percent > (float)atof(itrigger))
+ {
+ WriteOpers("*** \2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick,mask.c_str(),percent);
+ return true;
+ }
+ return false;
+}
+
+bool InspIRCd::IPMatchesEveryone(const std::string &ip, userrec* user)
+{
+ char itrigger[MAXBUF];
+ long matches = 0;
+
+ if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF))
+ strlcpy(itrigger,"95.5",MAXBUF);
+
+ if (Config->ConfValueBool(Config->config_data, "insane","ipmasks",0))
+ return false;
+
+ for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++)
+ {
+ if (match(u->second->GetIPString(),ip.c_str(),true))
+ matches++;
+ }
+
+ if (!matches)
+ return false;
+
+ float percent = ((float)matches / (float)clientlist->size()) * 100;
+ if (percent > (float)atof(itrigger))
+ {
+ WriteOpers("*** \2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick,ip.c_str(),percent);
+ return true;
+ }
+ return false;
+}
+
+bool InspIRCd::NickMatchesEveryone(const std::string &nick, userrec* user)
+{
+ char itrigger[MAXBUF];
+ long matches = 0;
+
+ if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF))
+ strlcpy(itrigger,"95.5",MAXBUF);
+
+ if (Config->ConfValueBool(Config->config_data, "insane","nickmasks",0))
+ return false;
+
+ for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++)
+ {
+ if (match(u->second->nick,nick.c_str()))
+ matches++;
+ }
+
+ if (!matches)
+ return false;
+
+ float percent = ((float)matches / (float)clientlist->size()) * 100;
+ if (percent > (float)atof(itrigger))
+ {
+ WriteOpers("*** \2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick,nick.c_str(),percent);
+ return true;
+ }
+ return false;
+}
diff --git a/src/configreader.cpp b/src/configreader.cpp
index df15d9a61..d4291692f 100644
--- a/src/configreader.cpp
+++ b/src/configreader.cpp
@@ -1 +1,1714 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include <sstream> #include <fstream> #include "xline.h" #include "exitcodes.h" #include "commands/cmd_whowas.h" std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules; ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance) { this->ClearStack(); *ServerName = *Network = *ServerDesc = *AdminName = '\0'; *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0'; *DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0'; *UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0'; WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0; log_file = NULL; NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false; CycleHosts = writelog = AllowHalfop = true; dns_timeout = DieDelay = 5; MaxTargets = 20; NetBufferSize = 10240; SoftLimit = MAXCLIENTS; MaxConn = SOMAXCONN; MaxWhoResults = 0; debugging = 0; MaxChans = 20; OperMaxChans = 30; LogLevel = DEFAULT; maxbans.clear(); } void ServerConfig::ClearStack() { include_stack.clear(); } Module* ServerConfig::GetIOHook(int port) { std::map<int,Module*>::iterator x = IOHookModule.find(port); return (x != IOHookModule.end() ? x->second : NULL); } Module* ServerConfig::GetIOHook(InspSocket* is) { std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is); return (x != SocketIOHookModule.end() ? x->second : NULL); } bool ServerConfig::AddIOHook(int port, Module* iomod) { if (!GetIOHook(port)) { IOHookModule[port] = iomod; return true; } else { throw ModuleException("Port already hooked by another module"); return false; } } bool ServerConfig::AddIOHook(Module* iomod, InspSocket* is) { if (!GetIOHook(is)) { SocketIOHookModule[is] = iomod; is->IsIOHooked = true; return true; } else { throw ModuleException("InspSocket derived class already hooked by another module"); return false; } } bool ServerConfig::DelIOHook(int port) { std::map<int,Module*>::iterator x = IOHookModule.find(port); if (x != IOHookModule.end()) { IOHookModule.erase(x); return true; } return false; } bool ServerConfig::DelIOHook(InspSocket* is) { std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is); if (x != SocketIOHookModule.end()) { SocketIOHookModule.erase(x); return true; } return false; } void ServerConfig::Update005() { std::stringstream out(data005); std::string token; std::string line5; int token_counter = 0; isupport.clear(); while (out >> token) { line5 = line5 + token + " "; token_counter++; if (token_counter >= 13) { char buf[MAXBUF]; snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str()); isupport.push_back(buf); line5.clear(); token_counter = 0; } } if (!line5.empty()) { char buf[MAXBUF]; snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str()); isupport.push_back(buf); } } void ServerConfig::Send005(userrec* user) { for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++) user->WriteServ("005 %s %s", user->nick, line->c_str()); } bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user) { int count = ConfValueEnum(this->config_data, tag); if (count > 1) { throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted."); return false; } if (count < 1) { throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required."); return false; } return true; } bool NoValidation(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { return true; } bool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if ((data.GetInteger() < 0) || (data.GetInteger() > 31)) { conf->GetInstance()->Log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20."); data.Set(20); } return true; } bool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS)) { conf->GetInstance()->Log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS); data.Set(MAXCLIENTS); } return true; } bool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if (data.GetInteger() > SOMAXCONN) conf->GetInstance()->Log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!"); return true; } bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance) { std::stringstream dcmds(data); std::string thiscmd; /* Enable everything first */ for (command_table::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++) x->second->Disable(false); /* Now disable all the ones which the user wants disabled */ while (dcmds >> thiscmd) { command_table::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd); if (cm != ServerInstance->Parser->cmdlist.end()) { cm->second->Disable(true); } } return true; } bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if (!*(data.GetString())) { std::string nameserver; #ifdef WINDOWS conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in the registry..."); nameserver = FindNameServerWin(); /* Windows stacks multiple nameservers in one registry key, seperated by commas. * Spotted by Cataclysm. */ if (nameserver.find(',') != std::string::npos) nameserver = nameserver.substr(0, nameserver.find(',')); data.Set(nameserver.c_str()); conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first active resolver in registry.", nameserver.c_str()); #else // attempt to look up their nameserver from /etc/resolv.conf conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf..."); ifstream resolv("/etc/resolv.conf"); bool found_server = false; if (resolv.is_open()) { while (resolv >> nameserver) { if ((nameserver == "nameserver") && (!found_server)) { resolv >> nameserver; data.Set(nameserver.c_str()); found_server = true; conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str()); } } if (!found_server) { conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!"); data.Set("127.0.0.1"); } } else { conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!"); data.Set("127.0.0.1"); } #endif } return true; } bool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { /* If we already have a servername, and they changed it, we should throw an exception. */ if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName)) { throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied."); /* XXX: We don't actually reach this return of course... */ return false; } if (!strchr(data.GetString(),'.')) { conf->GetInstance()->Log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.'); std::string moo = std::string(data.GetString()).append("."); data.Set(moo.c_str()); } return true; } bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024)) { conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240."); data.Set(10240); } return true; } bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if ((data.GetInteger() > 65535) || (data.GetInteger() < 1)) { conf->GetInstance()->Log(DEFAULT,"<options:maxwhoresults> size out of range, setting to default of 128."); data.Set(128); } return true; } bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { std::string dbg = data.GetString(); conf->LogLevel = DEFAULT; if (dbg == "debug") conf->LogLevel = DEBUG; else if (dbg == "verbose") conf->LogLevel = VERBOSE; else if (dbg == "default") conf->LogLevel = DEFAULT; else if (dbg == "sparse") conf->LogLevel = SPARSE; else if (dbg == "none") conf->LogLevel = NONE; conf->debugging = (conf->LogLevel == DEBUG); return true; } bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { conf->ReadFile(conf->MOTD, data.GetString()); return true; } bool ValidateNotEmpty(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { if (!*data.GetString()) throw CoreException(std::string("The value for ")+tag+" cannot be empty!"); return true; } bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { conf->ReadFile(conf->RULES, data.GetString()); return true; } bool ValidateModeLists(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { memset(conf->HideModeLists, 0, 256); for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x) conf->HideModeLists[*x] = true; return true; } bool ValidateExemptChanOps(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { memset(conf->ExemptChanOps, 0, 256); for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x) conf->ExemptChanOps[*x] = true; return true; } bool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) { conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString()); if (conf->WhoWasGroupSize < 0) conf->WhoWasGroupSize = 0; if (conf->WhoWasMaxGroups < 0) conf->WhoWasMaxGroups = 0; if (conf->WhoWasMaxKeep < 3600) { conf->WhoWasMaxKeep = 3600; conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600"); } command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS"); if (whowas_command) { std::deque<classbase*> params; whowas_command->HandleInternal(WHOWAS_PRUNE, params); } return true; } /* Callback called before processing the first <connect> tag */ bool InitConnect(ServerConfig* conf, const char* tag) { conf->GetInstance()->Log(DEFAULT,"Reading connect classes..."); conf->Classes.clear(); return true; } /* Callback called to process a single <connect> tag */ bool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { ConnectClass c; const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */ const char* deny = values[1].GetString(); const char* password = values[2].GetString(); int timeout = values[3].GetInteger(); int pingfreq = values[4].GetInteger(); int flood = values[5].GetInteger(); int threshold = values[6].GetInteger(); int sendq = values[7].GetInteger(); int recvq = values[8].GetInteger(); int localmax = values[9].GetInteger(); int globalmax = values[10].GetInteger(); if (*allow) { ConnectClass c(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax); conf->Classes.push_back(c); } else { ConnectClass c(deny); conf->Classes.push_back(c); } return true; } /* Callback called when there are no more <connect> tags */ bool DoneConnect(ServerConfig* conf, const char* tag) { return true; } /* Callback called before processing the first <uline> tag */ bool InitULine(ServerConfig* conf, const char* tag) { conf->ulines.clear(); return true; } /* Callback called to process a single <uline> tag */ bool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* server = values[0].GetString(); const bool silent = values[1].GetBool(); conf->ulines[server] = silent; return true; } /* Callback called when there are no more <uline> tags */ bool DoneULine(ServerConfig* conf, const char* tag) { return true; } /* Callback called before processing the first <module> tag */ bool InitModule(ServerConfig* conf, const char* tag) { old_module_names.clear(); new_module_names.clear(); added_modules.clear(); removed_modules.clear(); for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++) { old_module_names.push_back(*t); } return true; } /* Callback called to process a single <module> tag */ bool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* modname = values[0].GetString(); new_module_names.push_back(modname); return true; } /* Callback called when there are no more <module> tags */ bool DoneModule(ServerConfig* conf, const char* tag) { // now create a list of new modules that are due to be loaded // and a seperate list of modules which are due to be unloaded for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++) { bool added = true; for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++) { if (*old == *_new) added = false; } if (added) added_modules.push_back(*_new); } for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++) { bool removed = true; for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++) { if (*newm == *oldm) removed = false; } if (removed) removed_modules.push_back(*oldm); } return true; } /* Callback called before processing the first <banlist> tag */ bool InitMaxBans(ServerConfig* conf, const char* tag) { conf->maxbans.clear(); return true; } /* Callback called to process a single <banlist> tag */ bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* channel = values[0].GetString(); int limit = values[1].GetInteger(); conf->maxbans[channel] = limit; return true; } /* Callback called when there are no more <banlist> tags. */ bool DoneMaxBans(ServerConfig* conf, const char* tag) { return true; } void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, userrec* user) { ServerInstance->Log(DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str()); if (bail) { /* Unneeded because of the ServerInstance->Log() aboive? */ printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str()); InspIRCd::Exit(EXIT_STATUS_CONFIG); } else { std::string errors = errormessage; std::string::size_type start; unsigned int prefixlen; start = 0; /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */ if (user) { prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11; user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick); while (start < errors.length()) { user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str()); start += 510 - prefixlen; } } else { ServerInstance->WriteOpers("There were errors in the configuration file:"); while (start < errors.length()) { ServerInstance->WriteOpers(errors.substr(start, 360).c_str()); start += 360; } } return; } } void ServerConfig::Read(bool bail, userrec* user) { static char debug[MAXBUF]; /* Temporary buffer for debugging value */ static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */ static char hidemodes[MAXBUF]; /* Modes to not allow listing from users below halfop */ static char exemptchanops[MAXBUF]; /* Exempt channel ops from these modes */ int rem = 0, add = 0; /* Number of modules added, number of modules removed */ std::ostringstream errstr; /* String stream containing the error output */ /* These tags MUST occur and must ONLY occur once in the config file */ static char* Once[] = { "server", "admin", "files", "power", "options", NULL }; /* These tags can occur ONCE or not at all */ InitialConfig Values[] = { {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit}, {"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn}, {"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation}, {"server", "name", "", new ValueContainerChar (this->ServerName), DT_CHARPTR, ValidateServerName}, {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation}, {"server", "network", "Network", new ValueContainerChar (this->Network), DT_CHARPTR, NoValidation}, {"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation}, {"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation}, {"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation}, {"files", "motd", "", new ValueContainerChar (this->motd), DT_CHARPTR, ValidateMotd}, {"files", "rules", "", new ValueContainerChar (this->rules), DT_CHARPTR, ValidateRules}, {"power", "diepass", "", new ValueContainerChar (this->diepass), DT_CHARPTR, ValidateNotEmpty}, {"power", "pause", "", new ValueContainerInt (&this->DieDelay), DT_INTEGER, NoValidation}, {"power", "restartpass", "", new ValueContainerChar (this->restartpass), DT_CHARPTR, ValidateNotEmpty}, {"options", "prefixquit", "", new ValueContainerChar (this->PrefixQuit), DT_CHARPTR, NoValidation}, {"options", "suffixquit", "", new ValueContainerChar (this->SuffixQuit), DT_CHARPTR, NoValidation}, {"options", "fixedquit", "", new ValueContainerChar (this->FixedQuit), DT_CHARPTR, NoValidation}, {"options", "loglevel", "default", new ValueContainerChar (debug), DT_CHARPTR, ValidateLogLevel}, {"options", "netbuffersize","10240", new ValueContainerInt (&this->NetBufferSize), DT_INTEGER, ValidateNetBufferSize}, {"options", "maxwho", "128", new ValueContainerInt (&this->MaxWhoResults), DT_INTEGER, ValidateMaxWho}, {"options", "allowhalfop", "0", new ValueContainerBool (&this->AllowHalfop), DT_BOOLEAN, NoValidation}, {"dns", "server", "", new ValueContainerChar (this->DNSServer), DT_CHARPTR, ValidateDnsServer}, {"dns", "timeout", "5", new ValueContainerInt (&this->dns_timeout), DT_INTEGER, NoValidation}, {"options", "moduledir", MOD_PATH, new ValueContainerChar (this->ModPath), DT_CHARPTR, NoValidation}, {"disabled", "commands", "", new ValueContainerChar (this->DisabledCommands), DT_CHARPTR, NoValidation}, {"options", "userstats", "", new ValueContainerChar (this->UserStats), DT_CHARPTR, NoValidation}, {"options", "customversion","", new ValueContainerChar (this->CustomVersion), DT_CHARPTR, NoValidation}, {"options", "hidesplits", "0", new ValueContainerBool (&this->HideSplits), DT_BOOLEAN, NoValidation}, {"options", "hidebans", "0", new ValueContainerBool (&this->HideBans), DT_BOOLEAN, NoValidation}, {"options", "hidewhois", "", new ValueContainerChar (this->HideWhoisServer), DT_CHARPTR, NoValidation}, {"options", "hidekills", "", new ValueContainerChar (this->HideKillsServer), DT_CHARPTR, NoValidation}, {"options", "operspywhois", "0", new ValueContainerBool (&this->OperSpyWhois), DT_BOOLEAN, NoValidation}, {"options", "nouserdns", "0", new ValueContainerBool (&this->NoUserDns), DT_BOOLEAN, NoValidation}, {"options", "syntaxhints", "0", new ValueContainerBool (&this->SyntaxHints), DT_BOOLEAN, NoValidation}, {"options", "cyclehosts", "0", new ValueContainerBool (&this->CycleHosts), DT_BOOLEAN, NoValidation}, {"options", "ircumsgprefix","0", new ValueContainerBool (&this->UndernetMsgPrefix), DT_BOOLEAN, NoValidation}, {"options", "announceinvites", "1", new ValueContainerBool (&this->AnnounceInvites), DT_BOOLEAN, NoValidation}, {"options", "hostintopic", "1", new ValueContainerBool (&this->FullHostInTopic), DT_BOOLEAN, NoValidation}, {"options", "hidemodes", "", new ValueContainerChar (hidemodes), DT_CHARPTR, ValidateModeLists}, {"options", "exemptchanops","", new ValueContainerChar (exemptchanops), DT_CHARPTR, ValidateExemptChanOps}, {"options", "defaultmodes", "nt", new ValueContainerChar (this->DefaultModes), DT_CHARPTR, NoValidation}, {"pid", "file", "", new ValueContainerChar (this->PID), DT_CHARPTR, NoValidation}, {"whowas", "groupsize", "10", new ValueContainerInt (&this->WhoWasGroupSize), DT_INTEGER, NoValidation}, {"whowas", "maxgroups", "10240", new ValueContainerInt (&this->WhoWasMaxGroups), DT_INTEGER, NoValidation}, {"whowas", "maxkeep", "3600", new ValueContainerChar (maxkeep), DT_CHARPTR, ValidateWhoWas}, {"die", "value", "", new ValueContainerChar (this->DieValue), DT_CHARPTR, NoValidation}, {"channels", "users", "20", new ValueContainerUInt (&this->MaxChans), DT_INTEGER, NoValidation}, {"channels", "opers", "60", new ValueContainerUInt (&this->OperMaxChans), DT_INTEGER, NoValidation}, {NULL} }; /* These tags can occur multiple times, and therefore they have special code to read them * which is different to the code for reading the singular tags listed above. */ MultiConfig MultiValues[] = { {"connect", {"allow", "deny", "password", "timeout", "pingfreq", "flood", "threshold", "sendq", "recvq", "localmax", "globalmax", "port", NULL}, {"", "", "", "", "120", "", "", "", "", "3", "3", "0", NULL}, {DT_CHARPTR, DT_CHARPTR, DT_CHARPTR, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER}, InitConnect, DoConnect, DoneConnect}, {"uline", {"server", "silent", NULL}, {"", "0", NULL}, {DT_CHARPTR, DT_BOOLEAN}, InitULine,DoULine,DoneULine}, {"banlist", {"chan", "limit", NULL}, {"", "", NULL}, {DT_CHARPTR, DT_INTEGER}, InitMaxBans, DoMaxBans, DoneMaxBans}, {"module", {"name", NULL}, {"", NULL}, {DT_CHARPTR}, InitModule, DoModule, DoneModule}, {"badip", {"reason", "ipmask", NULL}, {"No reason", "", NULL}, {DT_CHARPTR, DT_CHARPTR}, InitXLine, DoZLine, DoneZLine}, {"badnick", {"reason", "nick", NULL}, {"No reason", "", NULL}, {DT_CHARPTR, DT_CHARPTR}, InitXLine, DoQLine, DoneQLine}, {"badhost", {"reason", "host", NULL}, {"No reason", "", NULL}, {DT_CHARPTR, DT_CHARPTR}, InitXLine, DoKLine, DoneKLine}, {"exception", {"reason", "host", NULL}, {"No reason", "", NULL}, {DT_CHARPTR, DT_CHARPTR}, InitXLine, DoELine, DoneELine}, {"type", {"name", "classes", NULL}, {"", "", NULL}, {DT_CHARPTR, DT_CHARPTR}, InitTypes, DoType, DoneClassesAndTypes}, {"class", {"name", "commands", NULL}, {"", "", NULL}, {DT_CHARPTR, DT_CHARPTR}, InitClasses, DoClass, DoneClassesAndTypes}, {NULL} }; include_stack.clear(); /* Load and parse the config file, if there are any errors then explode */ /* Make a copy here so if it fails then we can carry on running with an unaffected config */ ConfigDataHash newconfig; if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr)) { /* If we succeeded, set the ircd config to the new one */ this->config_data = newconfig; } else { ReportConfigError(errstr.str(), bail, user); return; } /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */ try { /* Check we dont have more than one of singular tags, or any of them missing */ for (int Index = 0; Once[Index]; Index++) if (!CheckOnce(Once[Index], bail, user)) return; /* Read the values of all the tags which occur once or not at all, and call their callbacks. */ for (int Index = 0; Values[Index].tag; Index++) { char item[MAXBUF]; int dt = Values[Index].datatype; bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0); dt &= ~DT_ALLOW_NEWLINE; ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines); ValueItem vi(item); if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi)) throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information."); switch (Values[Index].datatype) { case DT_CHARPTR: { ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val; /* Make sure we also copy the null terminator */ vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1); } break; case DT_INTEGER: { int val = vi.GetInteger(); ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val; vci->Set(&val, sizeof(int)); } break; case DT_BOOLEAN: { bool val = vi.GetBool(); ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val; vcb->Set(&val, sizeof(bool)); } break; default: /* You don't want to know what happens if someones bad code sends us here. */ break; } /* We're done with this now */ delete Values[Index].val; } /* Read the multiple-tag items (class tags, connect tags, etc) * and call the callbacks associated with them. We have three * callbacks for these, a 'start', 'item' and 'end' callback. */ for (int Index = 0; MultiValues[Index].tag; Index++) { MultiValues[Index].init_function(this, MultiValues[Index].tag); int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag); for (int tagnum = 0; tagnum < number_of_tags; tagnum++) { ValueList vl; for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++) { int dt = MultiValues[Index].datatype[valuenum]; bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0); dt &= ~DT_ALLOW_NEWLINE; switch (dt) { case DT_CHARPTR: { char item[MAXBUF]; if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) vl.push_back(ValueItem(item)); else vl.push_back(ValueItem("")); } break; case DT_INTEGER: { int item = 0; if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item)) vl.push_back(ValueItem(item)); else vl.push_back(ValueItem(0)); } break; case DT_BOOLEAN: { bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum); vl.push_back(ValueItem(item)); } break; default: /* Someone was smoking craq if we got here, and we're all gonna die. */ break; } } MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype); } MultiValues[Index].finish_function(this, MultiValues[Index].tag); } } catch (CoreException &ce) { ReportConfigError(ce.GetReason(), bail, user); return; } // write once here, to try it out and make sure its ok ServerInstance->WritePID(this->PID); ServerInstance->Log(DEFAULT,"Done reading configuration file."); /* If we're rehashing, let's load any new modules, and unload old ones */ if (!bail) { int found_ports = 0; FailedPortList pl; ServerInstance->BindPorts(false, found_ports, pl); if (pl.size()) { user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick); user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick); int j = 1; for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) { user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second); } } if (!removed_modules.empty()) { for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++) { if (ServerInstance->UnloadModule(removing->c_str())) { ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str()); if (user) user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str()); rem++; } else { if (user) user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError()); } } } if (!added_modules.empty()) { for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++) { if (ServerInstance->LoadModule(adding->c_str())) { ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str()); if (user) user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str()); add++; } else { if (user) user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError()); } } } ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size()); } if (user) user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick); else ServerInstance->WriteOpers("*** Successfully rehashed server."); } bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream) { std::ifstream conf(filename); std::string line; char ch; long linenumber; bool in_tag; bool in_quote; bool in_comment; int character_count = 0; linenumber = 1; in_tag = false; in_quote = false; in_comment = false; /* Check if the file open failed first */ if (!conf) { errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl; return false; } /* Fix the chmod of the file to restrict it to the current user and group */ chmod(filename,0600); for (unsigned int t = 0; t < include_stack.size(); t++) { if (std::string(filename) == include_stack[t]) { errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl; return false; } } /* It's not already included, add it to the list of files we've loaded */ include_stack.push_back(filename); /* Start reading characters... */ while(conf.get(ch)) { /* * Fix for moronic windows issue spotted by Adremelech. * Some windows editors save text files as utf-16, which is * a total pain in the ass to parse. Users should save in the * right config format! If we ever see a file where the first * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then * this is most likely a utf-16 file. Bail out and insult user. */ if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE')) { errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl; return false; } /* * Here we try and get individual tags on separate lines, * this would be so easy if we just made people format * their config files like that, but they don't so... * We check for a '<' and then know the line is over when * we get a '>' not inside quotes. If we find two '<' and * no '>' then die with an error. */ if((ch == '#') && !in_quote) in_comment = true; switch(ch) { case '\n': if (in_quote) line += '\n'; linenumber++; case '\r': if (!in_quote) in_comment = false; case '\0': continue; case '\t': ch = ' '; } if(in_comment) continue; /* XXX: Added by Brain, May 1st 2006 - Escaping of characters. * Note that this WILL NOT usually allow insertion of newlines, * because a newline is two characters long. Use it primarily to * insert the " symbol. * * Note that this also involves a further check when parsing the line, * which can be found below. */ if ((ch == '\\') && (in_quote) && (in_tag)) { line += ch; char real_character; if (conf.get(real_character)) { if (real_character == 'n') real_character = '\n'; line += real_character; continue; } else { errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl; return false; } } if (ch != '\r') line += ch; if(ch == '<') { if(in_tag) { if(!in_quote) { errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl; return false; } } else { if(in_quote) { errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl; return false; } else { // errorstream << "Opening new config tag on line " << linenumber << std::endl; in_tag = true; } } } else if(ch == '"') { if(in_tag) { if(in_quote) { // errorstream << "Closing quote in config tag on line " << linenumber << std::endl; in_quote = false; } else { // errorstream << "Opening quote in config tag on line " << linenumber << std::endl; in_quote = true; } } else { if(in_quote) { errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl; } else { errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl; } } } else if(ch == '>') { if(!in_quote) { if(in_tag) { // errorstream << "Closing config tag on line " << linenumber << std::endl; in_tag = false; /* * If this finds an <include> then ParseLine can simply call * LoadConf() and load the included config into the same ConfigDataHash */ if(!this->ParseLine(target, line, linenumber, errorstream)) return false; line.clear(); } else { errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl; return false; } } } } return true; } bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream) { return this->LoadConf(target, filename.c_str(), errorstream); } bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream) { std::string tagname; std::string current_key; std::string current_value; KeyValList results; bool got_name; bool got_key; bool in_quote; got_name = got_key = in_quote = false; //std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl; for(std::string::iterator c = line.begin(); c != line.end(); c++) { if(!got_name) { /* We don't know the tag name yet. */ if(*c != ' ') { if(*c != '<') { tagname += *c; } } else { /* We got to a space, we should have the tagname now. */ if(tagname.length()) { got_name = true; } } } else { /* We have the tag name */ if (!got_key) { /* We're still reading the key name */ if (*c != '=') { if (*c != ' ') { current_key += *c; } } else { /* We got an '=', end of the key name. */ got_key = true; } } else { /* We have the key name, now we're looking for quotes and the value */ /* Correctly handle escaped characters here. * See the XXX'ed section above. */ if ((*c == '\\') && (in_quote)) { c++; if (*c == 'n') current_value += '\n'; else current_value += *c; continue; } else if ((*c == '\n') && (in_quote)) { /* Got a 'real' \n, treat it as part of the value */ current_value += '\n'; continue; } else if ((*c == '\r') && (in_quote)) /* Got a \r, drop it */ continue; if (*c == '"') { if (!in_quote) { /* We're not already in a quote. */ in_quote = true; } else { /* Leaving quotes, we have the value */ results.push_back(KeyVal(current_key, current_value)); // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl; in_quote = false; got_key = false; if((tagname == "include") && (current_key == "file")) { if(!this->DoInclude(target, current_value, errorstream)) return false; } current_key.clear(); current_value.clear(); } } else { if(in_quote) { current_value += *c; } } } } } /* Finished parsing the tag, add it to the config hash */ target.insert(std::pair<std::string, KeyValList > (tagname, results)); return true; } bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream) { std::string confpath; std::string newfile; std::string::size_type pos; confpath = ServerInstance->ConfigFileName; newfile = file; for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++) { if (*c == '\\') { *c = '/'; } } if (file[0] != '/') { if((pos = confpath.rfind("/")) != std::string::npos) { /* Leaves us with just the path */ newfile = confpath.substr(0, pos) + std::string("/") + newfile; } else { errorstream << "Couldn't get config path from: " << confpath << std::endl; return false; } } return LoadConf(target, newfile, errorstream); } bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds) { return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds); } bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds) { std::string value; bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds); strlcpy(result, value.c_str(), length); return r; } bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds) { return ConfValue(target, tag, var, "", index, result, allow_linefeeds); } bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds) { ConfigDataHash::size_type pos = index; if((pos >= 0) && (pos < target.count(tag))) { ConfigDataHash::iterator iter = target.find(tag); for(int i = 0; i < index; i++) iter++; for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++) { if(j->first == var) { if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos)) { ServerInstance->Log(DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces."); for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++) if (*n == '\n') *n = ' '; } else { result = j->second; return true; } } } if (!default_value.empty()) { result = default_value; return true; } } else if(pos == 0) { if (!default_value.empty()) { result = default_value; return true; } } return false; } bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result) { return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result); } bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result) { return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result); } bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result) { return ConfValueInteger(target, tag, var, "", index, result); } bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result) { std::string value; std::istringstream stream; bool r = ConfValue(target, tag, var, default_value, index, value); stream.str(value); if(!(stream >> result)) return false; else { if (!value.empty()) { if (value.substr(0,2) == "0x") { char* endptr; value.erase(0,2); result = strtol(value.c_str(), &endptr, 16); /* No digits found */ if (endptr == value.c_str()) return false; } else { char denominator = *(value.end() - 1); switch (toupper(denominator)) { case 'K': /* Kilobytes -> bytes */ result = result * 1024; break; case 'M': /* Megabytes -> bytes */ result = result * 1024 * 1024; break; case 'G': /* Gigabytes -> bytes */ result = result * 1024 * 1024 * 1024; break; } } } } return r; } bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index) { return ConfValueBool(target, std::string(tag), std::string(var), "", index); } bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index) { return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index); } bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index) { return ConfValueBool(target, tag, var, "", index); } bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index) { std::string result; if(!ConfValue(target, tag, var, default_value, index, result)) return false; return ((result == "yes") || (result == "true") || (result == "1")); } int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag) { return target.count(tag); } int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag) { return target.count(tag); } int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index) { return ConfVarEnum(target, std::string(tag), index); } int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index) { ConfigDataHash::size_type pos = index; if((pos >= 0) && (pos < target.count(tag))) { ConfigDataHash::const_iterator iter = target.find(tag); for(int i = 0; i < index; i++) iter++; return iter->second.size(); } return 0; } /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'. */ bool ServerConfig::ReadFile(file_cache &F, const char* fname) { if (!fname || !*fname) return false; FILE* file = NULL; char linebuf[MAXBUF]; F.clear(); if ((*fname != '/') && (*fname != '\\')) { std::string::size_type pos; std::string confpath = ServerInstance->ConfigFileName; std::string newfile = fname; if ((pos = confpath.rfind("/")) != std::string::npos) newfile = confpath.substr(0, pos) + std::string("/") + fname; else if ((pos = confpath.rfind("\\")) != std::string::npos) newfile = confpath.substr(0, pos) + std::string("\\") + fname; if (!FileExists(newfile.c_str())) return false; file = fopen(newfile.c_str(), "r"); } else { if (!FileExists(fname)) return false; file = fopen(fname, "r"); } if (file) { while (!feof(file)) { if (fgets(linebuf, sizeof(linebuf), file)) linebuf[strlen(linebuf)-1] = 0; else *linebuf = 0; if (!feof(file)) { F.push_back(*linebuf ? linebuf : " "); } } fclose(file); } else return false; return true; } bool ServerConfig::FileExists(const char* file) { struct stat sb; if (stat(file, &sb) == -1) return false; if ((sb.st_mode & S_IFDIR) > 0) return false; FILE *input; if ((input = fopen (file, "r")) == NULL) return false; else { fclose(input); return true; } } char* ServerConfig::CleanFilename(char* name) { char* p = name + strlen(name); while ((p != name) && (*p != '/') && (*p != '\\')) p--; return (p != name ? ++p : p); } bool ServerConfig::DirValid(const char* dirandfile) { #ifdef WINDOWS return true; #endif char work[1024]; char buffer[1024]; char otherdir[1024]; int p; strlcpy(work, dirandfile, 1024); p = strlen(work); // we just want the dir while (*work) { if (work[p] == '/') { work[p] = '\0'; break; } work[p--] = '\0'; } // Get the current working directory if (getcwd(buffer, 1024 ) == NULL ) return false; if (chdir(work) == -1) return false; if (getcwd(otherdir, 1024 ) == NULL ) return false; if (chdir(buffer) == -1) return false; size_t t = strlen(work); if (strlen(otherdir) >= t) { otherdir[t] = '\0'; if (!strcmp(otherdir,work)) { return true; } return false; } else { return false; } } std::string ServerConfig::GetFullProgDir() { char buffer[PATH_MAX+1]; #ifdef WINDOWS /* Windows has specific api calls to get the exe path that never fail. * For once, windows has something of use, compared to the POSIX code * for this, this is positively neato. */ if (GetModuleFileName(NULL, buffer, MAX_PATH)) { std::string fullpath = buffer; std::string::size_type n = fullpath.rfind("\\inspircd.exe"); return std::string(fullpath, 0, n); } #else // Get the current working directory if (getcwd(buffer, PATH_MAX)) { std::string remainder = this->argv[0]; /* Does argv[0] start with /? its a full path, use it */ if (remainder[0] == '/') { std::string::size_type n = remainder.rfind("/inspircd"); return std::string(remainder, 0, n); } std::string fullpath = std::string(buffer) + "/" + remainder; std::string::size_type n = fullpath.rfind("/inspircd"); return std::string(fullpath, 0, n); } #endif return "/"; } InspIRCd* ServerConfig::GetInstance() { return ServerInstance; } ValueItem::ValueItem(int value) { std::stringstream n; n << value; v = n.str(); } ValueItem::ValueItem(bool value) { std::stringstream n; n << value; v = n.str(); } ValueItem::ValueItem(char* value) { v = value; } void ValueItem::Set(char* value) { v = value; } void ValueItem::Set(const char* value) { v = value; } void ValueItem::Set(int value) { std::stringstream n; n << value; v = n.str(); } int ValueItem::GetInteger() { if (v.empty()) return 0; return atoi(v.c_str()); } char* ValueItem::GetString() { return (char*)v.c_str(); } bool ValueItem::GetBool() { return (GetInteger() || v == "yes" || v == "true"); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include <sstream>
+#include <fstream>
+#include "xline.h"
+#include "exitcodes.h"
+#include "commands/cmd_whowas.h"
+
+std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
+
+ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ this->ClearStack();
+ *ServerName = *Network = *ServerDesc = *AdminName = '\0';
+ *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0';
+ *DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
+ *UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0';
+ WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
+ log_file = NULL;
+ NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false;
+ CycleHosts = writelog = AllowHalfop = true;
+ dns_timeout = DieDelay = 5;
+ MaxTargets = 20;
+ NetBufferSize = 10240;
+ SoftLimit = MAXCLIENTS;
+ MaxConn = SOMAXCONN;
+ MaxWhoResults = 0;
+ debugging = 0;
+ MaxChans = 20;
+ OperMaxChans = 30;
+ LogLevel = DEFAULT;
+ maxbans.clear();
+}
+
+void ServerConfig::ClearStack()
+{
+ include_stack.clear();
+}
+
+Module* ServerConfig::GetIOHook(int port)
+{
+ std::map<int,Module*>::iterator x = IOHookModule.find(port);
+ return (x != IOHookModule.end() ? x->second : NULL);
+}
+
+Module* ServerConfig::GetIOHook(InspSocket* is)
+{
+ std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);
+ return (x != SocketIOHookModule.end() ? x->second : NULL);
+}
+
+bool ServerConfig::AddIOHook(int port, Module* iomod)
+{
+ if (!GetIOHook(port))
+ {
+ IOHookModule[port] = iomod;
+ return true;
+ }
+ else
+ {
+ throw ModuleException("Port already hooked by another module");
+ return false;
+ }
+}
+
+bool ServerConfig::AddIOHook(Module* iomod, InspSocket* is)
+{
+ if (!GetIOHook(is))
+ {
+ SocketIOHookModule[is] = iomod;
+ is->IsIOHooked = true;
+ return true;
+ }
+ else
+ {
+ throw ModuleException("InspSocket derived class already hooked by another module");
+ return false;
+ }
+}
+
+bool ServerConfig::DelIOHook(int port)
+{
+ std::map<int,Module*>::iterator x = IOHookModule.find(port);
+ if (x != IOHookModule.end())
+ {
+ IOHookModule.erase(x);
+ return true;
+ }
+ return false;
+}
+
+bool ServerConfig::DelIOHook(InspSocket* is)
+{
+ std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);
+ if (x != SocketIOHookModule.end())
+ {
+ SocketIOHookModule.erase(x);
+ return true;
+ }
+ return false;
+}
+
+void ServerConfig::Update005()
+{
+ std::stringstream out(data005);
+ std::string token;
+ std::string line5;
+ int token_counter = 0;
+ isupport.clear();
+ while (out >> token)
+ {
+ line5 = line5 + token + " ";
+ token_counter++;
+ if (token_counter >= 13)
+ {
+ char buf[MAXBUF];
+ snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
+ isupport.push_back(buf);
+ line5.clear();
+ token_counter = 0;
+ }
+ }
+ if (!line5.empty())
+ {
+ char buf[MAXBUF];
+ snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
+ isupport.push_back(buf);
+ }
+}
+
+void ServerConfig::Send005(userrec* user)
+{
+ for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
+ user->WriteServ("005 %s %s", user->nick, line->c_str());
+}
+
+bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
+{
+ int count = ConfValueEnum(this->config_data, tag);
+
+ if (count > 1)
+ {
+ throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted.");
+ return false;
+ }
+ if (count < 1)
+ {
+ throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required.");
+ return false;
+ }
+ return true;
+}
+
+bool NoValidation(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ return true;
+}
+
+bool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if ((data.GetInteger() < 0) || (data.GetInteger() > 31))
+ {
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
+ data.Set(20);
+ }
+ return true;
+}
+
+bool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS))
+ {
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
+ data.Set(MAXCLIENTS);
+ }
+ return true;
+}
+
+bool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if (data.GetInteger() > SOMAXCONN)
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
+ return true;
+}
+
+bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance)
+{
+ std::stringstream dcmds(data);
+ std::string thiscmd;
+
+ /* Enable everything first */
+ for (command_table::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
+ x->second->Disable(false);
+
+ /* Now disable all the ones which the user wants disabled */
+ while (dcmds >> thiscmd)
+ {
+ command_table::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
+ if (cm != ServerInstance->Parser->cmdlist.end())
+ {
+ cm->second->Disable(true);
+ }
+ }
+ return true;
+}
+
+bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if (!*(data.GetString()))
+ {
+ std::string nameserver;
+#ifdef WINDOWS
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in the registry...");
+ nameserver = FindNameServerWin();
+ /* Windows stacks multiple nameservers in one registry key, seperated by commas.
+ * Spotted by Cataclysm.
+ */
+ if (nameserver.find(',') != std::string::npos)
+ nameserver = nameserver.substr(0, nameserver.find(','));
+ data.Set(nameserver.c_str());
+ conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first active resolver in registry.", nameserver.c_str());
+#else
+ // attempt to look up their nameserver from /etc/resolv.conf
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+ ifstream resolv("/etc/resolv.conf");
+ bool found_server = false;
+
+ if (resolv.is_open())
+ {
+ while (resolv >> nameserver)
+ {
+ if ((nameserver == "nameserver") && (!found_server))
+ {
+ resolv >> nameserver;
+ data.Set(nameserver.c_str());
+ found_server = true;
+ conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
+ }
+ }
+
+ if (!found_server)
+ {
+ conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
+ data.Set("127.0.0.1");
+ }
+ }
+ else
+ {
+ conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
+ data.Set("127.0.0.1");
+ }
+#endif
+ }
+ return true;
+}
+
+bool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ /* If we already have a servername, and they changed it, we should throw an exception. */
+ if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName))
+ {
+ throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied.");
+ /* XXX: We don't actually reach this return of course... */
+ return false;
+ }
+ if (!strchr(data.GetString(),'.'))
+ {
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.');
+ std::string moo = std::string(data.GetString()).append(".");
+ data.Set(moo.c_str());
+ }
+ return true;
+}
+
+bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024))
+ {
+ conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
+ data.Set(10240);
+ }
+ return true;
+}
+
+bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if ((data.GetInteger() > 65535) || (data.GetInteger() < 1))
+ {
+ conf->GetInstance()->Log(DEFAULT,"<options:maxwhoresults> size out of range, setting to default of 128.");
+ data.Set(128);
+ }
+ return true;
+}
+
+bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ std::string dbg = data.GetString();
+ conf->LogLevel = DEFAULT;
+
+ if (dbg == "debug")
+ conf->LogLevel = DEBUG;
+ else if (dbg == "verbose")
+ conf->LogLevel = VERBOSE;
+ else if (dbg == "default")
+ conf->LogLevel = DEFAULT;
+ else if (dbg == "sparse")
+ conf->LogLevel = SPARSE;
+ else if (dbg == "none")
+ conf->LogLevel = NONE;
+
+ conf->debugging = (conf->LogLevel == DEBUG);
+
+ return true;
+}
+
+bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ conf->ReadFile(conf->MOTD, data.GetString());
+ return true;
+}
+
+bool ValidateNotEmpty(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ if (!*data.GetString())
+ throw CoreException(std::string("The value for ")+tag+" cannot be empty!");
+ return true;
+}
+
+bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ conf->ReadFile(conf->RULES, data.GetString());
+ return true;
+}
+
+bool ValidateModeLists(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ memset(conf->HideModeLists, 0, 256);
+ for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
+ conf->HideModeLists[*x] = true;
+ return true;
+}
+
+bool ValidateExemptChanOps(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ memset(conf->ExemptChanOps, 0, 256);
+ for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
+ conf->ExemptChanOps[*x] = true;
+ return true;
+}
+
+bool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
+{
+ conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString());
+
+ if (conf->WhoWasGroupSize < 0)
+ conf->WhoWasGroupSize = 0;
+
+ if (conf->WhoWasMaxGroups < 0)
+ conf->WhoWasMaxGroups = 0;
+
+ if (conf->WhoWasMaxKeep < 3600)
+ {
+ conf->WhoWasMaxKeep = 3600;
+ conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600");
+ }
+
+ command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS");
+ if (whowas_command)
+ {
+ std::deque<classbase*> params;
+ whowas_command->HandleInternal(WHOWAS_PRUNE, params);
+ }
+
+ return true;
+}
+
+/* Callback called before processing the first <connect> tag
+ */
+bool InitConnect(ServerConfig* conf, const char* tag)
+{
+ conf->GetInstance()->Log(DEFAULT,"Reading connect classes...");
+ conf->Classes.clear();
+ return true;
+}
+
+/* Callback called to process a single <connect> tag
+ */
+bool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ ConnectClass c;
+ const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */
+ const char* deny = values[1].GetString();
+ const char* password = values[2].GetString();
+ int timeout = values[3].GetInteger();
+ int pingfreq = values[4].GetInteger();
+ int flood = values[5].GetInteger();
+ int threshold = values[6].GetInteger();
+ int sendq = values[7].GetInteger();
+ int recvq = values[8].GetInteger();
+ int localmax = values[9].GetInteger();
+ int globalmax = values[10].GetInteger();
+
+ if (*allow)
+ {
+ ConnectClass c(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax);
+ conf->Classes.push_back(c);
+ }
+ else
+ {
+ ConnectClass c(deny);
+ conf->Classes.push_back(c);
+ }
+
+ return true;
+}
+
+/* Callback called when there are no more <connect> tags
+ */
+bool DoneConnect(ServerConfig* conf, const char* tag)
+{
+ return true;
+}
+
+/* Callback called before processing the first <uline> tag
+ */
+bool InitULine(ServerConfig* conf, const char* tag)
+{
+ conf->ulines.clear();
+ return true;
+}
+
+/* Callback called to process a single <uline> tag
+ */
+bool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* server = values[0].GetString();
+ const bool silent = values[1].GetBool();
+ conf->ulines[server] = silent;
+ return true;
+}
+
+/* Callback called when there are no more <uline> tags
+ */
+bool DoneULine(ServerConfig* conf, const char* tag)
+{
+ return true;
+}
+
+/* Callback called before processing the first <module> tag
+ */
+bool InitModule(ServerConfig* conf, const char* tag)
+{
+ old_module_names.clear();
+ new_module_names.clear();
+ added_modules.clear();
+ removed_modules.clear();
+ for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++)
+ {
+ old_module_names.push_back(*t);
+ }
+ return true;
+}
+
+/* Callback called to process a single <module> tag
+ */
+bool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* modname = values[0].GetString();
+ new_module_names.push_back(modname);
+ return true;
+}
+
+/* Callback called when there are no more <module> tags
+ */
+bool DoneModule(ServerConfig* conf, const char* tag)
+{
+ // now create a list of new modules that are due to be loaded
+ // and a seperate list of modules which are due to be unloaded
+ for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
+ {
+ bool added = true;
+
+ for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
+ {
+ if (*old == *_new)
+ added = false;
+ }
+
+ if (added)
+ added_modules.push_back(*_new);
+ }
+
+ for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
+ {
+ bool removed = true;
+ for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
+ {
+ if (*newm == *oldm)
+ removed = false;
+ }
+
+ if (removed)
+ removed_modules.push_back(*oldm);
+ }
+ return true;
+}
+
+/* Callback called before processing the first <banlist> tag
+ */
+bool InitMaxBans(ServerConfig* conf, const char* tag)
+{
+ conf->maxbans.clear();
+ return true;
+}
+
+/* Callback called to process a single <banlist> tag
+ */
+bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* channel = values[0].GetString();
+ int limit = values[1].GetInteger();
+ conf->maxbans[channel] = limit;
+ return true;
+}
+
+/* Callback called when there are no more <banlist> tags.
+ */
+bool DoneMaxBans(ServerConfig* conf, const char* tag)
+{
+ return true;
+}
+
+void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, userrec* user)
+{
+ ServerInstance->Log(DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str());
+ if (bail)
+ {
+ /* Unneeded because of the ServerInstance->Log() aboive? */
+ printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str());
+ InspIRCd::Exit(EXIT_STATUS_CONFIG);
+ }
+ else
+ {
+ std::string errors = errormessage;
+ std::string::size_type start;
+ unsigned int prefixlen;
+ start = 0;
+ /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */
+ if (user)
+ {
+ prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11;
+ user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick);
+ while (start < errors.length())
+ {
+ user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str());
+ start += 510 - prefixlen;
+ }
+ }
+ else
+ {
+ ServerInstance->WriteOpers("There were errors in the configuration file:");
+ while (start < errors.length())
+ {
+ ServerInstance->WriteOpers(errors.substr(start, 360).c_str());
+ start += 360;
+ }
+ }
+ return;
+ }
+}
+
+void ServerConfig::Read(bool bail, userrec* user)
+{
+ static char debug[MAXBUF]; /* Temporary buffer for debugging value */
+ static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */
+ static char hidemodes[MAXBUF]; /* Modes to not allow listing from users below halfop */
+ static char exemptchanops[MAXBUF]; /* Exempt channel ops from these modes */
+ int rem = 0, add = 0; /* Number of modules added, number of modules removed */
+ std::ostringstream errstr; /* String stream containing the error output */
+
+ /* These tags MUST occur and must ONLY occur once in the config file */
+ static char* Once[] = { "server", "admin", "files", "power", "options", NULL };
+
+ /* These tags can occur ONCE or not at all */
+ InitialConfig Values[] = {
+ {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit},
+ {"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn},
+ {"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation},
+ {"server", "name", "", new ValueContainerChar (this->ServerName), DT_CHARPTR, ValidateServerName},
+ {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation},
+ {"server", "network", "Network", new ValueContainerChar (this->Network), DT_CHARPTR, NoValidation},
+ {"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation},
+ {"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation},
+ {"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation},
+ {"files", "motd", "", new ValueContainerChar (this->motd), DT_CHARPTR, ValidateMotd},
+ {"files", "rules", "", new ValueContainerChar (this->rules), DT_CHARPTR, ValidateRules},
+ {"power", "diepass", "", new ValueContainerChar (this->diepass), DT_CHARPTR, ValidateNotEmpty},
+ {"power", "pause", "", new ValueContainerInt (&this->DieDelay), DT_INTEGER, NoValidation},
+ {"power", "restartpass", "", new ValueContainerChar (this->restartpass), DT_CHARPTR, ValidateNotEmpty},
+ {"options", "prefixquit", "", new ValueContainerChar (this->PrefixQuit), DT_CHARPTR, NoValidation},
+ {"options", "suffixquit", "", new ValueContainerChar (this->SuffixQuit), DT_CHARPTR, NoValidation},
+ {"options", "fixedquit", "", new ValueContainerChar (this->FixedQuit), DT_CHARPTR, NoValidation},
+ {"options", "loglevel", "default", new ValueContainerChar (debug), DT_CHARPTR, ValidateLogLevel},
+ {"options", "netbuffersize","10240", new ValueContainerInt (&this->NetBufferSize), DT_INTEGER, ValidateNetBufferSize},
+ {"options", "maxwho", "128", new ValueContainerInt (&this->MaxWhoResults), DT_INTEGER, ValidateMaxWho},
+ {"options", "allowhalfop", "0", new ValueContainerBool (&this->AllowHalfop), DT_BOOLEAN, NoValidation},
+ {"dns", "server", "", new ValueContainerChar (this->DNSServer), DT_CHARPTR, ValidateDnsServer},
+ {"dns", "timeout", "5", new ValueContainerInt (&this->dns_timeout), DT_INTEGER, NoValidation},
+ {"options", "moduledir", MOD_PATH, new ValueContainerChar (this->ModPath), DT_CHARPTR, NoValidation},
+ {"disabled", "commands", "", new ValueContainerChar (this->DisabledCommands), DT_CHARPTR, NoValidation},
+ {"options", "userstats", "", new ValueContainerChar (this->UserStats), DT_CHARPTR, NoValidation},
+ {"options", "customversion","", new ValueContainerChar (this->CustomVersion), DT_CHARPTR, NoValidation},
+ {"options", "hidesplits", "0", new ValueContainerBool (&this->HideSplits), DT_BOOLEAN, NoValidation},
+ {"options", "hidebans", "0", new ValueContainerBool (&this->HideBans), DT_BOOLEAN, NoValidation},
+ {"options", "hidewhois", "", new ValueContainerChar (this->HideWhoisServer), DT_CHARPTR, NoValidation},
+ {"options", "hidekills", "", new ValueContainerChar (this->HideKillsServer), DT_CHARPTR, NoValidation},
+ {"options", "operspywhois", "0", new ValueContainerBool (&this->OperSpyWhois), DT_BOOLEAN, NoValidation},
+ {"options", "nouserdns", "0", new ValueContainerBool (&this->NoUserDns), DT_BOOLEAN, NoValidation},
+ {"options", "syntaxhints", "0", new ValueContainerBool (&this->SyntaxHints), DT_BOOLEAN, NoValidation},
+ {"options", "cyclehosts", "0", new ValueContainerBool (&this->CycleHosts), DT_BOOLEAN, NoValidation},
+ {"options", "ircumsgprefix","0", new ValueContainerBool (&this->UndernetMsgPrefix), DT_BOOLEAN, NoValidation},
+ {"options", "announceinvites", "1", new ValueContainerBool (&this->AnnounceInvites), DT_BOOLEAN, NoValidation},
+ {"options", "hostintopic", "1", new ValueContainerBool (&this->FullHostInTopic), DT_BOOLEAN, NoValidation},
+ {"options", "hidemodes", "", new ValueContainerChar (hidemodes), DT_CHARPTR, ValidateModeLists},
+ {"options", "exemptchanops","", new ValueContainerChar (exemptchanops), DT_CHARPTR, ValidateExemptChanOps},
+ {"options", "defaultmodes", "nt", new ValueContainerChar (this->DefaultModes), DT_CHARPTR, NoValidation},
+ {"pid", "file", "", new ValueContainerChar (this->PID), DT_CHARPTR, NoValidation},
+ {"whowas", "groupsize", "10", new ValueContainerInt (&this->WhoWasGroupSize), DT_INTEGER, NoValidation},
+ {"whowas", "maxgroups", "10240", new ValueContainerInt (&this->WhoWasMaxGroups), DT_INTEGER, NoValidation},
+ {"whowas", "maxkeep", "3600", new ValueContainerChar (maxkeep), DT_CHARPTR, ValidateWhoWas},
+ {"die", "value", "", new ValueContainerChar (this->DieValue), DT_CHARPTR, NoValidation},
+ {"channels", "users", "20", new ValueContainerUInt (&this->MaxChans), DT_INTEGER, NoValidation},
+ {"channels", "opers", "60", new ValueContainerUInt (&this->OperMaxChans), DT_INTEGER, NoValidation},
+ {NULL}
+ };
+
+ /* These tags can occur multiple times, and therefore they have special code to read them
+ * which is different to the code for reading the singular tags listed above.
+ */
+ MultiConfig MultiValues[] = {
+
+ {"connect",
+ {"allow", "deny", "password", "timeout", "pingfreq", "flood",
+ "threshold", "sendq", "recvq", "localmax", "globalmax", "port",
+ NULL},
+ {"", "", "", "", "120", "",
+ "", "", "", "3", "3", "0",
+ NULL},
+ {DT_CHARPTR, DT_CHARPTR, DT_CHARPTR, DT_INTEGER, DT_INTEGER, DT_INTEGER,
+ DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER},
+ InitConnect, DoConnect, DoneConnect},
+
+ {"uline",
+ {"server", "silent", NULL},
+ {"", "0", NULL},
+ {DT_CHARPTR, DT_BOOLEAN},
+ InitULine,DoULine,DoneULine},
+
+ {"banlist",
+ {"chan", "limit", NULL},
+ {"", "", NULL},
+ {DT_CHARPTR, DT_INTEGER},
+ InitMaxBans, DoMaxBans, DoneMaxBans},
+
+ {"module",
+ {"name", NULL},
+ {"", NULL},
+ {DT_CHARPTR},
+ InitModule, DoModule, DoneModule},
+
+ {"badip",
+ {"reason", "ipmask", NULL},
+ {"No reason", "", NULL},
+ {DT_CHARPTR, DT_CHARPTR},
+ InitXLine, DoZLine, DoneZLine},
+
+ {"badnick",
+ {"reason", "nick", NULL},
+ {"No reason", "", NULL},
+ {DT_CHARPTR, DT_CHARPTR},
+ InitXLine, DoQLine, DoneQLine},
+
+ {"badhost",
+ {"reason", "host", NULL},
+ {"No reason", "", NULL},
+ {DT_CHARPTR, DT_CHARPTR},
+ InitXLine, DoKLine, DoneKLine},
+
+ {"exception",
+ {"reason", "host", NULL},
+ {"No reason", "", NULL},
+ {DT_CHARPTR, DT_CHARPTR},
+ InitXLine, DoELine, DoneELine},
+
+ {"type",
+ {"name", "classes", NULL},
+ {"", "", NULL},
+ {DT_CHARPTR, DT_CHARPTR},
+ InitTypes, DoType, DoneClassesAndTypes},
+
+ {"class",
+ {"name", "commands", NULL},
+ {"", "", NULL},
+ {DT_CHARPTR, DT_CHARPTR},
+ InitClasses, DoClass, DoneClassesAndTypes},
+
+ {NULL}
+ };
+
+ include_stack.clear();
+
+ /* Load and parse the config file, if there are any errors then explode */
+
+ /* Make a copy here so if it fails then we can carry on running with an unaffected config */
+ ConfigDataHash newconfig;
+
+ if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr))
+ {
+ /* If we succeeded, set the ircd config to the new one */
+ this->config_data = newconfig;
+ }
+ else
+ {
+ ReportConfigError(errstr.str(), bail, user);
+ return;
+ }
+
+ /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
+ try
+ {
+ /* Check we dont have more than one of singular tags, or any of them missing
+ */
+ for (int Index = 0; Once[Index]; Index++)
+ if (!CheckOnce(Once[Index], bail, user))
+ return;
+
+ /* Read the values of all the tags which occur once or not at all, and call their callbacks.
+ */
+ for (int Index = 0; Values[Index].tag; Index++)
+ {
+ char item[MAXBUF];
+ int dt = Values[Index].datatype;
+ bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
+ dt &= ~DT_ALLOW_NEWLINE;
+
+ ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);
+ ValueItem vi(item);
+
+ if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
+ throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
+
+ switch (Values[Index].datatype)
+ {
+ case DT_CHARPTR:
+ {
+ ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
+ /* Make sure we also copy the null terminator */
+ vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
+ }
+ break;
+ case DT_INTEGER:
+ {
+ int val = vi.GetInteger();
+ ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;
+ vci->Set(&val, sizeof(int));
+ }
+ break;
+ case DT_BOOLEAN:
+ {
+ bool val = vi.GetBool();
+ ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;
+ vcb->Set(&val, sizeof(bool));
+ }
+ break;
+ default:
+ /* You don't want to know what happens if someones bad code sends us here. */
+ break;
+ }
+
+ /* We're done with this now */
+ delete Values[Index].val;
+ }
+
+ /* Read the multiple-tag items (class tags, connect tags, etc)
+ * and call the callbacks associated with them. We have three
+ * callbacks for these, a 'start', 'item' and 'end' callback.
+ */
+ for (int Index = 0; MultiValues[Index].tag; Index++)
+ {
+ MultiValues[Index].init_function(this, MultiValues[Index].tag);
+
+ int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
+
+ for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
+ {
+ ValueList vl;
+ for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
+ {
+ int dt = MultiValues[Index].datatype[valuenum];
+ bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
+ dt &= ~DT_ALLOW_NEWLINE;
+
+ switch (dt)
+ {
+ case DT_CHARPTR:
+ {
+ char item[MAXBUF];
+ if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
+ vl.push_back(ValueItem(item));
+ else
+ vl.push_back(ValueItem(""));
+ }
+ break;
+ case DT_INTEGER:
+ {
+ int item = 0;
+ if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))
+ vl.push_back(ValueItem(item));
+ else
+ vl.push_back(ValueItem(0));
+ }
+ break;
+ case DT_BOOLEAN:
+ {
+ bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);
+ vl.push_back(ValueItem(item));
+ }
+ break;
+ default:
+ /* Someone was smoking craq if we got here, and we're all gonna die. */
+ break;
+ }
+ }
+
+ MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
+ }
+
+ MultiValues[Index].finish_function(this, MultiValues[Index].tag);
+ }
+
+ }
+
+ catch (CoreException &ce)
+ {
+ ReportConfigError(ce.GetReason(), bail, user);
+ return;
+ }
+
+ // write once here, to try it out and make sure its ok
+ ServerInstance->WritePID(this->PID);
+
+ ServerInstance->Log(DEFAULT,"Done reading configuration file.");
+
+ /* If we're rehashing, let's load any new modules, and unload old ones
+ */
+ if (!bail)
+ {
+ int found_ports = 0;
+ FailedPortList pl;
+ ServerInstance->BindPorts(false, found_ports, pl);
+
+ if (pl.size())
+ {
+ user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick);
+ user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick);
+ int j = 1;
+ for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
+ {
+ user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
+ }
+ }
+
+ if (!removed_modules.empty())
+ {
+ for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
+ {
+ if (ServerInstance->UnloadModule(removing->c_str()))
+ {
+ ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
+
+ if (user)
+ user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
+
+ rem++;
+ }
+ else
+ {
+ if (user)
+ user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
+ }
+ }
+ }
+
+ if (!added_modules.empty())
+ {
+ for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
+ {
+ if (ServerInstance->LoadModule(adding->c_str()))
+ {
+ ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
+
+ if (user)
+ user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
+
+ add++;
+ }
+ else
+ {
+ if (user)
+ user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
+ }
+ }
+ }
+
+ ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
+ }
+
+ if (user)
+ user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick);
+ else
+ ServerInstance->WriteOpers("*** Successfully rehashed server.");
+}
+
+bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
+{
+ std::ifstream conf(filename);
+ std::string line;
+ char ch;
+ long linenumber;
+ bool in_tag;
+ bool in_quote;
+ bool in_comment;
+ int character_count = 0;
+
+ linenumber = 1;
+ in_tag = false;
+ in_quote = false;
+ in_comment = false;
+
+ /* Check if the file open failed first */
+ if (!conf)
+ {
+ errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
+ return false;
+ }
+
+ /* Fix the chmod of the file to restrict it to the current user and group */
+ chmod(filename,0600);
+
+ for (unsigned int t = 0; t < include_stack.size(); t++)
+ {
+ if (std::string(filename) == include_stack[t])
+ {
+ errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
+ return false;
+ }
+ }
+
+ /* It's not already included, add it to the list of files we've loaded */
+ include_stack.push_back(filename);
+
+ /* Start reading characters... */
+ while(conf.get(ch))
+ {
+
+ /*
+ * Fix for moronic windows issue spotted by Adremelech.
+ * Some windows editors save text files as utf-16, which is
+ * a total pain in the ass to parse. Users should save in the
+ * right config format! If we ever see a file where the first
+ * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
+ * this is most likely a utf-16 file. Bail out and insult user.
+ */
+ if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
+ {
+ errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
+ return false;
+ }
+
+ /*
+ * Here we try and get individual tags on separate lines,
+ * this would be so easy if we just made people format
+ * their config files like that, but they don't so...
+ * We check for a '<' and then know the line is over when
+ * we get a '>' not inside quotes. If we find two '<' and
+ * no '>' then die with an error.
+ */
+
+ if((ch == '#') && !in_quote)
+ in_comment = true;
+
+ switch(ch)
+ {
+ case '\n':
+ if (in_quote)
+ line += '\n';
+ linenumber++;
+ case '\r':
+ if (!in_quote)
+ in_comment = false;
+ case '\0':
+ continue;
+ case '\t':
+ ch = ' ';
+ }
+
+ if(in_comment)
+ continue;
+
+ /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
+ * Note that this WILL NOT usually allow insertion of newlines,
+ * because a newline is two characters long. Use it primarily to
+ * insert the " symbol.
+ *
+ * Note that this also involves a further check when parsing the line,
+ * which can be found below.
+ */
+ if ((ch == '\\') && (in_quote) && (in_tag))
+ {
+ line += ch;
+ char real_character;
+ if (conf.get(real_character))
+ {
+ if (real_character == 'n')
+ real_character = '\n';
+ line += real_character;
+ continue;
+ }
+ else
+ {
+ errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
+ return false;
+ }
+ }
+
+ if (ch != '\r')
+ line += ch;
+
+ if(ch == '<')
+ {
+ if(in_tag)
+ {
+ if(!in_quote)
+ {
+ errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;
+ return false;
+ }
+ }
+ else
+ {
+ if(in_quote)
+ {
+ errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;
+ return false;
+ }
+ else
+ {
+ // errorstream << "Opening new config tag on line " << linenumber << std::endl;
+ in_tag = true;
+ }
+ }
+ }
+ else if(ch == '"')
+ {
+ if(in_tag)
+ {
+ if(in_quote)
+ {
+ // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
+ in_quote = false;
+ }
+ else
+ {
+ // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
+ in_quote = true;
+ }
+ }
+ else
+ {
+ if(in_quote)
+ {
+ errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;
+ }
+ else
+ {
+ errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;
+ }
+ }
+ }
+ else if(ch == '>')
+ {
+ if(!in_quote)
+ {
+ if(in_tag)
+ {
+ // errorstream << "Closing config tag on line " << linenumber << std::endl;
+ in_tag = false;
+
+ /*
+ * If this finds an <include> then ParseLine can simply call
+ * LoadConf() and load the included config into the same ConfigDataHash
+ */
+
+ if(!this->ParseLine(target, line, linenumber, errorstream))
+ return false;
+
+ line.clear();
+ }
+ else
+ {
+ errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
+{
+ return this->LoadConf(target, filename.c_str(), errorstream);
+}
+
+bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)
+{
+ std::string tagname;
+ std::string current_key;
+ std::string current_value;
+ KeyValList results;
+ bool got_name;
+ bool got_key;
+ bool in_quote;
+
+ got_name = got_key = in_quote = false;
+
+ //std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;
+
+ for(std::string::iterator c = line.begin(); c != line.end(); c++)
+ {
+ if(!got_name)
+ {
+ /* We don't know the tag name yet. */
+
+ if(*c != ' ')
+ {
+ if(*c != '<')
+ {
+ tagname += *c;
+ }
+ }
+ else
+ {
+ /* We got to a space, we should have the tagname now. */
+ if(tagname.length())
+ {
+ got_name = true;
+ }
+ }
+ }
+ else
+ {
+ /* We have the tag name */
+ if (!got_key)
+ {
+ /* We're still reading the key name */
+ if (*c != '=')
+ {
+ if (*c != ' ')
+ {
+ current_key += *c;
+ }
+ }
+ else
+ {
+ /* We got an '=', end of the key name. */
+ got_key = true;
+ }
+ }
+ else
+ {
+ /* We have the key name, now we're looking for quotes and the value */
+
+ /* Correctly handle escaped characters here.
+ * See the XXX'ed section above.
+ */
+ if ((*c == '\\') && (in_quote))
+ {
+ c++;
+ if (*c == 'n')
+ current_value += '\n';
+ else
+ current_value += *c;
+ continue;
+ }
+ else if ((*c == '\n') && (in_quote))
+ {
+ /* Got a 'real' \n, treat it as part of the value */
+ current_value += '\n';
+ continue;
+ }
+ else if ((*c == '\r') && (in_quote))
+ /* Got a \r, drop it */
+ continue;
+
+ if (*c == '"')
+ {
+ if (!in_quote)
+ {
+ /* We're not already in a quote. */
+ in_quote = true;
+ }
+ else
+ {
+ /* Leaving quotes, we have the value */
+ results.push_back(KeyVal(current_key, current_value));
+
+ // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
+
+ in_quote = false;
+ got_key = false;
+
+ if((tagname == "include") && (current_key == "file"))
+ {
+ if(!this->DoInclude(target, current_value, errorstream))
+ return false;
+ }
+
+ current_key.clear();
+ current_value.clear();
+ }
+ }
+ else
+ {
+ if(in_quote)
+ {
+ current_value += *c;
+ }
+ }
+ }
+ }
+ }
+
+ /* Finished parsing the tag, add it to the config hash */
+ target.insert(std::pair<std::string, KeyValList > (tagname, results));
+
+ return true;
+}
+
+bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
+{
+ std::string confpath;
+ std::string newfile;
+ std::string::size_type pos;
+
+ confpath = ServerInstance->ConfigFileName;
+ newfile = file;
+
+ for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)
+ {
+ if (*c == '\\')
+ {
+ *c = '/';
+ }
+ }
+
+ if (file[0] != '/')
+ {
+ if((pos = confpath.rfind("/")) != std::string::npos)
+ {
+ /* Leaves us with just the path */
+ newfile = confpath.substr(0, pos) + std::string("/") + newfile;
+ }
+ else
+ {
+ errorstream << "Couldn't get config path from: " << confpath << std::endl;
+ return false;
+ }
+ }
+
+ return LoadConf(target, newfile, errorstream);
+}
+
+bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)
+{
+ return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds);
+}
+
+bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds)
+{
+ std::string value;
+ bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds);
+ strlcpy(result, value.c_str(), length);
+ return r;
+}
+
+bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds)
+{
+ return ConfValue(target, tag, var, "", index, result, allow_linefeeds);
+}
+
+bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds)
+{
+ ConfigDataHash::size_type pos = index;
+ if((pos >= 0) && (pos < target.count(tag)))
+ {
+ ConfigDataHash::iterator iter = target.find(tag);
+
+ for(int i = 0; i < index; i++)
+ iter++;
+
+ for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++)
+ {
+ if(j->first == var)
+ {
+ if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos))
+ {
+ ServerInstance->Log(DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
+ for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++)
+ if (*n == '\n')
+ *n = ' ';
+ }
+ else
+ {
+ result = j->second;
+ return true;
+ }
+ }
+ }
+ if (!default_value.empty())
+ {
+ result = default_value;
+ return true;
+ }
+ }
+ else if(pos == 0)
+ {
+ if (!default_value.empty())
+ {
+ result = default_value;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
+{
+ return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result);
+}
+
+bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result)
+{
+ return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result);
+}
+
+bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
+{
+ return ConfValueInteger(target, tag, var, "", index, result);
+}
+
+bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)
+{
+ std::string value;
+ std::istringstream stream;
+ bool r = ConfValue(target, tag, var, default_value, index, value);
+ stream.str(value);
+ if(!(stream >> result))
+ return false;
+ else
+ {
+ if (!value.empty())
+ {
+ if (value.substr(0,2) == "0x")
+ {
+ char* endptr;
+
+ value.erase(0,2);
+ result = strtol(value.c_str(), &endptr, 16);
+
+ /* No digits found */
+ if (endptr == value.c_str())
+ return false;
+ }
+ else
+ {
+ char denominator = *(value.end() - 1);
+ switch (toupper(denominator))
+ {
+ case 'K':
+ /* Kilobytes -> bytes */
+ result = result * 1024;
+ break;
+ case 'M':
+ /* Megabytes -> bytes */
+ result = result * 1024 * 1024;
+ break;
+ case 'G':
+ /* Gigabytes -> bytes */
+ result = result * 1024 * 1024 * 1024;
+ break;
+ }
+ }
+ }
+ }
+ return r;
+}
+
+
+bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
+{
+ return ConfValueBool(target, std::string(tag), std::string(var), "", index);
+}
+
+bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index)
+{
+ return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index);
+}
+
+bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
+{
+ return ConfValueBool(target, tag, var, "", index);
+}
+
+bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index)
+{
+ std::string result;
+ if(!ConfValue(target, tag, var, default_value, index, result))
+ return false;
+
+ return ((result == "yes") || (result == "true") || (result == "1"));
+}
+
+int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
+{
+ return target.count(tag);
+}
+
+int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
+{
+ return target.count(tag);
+}
+
+int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
+{
+ return ConfVarEnum(target, std::string(tag), index);
+}
+
+int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
+{
+ ConfigDataHash::size_type pos = index;
+
+ if((pos >= 0) && (pos < target.count(tag)))
+ {
+ ConfigDataHash::const_iterator iter = target.find(tag);
+
+ for(int i = 0; i < index; i++)
+ iter++;
+
+ return iter->second.size();
+ }
+
+ return 0;
+}
+
+/** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
+ */
+bool ServerConfig::ReadFile(file_cache &F, const char* fname)
+{
+ if (!fname || !*fname)
+ return false;
+
+ FILE* file = NULL;
+ char linebuf[MAXBUF];
+
+ F.clear();
+
+ if ((*fname != '/') && (*fname != '\\'))
+ {
+ std::string::size_type pos;
+ std::string confpath = ServerInstance->ConfigFileName;
+ std::string newfile = fname;
+
+ if ((pos = confpath.rfind("/")) != std::string::npos)
+ newfile = confpath.substr(0, pos) + std::string("/") + fname;
+ else if ((pos = confpath.rfind("\\")) != std::string::npos)
+ newfile = confpath.substr(0, pos) + std::string("\\") + fname;
+
+ if (!FileExists(newfile.c_str()))
+ return false;
+ file = fopen(newfile.c_str(), "r");
+ }
+ else
+ {
+ if (!FileExists(fname))
+ return false;
+ file = fopen(fname, "r");
+ }
+
+ if (file)
+ {
+ while (!feof(file))
+ {
+ if (fgets(linebuf, sizeof(linebuf), file))
+ linebuf[strlen(linebuf)-1] = 0;
+ else
+ *linebuf = 0;
+
+ if (!feof(file))
+ {
+ F.push_back(*linebuf ? linebuf : " ");
+ }
+ }
+
+ fclose(file);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+bool ServerConfig::FileExists(const char* file)
+{
+ struct stat sb;
+ if (stat(file, &sb) == -1)
+ return false;
+
+ if ((sb.st_mode & S_IFDIR) > 0)
+ return false;
+
+ FILE *input;
+ if ((input = fopen (file, "r")) == NULL)
+ return false;
+ else
+ {
+ fclose(input);
+ return true;
+ }
+}
+
+char* ServerConfig::CleanFilename(char* name)
+{
+ char* p = name + strlen(name);
+ while ((p != name) && (*p != '/') && (*p != '\\')) p--;
+ return (p != name ? ++p : p);
+}
+
+
+bool ServerConfig::DirValid(const char* dirandfile)
+{
+#ifdef WINDOWS
+ return true;
+#endif
+
+ char work[1024];
+ char buffer[1024];
+ char otherdir[1024];
+ int p;
+
+ strlcpy(work, dirandfile, 1024);
+ p = strlen(work);
+
+ // we just want the dir
+ while (*work)
+ {
+ if (work[p] == '/')
+ {
+ work[p] = '\0';
+ break;
+ }
+
+ work[p--] = '\0';
+ }
+
+ // Get the current working directory
+ if (getcwd(buffer, 1024 ) == NULL )
+ return false;
+
+ if (chdir(work) == -1)
+ return false;
+
+ if (getcwd(otherdir, 1024 ) == NULL )
+ return false;
+
+ if (chdir(buffer) == -1)
+ return false;
+
+ size_t t = strlen(work);
+
+ if (strlen(otherdir) >= t)
+ {
+ otherdir[t] = '\0';
+ if (!strcmp(otherdir,work))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+std::string ServerConfig::GetFullProgDir()
+{
+ char buffer[PATH_MAX+1];
+#ifdef WINDOWS
+ /* Windows has specific api calls to get the exe path that never fail.
+ * For once, windows has something of use, compared to the POSIX code
+ * for this, this is positively neato.
+ */
+ if (GetModuleFileName(NULL, buffer, MAX_PATH))
+ {
+ std::string fullpath = buffer;
+ std::string::size_type n = fullpath.rfind("\\inspircd.exe");
+ return std::string(fullpath, 0, n);
+ }
+#else
+ // Get the current working directory
+ if (getcwd(buffer, PATH_MAX))
+ {
+ std::string remainder = this->argv[0];
+
+ /* Does argv[0] start with /? its a full path, use it */
+ if (remainder[0] == '/')
+ {
+ std::string::size_type n = remainder.rfind("/inspircd");
+ return std::string(remainder, 0, n);
+ }
+
+ std::string fullpath = std::string(buffer) + "/" + remainder;
+ std::string::size_type n = fullpath.rfind("/inspircd");
+ return std::string(fullpath, 0, n);
+ }
+#endif
+ return "/";
+}
+
+InspIRCd* ServerConfig::GetInstance()
+{
+ return ServerInstance;
+}
+
+
+ValueItem::ValueItem(int value)
+{
+ std::stringstream n;
+ n << value;
+ v = n.str();
+}
+
+ValueItem::ValueItem(bool value)
+{
+ std::stringstream n;
+ n << value;
+ v = n.str();
+}
+
+ValueItem::ValueItem(char* value)
+{
+ v = value;
+}
+
+void ValueItem::Set(char* value)
+{
+ v = value;
+}
+
+void ValueItem::Set(const char* value)
+{
+ v = value;
+}
+
+void ValueItem::Set(int value)
+{
+ std::stringstream n;
+ n << value;
+ v = n.str();
+}
+
+int ValueItem::GetInteger()
+{
+ if (v.empty())
+ return 0;
+ return atoi(v.c_str());
+}
+
+char* ValueItem::GetString()
+{
+ return (char*)v.c_str();
+}
+
+bool ValueItem::GetBool()
+{
+ return (GetInteger() || v == "yes" || v == "true");
+}
+
diff --git a/src/cull_list.cpp b/src/cull_list.cpp
index 049d2e6ba..ee257350e 100644
--- a/src/cull_list.cpp
+++ b/src/cull_list.cpp
@@ -1 +1,202 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "cull_list.h" CullItem::CullItem(userrec* u, std::string &r, const char* o_reason) { this->user = u; this->reason = r; this->silent = false; /* Seperate oper reason not set, use the user reason */ if (*o_reason) this->oper_reason = o_reason; else this->oper_reason = r; } CullItem::CullItem(userrec* u, const char* r, const char* o_reason) { this->user = u; this->reason = r; this->silent = false; /* Seperate oper reason not set, use the user reason */ if (*o_reason) this->oper_reason = o_reason; else this->oper_reason = r; } void CullItem::MakeSilent() { this->silent = true; } bool CullItem::IsSilent() { return this->silent; } CullItem::~CullItem() { } userrec* CullItem::GetUser() { return this->user; } std::string& CullItem::GetReason() { return this->reason; } std::string& CullItem::GetOperReason() { return this->oper_reason; } CullList::CullList(InspIRCd* Instance) : ServerInstance(Instance) { list.clear(); exempt.clear(); } void CullList::AddItem(userrec* user, std::string &reason, const char* o_reason) { AddItem(user, reason.c_str(), o_reason); } void CullList::AddItem(userrec* user, const char* reason, const char* o_reason) { if (exempt.find(user) == exempt.end()) { CullItem item(user, reason, o_reason); list.push_back(item); exempt[user] = user; } } void CullList::MakeSilent(userrec* user) { for (std::vector<CullItem>::iterator a = list.begin(); a != list.end(); ++a) { if (a->GetUser() == user) { a->MakeSilent(); break; } } return; } int CullList::Apply() { int n = list.size(); while (list.size()) { std::vector<CullItem>::iterator a = list.begin(); user_hash::iterator iter = ServerInstance->clientlist->find(a->GetUser()->nick); std::map<userrec*, userrec*>::iterator exemptiter = exempt.find(a->GetUser()); const char* preset_reason = a->GetUser()->GetOperQuit(); std::string reason = a->GetReason(); std::string oper_reason = *preset_reason ? preset_reason : a->GetOperReason(); if (reason.length() > MAXQUIT - 1) reason.resize(MAXQUIT - 1); if (oper_reason.length() > MAXQUIT - 1) oper_reason.resize(MAXQUIT - 1); if (a->GetUser()->registered != REG_ALL) if (ServerInstance->unregistered_count) ServerInstance->unregistered_count--; if (IS_LOCAL(a->GetUser())) { a->GetUser()->Write("ERROR :Closing link (%s@%s) [%s]", a->GetUser()->ident, a->GetUser()->host, oper_reason.c_str()); if ((!a->GetUser()->sendq.empty()) && (!(*a->GetUser()->GetWriteError()))) a->GetUser()->FlushWriteBuf(); } if (a->GetUser()->registered == REG_ALL) { FOREACH_MOD_I(ServerInstance,I_OnUserQuit,OnUserQuit(a->GetUser(), reason, oper_reason)); a->GetUser()->PurgeEmptyChannels(); a->GetUser()->WriteCommonQuit(reason, oper_reason); } FOREACH_MOD_I(ServerInstance,I_OnUserDisconnect,OnUserDisconnect(a->GetUser())); if (IS_LOCAL(a->GetUser())) { if (ServerInstance->Config->GetIOHook(a->GetUser()->GetPort())) { try { ServerInstance->Config->GetIOHook(a->GetUser()->GetPort())->OnRawSocketClose(a->GetUser()->GetFd()); } catch (CoreException& modexcept) { ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } } ServerInstance->SE->DelFd(a->GetUser()); a->GetUser()->CloseSocket(); } /* * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything * if they were an oper with +sn +qQ. */ if (a->GetUser()->registered == REG_ALL) { if (IS_LOCAL(a->GetUser())) { if (!a->IsSilent()) { ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s!%s@%s [%s]",a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str()); } } else { if ((!ServerInstance->SilentULine(a->GetUser()->server)) && (!a->IsSilent())) { ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s!%s@%s [%s]",a->GetUser()->server,a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str()); } } a->GetUser()->AddToWhoWas(); } if (iter != ServerInstance->clientlist->end()) { if (IS_LOCAL(a->GetUser())) { std::vector<userrec*>::iterator x = find(ServerInstance->local_users.begin(),ServerInstance->local_users.end(),a->GetUser()); if (x != ServerInstance->local_users.end()) ServerInstance->local_users.erase(x); } ServerInstance->clientlist->erase(iter); DELETE(a->GetUser()); } list.erase(list.begin()); exempt.erase(exemptiter); } return n; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "cull_list.h"
+
+CullItem::CullItem(userrec* u, std::string &r, const char* o_reason)
+{
+ this->user = u;
+ this->reason = r;
+ this->silent = false;
+ /* Seperate oper reason not set, use the user reason */
+ if (*o_reason)
+ this->oper_reason = o_reason;
+ else
+ this->oper_reason = r;
+}
+
+CullItem::CullItem(userrec* u, const char* r, const char* o_reason)
+{
+ this->user = u;
+ this->reason = r;
+ this->silent = false;
+ /* Seperate oper reason not set, use the user reason */
+ if (*o_reason)
+ this->oper_reason = o_reason;
+ else
+ this->oper_reason = r;
+}
+
+void CullItem::MakeSilent()
+{
+ this->silent = true;
+}
+
+bool CullItem::IsSilent()
+{
+ return this->silent;
+}
+
+CullItem::~CullItem()
+{
+}
+
+userrec* CullItem::GetUser()
+{
+ return this->user;
+}
+
+std::string& CullItem::GetReason()
+{
+ return this->reason;
+}
+
+std::string& CullItem::GetOperReason()
+{
+ return this->oper_reason;
+}
+
+CullList::CullList(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ list.clear();
+ exempt.clear();
+}
+
+void CullList::AddItem(userrec* user, std::string &reason, const char* o_reason)
+{
+ AddItem(user, reason.c_str(), o_reason);
+}
+
+
+void CullList::AddItem(userrec* user, const char* reason, const char* o_reason)
+{
+ if (exempt.find(user) == exempt.end())
+ {
+ CullItem item(user, reason, o_reason);
+ list.push_back(item);
+ exempt[user] = user;
+ }
+}
+
+void CullList::MakeSilent(userrec* user)
+{
+ for (std::vector<CullItem>::iterator a = list.begin(); a != list.end(); ++a)
+ {
+ if (a->GetUser() == user)
+ {
+ a->MakeSilent();
+ break;
+ }
+ }
+ return;
+}
+
+int CullList::Apply()
+{
+ int n = list.size();
+ while (list.size())
+ {
+ std::vector<CullItem>::iterator a = list.begin();
+
+ user_hash::iterator iter = ServerInstance->clientlist->find(a->GetUser()->nick);
+ std::map<userrec*, userrec*>::iterator exemptiter = exempt.find(a->GetUser());
+ const char* preset_reason = a->GetUser()->GetOperQuit();
+ std::string reason = a->GetReason();
+ std::string oper_reason = *preset_reason ? preset_reason : a->GetOperReason();
+
+ if (reason.length() > MAXQUIT - 1)
+ reason.resize(MAXQUIT - 1);
+ if (oper_reason.length() > MAXQUIT - 1)
+ oper_reason.resize(MAXQUIT - 1);
+
+ if (a->GetUser()->registered != REG_ALL)
+ if (ServerInstance->unregistered_count)
+ ServerInstance->unregistered_count--;
+
+ if (IS_LOCAL(a->GetUser()))
+ {
+ a->GetUser()->Write("ERROR :Closing link (%s@%s) [%s]", a->GetUser()->ident, a->GetUser()->host, oper_reason.c_str());
+ if ((!a->GetUser()->sendq.empty()) && (!(*a->GetUser()->GetWriteError())))
+ a->GetUser()->FlushWriteBuf();
+ }
+
+ if (a->GetUser()->registered == REG_ALL)
+ {
+ FOREACH_MOD_I(ServerInstance,I_OnUserQuit,OnUserQuit(a->GetUser(), reason, oper_reason));
+ a->GetUser()->PurgeEmptyChannels();
+ a->GetUser()->WriteCommonQuit(reason, oper_reason);
+ }
+
+ FOREACH_MOD_I(ServerInstance,I_OnUserDisconnect,OnUserDisconnect(a->GetUser()));
+
+ if (IS_LOCAL(a->GetUser()))
+ {
+ if (ServerInstance->Config->GetIOHook(a->GetUser()->GetPort()))
+ {
+ try
+ {
+ ServerInstance->Config->GetIOHook(a->GetUser()->GetPort())->OnRawSocketClose(a->GetUser()->GetFd());
+ }
+ catch (CoreException& modexcept)
+ {
+ ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+
+ ServerInstance->SE->DelFd(a->GetUser());
+ a->GetUser()->CloseSocket();
+ }
+
+ /*
+ * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
+ * if they were an oper with +sn +qQ.
+ */
+ if (a->GetUser()->registered == REG_ALL)
+ {
+ if (IS_LOCAL(a->GetUser()))
+ {
+ if (!a->IsSilent())
+ {
+ ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s!%s@%s [%s]",a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str());
+ }
+ }
+ else
+ {
+ if ((!ServerInstance->SilentULine(a->GetUser()->server)) && (!a->IsSilent()))
+ {
+ ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s!%s@%s [%s]",a->GetUser()->server,a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str());
+ }
+ }
+ a->GetUser()->AddToWhoWas();
+ }
+
+ if (iter != ServerInstance->clientlist->end())
+ {
+ if (IS_LOCAL(a->GetUser()))
+ {
+ std::vector<userrec*>::iterator x = find(ServerInstance->local_users.begin(),ServerInstance->local_users.end(),a->GetUser());
+ if (x != ServerInstance->local_users.end())
+ ServerInstance->local_users.erase(x);
+ }
+ ServerInstance->clientlist->erase(iter);
+ DELETE(a->GetUser());
+ }
+
+ list.erase(list.begin());
+ exempt.erase(exemptiter);
+ }
+ return n;
+}
+
diff --git a/src/dns.cpp b/src/dns.cpp
index 6e12662a7..fab9631b7 100644
--- a/src/dns.cpp
+++ b/src/dns.cpp
@@ -1 +1,1169 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* 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" #include "inspircd_se_config.h" #endif #include "dns.h" #include "inspircd.h" #include "socketengine.h" #include "configreader.h" #include "socket.h" using irc::sockets::insp_inaddr; using irc::sockets::insp_ntoa; using irc::sockets::insp_aton; using irc::sockets::OpenTCPSocket; /** 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(InspIRCd* Instance, DNS* dns, int id, const std::string &original); ~DNSRequest(); DNSInfo ResultIsReady(DNSHeader &h, int length); int SendRequests(const DNSHeader *header, const int length, QueryType qt); }; class CacheTimer : public InspTimer { private: InspIRCd* ServerInstance; DNS* dns; public: CacheTimer(InspIRCd* Instance, DNS* thisdns) : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { } virtual void Tick(time_t TIME) { dns->PruneCache(); } }; class RequestTimeout : public InspTimer { InspIRCd* ServerInstance; DNSRequest* watch; int watchid; public: RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id) { } void Tick(time_t TIME) { 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); return; } } }; /* Allocate the processing buffer */ DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns) { res = new unsigned char[512]; *res = 0; orig = original; RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id); Instance->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) { unsigned char payload[sizeof(DNSHeader)]; this->rr_class = 1; this->type = qt; DNS::EmptyHeader(payload,header,length); #ifdef IPV6 if (this->dnsobj->socketfamily == AF_INET6) { sockaddr_in6 addr; memset(&addr,0,sizeof(addr)); memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(DNS::QUERY_PORT); if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12) return -1; } else { sockaddr_in addr; memset(&addr,0,sizeof(addr)); memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr)); addr.sin_family = AF_INET; addr.sin_port = htons(DNS::QUERY_PORT); if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12) return -1; } #else sockaddr_in addr; memset(&addr,0,sizeof(addr)); memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr)); addr.sin_family = AF_INET; addr.sin_port = htons(DNS::QUERY_PORT); if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12) return -1; #endif 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 */ id = this->PRNG() & DNS::MAX_REQUEST_ID; /* If this id is already 'in flight', pick another. */ while (requests[id]) id = this->PRNG() & DNS::MAX_REQUEST_ID; DNSRequest* req = new DNSRequest(ServerInstance, 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() { ip6munge = false; int portpass = 0; if (this->GetFd() > -1) { if (ServerInstance && ServerInstance->SE) ServerInstance->SE->DelFd(this); shutdown(this->GetFd(), 2); close(this->GetFd()); this->SetFd(-1); /* Rehash the cache */ this->PruneCache(); } else { /* Create initial dns cache */ this->cache = new dnscache(); } if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer)) { ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled."); ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate"); ServerInstance->Log(DEFAULT," to a true IPv6 environment."); this->ip6munge = true; } this->socketfamily = AF_INET; #ifdef IPV6 if (strchr(ServerInstance->Config->DNSServer,':')) { this->socketfamily = AF_INET6; inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6); } else { inet_aton(ServerInstance->Config->DNSServer, &this->myserver4); portpass = -1; } #else inet_aton(ServerInstance->Config->DNSServer, &this->myserver4); #endif /* Initialize mastersocket */ int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM); this->SetFd(s); /* Have we got a socket and is it nonblocking? */ if (this->GetFd() != -1) { /* Bind the port - port 0 INADDR_ANY */ if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false)) { /* Failed to bind */ shutdown(this->GetFd(),2); close(this->GetFd()); this->SetFd(-1); } if (this->GetFd() >= 0) { /* Hook the descriptor into the socket engine */ if (ServerInstance && ServerInstance->SE) { if (!ServerInstance->SE->AddFd(this)) { ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve."); shutdown(this->GetFd(),2); close(this->GetFd()); this->SetFd(-1); } } } } } /** Initialise the DNS UDP socket so that we can send requests */ DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance) { /* Clear the Resolver class table */ memset(Classes,0,sizeof(Classes)); /* Clear the requests class table */ memset(requests,0,sizeof(requests)); /* Set the id of the next request to 0 */ currid = 0; /* 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(ServerInstance, 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::GetName(const insp_inaddr *ip) { char query[128]; DNSHeader h; int id; int length; #ifdef IPV6 unsigned char* c = (unsigned char*)&ip->s6_addr; if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 && c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 && c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF) sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]); else DNS::MakeIP6Int(query, (in6_addr*)ip); #else unsigned char* c = (unsigned char*)&ip->s_addr; sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]); #endif if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1) return -1; DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip)); if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -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; #ifdef SUPPORT_IP6LINKS if (fp == PROTOCOL_IPV6) { in6_addr i; if (inet_pton(AF_INET6, ip, &i) > 0) { DNS::MakeIP6Int(query, &i); } else /* Invalid IP address */ return -1; } else #endif { 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 /* Invalid IP address */ return -1; } if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1) return -1; DNSRequest* req = this->AddQuery(&h, id, ip); if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)) return -1; return id; } /** Build an ipv6 reverse domain from an in6_addr */ void DNS::MakeIP6Int(char* query, const in6_addr *ip) { #ifdef SUPPORT_IP6LINKS 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 */ #else *query = 0; #endif } /** 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)]; sockaddr* from = new sockaddr[2]; #ifdef IPV6 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); #else socklen_t x = sizeof(sockaddr_in); #endif const char* ipaddr_from; unsigned short int port_from = 0; int length = _recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x); /* Did we get the whole header? */ if (length < 12) { /* Nope - something screwed up. */ delete[] from; 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 client, which is possible without this check. * * -- Thanks jilles for pointing this one out. */ #ifdef IPV6 char nbuf[MAXBUF]; if (this->socketfamily == AF_INET6) { ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf)); port_from = ntohs(((sockaddr_in6*)from)->sin6_port); } else #endif { ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr); port_from = ntohs(((sockaddr_in*)from)->sin_port); } delete[] from; /* We cant perform this security check if you're using 4in6. * Tough luck to you, choose one or't other! */ if (!ip6munge) { if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer))) { 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... */ 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: { snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x", (ntohs(data.first[0]) + ntohs(data.first[1] << 8)), (ntohs(data.first[2]) + ntohs(data.first[3] << 8)), (ntohs(data.first[4]) + ntohs(data.first[5] << 8)), (ntohs(data.first[6]) + ntohs(data.first[7] << 8)), (ntohs(data.first[8]) + ntohs(data.first[9] << 8)), (ntohs(data.first[10]) + ntohs(data.first[11] << 8)), (ntohs(data.first[12]) + ntohs(data.first[13] << 8)), (ntohs(data.first[14]) + ntohs(data.first[15] << 8))); char* c = strstr(formatted,":0:"); if (c != NULL) { memmove(c+1,c+2,strlen(c+2) + 1); c += 2; while (memcmp(c,"0:",2) == 0) memmove(c,c+2,strlen(c+2) + 1); if (memcmp(c,"0",2) == 0) *c = 0; if (memcmp(formatted,"0::",3) == 0) memmove(formatted,formatted + 1, strlen(formatted + 1) + 1); } 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); break; default: break; } /* Build the reply with the id and hostname/ip in it */ std::string ro = req->orig; delete req; return DNSResult(this_id,resultstr,ttl,ro); } } /** A result is ready, process it */ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length) { int i = 0; int q = 0; int curanswer, o; 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. */ 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 (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; 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 valid 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) { case DNS_QUERY_CNAME: /* CNAME and PTR have the same processing code */ case DNS_QUERY_PTR: o = 0; q = 0; while (q == 0 && i < length && o + 256 < 1023) { if (header.payload[i] > 63) { memcpy(&ptr,&header.payload[i],2); i = ntohs(ptr) - 0xC000 - 12; } else { if (header.payload[i] == 0) { q = 1; } else { res[o] = 0; if (o != 0) res[o++] = '.'; 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: memcpy(res,&header.payload[i],rr.rdlength); res[rr.rdlength] = 0; break; case DNS_QUERY_A: memcpy(res,&header.payload[i],rr.rdlength); res[rr.rdlength] = 0; break; default: memcpy(res,&header.payload[i],rr.rdlength); res[rr.rdlength] = 0; break; } return std::make_pair(res,"No error");; } /** Close the master socket */ DNS::~DNS() { shutdown(this->GetFd(), 2); close(this->GetFd()); ServerInstance->Timers->DelTimer(this->PruneTimer); delete this->PruneTimer; } 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(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt) { cached = false; CQ = ServerInstance->Res->GetCache(source); if (CQ) { time_left = CQ->CalcTTLRemaining(); if (!time_left) { ServerInstance->Res->DelCache(source); } else { cached = true; return; } } insp_inaddr binip; switch (querytype) { case DNS_QUERY_A: this->myid = ServerInstance->Res->GetIP(source.c_str()); break; case DNS_QUERY_PTR: if (insp_aton(source.c_str(), &binip) > 0) { /* Valid ip address */ this->myid = ServerInstance->Res->GetName(&binip); } else { this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup"); throw ModuleException("Resolver: Bad IP address"); return; } 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: this->myid = -1; break; } if (this->myid == -1) { this->OnError(RESOLVER_NSDOWN, "Nameserver is down"); throw ModuleException("Resolver: Couldnt get an id to make a request"); /* We shouldnt get here really */ return; } } /** Called when an error occurs */ void Resolver::OnError(ResolverError e, const std::string &errormessage) { /* 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 et, int errornum) { /* Fetch the id and result of the next available packet */ DNSResult res = this->GetResult(); /* 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; } } 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())) this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, 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) { /* 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; } else /* Duplicate id */ return false; } else { /* Pointer or id not valid. * Free the item and return */ if (r) 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(RESLOVER_FORCEUNLOAD, "Parent module is unloading"); delete Classes[i]; Classes[i] = NULL; } } } } /** Generate pseudo-random number */ unsigned long DNS::PRNG() { #ifndef WIN32 unsigned long val = 0; timeval n; serverstats* s = ServerInstance->stats; gettimeofday(&n,NULL); val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec; val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad; val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size(); return val; #else unsigned long val = 0; serverstats* s = ServerInstance->stats; val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL); val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad; val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv); return val; #endif } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/*
+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"
+#include "inspircd_se_config.h"
+#endif
+
+#include "dns.h"
+#include "inspircd.h"
+#include "socketengine.h"
+#include "configreader.h"
+#include "socket.h"
+
+using irc::sockets::insp_inaddr;
+using irc::sockets::insp_ntoa;
+using irc::sockets::insp_aton;
+using irc::sockets::OpenTCPSocket;
+
+/** 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(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
+ ~DNSRequest();
+ DNSInfo ResultIsReady(DNSHeader &h, int length);
+ int SendRequests(const DNSHeader *header, const int length, QueryType qt);
+};
+
+class CacheTimer : public InspTimer
+{
+ private:
+ InspIRCd* ServerInstance;
+ DNS* dns;
+ public:
+ CacheTimer(InspIRCd* Instance, DNS* thisdns)
+ : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
+
+ virtual void Tick(time_t TIME)
+ {
+ dns->PruneCache();
+ }
+};
+
+class RequestTimeout : public InspTimer
+{
+ InspIRCd* ServerInstance;
+ DNSRequest* watch;
+ int watchid;
+ public:
+ RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
+ {
+ }
+
+ void Tick(time_t TIME)
+ {
+ 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);
+ return;
+ }
+ }
+};
+
+/* Allocate the processing buffer */
+DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
+{
+ res = new unsigned char[512];
+ *res = 0;
+ orig = original;
+ RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
+ Instance->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)
+{
+ unsigned char payload[sizeof(DNSHeader)];
+
+ this->rr_class = 1;
+ this->type = qt;
+
+ DNS::EmptyHeader(payload,header,length);
+
+#ifdef IPV6
+ if (this->dnsobj->socketfamily == AF_INET6)
+ {
+ sockaddr_in6 addr;
+ memset(&addr,0,sizeof(addr));
+ memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(DNS::QUERY_PORT);
+ if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
+ return -1;
+ }
+ else
+ {
+ sockaddr_in addr;
+ memset(&addr,0,sizeof(addr));
+ memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(DNS::QUERY_PORT);
+ if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
+ return -1;
+ }
+#else
+ sockaddr_in addr;
+ memset(&addr,0,sizeof(addr));
+ memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(DNS::QUERY_PORT);
+ if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
+ return -1;
+#endif
+
+ 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 */
+ id = this->PRNG() & DNS::MAX_REQUEST_ID;
+
+ /* If this id is already 'in flight', pick another. */
+ while (requests[id])
+ id = this->PRNG() & DNS::MAX_REQUEST_ID;
+
+ DNSRequest* req = new DNSRequest(ServerInstance, 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()
+{
+ ip6munge = false;
+ int portpass = 0;
+
+ if (this->GetFd() > -1)
+ {
+ if (ServerInstance && ServerInstance->SE)
+ ServerInstance->SE->DelFd(this);
+ shutdown(this->GetFd(), 2);
+ close(this->GetFd());
+ this->SetFd(-1);
+
+ /* Rehash the cache */
+ this->PruneCache();
+ }
+ else
+ {
+ /* Create initial dns cache */
+ this->cache = new dnscache();
+ }
+
+ if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
+ {
+ ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
+ ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
+ ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
+ this->ip6munge = true;
+ }
+
+ this->socketfamily = AF_INET;
+#ifdef IPV6
+ if (strchr(ServerInstance->Config->DNSServer,':'))
+ {
+ this->socketfamily = AF_INET6;
+ inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
+ }
+ else
+ {
+ inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
+ portpass = -1;
+ }
+#else
+ inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
+#endif
+
+ /* Initialize mastersocket */
+ int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
+ this->SetFd(s);
+
+ /* Have we got a socket and is it nonblocking? */
+ if (this->GetFd() != -1)
+ {
+ /* Bind the port - port 0 INADDR_ANY */
+ if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
+ {
+ /* Failed to bind */
+ shutdown(this->GetFd(),2);
+ close(this->GetFd());
+ this->SetFd(-1);
+ }
+
+ if (this->GetFd() >= 0)
+ {
+ /* Hook the descriptor into the socket engine */
+ if (ServerInstance && ServerInstance->SE)
+ {
+ if (!ServerInstance->SE->AddFd(this))
+ {
+ ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
+ shutdown(this->GetFd(),2);
+ close(this->GetFd());
+ this->SetFd(-1);
+ }
+ }
+ }
+ }
+}
+
+/** Initialise the DNS UDP socket so that we can send requests */
+DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ /* Clear the Resolver class table */
+ memset(Classes,0,sizeof(Classes));
+
+ /* Clear the requests class table */
+ memset(requests,0,sizeof(requests));
+
+ /* Set the id of the next request to 0
+ */
+ currid = 0;
+
+ /* 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(ServerInstance, 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::GetName(const insp_inaddr *ip)
+{
+ char query[128];
+ DNSHeader h;
+ int id;
+ int length;
+
+#ifdef IPV6
+ unsigned char* c = (unsigned char*)&ip->s6_addr;
+ if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
+ c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
+ c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
+ sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
+ else
+ DNS::MakeIP6Int(query, (in6_addr*)ip);
+#else
+ unsigned char* c = (unsigned char*)&ip->s_addr;
+ sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
+#endif
+
+ if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
+ return -1;
+
+ DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
+
+ if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -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;
+#ifdef SUPPORT_IP6LINKS
+ if (fp == PROTOCOL_IPV6)
+ {
+ in6_addr i;
+ if (inet_pton(AF_INET6, ip, &i) > 0)
+ {
+ DNS::MakeIP6Int(query, &i);
+ }
+ else
+ /* Invalid IP address */
+ return -1;
+ }
+ else
+#endif
+ {
+ 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
+ /* Invalid IP address */
+ return -1;
+ }
+
+ if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
+ return -1;
+
+ DNSRequest* req = this->AddQuery(&h, id, ip);
+
+ if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
+ return -1;
+
+ return id;
+}
+
+/** Build an ipv6 reverse domain from an in6_addr
+ */
+void DNS::MakeIP6Int(char* query, const in6_addr *ip)
+{
+#ifdef SUPPORT_IP6LINKS
+ 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 */
+#else
+ *query = 0;
+#endif
+}
+
+/** 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)];
+ sockaddr* from = new sockaddr[2];
+#ifdef IPV6
+ socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
+#else
+ socklen_t x = sizeof(sockaddr_in);
+#endif
+ const char* ipaddr_from;
+ unsigned short int port_from = 0;
+ int length = _recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x);
+
+ /* Did we get the whole header? */
+ if (length < 12)
+ {
+ /* Nope - something screwed up. */
+ delete[] from;
+ 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 client, which is possible without this check.
+ *
+ * -- Thanks jilles for pointing this one out.
+ */
+#ifdef IPV6
+ char nbuf[MAXBUF];
+ if (this->socketfamily == AF_INET6)
+ {
+ ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
+ port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
+ }
+ else
+#endif
+ {
+ ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
+ port_from = ntohs(((sockaddr_in*)from)->sin_port);
+ }
+
+ delete[] from;
+
+ /* We cant perform this security check if you're using 4in6.
+ * Tough luck to you, choose one or't other!
+ */
+ if (!ip6munge)
+ {
+ if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
+ {
+ 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... */
+ 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:
+ {
+ snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
+ (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
+ (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
+ (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
+ (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
+ (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
+ (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
+ (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
+ (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
+ char* c = strstr(formatted,":0:");
+ if (c != NULL)
+ {
+ memmove(c+1,c+2,strlen(c+2) + 1);
+ c += 2;
+ while (memcmp(c,"0:",2) == 0)
+ memmove(c,c+2,strlen(c+2) + 1);
+ if (memcmp(c,"0",2) == 0)
+ *c = 0;
+ if (memcmp(formatted,"0::",3) == 0)
+ memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
+ }
+ 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);
+ break;
+
+ default:
+ break;
+
+ }
+
+ /* Build the reply with the id and hostname/ip in it */
+ std::string ro = req->orig;
+ delete req;
+ return DNSResult(this_id,resultstr,ttl,ro);
+ }
+}
+
+/** A result is ready, process it */
+DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
+{
+ int i = 0;
+ int q = 0;
+ int curanswer, o;
+ 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. */
+
+ 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 (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;
+ 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 valid 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)
+ {
+ case DNS_QUERY_CNAME:
+ /* CNAME and PTR have the same processing code */
+ case DNS_QUERY_PTR:
+ o = 0;
+ q = 0;
+ while (q == 0 && i < length && o + 256 < 1023)
+ {
+ if (header.payload[i] > 63)
+ {
+ memcpy(&ptr,&header.payload[i],2);
+ i = ntohs(ptr) - 0xC000 - 12;
+ }
+ else
+ {
+ if (header.payload[i] == 0)
+ {
+ q = 1;
+ }
+ else
+ {
+ res[o] = 0;
+ if (o != 0)
+ res[o++] = '.';
+ 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:
+ memcpy(res,&header.payload[i],rr.rdlength);
+ res[rr.rdlength] = 0;
+ break;
+ case DNS_QUERY_A:
+ memcpy(res,&header.payload[i],rr.rdlength);
+ res[rr.rdlength] = 0;
+ break;
+ default:
+ memcpy(res,&header.payload[i],rr.rdlength);
+ res[rr.rdlength] = 0;
+ break;
+ }
+ return std::make_pair(res,"No error");;
+}
+
+/** Close the master socket */
+DNS::~DNS()
+{
+ shutdown(this->GetFd(), 2);
+ close(this->GetFd());
+ ServerInstance->Timers->DelTimer(this->PruneTimer);
+ delete this->PruneTimer;
+}
+
+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(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
+{
+ cached = false;
+
+ CQ = ServerInstance->Res->GetCache(source);
+ if (CQ)
+ {
+ time_left = CQ->CalcTTLRemaining();
+ if (!time_left)
+ {
+ ServerInstance->Res->DelCache(source);
+ }
+ else
+ {
+ cached = true;
+ return;
+ }
+ }
+
+ insp_inaddr binip;
+
+ switch (querytype)
+ {
+ case DNS_QUERY_A:
+ this->myid = ServerInstance->Res->GetIP(source.c_str());
+ break;
+
+ case DNS_QUERY_PTR:
+ if (insp_aton(source.c_str(), &binip) > 0)
+ {
+ /* Valid ip address */
+ this->myid = ServerInstance->Res->GetName(&binip);
+ }
+ else
+ {
+ this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
+ throw ModuleException("Resolver: Bad IP address");
+ return;
+ }
+ 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:
+ this->myid = -1;
+ break;
+ }
+ if (this->myid == -1)
+ {
+ this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
+ throw ModuleException("Resolver: Couldnt get an id to make a request");
+ /* We shouldnt get here really */
+ return;
+ }
+}
+
+/** Called when an error occurs */
+void Resolver::OnError(ResolverError e, const std::string &errormessage)
+{
+ /* 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 et, int errornum)
+{
+ /* Fetch the id and result of the next available packet */
+ DNSResult res = this->GetResult();
+ /* 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;
+ }
+ }
+ 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()))
+ this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, 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)
+{
+ /* 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;
+ }
+ else
+ /* Duplicate id */
+ return false;
+ }
+ else
+ {
+ /* Pointer or id not valid.
+ * Free the item and return
+ */
+ if (r)
+ 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(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
+ delete Classes[i];
+ Classes[i] = NULL;
+ }
+ }
+ }
+}
+
+/** Generate pseudo-random number */
+unsigned long DNS::PRNG()
+{
+#ifndef WIN32
+ unsigned long val = 0;
+ timeval n;
+ serverstats* s = ServerInstance->stats;
+ gettimeofday(&n,NULL);
+ val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
+ val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
+ val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
+ return val;
+#else
+ unsigned long val = 0;
+ serverstats* s = ServerInstance->stats;
+ val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL);
+ val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
+ val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv);
+ return val;
+#endif
+}
+
diff --git a/src/dynamic.cpp b/src/dynamic.cpp
index 5d7759b47..179113cae 100644
--- a/src/dynamic.cpp
+++ b/src/dynamic.cpp
@@ -1 +1,89 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "dynamic.h" #ifndef WIN32 #include <dlfcn.h> #endif DLLManager::DLLManager(InspIRCd* ServerInstance, const char *fname) { err = NULL; if (!strstr(fname,".so")) { err = "This doesn't look like a module file to me..."; return; } h = dlopen(fname, RTLD_NOW|RTLD_LOCAL); if (!h) { err = (char*)dlerror(); return; } } DLLManager::~DLLManager() { // close the library if it isn't null if (h) dlclose(h); } bool DLLManager::GetSymbol(void** v, const char* sym_name) { // try extract a symbol from the library // get any error message is there is any if (h) { dlerror(); // clear value *v = dlsym(h, sym_name); err = (char*)dlerror(); if (!*v || err) return false; } if (err) { return false; } else { return true; } } DLLFactoryBase::DLLFactoryBase(InspIRCd* Instance, const char* fname, const char* symbol) : DLLManager(Instance, fname) { // try get the factory function if there is no error yet factory_func = 0; if (!LastError()) { if (!GetSymbol( (void **)&factory_func, symbol ? symbol : "init_module")) { throw ModuleException("Missing init_module() entrypoint!"); } } } DLLFactoryBase::~DLLFactoryBase() { } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "dynamic.h"
+#ifndef WIN32
+#include <dlfcn.h>
+#endif
+
+DLLManager::DLLManager(InspIRCd* ServerInstance, const char *fname)
+{
+ err = NULL;
+
+ if (!strstr(fname,".so"))
+ {
+ err = "This doesn't look like a module file to me...";
+ return;
+ }
+
+ h = dlopen(fname, RTLD_NOW|RTLD_LOCAL);
+ if (!h)
+ {
+ err = (char*)dlerror();
+ return;
+ }
+}
+
+DLLManager::~DLLManager()
+{
+ // close the library if it isn't null
+ if (h)
+ dlclose(h);
+}
+
+
+
+bool DLLManager::GetSymbol(void** v, const char* sym_name)
+{
+ // try extract a symbol from the library
+ // get any error message is there is any
+
+ if (h)
+ {
+ dlerror(); // clear value
+ *v = dlsym(h, sym_name);
+ err = (char*)dlerror();
+ if (!*v || err)
+ return false;
+ }
+
+ if (err)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+DLLFactoryBase::DLLFactoryBase(InspIRCd* Instance, const char* fname, const char* symbol) : DLLManager(Instance, fname)
+{
+ // try get the factory function if there is no error yet
+ factory_func = 0;
+
+ if (!LastError())
+ {
+ if (!GetSymbol( (void **)&factory_func, symbol ? symbol : "init_module"))
+ {
+ throw ModuleException("Missing init_module() entrypoint!");
+ }
+ }
+}
+
+DLLFactoryBase::~DLLFactoryBase()
+{
+}
+
diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp
index e5a6acffe..caf93ec1b 100644
--- a/src/hashcomp.cpp
+++ b/src/hashcomp.cpp
@@ -1 +1,619 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "hashcomp.h" #ifndef WIN32 #include <ext/hash_map> #define nspace __gnu_cxx #else #include <hash_map> #define nspace stdext using stdext::hash_map; #endif /****************************************************** * * The hash functions of InspIRCd are the centrepoint * of the entire system. If these functions are * inefficient or wasteful, the whole program suffers * as a result. A lot of C programmers in the ircd * 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 * 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 * overloaded comparison and hash value operators which * cause it to act in an irc-like way. The features we * add to the standard hash_map are: * * Case insensitivity: The hash_map will be case * insensitive. * * Scandanavian Comparisons: The characters [, ], \ will * be considered the lowercase of {, } and |. * ******************************************************/ using namespace irc::sockets; /* convert a string to lowercase. Note following special circumstances * taken from RFC 1459. Many "official" server branches still hold to this * rule so i will too; * * Because of IRC's scandanavian origin, the characters {}| are * considered to be the lower case equivalents of the characters []\, * respectively. This is a critical issue when determining the * equivalence of two nicknames. */ void nspace::strlower(char *n) { if (n) { for (char* t = n; *t; t++) *t = lowermap[(unsigned char)*t]; } } #ifndef WIN32 size_t nspace::hash<string>::operator()(const string &s) const #else size_t nspace::hash_compare<string, std::less<string> >::operator()(const 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 lowermap[*x]. * This avoids a copy to use hash<const char*> */ register 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 + lowermap[(unsigned char)*x]; return t; } #ifndef WIN32 size_t nspace::hash<irc::string>::operator()(const irc::string &s) const #else size_t nspace::hash_compare<irc::string, std::less<irc::string> >::operator()(const irc::string &s) const #endif { register size_t t = 0; for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */ t = 5 * t + lowermap[(unsigned char)*x]; return t; } bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const { unsigned char* n1 = (unsigned char*)s1.c_str(); unsigned char* n2 = (unsigned char*)s2.c_str(); for (; *n1 && *n2; n1++, n2++) if (lowermap[*n1] != lowermap[*n2]) return false; return (lowermap[*n1] == lowermap[*n2]); } /****************************************************** * * This is the implementation of our special irc::string * class which is a case-insensitive equivalent to * std::string which is not only case-insensitive but * can also do scandanavian comparisons, e.g. { = [, etc. * * This class depends on the const array 'lowermap'. * ******************************************************/ bool irc::irc_char_traits::eq(char c1st, char c2nd) { return lowermap[(unsigned char)c1st] == lowermap[(unsigned char)c2nd]; } bool irc::irc_char_traits::ne(char c1st, char c2nd) { return lowermap[(unsigned char)c1st] != lowermap[(unsigned char)c2nd]; } bool irc::irc_char_traits::lt(char c1st, char c2nd) { return lowermap[(unsigned char)c1st] < lowermap[(unsigned char)c2nd]; } int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n) { for(unsigned int i = 0; i < n; i++) { if(lowermap[(unsigned char)*str1] > lowermap[(unsigned char)*str2]) return 1; if(lowermap[(unsigned char)*str1] < lowermap[(unsigned char)*str2]) return -1; if(*str1 == 0 || *str2 == 0) return 0; str1++; str2++; } return 0; } const char* irc::irc_char_traits::find(const char* s1, int n, char c) { while(n-- > 0 && lowermap[(unsigned char)*s1] != lowermap[(unsigned char)c]) s1++; return s1; } irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false) { /* Record starting position and current position */ last_starting_position = tokens.begin(); n = tokens.begin(); } irc::tokenstream::~tokenstream() { } bool irc::tokenstream::GetToken(std::string &token) { 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; } last_pushed = false; if ((*n == ' ') || (n+1 == tokens.end())) { /* If we find a space, or end of string, this is the end of a token. */ last_starting_position = n+1; last_pushed = true; std::string strip(lsp, n+1 == tokens.end() ? n+1 : n++); while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1)) strip.erase(strip.end() - 1); token = strip; return !token.empty(); } n++; } token.clear(); return false; } bool irc::tokenstream::GetToken(irc::string &token) { std::string stdstring; bool returnval = GetToken(stdstring); token = assign(stdstring); return returnval; } bool irc::tokenstream::GetToken(int &token) { std::string tok; bool returnval = GetToken(tok); token = ConvToInt(tok); return returnval; } bool irc::tokenstream::GetToken(long &token) { std::string tok; bool returnval = GetToken(tok); token = ConvToInt(tok); return returnval; } irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator) { last_starting_position = tokens.begin(); n = tokens.begin(); } const std::string irc::sepstream::GetToken() { std::string::iterator lsp = last_starting_position; while (n != tokens.end()) { if ((*n == sep) || (n+1 == tokens.end())) { last_starting_position = n+1; std::string strip = std::string(lsp, n+1 == tokens.end() ? n+1 : n++); while ((strip.length()) && (strip.find_last_of(sep) == strip.length() - 1)) strip.erase(strip.end() - 1); return strip; } n++; } return ""; } 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::deque<std::string> &result, int max_line_size) { if (sequence.empty()) { result.clear(); return 0; } int n = 0; int size = 1; /* Account for initial +/- char */ int nextsize = 0; result.clear(); result.push_back(adding ? "+" : "-"); if (sequence.size() > 1) nextsize = sequence[1].length() + 2; while (!sequence[0].empty() && (sequence.size() > 1) && (result.size() < MAXMODES) && ((size + nextsize) < max_line_size)) { result[0] += *(sequence[0].begin()); if (!sequence[1].empty()) { result.push_back(sequence[1]); size += nextsize; /* Account for mode character and whitespace */ } sequence[0].erase(sequence[0].begin()); sequence.erase(sequence.begin() + 1); if (sequence.size() > 1) nextsize = sequence[1].length() + 2; n++; } return n; } irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end) { for (int v = begin; v < end; v++) joined.append(sequence[v]).append(seperator); joined.append(sequence[end]); } irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end) { for (int v = begin; v < end; v++) joined.append(sequence[v]).append(seperator); joined.append(sequence[end]); } irc::stringjoiner::stringjoiner(const std::string &seperator, const char** sequence, int begin, int end) { for (int v = begin; v < end; v++) joined.append(sequence[v]).append(seperator); joined.append(sequence[end]); } std::string& irc::stringjoiner::GetJoined() { return joined; } irc::portparser::portparser(const std::string &source, bool allow_overlapped) : in_range(0), range_begin(0), range_end(0), overlapped(allow_overlapped) { sep = new irc::commasepstream(source); overlap_set.clear(); } irc::portparser::~portparser() { delete sep; } bool irc::portparser::Overlaps(long val) { if (!overlapped) return false; if (overlap_set.find(val) == overlap_set.end()) { overlap_set[val] = true; return false; } else return true; } long irc::portparser::GetToken() { if (in_range > 0) { in_range++; if (in_range <= range_end) { if (!Overlaps(in_range)) { return in_range; } else { while (((Overlaps(in_range)) && (in_range <= range_end))) in_range++; if (in_range <= range_end) return in_range; } } else in_range = 0; } std::string x = sep->GetToken(); if (x.empty()) return 0; while (Overlaps(atoi(x.c_str()))) { x = sep->GetToken(); if (x.empty()) return 0; } 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()); range_begin = atoi(sbegin.c_str()); range_end = atoi(send.c_str()); if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end)) { in_range = range_begin; return in_range; } else { /* Assume its just the one port */ return atoi(sbegin.c_str()); } } else { return atoi(x.c_str()); } } irc::dynamicbitmask::dynamicbitmask() : bits_size(4) { /* We start with 4 bytes allocated which is room * for 4 items. Something makes me doubt its worth * allocating less than 4 bytes. */ bits = new unsigned char[bits_size]; memset(bits, 0, bits_size); } irc::dynamicbitmask::~dynamicbitmask() { /* Tidy up the entire used memory on delete */ delete[] bits; } irc::bitfield irc::dynamicbitmask::Allocate() { /* Yeah, this isnt too efficient, however a module or the core * should only be allocating bitfields on load, the Toggle and * Get methods are O(1) as these are called much more often. */ unsigned char* freebits = this->GetFreeBits(); for (unsigned char i = 0; i < bits_size; i++) { /* Yes, this is right. You'll notice we terminate the loop when !current_pos, * this is because we logic shift our bit off the end of unsigned char, and its * lost, making the loop counter 0 when we're done. */ for (unsigned char current_pos = 1; current_pos; current_pos = current_pos << 1) { if (!(freebits[i] & current_pos)) { freebits[i] |= current_pos; return std::make_pair(i, current_pos); } } } /* We dont have any free space left, increase by one */ if (bits_size == 255) /* Oh dear, cant grow it any further */ throw std::bad_alloc(); unsigned char old_bits_size = bits_size; bits_size++; /* Allocate new bitfield space */ unsigned char* temp_bits = new unsigned char[bits_size]; unsigned char* temp_freebits = new unsigned char[bits_size]; /* Copy the old data in */ memcpy(temp_bits, bits, old_bits_size); memcpy(temp_freebits, freebits, old_bits_size); /* Delete the old data pointers */ delete[] bits; delete[] freebits; /* Swap the pointers over so now the new * pointers point to our member values */ bits = temp_bits; freebits = temp_freebits; this->SetFreeBits(freebits); /* Initialize the new byte on the end of * the bitfields, pre-allocate the one bit * for this allocation */ bits[old_bits_size] = 0; freebits[old_bits_size] = 1; /* We already know where we just allocated * the bitfield, so no loop needed */ return std::make_pair(old_bits_size, 1); } bool irc::dynamicbitmask::Deallocate(irc::bitfield &pos) { /* We dont bother to shrink the bitfield * on deallocation, the most we could do * is save one byte (!) and this would cost * us a loop (ugly O(n) stuff) so we just * clear the bit and leave the memory * claimed -- nobody will care about one * byte. */ if (pos.first < bits_size) { this->GetFreeBits()[pos.first] &= ~pos.second; return true; } /* They gave a bitfield outside of the * length of our array. BAD programmer. */ return false; } void irc::dynamicbitmask::Toggle(irc::bitfield &pos, bool state) { /* Range check the value */ if (pos.first < bits_size) { if (state) /* Set state, OR the state in */ bits[pos.first] |= pos.second; else /* Clear state, AND the !state out */ bits[pos.first] &= ~pos.second; } } bool irc::dynamicbitmask::Get(irc::bitfield &pos) { /* Range check the value */ if (pos.first < bits_size) return (bits[pos.first] & pos.second); else /* We can't return false, otherwise we can't * distinguish between failure and a cleared bit! * Our only sensible choice is to throw (ew). */ throw ModuleException("irc::dynamicbitmask::Get(): Invalid bitfield, out of range"); } unsigned char irc::dynamicbitmask::GetSize() { return bits_size; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "hashcomp.h"
+#ifndef WIN32
+#include <ext/hash_map>
+#define nspace __gnu_cxx
+#else
+#include <hash_map>
+#define nspace stdext
+using stdext::hash_map;
+#endif
+
+/******************************************************
+ *
+ * The hash functions of InspIRCd are the centrepoint
+ * of the entire system. If these functions are
+ * inefficient or wasteful, the whole program suffers
+ * as a result. A lot of C programmers in the ircd
+ * 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
+ * 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
+ * overloaded comparison and hash value operators which
+ * cause it to act in an irc-like way. The features we
+ * add to the standard hash_map are:
+ *
+ * Case insensitivity: The hash_map will be case
+ * insensitive.
+ *
+ * Scandanavian Comparisons: The characters [, ], \ will
+ * be considered the lowercase of {, } and |.
+ *
+ ******************************************************/
+
+using namespace irc::sockets;
+
+/* convert a string to lowercase. Note following special circumstances
+ * taken from RFC 1459. Many "official" server branches still hold to this
+ * rule so i will too;
+ *
+ * Because of IRC's scandanavian origin, the characters {}| are
+ * considered to be the lower case equivalents of the characters []\,
+ * respectively. This is a critical issue when determining the
+ * equivalence of two nicknames.
+ */
+void nspace::strlower(char *n)
+{
+ if (n)
+ {
+ for (char* t = n; *t; t++)
+ *t = lowermap[(unsigned char)*t];
+ }
+}
+
+#ifndef WIN32
+size_t nspace::hash<string>::operator()(const string &s) const
+#else
+size_t nspace::hash_compare<string, std::less<string> >::operator()(const 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 lowermap[*x].
+ * This avoids a copy to use hash<const char*>
+ */
+ register 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 + lowermap[(unsigned char)*x];
+ return t;
+}
+
+#ifndef WIN32
+size_t nspace::hash<irc::string>::operator()(const irc::string &s) const
+#else
+size_t nspace::hash_compare<irc::string, std::less<irc::string> >::operator()(const irc::string &s) const
+#endif
+{
+ register size_t t = 0;
+ for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
+ t = 5 * t + lowermap[(unsigned char)*x];
+ return t;
+}
+
+bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const
+{
+ unsigned char* n1 = (unsigned char*)s1.c_str();
+ unsigned char* n2 = (unsigned char*)s2.c_str();
+ for (; *n1 && *n2; n1++, n2++)
+ if (lowermap[*n1] != lowermap[*n2])
+ return false;
+ return (lowermap[*n1] == lowermap[*n2]);
+}
+
+/******************************************************
+ *
+ * This is the implementation of our special irc::string
+ * class which is a case-insensitive equivalent to
+ * std::string which is not only case-insensitive but
+ * can also do scandanavian comparisons, e.g. { = [, etc.
+ *
+ * This class depends on the const array 'lowermap'.
+ *
+ ******************************************************/
+
+bool irc::irc_char_traits::eq(char c1st, char c2nd)
+{
+ return lowermap[(unsigned char)c1st] == lowermap[(unsigned char)c2nd];
+}
+
+bool irc::irc_char_traits::ne(char c1st, char c2nd)
+{
+ return lowermap[(unsigned char)c1st] != lowermap[(unsigned char)c2nd];
+}
+
+bool irc::irc_char_traits::lt(char c1st, char c2nd)
+{
+ return lowermap[(unsigned char)c1st] < lowermap[(unsigned char)c2nd];
+}
+
+int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n)
+{
+ for(unsigned int i = 0; i < n; i++)
+ {
+ if(lowermap[(unsigned char)*str1] > lowermap[(unsigned char)*str2])
+ return 1;
+
+ if(lowermap[(unsigned char)*str1] < lowermap[(unsigned char)*str2])
+ return -1;
+
+ if(*str1 == 0 || *str2 == 0)
+ return 0;
+
+ str1++;
+ str2++;
+ }
+ return 0;
+}
+
+const char* irc::irc_char_traits::find(const char* s1, int n, char c)
+{
+ while(n-- > 0 && lowermap[(unsigned char)*s1] != lowermap[(unsigned char)c])
+ s1++;
+ return s1;
+}
+
+irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false)
+{
+ /* Record starting position and current position */
+ last_starting_position = tokens.begin();
+ n = tokens.begin();
+}
+
+irc::tokenstream::~tokenstream()
+{
+}
+
+bool irc::tokenstream::GetToken(std::string &token)
+{
+ 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;
+ }
+
+ last_pushed = false;
+
+ if ((*n == ' ') || (n+1 == tokens.end()))
+ {
+ /* If we find a space, or end of string, this is the end of a token.
+ */
+ last_starting_position = n+1;
+ last_pushed = true;
+
+ std::string strip(lsp, n+1 == tokens.end() ? n+1 : n++);
+ while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
+ strip.erase(strip.end() - 1);
+
+ token = strip;
+ return !token.empty();
+ }
+
+ n++;
+ }
+ token.clear();
+ return false;
+}
+
+bool irc::tokenstream::GetToken(irc::string &token)
+{
+ std::string stdstring;
+ bool returnval = GetToken(stdstring);
+ token = assign(stdstring);
+ return returnval;
+}
+
+bool irc::tokenstream::GetToken(int &token)
+{
+ std::string tok;
+ bool returnval = GetToken(tok);
+ token = ConvToInt(tok);
+ return returnval;
+}
+
+bool irc::tokenstream::GetToken(long &token)
+{
+ std::string tok;
+ bool returnval = GetToken(tok);
+ token = ConvToInt(tok);
+ return returnval;
+}
+
+irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
+{
+ last_starting_position = tokens.begin();
+ n = tokens.begin();
+}
+
+const std::string irc::sepstream::GetToken()
+{
+ std::string::iterator lsp = last_starting_position;
+
+ while (n != tokens.end())
+ {
+ if ((*n == sep) || (n+1 == tokens.end()))
+ {
+ last_starting_position = n+1;
+ std::string strip = std::string(lsp, n+1 == tokens.end() ? n+1 : n++);
+
+ while ((strip.length()) && (strip.find_last_of(sep) == strip.length() - 1))
+ strip.erase(strip.end() - 1);
+
+ return strip;
+ }
+
+ n++;
+ }
+
+ return "";
+}
+
+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::deque<std::string> &result, int max_line_size)
+{
+ if (sequence.empty())
+ {
+ result.clear();
+ return 0;
+ }
+
+ int n = 0;
+ int size = 1; /* Account for initial +/- char */
+ int nextsize = 0;
+ result.clear();
+ result.push_back(adding ? "+" : "-");
+
+ if (sequence.size() > 1)
+ nextsize = sequence[1].length() + 2;
+
+ while (!sequence[0].empty() && (sequence.size() > 1) && (result.size() < MAXMODES) && ((size + nextsize) < max_line_size))
+ {
+ result[0] += *(sequence[0].begin());
+ if (!sequence[1].empty())
+ {
+ result.push_back(sequence[1]);
+ size += nextsize; /* Account for mode character and whitespace */
+ }
+ sequence[0].erase(sequence[0].begin());
+ sequence.erase(sequence.begin() + 1);
+
+ if (sequence.size() > 1)
+ nextsize = sequence[1].length() + 2;
+
+ n++;
+ }
+
+ return n;
+}
+
+irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
+{
+ for (int v = begin; v < end; v++)
+ joined.append(sequence[v]).append(seperator);
+ joined.append(sequence[end]);
+}
+
+irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end)
+{
+ for (int v = begin; v < end; v++)
+ joined.append(sequence[v]).append(seperator);
+ joined.append(sequence[end]);
+}
+
+irc::stringjoiner::stringjoiner(const std::string &seperator, const char** sequence, int begin, int end)
+{
+ for (int v = begin; v < end; v++)
+ joined.append(sequence[v]).append(seperator);
+ joined.append(sequence[end]);
+}
+
+std::string& irc::stringjoiner::GetJoined()
+{
+ return joined;
+}
+
+irc::portparser::portparser(const std::string &source, bool allow_overlapped) : in_range(0), range_begin(0), range_end(0), overlapped(allow_overlapped)
+{
+ sep = new irc::commasepstream(source);
+ overlap_set.clear();
+}
+
+irc::portparser::~portparser()
+{
+ delete sep;
+}
+
+bool irc::portparser::Overlaps(long val)
+{
+ if (!overlapped)
+ return false;
+
+ if (overlap_set.find(val) == overlap_set.end())
+ {
+ overlap_set[val] = true;
+ return false;
+ }
+ else
+ return true;
+}
+
+long irc::portparser::GetToken()
+{
+ if (in_range > 0)
+ {
+ in_range++;
+ if (in_range <= range_end)
+ {
+ if (!Overlaps(in_range))
+ {
+ return in_range;
+ }
+ else
+ {
+ while (((Overlaps(in_range)) && (in_range <= range_end)))
+ in_range++;
+
+ if (in_range <= range_end)
+ return in_range;
+ }
+ }
+ else
+ in_range = 0;
+ }
+
+ std::string x = sep->GetToken();
+
+ if (x.empty())
+ return 0;
+
+ while (Overlaps(atoi(x.c_str())))
+ {
+ x = sep->GetToken();
+
+ if (x.empty())
+ return 0;
+ }
+
+ 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());
+ range_begin = atoi(sbegin.c_str());
+ range_end = atoi(send.c_str());
+
+ if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end))
+ {
+ in_range = range_begin;
+ return in_range;
+ }
+ else
+ {
+ /* Assume its just the one port */
+ return atoi(sbegin.c_str());
+ }
+ }
+ else
+ {
+ return atoi(x.c_str());
+ }
+}
+
+irc::dynamicbitmask::dynamicbitmask() : bits_size(4)
+{
+ /* We start with 4 bytes allocated which is room
+ * for 4 items. Something makes me doubt its worth
+ * allocating less than 4 bytes.
+ */
+ bits = new unsigned char[bits_size];
+ memset(bits, 0, bits_size);
+}
+
+irc::dynamicbitmask::~dynamicbitmask()
+{
+ /* Tidy up the entire used memory on delete */
+ delete[] bits;
+}
+
+irc::bitfield irc::dynamicbitmask::Allocate()
+{
+ /* Yeah, this isnt too efficient, however a module or the core
+ * should only be allocating bitfields on load, the Toggle and
+ * Get methods are O(1) as these are called much more often.
+ */
+ unsigned char* freebits = this->GetFreeBits();
+ for (unsigned char i = 0; i < bits_size; i++)
+ {
+ /* Yes, this is right. You'll notice we terminate the loop when !current_pos,
+ * this is because we logic shift our bit off the end of unsigned char, and its
+ * lost, making the loop counter 0 when we're done.
+ */
+ for (unsigned char current_pos = 1; current_pos; current_pos = current_pos << 1)
+ {
+ if (!(freebits[i] & current_pos))
+ {
+ freebits[i] |= current_pos;
+ return std::make_pair(i, current_pos);
+ }
+ }
+ }
+ /* We dont have any free space left, increase by one */
+
+ if (bits_size == 255)
+ /* Oh dear, cant grow it any further */
+ throw std::bad_alloc();
+
+ unsigned char old_bits_size = bits_size;
+ bits_size++;
+ /* Allocate new bitfield space */
+ unsigned char* temp_bits = new unsigned char[bits_size];
+ unsigned char* temp_freebits = new unsigned char[bits_size];
+ /* Copy the old data in */
+ memcpy(temp_bits, bits, old_bits_size);
+ memcpy(temp_freebits, freebits, old_bits_size);
+ /* Delete the old data pointers */
+ delete[] bits;
+ delete[] freebits;
+ /* Swap the pointers over so now the new
+ * pointers point to our member values
+ */
+ bits = temp_bits;
+ freebits = temp_freebits;
+ this->SetFreeBits(freebits);
+ /* Initialize the new byte on the end of
+ * the bitfields, pre-allocate the one bit
+ * for this allocation
+ */
+ bits[old_bits_size] = 0;
+ freebits[old_bits_size] = 1;
+ /* We already know where we just allocated
+ * the bitfield, so no loop needed
+ */
+ return std::make_pair(old_bits_size, 1);
+}
+
+bool irc::dynamicbitmask::Deallocate(irc::bitfield &pos)
+{
+ /* We dont bother to shrink the bitfield
+ * on deallocation, the most we could do
+ * is save one byte (!) and this would cost
+ * us a loop (ugly O(n) stuff) so we just
+ * clear the bit and leave the memory
+ * claimed -- nobody will care about one
+ * byte.
+ */
+ if (pos.first < bits_size)
+ {
+ this->GetFreeBits()[pos.first] &= ~pos.second;
+ return true;
+ }
+ /* They gave a bitfield outside of the
+ * length of our array. BAD programmer.
+ */
+ return false;
+}
+
+void irc::dynamicbitmask::Toggle(irc::bitfield &pos, bool state)
+{
+ /* Range check the value */
+ if (pos.first < bits_size)
+ {
+ if (state)
+ /* Set state, OR the state in */
+ bits[pos.first] |= pos.second;
+ else
+ /* Clear state, AND the !state out */
+ bits[pos.first] &= ~pos.second;
+ }
+}
+
+bool irc::dynamicbitmask::Get(irc::bitfield &pos)
+{
+ /* Range check the value */
+ if (pos.first < bits_size)
+ return (bits[pos.first] & pos.second);
+ else
+ /* We can't return false, otherwise we can't
+ * distinguish between failure and a cleared bit!
+ * Our only sensible choice is to throw (ew).
+ */
+ throw ModuleException("irc::dynamicbitmask::Get(): Invalid bitfield, out of range");
+}
+
+unsigned char irc::dynamicbitmask::GetSize()
+{
+ return bits_size;
+}
+
diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp
index b5d8f5630..da3fa5e1b 100644
--- a/src/helperfuncs.cpp
+++ b/src/helperfuncs.cpp
@@ -1 +1,534 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <stdarg.h> #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "mode.h" #include "xline.h" #include "exitcodes.h" static char TIMESTR[26]; static time_t LAST = 0; /** Log() * Write a line of text `text' to the logfile (and stdout, if in nofork) if the level `level' * is greater than the configured loglevel. */ void InspIRCd::Log(int level, const char* text, ...) { /* Do this check again here so that we save pointless vsnprintf calls */ if ((level < Config->LogLevel) && !Config->forcedebug) return; va_list argsPtr; char textbuffer[65536]; va_start(argsPtr, text); vsnprintf(textbuffer, 65536, text, argsPtr); va_end(argsPtr); this->Log(level, std::string(textbuffer)); } void InspIRCd::Log(int level, const std::string &text) { if (!this->Config) return; /* If we were given -debug we output all messages, regardless of configured loglevel */ if ((level < Config->LogLevel) && !Config->forcedebug) return; if (Time() != LAST) { time_t local = Time(); struct tm *timeinfo = localtime(&local); strlcpy(TIMESTR,asctime(timeinfo),26); TIMESTR[24] = ':'; LAST = Time(); } if (Config->log_file && Config->writelog) { std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n"; this->Logger->WriteLogLine(out); } if (Config->nofork) { printf("%s %s\n", TIMESTR, text.c_str()); } } std::string InspIRCd::GetServerDescription(const char* servername) { std::string description; FOREACH_MOD_I(this,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; } } /* XXX - We don't use WriteMode for this because WriteMode is very slow and * this isnt. Basically WriteMode has to iterate ALL the users 'n' times for * the number of modes provided, e.g. if you send WriteMode 'og' to write to * opers with globops, and you have 2000 users, thats 4000 iterations. WriteOpers * uses the oper list, which means if you have 2000 users but only 5 opers, * it iterates 5 times. */ void InspIRCd::WriteOpers(const char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteOpers(std::string(textbuffer)); } void InspIRCd::WriteOpers(const std::string &text) { for (std::vector<userrec*>::iterator i = this->all_opers.begin(); i != this->all_opers.end(); i++) { userrec* a = *i; if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE]) { // send server notices to all with +s a->WriteServ("NOTICE %s :%s",a->nick,text.c_str()); } } } void InspIRCd::ServerNoticeAll(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",Config->ServerName,textbuffer); for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) { userrec* t = *i; t->WriteServ(std::string(formatbuffer)); } } void InspIRCd::ServerPrivmsgAll(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,"PRIVMSG $%s :%s",Config->ServerName,textbuffer); for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) { userrec* t = *i; t->WriteServ(std::string(formatbuffer)); } } void InspIRCd::WriteMode(const char* modes, int flags, const char* text, ...) { char textbuffer[MAXBUF]; int modelen; va_list argsPtr; if (!text || !modes || !flags) { this->Log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter"); return; } va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); modelen = strlen(modes); if (flags == WM_AND) { for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) { userrec* t = *i; bool send_to_user = true; for (int n = 0; n < modelen; n++) { if (!t->modes[modes[n]-65]) { send_to_user = false; break; } } if (send_to_user) t->WriteServ("NOTICE %s :%s",t->nick,textbuffer); } } else if (flags == WM_OR) { for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) { userrec* t = *i; bool send_to_user = false; for (int n = 0; n < modelen; n++) { if (t->modes[modes[n]-65]) { send_to_user = true; break; } } if (send_to_user) t->WriteServ("NOTICE %s :%s",t->nick,textbuffer); } } } /* Find a user record by nickname and return a pointer to it */ userrec* InspIRCd::FindNick(const std::string &nick) { user_hash::iterator iter = clientlist->find(nick); if (iter == clientlist->end()) /* Couldn't find it */ return NULL; return iter->second; } userrec* InspIRCd::FindNick(const char* nick) { user_hash::iterator iter = clientlist->find(nick); if (iter == clientlist->end()) return NULL; return iter->second; } /* find a channel record by channel name and return a pointer to it */ chanrec* 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; } chanrec* InspIRCd::FindChan(const std::string &chan) { chan_hash::iterator iter = chanlist->find(chan); if (iter == chanlist->end()) /* Couldn't find it */ return NULL; return iter->second; } /* * sends out an error notice to all connected clients (not to be used * lightly!) */ void InspIRCd::SendError(const std::string &s) { for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++) { if ((*i)->registered == REG_ALL) { (*i)->WriteServ("NOTICE %s :%s",(*i)->nick,s.c_str()); } else { /* Unregistered connections receive ERROR, not a NOTICE */ (*i)->Write("ERROR :" + s); } /* This might generate a whole load of EAGAIN, but we dont really * care about this, as if we call SendError something catastrophic * has occured anyway, and we wont receive the events for these. */ (*i)->FlushWriteBuf(); } } // this function counts all users connected, wether they are registered or NOT. int InspIRCd::UserCount() { return clientlist->size(); } // this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state int InspIRCd::RegisteredUserCount() { return clientlist->size() - this->UnregisteredUserCount(); } int InspIRCd::ModeCount(const char mode) { ModeHandler* mh = this->Modes->FindMode(mode, MODETYPE_USER); if (mh) return mh->GetCount(); else return 0; } int InspIRCd::InvisibleUserCount() { return ModeCount('i'); } int InspIRCd::OperCount() { return this->all_opers.size(); } int InspIRCd::UnregisteredUserCount() { return this->unregistered_count; } long InspIRCd::ChannelCount() { return chanlist->size(); } long InspIRCd::LocalUserCount() { /* Doesnt count unregistered clients */ return (local_users.size() - this->UnregisteredUserCount()); } bool InspIRCd::IsChannel(const char *chname) { char *c; /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */ if (!chname || *chname != '#') { return false; } c = (char *)chname + 1; while (*c) { switch (*c) { case ' ': case ',': case 7: return false; } c++; } /* too long a name - note funky pointer arithmetic here. */ if ((c - chname) > CHANMAX) { return false; } return true; } bool InspIRCd::IsNick(const char* n) { if (!n || !*n) return false; int p = 0; for (char* i = (char*)n; *i; i++, p++) { if ((*i >= 'A') && (*i <= '}')) { /* "A"-"}" can occur anywhere in a nickname */ continue; } if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n)) { /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */ continue; } /* invalid character! abort */ return false; } /* too long? or not -- pointer arithmetic rocks */ return (p < NICKMAX - 1); } bool InspIRCd::IsIdent(const char* n) { if (!n || !*n) return false; for (char* i = (char*)n; *i; i++) { if ((*i >= 'A') && (*i <= '}')) { continue; } if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.')) { continue; } return false; } return true; } void InspIRCd::OpenLog(char** argv, int argc) { Config->MyDir = Config->GetFullProgDir(); if (!*this->LogFileName) { if (Config->logpath.empty()) { #ifndef DARWIN Config->logpath = Config->MyDir + "/ircd.log"; #else Config->logpath = "/var/log/ircd.log"; #endif } Config->log_file = fopen(Config->logpath.c_str(),"a+"); } else { Config->log_file = fopen(this->LogFileName,"a+"); } if (!Config->log_file) { printf("ERROR: Could not write to logfile %s: %s\n\n", Config->logpath.c_str(), strerror(errno)); Exit(EXIT_STATUS_LOG); } this->Logger = new FileLogger(this, Config->log_file); } void InspIRCd::CheckRoot() { #ifndef DARWIN if (geteuid() == 0) { printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n"); this->Log(DEFAULT,"Cant start as root"); #else if (geteuid() != 16) { printf("WARNING!!! You are not running inspircd as the ircdaemon user!!! YOU CAN NOT DO THIS!!!\n\n"); this->Log(DEFAULT,"Must start as user ircdaemon"); #endif Exit(EXIT_STATUS_ROOT); } } void InspIRCd::CheckDie() { if (*Config->DieValue) { printf("WARNING: %s\n\n",Config->DieValue); this->Log(DEFAULT,"Died because of <die> tag: %s",Config->DieValue); Exit(EXIT_STATUS_DIETAG); } } /* We must load the modules AFTER initializing the socket engine, now */ void InspIRCd::LoadAllModules() { char configToken[MAXBUF]; Config->module_names.clear(); this->ModCount = -1; for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "module"); count++) { Config->ConfValue(Config->config_data, "module", "name", count, configToken, MAXBUF); printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken); if (!this->LoadModule(configToken)) { this->Log(DEFAULT,"There was an error loading the module '%s': %s", configToken, this->ModuleError()); printf_c("\n[\033[1;31m*\033[0m] There was an error loading the module '%s': %s\n\n", configToken, this->ModuleError()); Exit(EXIT_STATUS_MODULE); } } printf_c("\nA total of \033[1;32m%d\033[0m module%s been loaded.\n", this->ModCount+1, this->ModCount+1 == 1 ? " has" : "s have"); this->Log(DEFAULT,"Total loaded modules: %d", this->ModCount+1); } void InspIRCd::SendWhoisLine(userrec* user, userrec* dest, int numeric, const std::string &text) { std::string copy_text = text; int MOD_RESULT = 0; FOREACH_RESULT_I(this, I_OnWhoisLine, OnWhoisLine(user, dest, numeric, copy_text)); if (!MOD_RESULT) user->WriteServ("%d %s", numeric, copy_text.c_str()); } void InspIRCd::SendWhoisLine(userrec* user, userrec* 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)); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <stdarg.h>
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "mode.h"
+#include "xline.h"
+#include "exitcodes.h"
+
+static char TIMESTR[26];
+static time_t LAST = 0;
+
+/** Log()
+ * Write a line of text `text' to the logfile (and stdout, if in nofork) if the level `level'
+ * is greater than the configured loglevel.
+ */
+void InspIRCd::Log(int level, const char* text, ...)
+{
+ /* Do this check again here so that we save pointless vsnprintf calls */
+ if ((level < Config->LogLevel) && !Config->forcedebug)
+ return;
+
+ va_list argsPtr;
+ char textbuffer[65536];
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, 65536, text, argsPtr);
+ va_end(argsPtr);
+
+ this->Log(level, std::string(textbuffer));
+}
+
+void InspIRCd::Log(int level, const std::string &text)
+{
+ if (!this->Config)
+ return;
+
+ /* If we were given -debug we output all messages, regardless of configured loglevel */
+ if ((level < Config->LogLevel) && !Config->forcedebug)
+ return;
+
+ if (Time() != LAST)
+ {
+ time_t local = Time();
+ struct tm *timeinfo = localtime(&local);
+
+ strlcpy(TIMESTR,asctime(timeinfo),26);
+ TIMESTR[24] = ':';
+ LAST = Time();
+ }
+
+ if (Config->log_file && Config->writelog)
+ {
+ std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n";
+ this->Logger->WriteLogLine(out);
+ }
+
+ if (Config->nofork)
+ {
+ printf("%s %s\n", TIMESTR, text.c_str());
+ }
+}
+
+std::string InspIRCd::GetServerDescription(const char* servername)
+{
+ std::string description;
+
+ FOREACH_MOD_I(this,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;
+ }
+}
+
+/* XXX - We don't use WriteMode for this because WriteMode is very slow and
+ * this isnt. Basically WriteMode has to iterate ALL the users 'n' times for
+ * the number of modes provided, e.g. if you send WriteMode 'og' to write to
+ * opers with globops, and you have 2000 users, thats 4000 iterations. WriteOpers
+ * uses the oper list, which means if you have 2000 users but only 5 opers,
+ * it iterates 5 times.
+ */
+void InspIRCd::WriteOpers(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteOpers(std::string(textbuffer));
+}
+
+void InspIRCd::WriteOpers(const std::string &text)
+{
+ for (std::vector<userrec*>::iterator i = this->all_opers.begin(); i != this->all_opers.end(); i++)
+ {
+ userrec* a = *i;
+ if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE])
+ {
+ // send server notices to all with +s
+ a->WriteServ("NOTICE %s :%s",a->nick,text.c_str());
+ }
+ }
+}
+
+void InspIRCd::ServerNoticeAll(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",Config->ServerName,textbuffer);
+
+ for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ {
+ userrec* t = *i;
+ t->WriteServ(std::string(formatbuffer));
+ }
+}
+
+void InspIRCd::ServerPrivmsgAll(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,"PRIVMSG $%s :%s",Config->ServerName,textbuffer);
+
+ for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ {
+ userrec* t = *i;
+ t->WriteServ(std::string(formatbuffer));
+ }
+}
+
+void InspIRCd::WriteMode(const char* modes, int flags, const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ int modelen;
+ va_list argsPtr;
+
+ if (!text || !modes || !flags)
+ {
+ this->Log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
+ return;
+ }
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+ modelen = strlen(modes);
+
+ if (flags == WM_AND)
+ {
+ for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ {
+ userrec* t = *i;
+ bool send_to_user = true;
+
+ for (int n = 0; n < modelen; n++)
+ {
+ if (!t->modes[modes[n]-65])
+ {
+ send_to_user = false;
+ break;
+ }
+ }
+ if (send_to_user)
+ t->WriteServ("NOTICE %s :%s",t->nick,textbuffer);
+ }
+ }
+ else
+ if (flags == WM_OR)
+ {
+ for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ {
+ userrec* t = *i;
+ bool send_to_user = false;
+
+ for (int n = 0; n < modelen; n++)
+ {
+ if (t->modes[modes[n]-65])
+ {
+ send_to_user = true;
+ break;
+ }
+ }
+ if (send_to_user)
+ t->WriteServ("NOTICE %s :%s",t->nick,textbuffer);
+ }
+ }
+}
+
+/* Find a user record by nickname and return a pointer to it */
+
+userrec* InspIRCd::FindNick(const std::string &nick)
+{
+ user_hash::iterator iter = clientlist->find(nick);
+
+ if (iter == clientlist->end())
+ /* Couldn't find it */
+ return NULL;
+
+ return iter->second;
+}
+
+userrec* InspIRCd::FindNick(const char* nick)
+{
+ user_hash::iterator iter = clientlist->find(nick);
+
+ if (iter == clientlist->end())
+ return NULL;
+
+ return iter->second;
+}
+
+/* find a channel record by channel name and return a pointer to it */
+
+chanrec* 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;
+}
+
+chanrec* InspIRCd::FindChan(const std::string &chan)
+{
+ chan_hash::iterator iter = chanlist->find(chan);
+
+ if (iter == chanlist->end())
+ /* Couldn't find it */
+ return NULL;
+
+ return iter->second;
+}
+
+/*
+ * sends out an error notice to all connected clients (not to be used
+ * lightly!)
+ */
+void InspIRCd::SendError(const std::string &s)
+{
+ for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
+ {
+ if ((*i)->registered == REG_ALL)
+ {
+ (*i)->WriteServ("NOTICE %s :%s",(*i)->nick,s.c_str());
+ }
+ else
+ {
+ /* Unregistered connections receive ERROR, not a NOTICE */
+ (*i)->Write("ERROR :" + s);
+ }
+ /* This might generate a whole load of EAGAIN, but we dont really
+ * care about this, as if we call SendError something catastrophic
+ * has occured anyway, and we wont receive the events for these.
+ */
+ (*i)->FlushWriteBuf();
+ }
+}
+
+// this function counts all users connected, wether they are registered or NOT.
+int InspIRCd::UserCount()
+{
+ return clientlist->size();
+}
+
+// this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state
+int InspIRCd::RegisteredUserCount()
+{
+ return clientlist->size() - this->UnregisteredUserCount();
+}
+
+int InspIRCd::ModeCount(const char mode)
+{
+ ModeHandler* mh = this->Modes->FindMode(mode, MODETYPE_USER);
+
+ if (mh)
+ return mh->GetCount();
+ else
+ return 0;
+}
+
+int InspIRCd::InvisibleUserCount()
+{
+ return ModeCount('i');
+}
+
+int InspIRCd::OperCount()
+{
+ return this->all_opers.size();
+}
+
+int InspIRCd::UnregisteredUserCount()
+{
+ return this->unregistered_count;
+}
+
+long InspIRCd::ChannelCount()
+{
+ return chanlist->size();
+}
+
+long InspIRCd::LocalUserCount()
+{
+ /* Doesnt count unregistered clients */
+ return (local_users.size() - this->UnregisteredUserCount());
+}
+
+bool InspIRCd::IsChannel(const char *chname)
+{
+ char *c;
+
+ /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
+ if (!chname || *chname != '#')
+ {
+ return false;
+ }
+
+ c = (char *)chname + 1;
+ while (*c)
+ {
+ switch (*c)
+ {
+ case ' ':
+ case ',':
+ case 7:
+ return false;
+ }
+
+ c++;
+ }
+
+ /* too long a name - note funky pointer arithmetic here. */
+ if ((c - chname) > CHANMAX)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool InspIRCd::IsNick(const char* n)
+{
+ if (!n || !*n)
+ return false;
+
+ int p = 0;
+ for (char* i = (char*)n; *i; i++, p++)
+ {
+ if ((*i >= 'A') && (*i <= '}'))
+ {
+ /* "A"-"}" can occur anywhere in a nickname */
+ continue;
+ }
+
+ if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
+ {
+ /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
+ continue;
+ }
+
+ /* invalid character! abort */
+ return false;
+ }
+
+ /* too long? or not -- pointer arithmetic rocks */
+ return (p < NICKMAX - 1);
+}
+
+
+bool InspIRCd::IsIdent(const char* n)
+{
+ if (!n || !*n)
+ return false;
+
+ for (char* i = (char*)n; *i; i++)
+ {
+ if ((*i >= 'A') && (*i <= '}'))
+ {
+ continue;
+ }
+
+ if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
+ {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+void InspIRCd::OpenLog(char** argv, int argc)
+{
+ Config->MyDir = Config->GetFullProgDir();
+
+ if (!*this->LogFileName)
+ {
+ if (Config->logpath.empty())
+ {
+#ifndef DARWIN
+ Config->logpath = Config->MyDir + "/ircd.log";
+#else
+ Config->logpath = "/var/log/ircd.log";
+#endif
+ }
+
+ Config->log_file = fopen(Config->logpath.c_str(),"a+");
+ }
+ else
+ {
+ Config->log_file = fopen(this->LogFileName,"a+");
+ }
+
+ if (!Config->log_file)
+ {
+ printf("ERROR: Could not write to logfile %s: %s\n\n", Config->logpath.c_str(), strerror(errno));
+ Exit(EXIT_STATUS_LOG);
+ }
+
+ this->Logger = new FileLogger(this, Config->log_file);
+}
+
+void InspIRCd::CheckRoot()
+{
+#ifndef DARWIN
+ if (geteuid() == 0)
+ {
+ printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
+ this->Log(DEFAULT,"Cant start as root");
+#else
+ if (geteuid() != 16)
+ {
+ printf("WARNING!!! You are not running inspircd as the ircdaemon user!!! YOU CAN NOT DO THIS!!!\n\n");
+ this->Log(DEFAULT,"Must start as user ircdaemon");
+#endif
+ Exit(EXIT_STATUS_ROOT);
+ }
+}
+
+void InspIRCd::CheckDie()
+{
+ if (*Config->DieValue)
+ {
+ printf("WARNING: %s\n\n",Config->DieValue);
+ this->Log(DEFAULT,"Died because of <die> tag: %s",Config->DieValue);
+ Exit(EXIT_STATUS_DIETAG);
+ }
+}
+
+/* We must load the modules AFTER initializing the socket engine, now */
+void InspIRCd::LoadAllModules()
+{
+ char configToken[MAXBUF];
+ Config->module_names.clear();
+ this->ModCount = -1;
+
+ for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "module"); count++)
+ {
+ Config->ConfValue(Config->config_data, "module", "name", count, configToken, MAXBUF);
+ printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken);
+
+ if (!this->LoadModule(configToken))
+ {
+ this->Log(DEFAULT,"There was an error loading the module '%s': %s", configToken, this->ModuleError());
+ printf_c("\n[\033[1;31m*\033[0m] There was an error loading the module '%s': %s\n\n", configToken, this->ModuleError());
+ Exit(EXIT_STATUS_MODULE);
+ }
+ }
+ printf_c("\nA total of \033[1;32m%d\033[0m module%s been loaded.\n", this->ModCount+1, this->ModCount+1 == 1 ? " has" : "s have");
+ this->Log(DEFAULT,"Total loaded modules: %d", this->ModCount+1);
+}
+
+void InspIRCd::SendWhoisLine(userrec* user, userrec* dest, int numeric, const std::string &text)
+{
+ std::string copy_text = text;
+
+ int MOD_RESULT = 0;
+ FOREACH_RESULT_I(this, I_OnWhoisLine, OnWhoisLine(user, dest, numeric, copy_text));
+
+ if (!MOD_RESULT)
+ user->WriteServ("%d %s", numeric, copy_text.c_str());
+}
+
+void InspIRCd::SendWhoisLine(userrec* user, userrec* 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));
+}
+
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index 7f9342706..858862e9d 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -1 +1,1307 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include <signal.h> #ifndef WIN32 #include <dirent.h> #include <unistd.h> #include <sys/resource.h> #include <dlfcn.h> #include <getopt.h> /* Some systems don't define RUSAGE_SELF. This should fix them. */ #ifndef RUSAGE_SELF #define RUSAGE_SELF 0 #endif #endif #include <exception> #include <fstream> #include "modules.h" #include "mode.h" #include "xline.h" #include "socketengine.h" #include "inspircd_se_config.h" #include "socket.h" #include "typedefs.h" #include "command_parse.h" #include "exitcodes.h" #ifdef WIN32 /* This MUST remain static and delcared outside the class, so that WriteProcessMemory can reference it properly */ static DWORD owner_processid = 0; DWORD WindowsForkStart(InspIRCd * Instance) { /* Windows implementation of fork() :P */ char module[MAX_PATH]; if(!GetModuleFileName(NULL, module, MAX_PATH)) { printf("GetModuleFileName() failed.\n"); return false; } STARTUPINFO startupinfo; PROCESS_INFORMATION procinfo; ZeroMemory(&startupinfo, sizeof(STARTUPINFO)); ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION)); // Fill in the startup info struct GetStartupInfo(&startupinfo); /* Default creation flags create the processes suspended */ DWORD startupflags = CREATE_SUSPENDED; /* On windows 2003/XP and above, we can use the value * CREATE_PRESERVE_CODE_AUTHZ_LEVEL which gives more access * to the process which we may require on these operating systems. */ OSVERSIONINFO vi; vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&vi); if ((vi.dwMajorVersion >= 5) && (vi.dwMinorVersion > 0)) startupflags |= CREATE_PRESERVE_CODE_AUTHZ_LEVEL; // Launch our "forked" process. BOOL bSuccess = CreateProcess ( module, // Module (exe) filename strdup(GetCommandLine()), // Command line (exe plus parameters from the OS) // NOTE: We cannot return the direct value of the // GetCommandLine function here, as the pointer is // passed straight to the child process, and will be // invalid once we exit as it goes out of context. // strdup() seems ok, though. 0, // PROCESS_SECURITY_ATTRIBUTES 0, // THREAD_SECURITY_ATTRIBUTES TRUE, // We went to inherit handles. startupflags, // Allow us full access to the process and suspend it. 0, // ENVIRONMENT 0, // CURRENT_DIRECTORY &startupinfo, // startup info &procinfo); // process info if(!bSuccess) { printf("CreateProcess() error: %s\n", dlerror()); return false; } // Set the owner process id in the target process. SIZE_T written = 0; DWORD pid = GetCurrentProcessId(); if(!WriteProcessMemory(procinfo.hProcess, &owner_processid, &pid, sizeof(DWORD), &written) || written != sizeof(DWORD)) { printf("WriteProcessMemory() failed: %s\n", dlerror()); return false; } // Resume the other thread (let it start) ResumeThread(procinfo.hThread); // Wait for the new process to kill us. If there is some error, the new process will end and we will end up at the next line. WaitForSingleObject(procinfo.hProcess, INFINITE); // If we hit this it means startup failed, default to 14 if this fails. DWORD ExitCode = 14; GetExitCodeProcess(procinfo.hProcess, &ExitCode); CloseHandle(procinfo.hThread); CloseHandle(procinfo.hProcess); return ExitCode; } void WindowsForkKillOwner(InspIRCd * Instance) { HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, owner_processid); if(!hProcess || !owner_processid) { printf("Could not open process id %u: %s.\n", owner_processid, dlerror()); Instance->Exit(14); } // die die die if(!TerminateProcess(hProcess, 0)) { printf("Could not TerminateProcess(): %s\n", dlerror()); Instance->Exit(14); } CloseHandle(hProcess); } #endif using irc::sockets::NonBlocking; using irc::sockets::Blocking; using irc::sockets::insp_ntoa; using irc::sockets::insp_inaddr; using irc::sockets::insp_sockaddr; InspIRCd* SI = NULL; /* Burlex: Moved from exitcodes.h -- due to duplicate symbols */ 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 */ }; void InspIRCd::AddServerName(const std::string &servername) { servernamelist::iterator itr = servernames.begin(); for(; itr != servernames.end(); ++itr) if(**itr == servername) return; string * ns = new string(servername); servernames.push_back(ns); } const char* InspIRCd::FindServerNamePtr(const std::string &servername) { servernamelist::iterator itr = servernames.begin(); for(; itr != servernames.end(); ++itr) if(**itr == servername) return (*itr)->c_str(); servernames.push_back(new string(servername)); itr = --servernames.end(); return (*itr)->c_str(); } bool InspIRCd::FindServerName(const std::string &servername) { servernamelist::iterator itr = servernames.begin(); for(; itr != servernames.end(); ++itr) if(**itr == servername) return true; return false; } void InspIRCd::Exit(int status) { #ifdef WINDOWS CloseIPC(); #endif if (SI) { SI->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")"); SI->Cleanup(); } exit (status); } void InspIRCd::Cleanup() { std::vector<std::string> mymodnames; int MyModCount = this->GetModuleCount(); for (unsigned int i = 0; i < Config->ports.size(); i++) { /* This calls the constructor and closes the listening socket */ delete Config->ports[i]; } Config->ports.clear(); /* Close all client sockets, or the new process inherits them */ for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++) { (*i)->SetWriteError("Server shutdown"); (*i)->CloseSocket(); } /* We do this more than once, so that any service providers get a * chance to be unhooked by the modules using them, but then get * a chance to be removed themsleves. */ for (int tries = 0; tries < 3; tries++) { MyModCount = this->GetModuleCount(); mymodnames.clear(); /* Unload all modules, so they get a chance to clean up their listeners */ for (int j = 0; j <= MyModCount; j++) mymodnames.push_back(Config->module_names[j]); for (int k = 0; k <= MyModCount; k++) this->UnloadModule(mymodnames[k].c_str()); } /* Close logging */ this->Logger->Close(); /* Cleanup Server Names */ for(servernamelist::iterator itr = servernames.begin(); itr != servernames.end(); ++itr) delete (*itr); #ifdef WINDOWS /* WSACleanup */ WSACleanup(); #endif } void InspIRCd::Restart(const std::string &reason) { /* SendError flushes each client's queue, * regardless of writeability state */ this->SendError(reason); this->Cleanup(); /* Figure out our filename (if theyve renamed it, we're boned) */ std::string me; #ifdef WINDOWS char module[MAX_PATH]; if (GetModuleFileName(NULL, module, MAX_PATH)) me = module; #else me = Config->MyDir + "/inspircd"; #endif if (execv(me.c_str(), Config->argv) == -1) { /* Will raise a SIGABRT if not trapped */ throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno)); } } void InspIRCd::Start() { printf_c("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__); printf_c("(C) InspIRCd Development Team.\033[0m\n\n"); printf_c("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special, pippijn, peavey, Burlex\033[0m\n"); printf_c("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n"); } void InspIRCd::Rehash(int status) { SI->WriteOpers("*** Rehashing config file %s due to SIGHUP",ServerConfig::CleanFilename(SI->ConfigFileName)); SI->CloseLog(); SI->OpenLog(SI->Config->argv, SI->Config->argc); SI->RehashUsersAndChans(); FOREACH_MOD_I(SI, I_OnGarbageCollect, OnGarbageCollect()); SI->Config->Read(false,NULL); SI->ResetMaxBans(); SI->Res->Rehash(); FOREACH_MOD_I(SI,I_OnRehash,OnRehash(NULL,"")); SI->BuildISupport(); } void InspIRCd::ResetMaxBans() { for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++) i->second->ResetMaxBans(); } /** Because hash_map doesnt free its buckets when we delete items (this is a 'feature') * we must occasionally rehash the hash (yes really). * 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. We dont do this on a timer, as its * very expensive, so instead we do it when the user types /REHASH and expects a * short delay anyway. */ void InspIRCd::RehashUsersAndChans() { user_hash* old_users = this->clientlist; chan_hash* old_chans = this->chanlist; this->clientlist = new user_hash(); this->chanlist = new chan_hash(); for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++) this->clientlist->insert(*n); delete old_users; for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++) this->chanlist->insert(*n); delete old_chans; } void InspIRCd::CloseLog() { this->Logger->Close(); } void InspIRCd::SetSignals() { #ifndef WIN32 signal(SIGALRM, SIG_IGN); signal(SIGHUP, InspIRCd::Rehash); signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); #endif signal(SIGTERM, InspIRCd::Exit); } void InspIRCd::QuickExit(int status) { exit(0); } bool InspIRCd::DaemonSeed() { #ifdef WINDOWS printf_c("InspIRCd Process ID: \033[1;32m%lu\033[0m\n", GetCurrentProcessId()); return true; #else signal(SIGTERM, InspIRCd::QuickExit); int childpid; if ((childpid = fork ()) < 0) return false; else if (childpid > 0) { /* We wait here for the child process to kill us, * so that the shell prompt doesnt come back over * the output. * Sending a kill with a signal of 0 just checks * if the child pid is still around. If theyre not, * they threw an error and we should give up. */ while (kill(childpid, 0) != -1) sleep(1); exit(0); } setsid (); umask (007); printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid()); signal(SIGTERM, InspIRCd::Exit); rlimit rl; if (getrlimit(RLIMIT_CORE, &rl) == -1) { this->Log(DEFAULT,"Failed to getrlimit()!"); return false; } else { rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_CORE, &rl) == -1) this->Log(DEFAULT,"setrlimit() failed, cannot increase coredump size."); } return true; #endif } void InspIRCd::WritePID(const std::string &filename) { std::string fname = (filename.empty() ? "inspircd.pid" : filename); if (*(fname.begin()) != '/') { std::string::size_type pos; std::string confpath = this->ConfigFileName; if ((pos = confpath.rfind("/")) != std::string::npos) { /* Leaves us with just the path */ fname = confpath.substr(0, pos) + std::string("/") + fname; } } std::ofstream outfile(fname.c_str()); if (outfile.is_open()) { outfile << getpid(); outfile.close(); } else { printf("Failed to write PID-file '%s', exiting.\n",fname.c_str()); this->Log(DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str()); Exit(EXIT_STATUS_PID); } } std::string InspIRCd::GetRevision() { return REVISION; } InspIRCd::InspIRCd(int argc, char** argv) : ModCount(-1), GlobalCulls(this) { int found_ports = 0; FailedPortList pl; int do_version = 0, do_nofork = 0, do_debug = 0, do_nolog = 0, do_root = 0; /* flag variables */ char c = 0; modules.resize(255); factory.resize(255); memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); this->unregistered_count = 0; this->clientlist = new user_hash(); this->chanlist = new chan_hash(); this->Config = new ServerConfig(this); this->Config->argv = argv; this->Config->argc = argc; chdir(Config->GetFullProgDir().c_str()); this->Config->opertypes.clear(); this->Config->operclass.clear(); this->SNO = new SnomaskManager(this); this->TIME = this->OLDTIME = this->startup_time = time(NULL); this->time_delta = 0; this->next_call = this->TIME + 3; srand(this->TIME); *this->LogFileName = 0; strlcpy(this->ConfigFileName, CONFIG_FILE, MAXBUF); 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 }, { 0, 0, 0, 0 } }; while ((c = getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1) { switch (c) { case 'f': /* Log filename was set */ strlcpy(LogFileName, optarg, MAXBUF); break; case 'c': /* Config filename was set */ strlcpy(ConfigFileName, optarg, MAXBUF); break; case 0: /* getopt_long_only() set an int variable, just keep going */ break; default: /* Unknown parameter! DANGER, INTRUDER.... err.... yeah. */ printf("Usage: %s [--nofork] [--nolog] [--debug] [--logfile <filename>] [--runasroot] [--version] [--config <config>]\n", argv[0]); Exit(EXIT_STATUS_ARGV); break; } } if (do_version) { printf("\n%s r%s\n", VERSION, REVISION); Exit(EXIT_STATUS_NOERROR); } #ifdef WIN32 // Handle forking if(!do_nofork && !owner_processid) { DWORD ExitCode = WindowsForkStart(this); if(ExitCode) Exit(ExitCode); } // Set up winsock WSADATA wsadata; WSAStartup(MAKEWORD(2,0), &wsadata); #endif if (!ServerConfig::FileExists(this->ConfigFileName)) { printf("ERROR: Cannot open config file: %s\nExiting...\n", this->ConfigFileName); this->Log(DEFAULT,"Unable to open config file %s", this->ConfigFileName); Exit(EXIT_STATUS_CONFIG); } this->Start(); /* Set the finished argument values */ Config->nofork = do_nofork; Config->forcedebug = do_debug; Config->writelog = !do_nolog; strlcpy(Config->MyExecutable,argv[0],MAXBUF); this->OpenLog(argv, argc); this->stats = new serverstats(); this->Timers = new TimerManager(this); this->Parser = new CommandParser(this); this->XLines = new XLineManager(this); Config->ClearStack(); Config->Read(true, NULL); if (!do_root) this->CheckRoot(); else { printf("* WARNING * WARNING * WARNING * WARNING * WARNING * \n\n"); printf("YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED\n"); printf("AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED\n"); printf("OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR\n"); printf("SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN\n"); printf("TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART\n"); printf("THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!\n"); printf("\nInspIRCd starting in 20 seconds, ctrl+c to abort...\n"); sleep(20); } this->SetSignals(); if (!Config->nofork) { if (!this->DaemonSeed()) { printf("ERROR: could not go into daemon mode. Shutting down.\n"); Log(DEFAULT,"ERROR: could not go into daemon mode. Shutting down."); Exit(EXIT_STATUS_FORK); } } /* Because of limitations in kqueue on freebsd, we must fork BEFORE we * initialize the socket engine. */ SocketEngineFactory* SEF = new SocketEngineFactory(); SE = SEF->Create(this); delete SEF; this->Modes = new ModeParser(this); this->AddServerName(Config->ServerName); CheckDie(); int bounditems = BindPorts(true, found_ports, pl); for(int t = 0; t < 255; t++) Config->global_implementation[t] = 0; memset(&Config->implement_lists,0,sizeof(Config->implement_lists)); printf("\n"); this->Res = new DNS(this); this->LoadAllModules(); /* Just in case no modules were loaded - fix for bug #101 */ this->BuildISupport(); InitializeDisabledCommands(Config->DisabledCommands, this); if ((Config->ports.size() == 0) && (found_ports > 0)) { printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n"); Log(DEFAULT,"ERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?"); Exit(EXIT_STATUS_BIND); } if (Config->ports.size() != (unsigned int)found_ports) { printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %d of %d client ports bound.\n\n", bounditems, found_ports); printf("The following port(s) failed to bind:\n"); int j = 1; for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) { printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second); } } #ifndef WINDOWS if (!Config->nofork) { if (kill(getppid(), SIGTERM) == -1) { printf("Error killing parent process: %s\n",strerror(errno)); Log(DEFAULT,"Error killing parent process: %s",strerror(errno)); } } if (isatty(0) && isatty(1) && isatty(2)) { /* We didn't start from a TTY, we must have started from a background process - * e.g. we are restarting, or being launched by cron. Dont kill parent, and dont * close stdin/stdout */ if (!do_nofork) { fclose(stdin); fclose(stderr); fclose(stdout); } else { Log(DEFAULT,"Keeping pseudo-tty open as we are running in the foreground."); } } #else InitIPC(); if(!Config->nofork) { WindowsForkKillOwner(this); FreeConsole(); } #endif printf("\nInspIRCd is now running!\n"); Log(DEFAULT,"Startup complete."); this->WritePID(Config->PID); } std::string InspIRCd::GetVersionString() { char versiondata[MAXBUF]; char dnsengine[] = "singlethread-object"; if (*Config->CustomVersion) { snprintf(versiondata,MAXBUF,"%s %s :%s",VERSION,Config->ServerName,Config->CustomVersion); } else { snprintf(versiondata,MAXBUF,"%s %s :%s [FLAGS=%s,%s,%s]",VERSION,Config->ServerName,SYSTEM,REVISION,SE->GetName().c_str(),dnsengine); } return versiondata; } char* InspIRCd::ModuleError() { return MODERR; } void InspIRCd::EraseFactory(int j) { int v = 0; for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++) { if (v == j) { delete *t; factory.erase(t); factory.push_back(NULL); return; } v++; } } void InspIRCd::EraseModule(int j) { int v1 = 0; for (ModuleList::iterator m = modules.begin(); m!= modules.end(); m++) { if (v1 == j) { DELETE(*m); modules.erase(m); modules.push_back(NULL); break; } v1++; } int v2 = 0; for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++) { if (v2 == j) { Config->module_names.erase(v); break; } v2++; } } void InspIRCd::MoveTo(std::string modulename,int slot) { unsigned int v2 = 256; for (unsigned int v = 0; v < Config->module_names.size(); v++) { if (Config->module_names[v] == modulename) { // found an instance, swap it with the item at the end v2 = v; break; } } if ((v2 != (unsigned int)slot) && (v2 < 256)) { // Swap the module names over Config->module_names[v2] = Config->module_names[slot]; Config->module_names[slot] = modulename; // now swap the module factories ircd_module* temp = factory[v2]; factory[v2] = factory[slot]; factory[slot] = temp; // now swap the module objects Module* temp_module = modules[v2]; modules[v2] = modules[slot]; modules[slot] = temp_module; // now swap the implement lists (we dont // need to swap the global or recount it) for (int n = 0; n < 255; n++) { char x = Config->implement_lists[v2][n]; Config->implement_lists[v2][n] = Config->implement_lists[slot][n]; Config->implement_lists[slot][n] = x; } } } void InspIRCd::MoveAfter(std::string modulename, std::string after) { for (unsigned int v = 0; v < Config->module_names.size(); v++) { if (Config->module_names[v] == after) { MoveTo(modulename, v); return; } } } void InspIRCd::MoveBefore(std::string modulename, std::string before) { for (unsigned int v = 0; v < Config->module_names.size(); v++) { if (Config->module_names[v] == before) { if (v > 0) { MoveTo(modulename, v-1); } else { MoveTo(modulename, v); } return; } } } void InspIRCd::MoveToFirst(std::string modulename) { MoveTo(modulename,0); } void InspIRCd::MoveToLast(std::string modulename) { MoveTo(modulename,this->GetModuleCount()); } void InspIRCd::BuildISupport() { // 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=" << MAXMODES-1 << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << NICKMAX-1; v << " CASEMAPPING=rfc1459 STATUSMSG=@%+ CHARSET=ascii TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=" << Config->MaxTargets << " AWAYLEN="; v << MAXAWAY << " CHANMODES=" << this->Modes->ChanModes() << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU"; Config->data005 = v.str(); FOREACH_MOD_I(this,I_On005Numeric,On005Numeric(Config->data005)); Config->Update005(); } bool InspIRCd::UnloadModule(const char* filename) { std::string filename_str = filename; for (unsigned int j = 0; j != Config->module_names.size(); j++) { if (Config->module_names[j] == filename_str) { if (modules[j]->GetVersion().Flags & VF_STATIC) { this->Log(DEFAULT,"Failed to unload STATIC module %s",filename); snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)"); return false; } std::pair<int,std::string> intercount = GetInterfaceInstanceCount(modules[j]); if (intercount.first > 0) { this->Log(DEFAULT,"Failed to unload module %s, being used by %d other(s) via interface '%s'",filename, intercount.first, intercount.second.c_str()); snprintf(MODERR,MAXBUF,"Module not unloadable (Still in use by %d other module%s which %s using its interface '%s') -- unload dependent modules first!", intercount.first, intercount.first > 1 ? "s" : "", intercount.first > 1 ? "are" : "is", intercount.second.c_str()); return false; } /* Give the module a chance to tidy out all its metadata */ for (chan_hash::iterator c = this->chanlist->begin(); c != this->chanlist->end(); c++) { modules[j]->OnCleanup(TYPE_CHANNEL,c->second); } for (user_hash::iterator u = this->clientlist->begin(); u != this->clientlist->end(); u++) { modules[j]->OnCleanup(TYPE_USER,u->second); } /* Tidy up any dangling resolvers */ this->Res->CleanResolvers(modules[j]); FOREACH_MOD_I(this,I_OnUnloadModule,OnUnloadModule(modules[j],Config->module_names[j])); for(int t = 0; t < 255; t++) { Config->global_implementation[t] -= Config->implement_lists[j][t]; } /* We have to renumber implement_lists after unload because the module numbers change! */ for(int j2 = j; j2 < 254; j2++) { for(int t = 0; t < 255; t++) { Config->implement_lists[j2][t] = Config->implement_lists[j2+1][t]; } } // found the module Parser->RemoveCommands(filename); this->EraseModule(j); this->EraseFactory(j); this->Log(DEFAULT,"Module %s unloaded",filename); this->ModCount--; BuildISupport(); return true; } } this->Log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename); snprintf(MODERR,MAXBUF,"Module not loaded"); return false; } bool InspIRCd::LoadModule(const char* filename) { /* Do we have a glob pattern in the filename? * The user wants to load multiple modules which * match the pattern. */ if (strchr(filename,'*') || (strchr(filename,'?'))) { int n_match = 0; DIR* library = opendir(Config->ModPath); if (library) { /* Try and locate and load all modules matching the pattern */ dirent* entry = NULL; while ((entry = readdir(library))) { if (this->MatchText(entry->d_name, filename)) { if (!this->LoadModule(entry->d_name)) n_match++; } } closedir(library); } /* Loadmodule will now return false if any one of the modules failed * to load (but wont abort when it encounters a bad one) and when 1 or * more modules were actually loaded. */ return (n_match > 0); } char modfile[MAXBUF]; snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename); std::string filename_str = filename; if (!ServerConfig::DirValid(modfile)) { this->Log(DEFAULT,"Module %s is not within the modules directory.",modfile); snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile); return false; } if (ServerConfig::FileExists(modfile)) { for (unsigned int j = 0; j < Config->module_names.size(); j++) { if (Config->module_names[j] == filename_str) { this->Log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile); snprintf(MODERR,MAXBUF,"Module already loaded"); return false; } } Module* m = NULL; ircd_module* a = NULL; try { a = new ircd_module(this, modfile); factory[this->ModCount+1] = a; if (factory[this->ModCount+1]->LastError()) { this->Log(DEFAULT,"Unable to load %s: %s",modfile,factory[this->ModCount+1]->LastError()); snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[this->ModCount+1]->LastError()); return false; } if ((long)factory[this->ModCount+1]->factory != -1) { m = factory[this->ModCount+1]->factory->CreateModule(this); Version v = m->GetVersion(); if (v.API != API_VERSION) { delete m; this->Log(DEFAULT,"Unable to load %s: Incorrect module API version: %d (our version: %d)",modfile,v.API,API_VERSION); snprintf(MODERR,MAXBUF,"Loader/Linker error: Incorrect module API version: %d (our version: %d)",v.API,API_VERSION); return false; } else { this->Log(DEFAULT,"New module introduced: %s (API version %d, Module version %d.%d.%d.%d)%s", filename, v.API, v.Major, v.Minor, v.Revision, v.Build, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]")); } modules[this->ModCount+1] = m; /* save the module and the module's classfactory, if * this isnt done, random crashes can occur :/ */ Config->module_names.push_back(filename); char* x = &Config->implement_lists[this->ModCount+1][0]; for(int t = 0; t < 255; t++) x[t] = 0; modules[this->ModCount+1]->Implements(x); for(int t = 0; t < 255; t++) Config->global_implementation[t] += Config->implement_lists[this->ModCount+1][t]; } else { this->Log(DEFAULT,"Unable to load %s",modfile); snprintf(MODERR,MAXBUF,"Factory function failed: Probably missing init_module() entrypoint."); return false; } } catch (CoreException& modexcept) { this->Log(DEFAULT,"Unable to load %s: %s",modfile,modexcept.GetReason()); snprintf(MODERR,MAXBUF,"Factory function of %s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); return false; } } else { this->Log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile); snprintf(MODERR,MAXBUF,"Module file could not be found"); return false; } this->ModCount++; FOREACH_MOD_I(this,I_OnLoadModule,OnLoadModule(modules[this->ModCount],filename_str)); // now work out which modules, if any, want to move to the back of the queue, // and if they do, move them there. std::vector<std::string> put_to_back; std::vector<std::string> put_to_front; std::map<std::string,std::string> put_before; std::map<std::string,std::string> put_after; for (unsigned int j = 0; j < Config->module_names.size(); j++) { if (modules[j]->Prioritize() == PRIORITY_LAST) put_to_back.push_back(Config->module_names[j]); else if (modules[j]->Prioritize() == PRIORITY_FIRST) put_to_front.push_back(Config->module_names[j]); else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_BEFORE) put_before[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8]; else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_AFTER) put_after[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8]; } for (unsigned int j = 0; j < put_to_back.size(); j++) MoveToLast(put_to_back[j]); for (unsigned int j = 0; j < put_to_front.size(); j++) MoveToFirst(put_to_front[j]); for (std::map<std::string,std::string>::iterator j = put_before.begin(); j != put_before.end(); j++) MoveBefore(j->first,j->second); for (std::map<std::string,std::string>::iterator j = put_after.begin(); j != put_after.end(); j++) MoveAfter(j->first,j->second); BuildISupport(); return true; } void InspIRCd::DoOneIteration(bool process_module_sockets) { #ifndef WIN32 static rusage ru; #else static time_t uptime; static struct tm * stime; static char window_title[100]; #endif /* time() seems to be a pretty expensive syscall, so avoid calling it too much. * Once per loop iteration is pleanty. */ OLDTIME = TIME; TIME = time(NULL); /* Run background module timers every few seconds * (the docs say modules shouldnt rely on accurate * timing using this event, so we dont have to * time this exactly). */ if (TIME != OLDTIME) { if (TIME < OLDTIME) WriteOpers("*** \002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME)); if ((TIME % 3600) == 0) { this->RehashUsersAndChans(); FOREACH_MOD_I(this, I_OnGarbageCollect, OnGarbageCollect()); } Timers->TickTimers(TIME); this->DoBackgroundUserStuff(TIME); if ((TIME % 5) == 0) { XLines->expire_lines(); FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME)); Timers->TickMissedTimers(TIME); } #ifndef WIN32 /* Same change as in cmd_stats.cpp, use RUSAGE_SELF rather than '0' -- Om */ if (!getrusage(RUSAGE_SELF, &ru)) { gettimeofday(&this->stats->LastSampled, NULL); this->stats->LastCPU = ru.ru_utime; } #else CheckIPC(this); if(Config->nofork) { uptime = Time() - startup_time; stime = gmtime(&uptime); snprintf(window_title, 100, "InspIRCd - %u clients, %u accepted connections - Up %u days, %.2u:%.2u:%.2u", LocalUserCount(), stats->statsAccept, stime->tm_yday, stime->tm_hour, stime->tm_min, stime->tm_sec); SetConsoleTitle(window_title); } #endif } /* Call the socket engine to wait on the active * file descriptors. The socket engine has everything's * descriptors in its list... dns, modules, users, * servers... so its nice and easy, just one call. * This will cause any read or write events to be * dispatched to their handlers. */ SE->DispatchEvents(); /* if any users was quit, take them out */ GlobalCulls.Apply(); /* If any inspsockets closed, remove them */ for (std::map<InspSocket*,InspSocket*>::iterator x = SocketCull.begin(); x != SocketCull.end(); ++x) { SE->DelFd(x->second); x->second->Close(); delete x->second; } SocketCull.clear(); } int InspIRCd::Run() { while (true) { DoOneIteration(true); } /* This is never reached -- we hope! */ return 0; } /**********************************************************************************/ /** * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*. */ int main(int argc, char** argv) { SI = new InspIRCd(argc, argv); SI->Run(); delete SI; return 0; } /* 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(userrec* user) { if (!Config->global_implementation[I_OnCheckReady]) return true; for (int i = 0; i <= this->GetModuleCount(); i++) { if (Config->implement_lists[i][I_OnCheckReady]) { int res = modules[i]->OnCheckReady(user); if (!res) return false; } } return true; } int InspIRCd::GetModuleCount() { return this->ModCount; } time_t InspIRCd::Time(bool delta) { if (delta) return TIME + time_delta; return TIME; } int InspIRCd::SetTimeDelta(int delta) { int old = time_delta; time_delta = delta; this->Log(DEBUG, "Time delta set to %d (was %d)", time_delta, old); return old; } void InspIRCd::AddLocalClone(userrec* user) { clonemap::iterator x = local_clones.find(user->GetIPString()); if (x != local_clones.end()) x->second++; else local_clones[user->GetIPString()] = 1; } void InspIRCd::AddGlobalClone(userrec* user) { clonemap::iterator y = global_clones.find(user->GetIPString()); if (y != global_clones.end()) y->second++; else global_clones[user->GetIPString()] = 1; } int InspIRCd::GetTimeDelta() { return time_delta; } bool FileLogger::Readable() { return false; } void FileLogger::HandleEvent(EventType et, int errornum) { this->WriteLogLine(""); if (log) ServerInstance->SE->DelFd(this); } void FileLogger::WriteLogLine(const std::string &line) { if (line.length()) buffer.append(line); if (log) { int written = fprintf(log,"%s",buffer.c_str()); #ifdef WINDOWS buffer.clear(); #else if ((written >= 0) && (written < (int)buffer.length())) { buffer.erase(0, buffer.length()); ServerInstance->SE->AddFd(this); } else if (written == -1) { if (errno == EAGAIN) ServerInstance->SE->AddFd(this); } else { /* Wrote the whole buffer, and no need for write callback */ buffer.clear(); } #endif if (writeops++ % 20) { fflush(log); } } } void FileLogger::Close() { if (log) { /* Burlex: Windows assumes nonblocking on FILE* pointers anyway, and also "file" fd's aren't the same * as socket fd's. */ #ifndef WIN32 int flags = fcntl(fileno(log), F_GETFL, 0); fcntl(fileno(log), F_SETFL, flags ^ O_NONBLOCK); #endif if (buffer.size()) fprintf(log,"%s",buffer.c_str()); #ifndef WINDOWS ServerInstance->SE->DelFd(this); #endif fflush(log); fclose(log); } buffer.clear(); } FileLogger::FileLogger(InspIRCd* Instance, FILE* logfile) : ServerInstance(Instance), log(logfile), writeops(0) { if (log) { irc::sockets::NonBlocking(fileno(log)); this->SetFd(fileno(log)); buffer.clear(); } } FileLogger::~FileLogger() { this->Close(); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include <signal.h>
+
+#ifndef WIN32
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <dlfcn.h>
+#include <getopt.h>
+
+/* Some systems don't define RUSAGE_SELF. This should fix them. */
+#ifndef RUSAGE_SELF
+ #define RUSAGE_SELF 0
+#endif
+
+#endif
+
+#include <exception>
+#include <fstream>
+#include "modules.h"
+#include "mode.h"
+#include "xline.h"
+#include "socketengine.h"
+#include "inspircd_se_config.h"
+#include "socket.h"
+#include "typedefs.h"
+#include "command_parse.h"
+#include "exitcodes.h"
+
+#ifdef WIN32
+
+/* This MUST remain static and delcared outside the class, so that WriteProcessMemory can reference it properly */
+static DWORD owner_processid = 0;
+
+DWORD WindowsForkStart(InspIRCd * Instance)
+{
+ /* Windows implementation of fork() :P */
+
+ char module[MAX_PATH];
+ if(!GetModuleFileName(NULL, module, MAX_PATH))
+ {
+ printf("GetModuleFileName() failed.\n");
+ return false;
+ }
+
+ STARTUPINFO startupinfo;
+ PROCESS_INFORMATION procinfo;
+ ZeroMemory(&startupinfo, sizeof(STARTUPINFO));
+ ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION));
+
+ // Fill in the startup info struct
+ GetStartupInfo(&startupinfo);
+
+ /* Default creation flags create the processes suspended */
+ DWORD startupflags = CREATE_SUSPENDED;
+
+ /* On windows 2003/XP and above, we can use the value
+ * CREATE_PRESERVE_CODE_AUTHZ_LEVEL which gives more access
+ * to the process which we may require on these operating systems.
+ */
+ OSVERSIONINFO vi;
+ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&vi);
+ if ((vi.dwMajorVersion >= 5) && (vi.dwMinorVersion > 0))
+ startupflags |= CREATE_PRESERVE_CODE_AUTHZ_LEVEL;
+
+ // Launch our "forked" process.
+ BOOL bSuccess = CreateProcess ( module, // Module (exe) filename
+ strdup(GetCommandLine()), // Command line (exe plus parameters from the OS)
+ // NOTE: We cannot return the direct value of the
+ // GetCommandLine function here, as the pointer is
+ // passed straight to the child process, and will be
+ // invalid once we exit as it goes out of context.
+ // strdup() seems ok, though.
+ 0, // PROCESS_SECURITY_ATTRIBUTES
+ 0, // THREAD_SECURITY_ATTRIBUTES
+ TRUE, // We went to inherit handles.
+ startupflags, // Allow us full access to the process and suspend it.
+ 0, // ENVIRONMENT
+ 0, // CURRENT_DIRECTORY
+ &startupinfo, // startup info
+ &procinfo); // process info
+
+ if(!bSuccess)
+ {
+ printf("CreateProcess() error: %s\n", dlerror());
+ return false;
+ }
+
+ // Set the owner process id in the target process.
+ SIZE_T written = 0;
+ DWORD pid = GetCurrentProcessId();
+ if(!WriteProcessMemory(procinfo.hProcess, &owner_processid, &pid, sizeof(DWORD), &written) || written != sizeof(DWORD))
+ {
+ printf("WriteProcessMemory() failed: %s\n", dlerror());
+ return false;
+ }
+
+ // Resume the other thread (let it start)
+ ResumeThread(procinfo.hThread);
+
+ // Wait for the new process to kill us. If there is some error, the new process will end and we will end up at the next line.
+ WaitForSingleObject(procinfo.hProcess, INFINITE);
+
+ // If we hit this it means startup failed, default to 14 if this fails.
+ DWORD ExitCode = 14;
+ GetExitCodeProcess(procinfo.hProcess, &ExitCode);
+ CloseHandle(procinfo.hThread);
+ CloseHandle(procinfo.hProcess);
+ return ExitCode;
+}
+
+void WindowsForkKillOwner(InspIRCd * Instance)
+{
+ HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, owner_processid);
+ if(!hProcess || !owner_processid)
+ {
+ printf("Could not open process id %u: %s.\n", owner_processid, dlerror());
+ Instance->Exit(14);
+ }
+
+ // die die die
+ if(!TerminateProcess(hProcess, 0))
+ {
+ printf("Could not TerminateProcess(): %s\n", dlerror());
+ Instance->Exit(14);
+ }
+
+ CloseHandle(hProcess);
+}
+
+#endif
+
+using irc::sockets::NonBlocking;
+using irc::sockets::Blocking;
+using irc::sockets::insp_ntoa;
+using irc::sockets::insp_inaddr;
+using irc::sockets::insp_sockaddr;
+
+InspIRCd* SI = NULL;
+
+/* Burlex: Moved from exitcodes.h -- due to duplicate symbols */
+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 */
+};
+
+void InspIRCd::AddServerName(const std::string &servername)
+{
+ servernamelist::iterator itr = servernames.begin();
+ for(; itr != servernames.end(); ++itr)
+ if(**itr == servername)
+ return;
+
+ string * ns = new string(servername);
+ servernames.push_back(ns);
+}
+
+const char* InspIRCd::FindServerNamePtr(const std::string &servername)
+{
+ servernamelist::iterator itr = servernames.begin();
+ for(; itr != servernames.end(); ++itr)
+ if(**itr == servername)
+ return (*itr)->c_str();
+
+ servernames.push_back(new string(servername));
+ itr = --servernames.end();
+ return (*itr)->c_str();
+}
+
+bool InspIRCd::FindServerName(const std::string &servername)
+{
+ servernamelist::iterator itr = servernames.begin();
+ for(; itr != servernames.end(); ++itr)
+ if(**itr == servername)
+ return true;
+ return false;
+}
+
+void InspIRCd::Exit(int status)
+{
+#ifdef WINDOWS
+ CloseIPC();
+#endif
+ if (SI)
+ {
+ SI->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
+ SI->Cleanup();
+ }
+ exit (status);
+}
+
+void InspIRCd::Cleanup()
+{
+ std::vector<std::string> mymodnames;
+ int MyModCount = this->GetModuleCount();
+
+ for (unsigned int i = 0; i < Config->ports.size(); i++)
+ {
+ /* This calls the constructor and closes the listening socket */
+ delete Config->ports[i];
+ }
+
+ Config->ports.clear();
+
+ /* Close all client sockets, or the new process inherits them */
+ for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
+ {
+ (*i)->SetWriteError("Server shutdown");
+ (*i)->CloseSocket();
+ }
+
+ /* We do this more than once, so that any service providers get a
+ * chance to be unhooked by the modules using them, but then get
+ * a chance to be removed themsleves.
+ */
+ for (int tries = 0; tries < 3; tries++)
+ {
+ MyModCount = this->GetModuleCount();
+ mymodnames.clear();
+
+ /* Unload all modules, so they get a chance to clean up their listeners */
+ for (int j = 0; j <= MyModCount; j++)
+ mymodnames.push_back(Config->module_names[j]);
+
+ for (int k = 0; k <= MyModCount; k++)
+ this->UnloadModule(mymodnames[k].c_str());
+ }
+
+ /* Close logging */
+ this->Logger->Close();
+
+ /* Cleanup Server Names */
+ for(servernamelist::iterator itr = servernames.begin(); itr != servernames.end(); ++itr)
+ delete (*itr);
+
+#ifdef WINDOWS
+ /* WSACleanup */
+ WSACleanup();
+#endif
+}
+
+void InspIRCd::Restart(const std::string &reason)
+{
+ /* SendError flushes each client's queue,
+ * regardless of writeability state
+ */
+ this->SendError(reason);
+
+ this->Cleanup();
+
+ /* Figure out our filename (if theyve renamed it, we're boned) */
+ std::string me;
+
+#ifdef WINDOWS
+ char module[MAX_PATH];
+ if (GetModuleFileName(NULL, module, MAX_PATH))
+ me = module;
+#else
+ me = Config->MyDir + "/inspircd";
+#endif
+
+ if (execv(me.c_str(), Config->argv) == -1)
+ {
+ /* Will raise a SIGABRT if not trapped */
+ throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
+ }
+}
+
+void InspIRCd::Start()
+{
+ printf_c("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
+ printf_c("(C) InspIRCd Development Team.\033[0m\n\n");
+ printf_c("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special, pippijn, peavey, Burlex\033[0m\n");
+ printf_c("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
+}
+
+void InspIRCd::Rehash(int status)
+{
+ SI->WriteOpers("*** Rehashing config file %s due to SIGHUP",ServerConfig::CleanFilename(SI->ConfigFileName));
+ SI->CloseLog();
+ SI->OpenLog(SI->Config->argv, SI->Config->argc);
+ SI->RehashUsersAndChans();
+ FOREACH_MOD_I(SI, I_OnGarbageCollect, OnGarbageCollect());
+ SI->Config->Read(false,NULL);
+ SI->ResetMaxBans();
+ SI->Res->Rehash();
+ FOREACH_MOD_I(SI,I_OnRehash,OnRehash(NULL,""));
+ SI->BuildISupport();
+}
+
+void InspIRCd::ResetMaxBans()
+{
+ for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
+ i->second->ResetMaxBans();
+}
+
+
+/** Because hash_map doesnt free its buckets when we delete items (this is a 'feature')
+ * we must occasionally rehash the hash (yes really).
+ * 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. We dont do this on a timer, as its
+ * very expensive, so instead we do it when the user types /REHASH and expects a
+ * short delay anyway.
+ */
+void InspIRCd::RehashUsersAndChans()
+{
+ user_hash* old_users = this->clientlist;
+ chan_hash* old_chans = this->chanlist;
+
+ this->clientlist = new user_hash();
+ this->chanlist = new chan_hash();
+
+ for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
+ this->clientlist->insert(*n);
+
+ delete old_users;
+
+ for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
+ this->chanlist->insert(*n);
+
+ delete old_chans;
+}
+
+void InspIRCd::CloseLog()
+{
+ this->Logger->Close();
+}
+
+void InspIRCd::SetSignals()
+{
+#ifndef WIN32
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, InspIRCd::Rehash);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+#endif
+ signal(SIGTERM, InspIRCd::Exit);
+}
+
+void InspIRCd::QuickExit(int status)
+{
+ exit(0);
+}
+
+bool InspIRCd::DaemonSeed()
+{
+#ifdef WINDOWS
+ printf_c("InspIRCd Process ID: \033[1;32m%lu\033[0m\n", GetCurrentProcessId());
+ return true;
+#else
+ signal(SIGTERM, InspIRCd::QuickExit);
+
+ int childpid;
+ if ((childpid = fork ()) < 0)
+ return false;
+ else if (childpid > 0)
+ {
+ /* We wait here for the child process to kill us,
+ * so that the shell prompt doesnt come back over
+ * the output.
+ * Sending a kill with a signal of 0 just checks
+ * if the child pid is still around. If theyre not,
+ * they threw an error and we should give up.
+ */
+ while (kill(childpid, 0) != -1)
+ sleep(1);
+ exit(0);
+ }
+ setsid ();
+ umask (007);
+ printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
+
+ signal(SIGTERM, InspIRCd::Exit);
+
+ rlimit rl;
+ if (getrlimit(RLIMIT_CORE, &rl) == -1)
+ {
+ this->Log(DEFAULT,"Failed to getrlimit()!");
+ return false;
+ }
+ else
+ {
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ this->Log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
+ }
+
+ return true;
+#endif
+}
+
+void InspIRCd::WritePID(const std::string &filename)
+{
+ std::string fname = (filename.empty() ? "inspircd.pid" : filename);
+ if (*(fname.begin()) != '/')
+ {
+ std::string::size_type pos;
+ std::string confpath = this->ConfigFileName;
+ if ((pos = confpath.rfind("/")) != std::string::npos)
+ {
+ /* Leaves us with just the path */
+ fname = confpath.substr(0, pos) + std::string("/") + fname;
+ }
+ }
+ std::ofstream outfile(fname.c_str());
+ if (outfile.is_open())
+ {
+ outfile << getpid();
+ outfile.close();
+ }
+ else
+ {
+ printf("Failed to write PID-file '%s', exiting.\n",fname.c_str());
+ this->Log(DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
+ Exit(EXIT_STATUS_PID);
+ }
+}
+
+std::string InspIRCd::GetRevision()
+{
+ return REVISION;
+}
+
+InspIRCd::InspIRCd(int argc, char** argv)
+ : ModCount(-1), GlobalCulls(this)
+{
+ int found_ports = 0;
+ FailedPortList pl;
+ int do_version = 0, do_nofork = 0, do_debug = 0, do_nolog = 0, do_root = 0; /* flag variables */
+ char c = 0;
+
+ modules.resize(255);
+ factory.resize(255);
+ memset(&server, 0, sizeof(server));
+ memset(&client, 0, sizeof(client));
+
+ this->unregistered_count = 0;
+
+ this->clientlist = new user_hash();
+ this->chanlist = new chan_hash();
+
+ this->Config = new ServerConfig(this);
+
+ this->Config->argv = argv;
+ this->Config->argc = argc;
+
+ chdir(Config->GetFullProgDir().c_str());
+
+ this->Config->opertypes.clear();
+ this->Config->operclass.clear();
+ this->SNO = new SnomaskManager(this);
+ this->TIME = this->OLDTIME = this->startup_time = time(NULL);
+ this->time_delta = 0;
+ this->next_call = this->TIME + 3;
+ srand(this->TIME);
+
+ *this->LogFileName = 0;
+ strlcpy(this->ConfigFileName, CONFIG_FILE, MAXBUF);
+
+ 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 },
+ { 0, 0, 0, 0 }
+ };
+
+ while ((c = getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'f':
+ /* Log filename was set */
+ strlcpy(LogFileName, optarg, MAXBUF);
+ break;
+ case 'c':
+ /* Config filename was set */
+ strlcpy(ConfigFileName, optarg, MAXBUF);
+ break;
+ case 0:
+ /* getopt_long_only() set an int variable, just keep going */
+ break;
+ default:
+ /* Unknown parameter! DANGER, INTRUDER.... err.... yeah. */
+ printf("Usage: %s [--nofork] [--nolog] [--debug] [--logfile <filename>] [--runasroot] [--version] [--config <config>]\n", argv[0]);
+ Exit(EXIT_STATUS_ARGV);
+ break;
+ }
+ }
+
+ if (do_version)
+ {
+ printf("\n%s r%s\n", VERSION, REVISION);
+ Exit(EXIT_STATUS_NOERROR);
+ }
+
+#ifdef WIN32
+
+ // Handle forking
+ if(!do_nofork && !owner_processid)
+ {
+ DWORD ExitCode = WindowsForkStart(this);
+ if(ExitCode)
+ Exit(ExitCode);
+ }
+
+ // Set up winsock
+ WSADATA wsadata;
+ WSAStartup(MAKEWORD(2,0), &wsadata);
+
+#endif
+ if (!ServerConfig::FileExists(this->ConfigFileName))
+ {
+ printf("ERROR: Cannot open config file: %s\nExiting...\n", this->ConfigFileName);
+ this->Log(DEFAULT,"Unable to open config file %s", this->ConfigFileName);
+ Exit(EXIT_STATUS_CONFIG);
+ }
+
+ this->Start();
+
+ /* Set the finished argument values */
+ Config->nofork = do_nofork;
+ Config->forcedebug = do_debug;
+ Config->writelog = !do_nolog;
+
+ strlcpy(Config->MyExecutable,argv[0],MAXBUF);
+
+ this->OpenLog(argv, argc);
+
+ this->stats = new serverstats();
+ this->Timers = new TimerManager(this);
+ this->Parser = new CommandParser(this);
+ this->XLines = new XLineManager(this);
+ Config->ClearStack();
+ Config->Read(true, NULL);
+
+ if (!do_root)
+ this->CheckRoot();
+ else
+ {
+ printf("* WARNING * WARNING * WARNING * WARNING * WARNING * \n\n");
+ printf("YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED\n");
+ printf("AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED\n");
+ printf("OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR\n");
+ printf("SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN\n");
+ printf("TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART\n");
+ printf("THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!\n");
+ printf("\nInspIRCd starting in 20 seconds, ctrl+c to abort...\n");
+ sleep(20);
+ }
+
+ this->SetSignals();
+
+ if (!Config->nofork)
+ {
+ if (!this->DaemonSeed())
+ {
+ printf("ERROR: could not go into daemon mode. Shutting down.\n");
+ Log(DEFAULT,"ERROR: could not go into daemon mode. Shutting down.");
+ Exit(EXIT_STATUS_FORK);
+ }
+ }
+
+
+ /* Because of limitations in kqueue on freebsd, we must fork BEFORE we
+ * initialize the socket engine.
+ */
+ SocketEngineFactory* SEF = new SocketEngineFactory();
+ SE = SEF->Create(this);
+ delete SEF;
+
+ this->Modes = new ModeParser(this);
+ this->AddServerName(Config->ServerName);
+ CheckDie();
+ int bounditems = BindPorts(true, found_ports, pl);
+
+ for(int t = 0; t < 255; t++)
+ Config->global_implementation[t] = 0;
+
+ memset(&Config->implement_lists,0,sizeof(Config->implement_lists));
+
+ printf("\n");
+
+ this->Res = new DNS(this);
+
+ this->LoadAllModules();
+ /* Just in case no modules were loaded - fix for bug #101 */
+ this->BuildISupport();
+ InitializeDisabledCommands(Config->DisabledCommands, this);
+
+ if ((Config->ports.size() == 0) && (found_ports > 0))
+ {
+ printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n");
+ Log(DEFAULT,"ERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?");
+ Exit(EXIT_STATUS_BIND);
+ }
+
+ if (Config->ports.size() != (unsigned int)found_ports)
+ {
+ printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %d of %d client ports bound.\n\n", bounditems, found_ports);
+ printf("The following port(s) failed to bind:\n");
+ int j = 1;
+ for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
+ {
+ printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
+ }
+ }
+#ifndef WINDOWS
+ if (!Config->nofork)
+ {
+ if (kill(getppid(), SIGTERM) == -1)
+ {
+ printf("Error killing parent process: %s\n",strerror(errno));
+ Log(DEFAULT,"Error killing parent process: %s",strerror(errno));
+ }
+ }
+
+ if (isatty(0) && isatty(1) && isatty(2))
+ {
+ /* We didn't start from a TTY, we must have started from a background process -
+ * e.g. we are restarting, or being launched by cron. Dont kill parent, and dont
+ * close stdin/stdout
+ */
+ if (!do_nofork)
+ {
+ fclose(stdin);
+ fclose(stderr);
+ fclose(stdout);
+ }
+ else
+ {
+ Log(DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
+ }
+ }
+#else
+ InitIPC();
+ if(!Config->nofork)
+ {
+ WindowsForkKillOwner(this);
+ FreeConsole();
+ }
+#endif
+ printf("\nInspIRCd is now running!\n");
+ Log(DEFAULT,"Startup complete.");
+
+ this->WritePID(Config->PID);
+}
+
+std::string InspIRCd::GetVersionString()
+{
+ char versiondata[MAXBUF];
+ char dnsengine[] = "singlethread-object";
+
+ if (*Config->CustomVersion)
+ {
+ snprintf(versiondata,MAXBUF,"%s %s :%s",VERSION,Config->ServerName,Config->CustomVersion);
+ }
+ else
+ {
+ snprintf(versiondata,MAXBUF,"%s %s :%s [FLAGS=%s,%s,%s]",VERSION,Config->ServerName,SYSTEM,REVISION,SE->GetName().c_str(),dnsengine);
+ }
+ return versiondata;
+}
+
+char* InspIRCd::ModuleError()
+{
+ return MODERR;
+}
+
+void InspIRCd::EraseFactory(int j)
+{
+ int v = 0;
+ for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
+ {
+ if (v == j)
+ {
+ delete *t;
+ factory.erase(t);
+ factory.push_back(NULL);
+ return;
+ }
+ v++;
+ }
+}
+
+void InspIRCd::EraseModule(int j)
+{
+ int v1 = 0;
+ for (ModuleList::iterator m = modules.begin(); m!= modules.end(); m++)
+ {
+ if (v1 == j)
+ {
+ DELETE(*m);
+ modules.erase(m);
+ modules.push_back(NULL);
+ break;
+ }
+ v1++;
+ }
+ int v2 = 0;
+ for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++)
+ {
+ if (v2 == j)
+ {
+ Config->module_names.erase(v);
+ break;
+ }
+ v2++;
+ }
+
+}
+
+void InspIRCd::MoveTo(std::string modulename,int slot)
+{
+ unsigned int v2 = 256;
+ for (unsigned int v = 0; v < Config->module_names.size(); v++)
+ {
+ if (Config->module_names[v] == modulename)
+ {
+ // found an instance, swap it with the item at the end
+ v2 = v;
+ break;
+ }
+ }
+ if ((v2 != (unsigned int)slot) && (v2 < 256))
+ {
+ // Swap the module names over
+ Config->module_names[v2] = Config->module_names[slot];
+ Config->module_names[slot] = modulename;
+ // now swap the module factories
+ ircd_module* temp = factory[v2];
+ factory[v2] = factory[slot];
+ factory[slot] = temp;
+ // now swap the module objects
+ Module* temp_module = modules[v2];
+ modules[v2] = modules[slot];
+ modules[slot] = temp_module;
+ // now swap the implement lists (we dont
+ // need to swap the global or recount it)
+ for (int n = 0; n < 255; n++)
+ {
+ char x = Config->implement_lists[v2][n];
+ Config->implement_lists[v2][n] = Config->implement_lists[slot][n];
+ Config->implement_lists[slot][n] = x;
+ }
+ }
+}
+
+void InspIRCd::MoveAfter(std::string modulename, std::string after)
+{
+ for (unsigned int v = 0; v < Config->module_names.size(); v++)
+ {
+ if (Config->module_names[v] == after)
+ {
+ MoveTo(modulename, v);
+ return;
+ }
+ }
+}
+
+void InspIRCd::MoveBefore(std::string modulename, std::string before)
+{
+ for (unsigned int v = 0; v < Config->module_names.size(); v++)
+ {
+ if (Config->module_names[v] == before)
+ {
+ if (v > 0)
+ {
+ MoveTo(modulename, v-1);
+ }
+ else
+ {
+ MoveTo(modulename, v);
+ }
+ return;
+ }
+ }
+}
+
+void InspIRCd::MoveToFirst(std::string modulename)
+{
+ MoveTo(modulename,0);
+}
+
+void InspIRCd::MoveToLast(std::string modulename)
+{
+ MoveTo(modulename,this->GetModuleCount());
+}
+
+void InspIRCd::BuildISupport()
+{
+ // 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=" << MAXMODES-1 << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << NICKMAX-1;
+ v << " CASEMAPPING=rfc1459 STATUSMSG=@%+ CHARSET=ascii TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=" << Config->MaxTargets << " AWAYLEN=";
+ v << MAXAWAY << " CHANMODES=" << this->Modes->ChanModes() << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU";
+ Config->data005 = v.str();
+ FOREACH_MOD_I(this,I_On005Numeric,On005Numeric(Config->data005));
+ Config->Update005();
+}
+
+bool InspIRCd::UnloadModule(const char* filename)
+{
+ std::string filename_str = filename;
+ for (unsigned int j = 0; j != Config->module_names.size(); j++)
+ {
+ if (Config->module_names[j] == filename_str)
+ {
+ if (modules[j]->GetVersion().Flags & VF_STATIC)
+ {
+ this->Log(DEFAULT,"Failed to unload STATIC module %s",filename);
+ snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
+ return false;
+ }
+ std::pair<int,std::string> intercount = GetInterfaceInstanceCount(modules[j]);
+ if (intercount.first > 0)
+ {
+ this->Log(DEFAULT,"Failed to unload module %s, being used by %d other(s) via interface '%s'",filename, intercount.first, intercount.second.c_str());
+ snprintf(MODERR,MAXBUF,"Module not unloadable (Still in use by %d other module%s which %s using its interface '%s') -- unload dependent modules first!",
+ intercount.first,
+ intercount.first > 1 ? "s" : "",
+ intercount.first > 1 ? "are" : "is",
+ intercount.second.c_str());
+ return false;
+ }
+ /* Give the module a chance to tidy out all its metadata */
+ for (chan_hash::iterator c = this->chanlist->begin(); c != this->chanlist->end(); c++)
+ {
+ modules[j]->OnCleanup(TYPE_CHANNEL,c->second);
+ }
+ for (user_hash::iterator u = this->clientlist->begin(); u != this->clientlist->end(); u++)
+ {
+ modules[j]->OnCleanup(TYPE_USER,u->second);
+ }
+
+ /* Tidy up any dangling resolvers */
+ this->Res->CleanResolvers(modules[j]);
+
+ FOREACH_MOD_I(this,I_OnUnloadModule,OnUnloadModule(modules[j],Config->module_names[j]));
+
+ for(int t = 0; t < 255; t++)
+ {
+ Config->global_implementation[t] -= Config->implement_lists[j][t];
+ }
+
+ /* We have to renumber implement_lists after unload because the module numbers change!
+ */
+ for(int j2 = j; j2 < 254; j2++)
+ {
+ for(int t = 0; t < 255; t++)
+ {
+ Config->implement_lists[j2][t] = Config->implement_lists[j2+1][t];
+ }
+ }
+
+ // found the module
+ Parser->RemoveCommands(filename);
+ this->EraseModule(j);
+ this->EraseFactory(j);
+ this->Log(DEFAULT,"Module %s unloaded",filename);
+ this->ModCount--;
+ BuildISupport();
+ return true;
+ }
+ }
+ this->Log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
+ snprintf(MODERR,MAXBUF,"Module not loaded");
+ return false;
+}
+
+bool InspIRCd::LoadModule(const char* filename)
+{
+ /* Do we have a glob pattern in the filename?
+ * The user wants to load multiple modules which
+ * match the pattern.
+ */
+ if (strchr(filename,'*') || (strchr(filename,'?')))
+ {
+ int n_match = 0;
+ DIR* library = opendir(Config->ModPath);
+ if (library)
+ {
+ /* Try and locate and load all modules matching the pattern */
+ dirent* entry = NULL;
+ while ((entry = readdir(library)))
+ {
+ if (this->MatchText(entry->d_name, filename))
+ {
+ if (!this->LoadModule(entry->d_name))
+ n_match++;
+ }
+ }
+ closedir(library);
+ }
+ /* Loadmodule will now return false if any one of the modules failed
+ * to load (but wont abort when it encounters a bad one) and when 1 or
+ * more modules were actually loaded.
+ */
+ return (n_match > 0);
+ }
+
+ char modfile[MAXBUF];
+ snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename);
+ std::string filename_str = filename;
+
+ if (!ServerConfig::DirValid(modfile))
+ {
+ this->Log(DEFAULT,"Module %s is not within the modules directory.",modfile);
+ snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
+ return false;
+ }
+ if (ServerConfig::FileExists(modfile))
+ {
+
+ for (unsigned int j = 0; j < Config->module_names.size(); j++)
+ {
+ if (Config->module_names[j] == filename_str)
+ {
+ this->Log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
+ snprintf(MODERR,MAXBUF,"Module already loaded");
+ return false;
+ }
+ }
+ Module* m = NULL;
+ ircd_module* a = NULL;
+ try
+ {
+ a = new ircd_module(this, modfile);
+ factory[this->ModCount+1] = a;
+ if (factory[this->ModCount+1]->LastError())
+ {
+ this->Log(DEFAULT,"Unable to load %s: %s",modfile,factory[this->ModCount+1]->LastError());
+ snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[this->ModCount+1]->LastError());
+ return false;
+ }
+ if ((long)factory[this->ModCount+1]->factory != -1)
+ {
+ m = factory[this->ModCount+1]->factory->CreateModule(this);
+
+ Version v = m->GetVersion();
+
+ if (v.API != API_VERSION)
+ {
+ delete m;
+ this->Log(DEFAULT,"Unable to load %s: Incorrect module API version: %d (our version: %d)",modfile,v.API,API_VERSION);
+ snprintf(MODERR,MAXBUF,"Loader/Linker error: Incorrect module API version: %d (our version: %d)",v.API,API_VERSION);
+ return false;
+ }
+ else
+ {
+ this->Log(DEFAULT,"New module introduced: %s (API version %d, Module version %d.%d.%d.%d)%s", filename, v.API, v.Major, v.Minor, v.Revision, v.Build, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
+ }
+
+ modules[this->ModCount+1] = m;
+ /* save the module and the module's classfactory, if
+ * this isnt done, random crashes can occur :/ */
+ Config->module_names.push_back(filename);
+
+ char* x = &Config->implement_lists[this->ModCount+1][0];
+ for(int t = 0; t < 255; t++)
+ x[t] = 0;
+
+ modules[this->ModCount+1]->Implements(x);
+
+ for(int t = 0; t < 255; t++)
+ Config->global_implementation[t] += Config->implement_lists[this->ModCount+1][t];
+ }
+ else
+ {
+ this->Log(DEFAULT,"Unable to load %s",modfile);
+ snprintf(MODERR,MAXBUF,"Factory function failed: Probably missing init_module() entrypoint.");
+ return false;
+ }
+ }
+ catch (CoreException& modexcept)
+ {
+ this->Log(DEFAULT,"Unable to load %s: %s",modfile,modexcept.GetReason());
+ snprintf(MODERR,MAXBUF,"Factory function of %s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ return false;
+ }
+ }
+ else
+ {
+ this->Log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
+ snprintf(MODERR,MAXBUF,"Module file could not be found");
+ return false;
+ }
+ this->ModCount++;
+ FOREACH_MOD_I(this,I_OnLoadModule,OnLoadModule(modules[this->ModCount],filename_str));
+ // now work out which modules, if any, want to move to the back of the queue,
+ // and if they do, move them there.
+ std::vector<std::string> put_to_back;
+ std::vector<std::string> put_to_front;
+ std::map<std::string,std::string> put_before;
+ std::map<std::string,std::string> put_after;
+ for (unsigned int j = 0; j < Config->module_names.size(); j++)
+ {
+ if (modules[j]->Prioritize() == PRIORITY_LAST)
+ put_to_back.push_back(Config->module_names[j]);
+ else if (modules[j]->Prioritize() == PRIORITY_FIRST)
+ put_to_front.push_back(Config->module_names[j]);
+ else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_BEFORE)
+ put_before[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
+ else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_AFTER)
+ put_after[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
+ }
+ for (unsigned int j = 0; j < put_to_back.size(); j++)
+ MoveToLast(put_to_back[j]);
+ for (unsigned int j = 0; j < put_to_front.size(); j++)
+ MoveToFirst(put_to_front[j]);
+ for (std::map<std::string,std::string>::iterator j = put_before.begin(); j != put_before.end(); j++)
+ MoveBefore(j->first,j->second);
+ for (std::map<std::string,std::string>::iterator j = put_after.begin(); j != put_after.end(); j++)
+ MoveAfter(j->first,j->second);
+ BuildISupport();
+ return true;
+}
+
+void InspIRCd::DoOneIteration(bool process_module_sockets)
+{
+#ifndef WIN32
+ static rusage ru;
+#else
+ static time_t uptime;
+ static struct tm * stime;
+ static char window_title[100];
+#endif
+
+ /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
+ * Once per loop iteration is pleanty.
+ */
+ OLDTIME = TIME;
+ TIME = time(NULL);
+
+ /* Run background module timers every few seconds
+ * (the docs say modules shouldnt rely on accurate
+ * timing using this event, so we dont have to
+ * time this exactly).
+ */
+ if (TIME != OLDTIME)
+ {
+ if (TIME < OLDTIME)
+ WriteOpers("*** \002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME));
+ if ((TIME % 3600) == 0)
+ {
+ this->RehashUsersAndChans();
+ FOREACH_MOD_I(this, I_OnGarbageCollect, OnGarbageCollect());
+ }
+ Timers->TickTimers(TIME);
+ this->DoBackgroundUserStuff(TIME);
+
+ if ((TIME % 5) == 0)
+ {
+ XLines->expire_lines();
+ FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME));
+ Timers->TickMissedTimers(TIME);
+ }
+#ifndef WIN32
+ /* Same change as in cmd_stats.cpp, use RUSAGE_SELF rather than '0' -- Om */
+ if (!getrusage(RUSAGE_SELF, &ru))
+ {
+ gettimeofday(&this->stats->LastSampled, NULL);
+ this->stats->LastCPU = ru.ru_utime;
+ }
+#else
+ CheckIPC(this);
+
+ if(Config->nofork)
+ {
+ uptime = Time() - startup_time;
+ stime = gmtime(&uptime);
+ snprintf(window_title, 100, "InspIRCd - %u clients, %u accepted connections - Up %u days, %.2u:%.2u:%.2u",
+ LocalUserCount(), stats->statsAccept, stime->tm_yday, stime->tm_hour, stime->tm_min, stime->tm_sec);
+ SetConsoleTitle(window_title);
+ }
+#endif
+ }
+
+ /* Call the socket engine to wait on the active
+ * file descriptors. The socket engine has everything's
+ * descriptors in its list... dns, modules, users,
+ * servers... so its nice and easy, just one call.
+ * This will cause any read or write events to be
+ * dispatched to their handlers.
+ */
+ SE->DispatchEvents();
+
+ /* if any users was quit, take them out */
+ GlobalCulls.Apply();
+
+ /* If any inspsockets closed, remove them */
+ for (std::map<InspSocket*,InspSocket*>::iterator x = SocketCull.begin(); x != SocketCull.end(); ++x)
+ {
+ SE->DelFd(x->second);
+ x->second->Close();
+ delete x->second;
+ }
+ SocketCull.clear();
+}
+
+int InspIRCd::Run()
+{
+ while (true)
+ {
+ DoOneIteration(true);
+ }
+ /* This is never reached -- we hope! */
+ return 0;
+}
+
+/**********************************************************************************/
+
+/**
+ * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*.
+ */
+
+int main(int argc, char** argv)
+{
+ SI = new InspIRCd(argc, argv);
+ SI->Run();
+ delete SI;
+ return 0;
+}
+
+/* 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(userrec* user)
+{
+ if (!Config->global_implementation[I_OnCheckReady])
+ return true;
+
+ for (int i = 0; i <= this->GetModuleCount(); i++)
+ {
+ if (Config->implement_lists[i][I_OnCheckReady])
+ {
+ int res = modules[i]->OnCheckReady(user);
+ if (!res)
+ return false;
+ }
+ }
+ return true;
+}
+
+int InspIRCd::GetModuleCount()
+{
+ return this->ModCount;
+}
+
+time_t InspIRCd::Time(bool delta)
+{
+ if (delta)
+ return TIME + time_delta;
+ return TIME;
+}
+
+int InspIRCd::SetTimeDelta(int delta)
+{
+ int old = time_delta;
+ time_delta = delta;
+ this->Log(DEBUG, "Time delta set to %d (was %d)", time_delta, old);
+ return old;
+}
+
+void InspIRCd::AddLocalClone(userrec* user)
+{
+ clonemap::iterator x = local_clones.find(user->GetIPString());
+ if (x != local_clones.end())
+ x->second++;
+ else
+ local_clones[user->GetIPString()] = 1;
+}
+
+void InspIRCd::AddGlobalClone(userrec* user)
+{
+ clonemap::iterator y = global_clones.find(user->GetIPString());
+ if (y != global_clones.end())
+ y->second++;
+ else
+ global_clones[user->GetIPString()] = 1;
+}
+
+int InspIRCd::GetTimeDelta()
+{
+ return time_delta;
+}
+
+bool FileLogger::Readable()
+{
+ return false;
+}
+
+void FileLogger::HandleEvent(EventType et, int errornum)
+{
+ this->WriteLogLine("");
+ if (log)
+ ServerInstance->SE->DelFd(this);
+}
+
+void FileLogger::WriteLogLine(const std::string &line)
+{
+ if (line.length())
+ buffer.append(line);
+
+ if (log)
+ {
+ int written = fprintf(log,"%s",buffer.c_str());
+#ifdef WINDOWS
+ buffer.clear();
+#else
+ if ((written >= 0) && (written < (int)buffer.length()))
+ {
+ buffer.erase(0, buffer.length());
+ ServerInstance->SE->AddFd(this);
+ }
+ else if (written == -1)
+ {
+ if (errno == EAGAIN)
+ ServerInstance->SE->AddFd(this);
+ }
+ else
+ {
+ /* Wrote the whole buffer, and no need for write callback */
+ buffer.clear();
+ }
+#endif
+ if (writeops++ % 20)
+ {
+ fflush(log);
+ }
+ }
+}
+
+void FileLogger::Close()
+{
+ if (log)
+ {
+ /* Burlex: Windows assumes nonblocking on FILE* pointers anyway, and also "file" fd's aren't the same
+ * as socket fd's. */
+#ifndef WIN32
+ int flags = fcntl(fileno(log), F_GETFL, 0);
+ fcntl(fileno(log), F_SETFL, flags ^ O_NONBLOCK);
+#endif
+ if (buffer.size())
+ fprintf(log,"%s",buffer.c_str());
+
+#ifndef WINDOWS
+ ServerInstance->SE->DelFd(this);
+#endif
+
+ fflush(log);
+ fclose(log);
+ }
+
+ buffer.clear();
+}
+
+FileLogger::FileLogger(InspIRCd* Instance, FILE* logfile) : ServerInstance(Instance), log(logfile), writeops(0)
+{
+ if (log)
+ {
+ irc::sockets::NonBlocking(fileno(log));
+ this->SetFd(fileno(log));
+ buffer.clear();
+ }
+}
+
+FileLogger::~FileLogger()
+{
+ this->Close();
+}
+
diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp
index a2c5cc47f..f29366c73 100644
--- a/src/inspsocket.cpp
+++ b/src/inspsocket.cpp
@@ -1 +1,750 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "socket.h" #include "configreader.h" #include "inspstring.h" #include "socketengine.h" #include "inspircd.h" using irc::sockets::OpenTCPSocket; bool InspSocket::Readable() { return ((this->state != I_CONNECTING) && (this->WaitingForWriteEvent == false)); } InspSocket::InspSocket(InspIRCd* SI) { this->Timeout = NULL; this->state = I_DISCONNECTED; this->fd = -1; this->WaitingForWriteEvent = false; this->Instance = SI; this->IsIOHooked = false; } InspSocket::InspSocket(InspIRCd* SI, int newfd, const char* ip) { this->Timeout = NULL; this->fd = newfd; this->state = I_CONNECTED; strlcpy(this->IP,ip,MAXBUF); this->WaitingForWriteEvent = false; this->Instance = SI; this->IsIOHooked = false; if (this->fd > -1) this->Instance->SE->AddFd(this); } InspSocket::InspSocket(InspIRCd* SI, const std::string &ipaddr, int aport, bool listening, unsigned long maxtime, const std::string &connectbindip) { this->cbindip = connectbindip; this->fd = -1; this->Instance = SI; strlcpy(host,ipaddr.c_str(),MAXBUF); this->WaitingForWriteEvent = false; this->IsIOHooked = false; this->Timeout = NULL; if (listening) { if ((this->fd = OpenTCPSocket(host)) == ERROR) { this->fd = -1; this->state = I_ERROR; this->OnError(I_ERR_SOCKET); return; } else { if (!SI->BindSocket(this->fd,aport,(char*)ipaddr.c_str())) { this->Close(); this->fd = -1; this->state = I_ERROR; this->OnError(I_ERR_BIND); this->ClosePending = true; return; } else { this->state = I_LISTENING; this->port = aport; if (this->fd > -1) { if (!this->Instance->SE->AddFd(this)) { this->Close(); this->state = I_ERROR; this->OnError(I_ERR_NOMOREFDS); } } return; } } } else { strlcpy(this->host,ipaddr.c_str(),MAXBUF); this->port = aport; bool ipvalid = true; #ifdef IPV6 if (strchr(host,':')) { in6_addr n; if (inet_pton(AF_INET6, host, &n) < 1) ipvalid = false; } else #endif { in_addr n; if (inet_aton(host,&n) < 1) ipvalid = false; } if (!ipvalid) { this->Instance->Log(DEBUG,"BUG: Hostname passed to InspSocket, rather than an IP address!"); this->OnError(I_ERR_CONNECT); this->Close(); this->fd = -1; this->state = I_ERROR; return; } else { strlcpy(this->IP,host,MAXBUF); timeout_val = maxtime; if (!this->DoConnect()) { this->OnError(I_ERR_CONNECT); this->Close(); this->fd = -1; this->state = I_ERROR; return; } } } } void InspSocket::WantWrite() { this->Instance->SE->WantWrite(this); this->WaitingForWriteEvent = true; } void InspSocket::SetQueues(int nfd) { // attempt to increase socket sendq and recvq as high as its possible int sendbuf = 32768; int recvbuf = 32768; setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const char *)&sendbuf,sizeof(sendbuf)); setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const char *)&recvbuf,sizeof(sendbuf)); } /* Most irc servers require you to specify the ip you want to bind to. * If you dont specify an IP, they rather dumbly bind to the first IP * of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP * addresses we've bound server ports to, and we try and bind our outbound * connections to the first usable non-loopback and non-any IP we find. * This is easier to configure when you have a lot of links and a lot * of servers to configure. */ bool InspSocket::BindAddr(const std::string &ip) { ConfigReader Conf(this->Instance); socklen_t size = sizeof(sockaddr_in); #ifdef IPV6 bool v6 = false; /* Are we looking for a binding to fit an ipv6 host? */ if ((ip.empty()) || (ip.find(':') != std::string::npos)) v6 = true; #endif int j = 0; while (j < Conf.Enumerate("bind") || (!ip.empty())) { std::string IP = ip.empty() ? Conf.ReadValue("bind","address",j) : ip; if (!ip.empty() || Conf.ReadValue("bind","type",j) == "servers") { if (!ip.empty() || ((IP != "*") && (IP != "127.0.0.1") && (!IP.empty()) && (IP != "::1"))) { sockaddr* s = new sockaddr[2]; #ifdef IPV6 if (v6) { in6_addr n; if (inet_pton(AF_INET6, IP.c_str(), &n) > 0) { memcpy(&((sockaddr_in6*)s)->sin6_addr, &n, sizeof(n)); ((sockaddr_in6*)s)->sin6_port = 0; ((sockaddr_in6*)s)->sin6_family = AF_INET6; size = sizeof(sockaddr_in6); } else { delete[] s; j++; continue; } } else #endif { in_addr n; if (inet_aton(IP.c_str(), &n) > 0) { ((sockaddr_in*)s)->sin_addr = n; ((sockaddr_in*)s)->sin_port = 0; ((sockaddr_in*)s)->sin_family = AF_INET; } else { delete[] s; j++; continue; } } if (bind(this->fd, s, size) < 0) { this->state = I_ERROR; this->OnError(I_ERR_BIND); this->fd = -1; delete[] s; return false; } delete[] s; return true; } } j++; } return true; } bool InspSocket::DoConnect() { sockaddr* addr = new sockaddr[2]; socklen_t size = sizeof(sockaddr_in); #ifdef IPV6 bool v6 = false; if ((!*this->host) || strchr(this->host, ':')) v6 = true; if (v6) { this->fd = socket(AF_INET6, SOCK_STREAM, 0); if ((this->fd > -1) && ((strstr(this->IP,"::ffff:") != (char*)&this->IP) && (strstr(this->IP,"::FFFF:") != (char*)&this->IP))) { if (!this->BindAddr(this->cbindip)) { delete[] addr; return false; } } } else #endif { this->fd = socket(AF_INET, SOCK_STREAM, 0); if (this->fd > -1) { if (!this->BindAddr(this->cbindip)) { delete[] addr; return false; } } } if (this->fd == -1) { this->state = I_ERROR; this->OnError(I_ERR_SOCKET); delete[] addr; return false; } #ifdef IPV6 if (v6) { in6_addr addy; if (inet_pton(AF_INET6, this->host, &addy) > 0) { ((sockaddr_in6*)addr)->sin6_family = AF_INET6; memcpy(&((sockaddr_in6*)addr)->sin6_addr, &addy, sizeof(addy)); ((sockaddr_in6*)addr)->sin6_port = htons(this->port); size = sizeof(sockaddr_in6); } } else #endif { in_addr addy; if (inet_aton(this->host, &addy) > 0) { ((sockaddr_in*)addr)->sin_family = AF_INET; ((sockaddr_in*)addr)->sin_addr = addy; ((sockaddr_in*)addr)->sin_port = htons(this->port); } } #ifndef WIN32 int flags = fcntl(this->fd, F_GETFL, 0); fcntl(this->fd, F_SETFL, flags | O_NONBLOCK); #else unsigned long flags = 0; ioctlsocket(this->fd, FIONBIO, &flags); #endif if (connect(this->fd, (sockaddr*)addr, size) == -1) { if (errno != EINPROGRESS) { this->OnError(I_ERR_CONNECT); this->Close(); this->state = I_ERROR; return false; } this->Timeout = new SocketTimeout(this->GetFd(), this->Instance, this, timeout_val, this->Instance->Time()); this->Instance->Timers->AddTimer(this->Timeout); } this->state = I_CONNECTING; if (this->fd > -1) { if (!this->Instance->SE->AddFd(this)) { this->OnError(I_ERR_NOMOREFDS); this->Close(); this->state = I_ERROR; return false; } this->SetQueues(this->fd); } return true; } void InspSocket::Close() { /* Save this, so we dont lose it, * otherise on failure, error messages * might be inaccurate. */ int save = errno; if (this->fd > -1) { if (this->IsIOHooked && Instance->Config->GetIOHook(this)) { try { Instance->Config->GetIOHook(this)->OnRawSocketClose(this->fd); } catch (CoreException& modexcept) { Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } } this->OnClose(); shutdown(this->fd,2); close(this->fd); if (Instance->SocketCull.find(this) == Instance->SocketCull.end()) Instance->SocketCull[this] = this; } errno = save; } std::string InspSocket::GetIP() { return this->IP; } char* InspSocket::Read() { #ifdef WINDOWS if ((fd < 0) || (m_internalFd > MAX_DESCRIPTORS)) #else if ((fd < 0) || (fd > MAX_DESCRIPTORS)) #endif return NULL; int n = 0; if (this->IsIOHooked) { int result2 = 0; int MOD_RESULT = 0; try { MOD_RESULT = Instance->Config->GetIOHook(this)->OnRawSocketRead(this->fd,this->ibuf,sizeof(this->ibuf),result2); } catch (CoreException& modexcept) { Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } if (MOD_RESULT < 0) { n = -1; errno = EAGAIN; } else { n = result2; } } else { n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0); } if ((n > 0) && (n <= (int)sizeof(this->ibuf))) { ibuf[n] = 0; return ibuf; } else { int err = errno; if (err == EAGAIN) return ""; else return NULL; } } void InspSocket::MarkAsClosed() { } // There are two possible outcomes to this function. // It will either write all of the data, or an undefined amount. // If an undefined amount is written the connection has failed // and should be aborted. int InspSocket::Write(const std::string &data) { /* Try and append the data to the back of the queue, and send it on its way */ outbuffer.push_back(data); this->Instance->SE->WantWrite(this); return (!this->FlushWriteBuffer()); } bool InspSocket::FlushWriteBuffer() { errno = 0; if ((this->fd > -1) && (this->state == I_CONNECTED)) { if (this->IsIOHooked) { while (outbuffer.size() && (errno != EAGAIN)) { try { /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to * implement their own buffering mechanisms */ Instance->Config->GetIOHook(this)->OnRawSocketWrite(this->fd, outbuffer[0].c_str(), outbuffer[0].length()); outbuffer.pop_front(); } catch (CoreException& modexcept) { Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); return true; } } } else { /* If we have multiple lines, try to send them all, * not just the first one -- Brain */ while (outbuffer.size() && (errno != EAGAIN)) { /* Send a line */ #ifndef WIN32 int result = write(this->fd,outbuffer[0].c_str(),outbuffer[0].length()); #else int result = send(this->fd,outbuffer[0].c_str(),outbuffer[0].length(), 0); #endif if (result > 0) { if ((unsigned int)result >= outbuffer[0].length()) { /* The whole block was written (usually a line) * Pop the block off the front of the queue, * dont set errno, because we are clear of errors * and want to try and write the next block too. */ outbuffer.pop_front(); } else { std::string temp = outbuffer[0].substr(result); outbuffer[0] = temp; /* We didnt get the whole line out. arses. * Try again next time, i guess. Set errno, * because we shouldnt be writing any more now, * until the socketengine says its safe to do so. */ errno = EAGAIN; } } else if ((result == -1) && (errno != EAGAIN)) { this->OnError(I_ERR_WRITE); this->state = I_ERROR; this->Instance->SE->DelFd(this); this->Close(); return true; } } } } if ((errno == EAGAIN) && (fd > -1)) { this->Instance->SE->WantWrite(this); } return (fd < 0); } void SocketTimeout::Tick(time_t now) { if (ServerInstance->SE->GetRef(this->sfd) != this->sock) return; if (this->sock->state == I_CONNECTING) { // for non-listening sockets, the timeout can occur // which causes termination of the connection after // the given number of seconds without a successful // connection. this->sock->OnTimeout(); this->sock->OnError(I_ERR_TIMEOUT); this->sock->timeout = true; /* NOTE: We must set this AFTER DelFd, as we added * this socket whilst writeable. This means that we * must DELETE the socket whilst writeable too! */ this->sock->state = I_ERROR; if (ServerInstance->SocketCull.find(this->sock) == ServerInstance->SocketCull.end()) ServerInstance->SocketCull[this->sock] = this->sock; } this->sock->Timeout = NULL; } bool InspSocket::Poll() { #ifdef WINDOWS if(Instance->SE->GetRef(this->fd) != this) return false; int incoming = -1; #else if (this->Instance->SE->GetRef(this->fd) != this) return false; int incoming = -1; if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; #endif switch (this->state) { case I_CONNECTING: /* Our socket was in write-state, so delete it and re-add it * in read-state. */ #ifndef WINDOWS if (this->fd > -1) { this->Instance->SE->DelFd(this); this->SetState(I_CONNECTED); if (!this->Instance->SE->AddFd(this)) return false; } #else this->SetState(I_CONNECTED); #endif Instance->Log(DEBUG,"Inspsocket I_CONNECTING state"); if (Instance->Config->GetIOHook(this)) { Instance->Log(DEBUG,"Hook for raw connect"); try { Instance->Config->GetIOHook(this)->OnRawSocketConnect(this->fd); } catch (CoreException& modexcept) { Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } } return this->OnConnected(); break; case I_LISTENING: { sockaddr* client = new sockaddr[2]; length = sizeof (sockaddr_in); std::string recvip; #ifdef IPV6 if ((!*this->host) || strchr(this->host, ':')) length = sizeof(sockaddr_in6); #endif incoming = _accept (this->fd, client, &length); #ifdef IPV6 if ((!*this->host) || strchr(this->host, ':')) { char buf[1024]; recvip = inet_ntop(AF_INET6, &((sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf)); } else #endif recvip = inet_ntoa(((sockaddr_in*)client)->sin_addr); this->OnIncomingConnection(incoming, (char*)recvip.c_str()); if (this->IsIOHooked) { try { Instance->Config->GetIOHook(this)->OnRawSocketAccept(incoming, recvip.c_str(), this->port); } catch (CoreException& modexcept) { Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } } this->SetQueues(incoming); delete[] client; return true; } break; case I_CONNECTED: /* Process the read event */ return this->OnDataReady(); break; default: break; } return true; } void InspSocket::SetState(InspSocketState s) { this->state = s; } InspSocketState InspSocket::GetState() { return this->state; } int InspSocket::GetFd() { return this->fd; } bool InspSocket::OnConnected() { return true; } void InspSocket::OnError(InspSocketError e) { return; } int InspSocket::OnDisconnect() { return 0; } int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; } bool InspSocket::OnDataReady() { return true; } bool InspSocket::OnWriteReady() { return true; } void InspSocket::OnTimeout() { return; } void InspSocket::OnClose() { return; } InspSocket::~InspSocket() { this->Close(); if (Timeout) { Instance->Timers->DelTimer(Timeout); Timeout = NULL; } } void InspSocket::HandleEvent(EventType et, int errornum) { switch (et) { case EVENT_ERROR: switch (errornum) { case ETIMEDOUT: this->OnError(I_ERR_TIMEOUT); break; case ECONNREFUSED: case 0: this->OnError(this->state == I_CONNECTING ? I_ERR_CONNECT : I_ERR_WRITE); break; case EADDRINUSE: this->OnError(I_ERR_BIND); break; case EPIPE: case EIO: this->OnError(I_ERR_WRITE); break; } if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) this->Instance->SocketCull[this] = this; return; break; case EVENT_READ: if (!this->Poll()) { if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) this->Instance->SocketCull[this] = this; return; } break; case EVENT_WRITE: if (this->WaitingForWriteEvent) { this->WaitingForWriteEvent = false; if (!this->OnWriteReady()) { if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) this->Instance->SocketCull[this] = this; return; } } if (this->state == I_CONNECTING) { /* This might look wrong as if we should be actually calling * with EVENT_WRITE, but trust me it is correct. There are some * writeability-state things in the read code, because of how * InspSocket used to work regarding write buffering in previous * versions of InspIRCd. - Brain */ this->HandleEvent(EVENT_READ); return; } else { if (this->FlushWriteBuffer()) { if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) this->Instance->SocketCull[this] = this; return; } } break; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "socket.h"
+#include "configreader.h"
+#include "inspstring.h"
+#include "socketengine.h"
+#include "inspircd.h"
+
+using irc::sockets::OpenTCPSocket;
+
+bool InspSocket::Readable()
+{
+ return ((this->state != I_CONNECTING) && (this->WaitingForWriteEvent == false));
+}
+
+InspSocket::InspSocket(InspIRCd* SI)
+{
+ this->Timeout = NULL;
+ this->state = I_DISCONNECTED;
+ this->fd = -1;
+ this->WaitingForWriteEvent = false;
+ this->Instance = SI;
+ this->IsIOHooked = false;
+}
+
+InspSocket::InspSocket(InspIRCd* SI, int newfd, const char* ip)
+{
+ this->Timeout = NULL;
+ this->fd = newfd;
+ this->state = I_CONNECTED;
+ strlcpy(this->IP,ip,MAXBUF);
+ this->WaitingForWriteEvent = false;
+ this->Instance = SI;
+ this->IsIOHooked = false;
+ if (this->fd > -1)
+ this->Instance->SE->AddFd(this);
+}
+
+InspSocket::InspSocket(InspIRCd* SI, const std::string &ipaddr, int aport, bool listening, unsigned long maxtime, const std::string &connectbindip)
+{
+ this->cbindip = connectbindip;
+ this->fd = -1;
+ this->Instance = SI;
+ strlcpy(host,ipaddr.c_str(),MAXBUF);
+ this->WaitingForWriteEvent = false;
+ this->IsIOHooked = false;
+ this->Timeout = NULL;
+ if (listening)
+ {
+ if ((this->fd = OpenTCPSocket(host)) == ERROR)
+ {
+ this->fd = -1;
+ this->state = I_ERROR;
+ this->OnError(I_ERR_SOCKET);
+ return;
+ }
+ else
+ {
+ if (!SI->BindSocket(this->fd,aport,(char*)ipaddr.c_str()))
+ {
+ this->Close();
+ this->fd = -1;
+ this->state = I_ERROR;
+ this->OnError(I_ERR_BIND);
+ this->ClosePending = true;
+ return;
+ }
+ else
+ {
+ this->state = I_LISTENING;
+ this->port = aport;
+ if (this->fd > -1)
+ {
+ if (!this->Instance->SE->AddFd(this))
+ {
+ this->Close();
+ this->state = I_ERROR;
+ this->OnError(I_ERR_NOMOREFDS);
+ }
+ }
+ return;
+ }
+ }
+ }
+ else
+ {
+ strlcpy(this->host,ipaddr.c_str(),MAXBUF);
+ this->port = aport;
+
+ bool ipvalid = true;
+#ifdef IPV6
+ if (strchr(host,':'))
+ {
+ in6_addr n;
+ if (inet_pton(AF_INET6, host, &n) < 1)
+ ipvalid = false;
+ }
+ else
+#endif
+ {
+ in_addr n;
+ if (inet_aton(host,&n) < 1)
+ ipvalid = false;
+ }
+ if (!ipvalid)
+ {
+ this->Instance->Log(DEBUG,"BUG: Hostname passed to InspSocket, rather than an IP address!");
+ this->OnError(I_ERR_CONNECT);
+ this->Close();
+ this->fd = -1;
+ this->state = I_ERROR;
+ return;
+ }
+ else
+ {
+ strlcpy(this->IP,host,MAXBUF);
+ timeout_val = maxtime;
+ if (!this->DoConnect())
+ {
+ this->OnError(I_ERR_CONNECT);
+ this->Close();
+ this->fd = -1;
+ this->state = I_ERROR;
+ return;
+ }
+ }
+ }
+}
+
+void InspSocket::WantWrite()
+{
+ this->Instance->SE->WantWrite(this);
+ this->WaitingForWriteEvent = true;
+}
+
+void InspSocket::SetQueues(int nfd)
+{
+ // attempt to increase socket sendq and recvq as high as its possible
+ int sendbuf = 32768;
+ int recvbuf = 32768;
+ setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const char *)&sendbuf,sizeof(sendbuf));
+ setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const char *)&recvbuf,sizeof(sendbuf));
+}
+
+/* Most irc servers require you to specify the ip you want to bind to.
+ * If you dont specify an IP, they rather dumbly bind to the first IP
+ * of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP
+ * addresses we've bound server ports to, and we try and bind our outbound
+ * connections to the first usable non-loopback and non-any IP we find.
+ * This is easier to configure when you have a lot of links and a lot
+ * of servers to configure.
+ */
+bool InspSocket::BindAddr(const std::string &ip)
+{
+ ConfigReader Conf(this->Instance);
+ socklen_t size = sizeof(sockaddr_in);
+#ifdef IPV6
+ bool v6 = false;
+ /* Are we looking for a binding to fit an ipv6 host? */
+ if ((ip.empty()) || (ip.find(':') != std::string::npos))
+ v6 = true;
+#endif
+ int j = 0;
+ while (j < Conf.Enumerate("bind") || (!ip.empty()))
+ {
+ std::string IP = ip.empty() ? Conf.ReadValue("bind","address",j) : ip;
+ if (!ip.empty() || Conf.ReadValue("bind","type",j) == "servers")
+ {
+ if (!ip.empty() || ((IP != "*") && (IP != "127.0.0.1") && (!IP.empty()) && (IP != "::1")))
+ {
+ sockaddr* s = new sockaddr[2];
+#ifdef IPV6
+ if (v6)
+ {
+ in6_addr n;
+ if (inet_pton(AF_INET6, IP.c_str(), &n) > 0)
+ {
+ memcpy(&((sockaddr_in6*)s)->sin6_addr, &n, sizeof(n));
+ ((sockaddr_in6*)s)->sin6_port = 0;
+ ((sockaddr_in6*)s)->sin6_family = AF_INET6;
+ size = sizeof(sockaddr_in6);
+ }
+ else
+ {
+ delete[] s;
+ j++;
+ continue;
+ }
+ }
+ else
+#endif
+ {
+ in_addr n;
+ if (inet_aton(IP.c_str(), &n) > 0)
+ {
+ ((sockaddr_in*)s)->sin_addr = n;
+ ((sockaddr_in*)s)->sin_port = 0;
+ ((sockaddr_in*)s)->sin_family = AF_INET;
+ }
+ else
+ {
+ delete[] s;
+ j++;
+ continue;
+ }
+ }
+
+ if (bind(this->fd, s, size) < 0)
+ {
+ this->state = I_ERROR;
+ this->OnError(I_ERR_BIND);
+ this->fd = -1;
+ delete[] s;
+ return false;
+ }
+
+ delete[] s;
+ return true;
+ }
+ }
+ j++;
+ }
+ return true;
+}
+
+bool InspSocket::DoConnect()
+{
+ sockaddr* addr = new sockaddr[2];
+ socklen_t size = sizeof(sockaddr_in);
+#ifdef IPV6
+ bool v6 = false;
+ if ((!*this->host) || strchr(this->host, ':'))
+ v6 = true;
+
+ if (v6)
+ {
+ this->fd = socket(AF_INET6, SOCK_STREAM, 0);
+ if ((this->fd > -1) && ((strstr(this->IP,"::ffff:") != (char*)&this->IP) && (strstr(this->IP,"::FFFF:") != (char*)&this->IP)))
+ {
+ if (!this->BindAddr(this->cbindip))
+ {
+ delete[] addr;
+ return false;
+ }
+ }
+ }
+ else
+#endif
+ {
+ this->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (this->fd > -1)
+ {
+ if (!this->BindAddr(this->cbindip))
+ {
+ delete[] addr;
+ return false;
+ }
+ }
+ }
+
+ if (this->fd == -1)
+ {
+ this->state = I_ERROR;
+ this->OnError(I_ERR_SOCKET);
+ delete[] addr;
+ return false;
+ }
+
+#ifdef IPV6
+ if (v6)
+ {
+ in6_addr addy;
+ if (inet_pton(AF_INET6, this->host, &addy) > 0)
+ {
+ ((sockaddr_in6*)addr)->sin6_family = AF_INET6;
+ memcpy(&((sockaddr_in6*)addr)->sin6_addr, &addy, sizeof(addy));
+ ((sockaddr_in6*)addr)->sin6_port = htons(this->port);
+ size = sizeof(sockaddr_in6);
+ }
+ }
+ else
+#endif
+ {
+ in_addr addy;
+ if (inet_aton(this->host, &addy) > 0)
+ {
+ ((sockaddr_in*)addr)->sin_family = AF_INET;
+ ((sockaddr_in*)addr)->sin_addr = addy;
+ ((sockaddr_in*)addr)->sin_port = htons(this->port);
+ }
+ }
+#ifndef WIN32
+ int flags = fcntl(this->fd, F_GETFL, 0);
+ fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
+#else
+ unsigned long flags = 0;
+ ioctlsocket(this->fd, FIONBIO, &flags);
+#endif
+ if (connect(this->fd, (sockaddr*)addr, size) == -1)
+ {
+ if (errno != EINPROGRESS)
+ {
+ this->OnError(I_ERR_CONNECT);
+ this->Close();
+ this->state = I_ERROR;
+ return false;
+ }
+
+ this->Timeout = new SocketTimeout(this->GetFd(), this->Instance, this, timeout_val, this->Instance->Time());
+ this->Instance->Timers->AddTimer(this->Timeout);
+ }
+ this->state = I_CONNECTING;
+ if (this->fd > -1)
+ {
+ if (!this->Instance->SE->AddFd(this))
+ {
+ this->OnError(I_ERR_NOMOREFDS);
+ this->Close();
+ this->state = I_ERROR;
+ return false;
+ }
+ this->SetQueues(this->fd);
+ }
+ return true;
+}
+
+
+void InspSocket::Close()
+{
+ /* Save this, so we dont lose it,
+ * otherise on failure, error messages
+ * might be inaccurate.
+ */
+ int save = errno;
+ if (this->fd > -1)
+ {
+ if (this->IsIOHooked && Instance->Config->GetIOHook(this))
+ {
+ try
+ {
+ Instance->Config->GetIOHook(this)->OnRawSocketClose(this->fd);
+ }
+ catch (CoreException& modexcept)
+ {
+ Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+ this->OnClose();
+ shutdown(this->fd,2);
+ close(this->fd);
+
+ if (Instance->SocketCull.find(this) == Instance->SocketCull.end())
+ Instance->SocketCull[this] = this;
+ }
+ errno = save;
+}
+
+std::string InspSocket::GetIP()
+{
+ return this->IP;
+}
+
+char* InspSocket::Read()
+{
+#ifdef WINDOWS
+ if ((fd < 0) || (m_internalFd > MAX_DESCRIPTORS))
+#else
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+#endif
+ return NULL;
+
+ int n = 0;
+
+ if (this->IsIOHooked)
+ {
+ int result2 = 0;
+ int MOD_RESULT = 0;
+ try
+ {
+ MOD_RESULT = Instance->Config->GetIOHook(this)->OnRawSocketRead(this->fd,this->ibuf,sizeof(this->ibuf),result2);
+ }
+ catch (CoreException& modexcept)
+ {
+ Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ if (MOD_RESULT < 0)
+ {
+ n = -1;
+ errno = EAGAIN;
+ }
+ else
+ {
+ n = result2;
+ }
+ }
+ else
+ {
+ n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0);
+ }
+
+ if ((n > 0) && (n <= (int)sizeof(this->ibuf)))
+ {
+ ibuf[n] = 0;
+ return ibuf;
+ }
+ else
+ {
+ int err = errno;
+ if (err == EAGAIN)
+ return "";
+ else
+ return NULL;
+ }
+}
+
+void InspSocket::MarkAsClosed()
+{
+}
+
+// There are two possible outcomes to this function.
+// It will either write all of the data, or an undefined amount.
+// If an undefined amount is written the connection has failed
+// and should be aborted.
+int InspSocket::Write(const std::string &data)
+{
+ /* Try and append the data to the back of the queue, and send it on its way
+ */
+ outbuffer.push_back(data);
+ this->Instance->SE->WantWrite(this);
+ return (!this->FlushWriteBuffer());
+}
+
+bool InspSocket::FlushWriteBuffer()
+{
+ errno = 0;
+ if ((this->fd > -1) && (this->state == I_CONNECTED))
+ {
+ if (this->IsIOHooked)
+ {
+ while (outbuffer.size() && (errno != EAGAIN))
+ {
+ try
+ {
+ /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
+ * implement their own buffering mechanisms
+ */
+ Instance->Config->GetIOHook(this)->OnRawSocketWrite(this->fd, outbuffer[0].c_str(), outbuffer[0].length());
+ outbuffer.pop_front();
+ }
+ catch (CoreException& modexcept)
+ {
+ Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ return true;
+ }
+ }
+ }
+ else
+ {
+ /* If we have multiple lines, try to send them all,
+ * not just the first one -- Brain
+ */
+ while (outbuffer.size() && (errno != EAGAIN))
+ {
+ /* Send a line */
+#ifndef WIN32
+ int result = write(this->fd,outbuffer[0].c_str(),outbuffer[0].length());
+#else
+ int result = send(this->fd,outbuffer[0].c_str(),outbuffer[0].length(), 0);
+#endif
+ if (result > 0)
+ {
+ if ((unsigned int)result >= outbuffer[0].length())
+ {
+ /* The whole block was written (usually a line)
+ * Pop the block off the front of the queue,
+ * dont set errno, because we are clear of errors
+ * and want to try and write the next block too.
+ */
+ outbuffer.pop_front();
+ }
+ else
+ {
+ std::string temp = outbuffer[0].substr(result);
+ outbuffer[0] = temp;
+ /* We didnt get the whole line out. arses.
+ * Try again next time, i guess. Set errno,
+ * because we shouldnt be writing any more now,
+ * until the socketengine says its safe to do so.
+ */
+ errno = EAGAIN;
+ }
+ }
+ else if ((result == -1) && (errno != EAGAIN))
+ {
+ this->OnError(I_ERR_WRITE);
+ this->state = I_ERROR;
+ this->Instance->SE->DelFd(this);
+ this->Close();
+ return true;
+ }
+ }
+ }
+ }
+
+ if ((errno == EAGAIN) && (fd > -1))
+ {
+ this->Instance->SE->WantWrite(this);
+ }
+
+ return (fd < 0);
+}
+
+void SocketTimeout::Tick(time_t now)
+{
+ if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
+ return;
+
+ if (this->sock->state == I_CONNECTING)
+ {
+ // for non-listening sockets, the timeout can occur
+ // which causes termination of the connection after
+ // the given number of seconds without a successful
+ // connection.
+ this->sock->OnTimeout();
+ this->sock->OnError(I_ERR_TIMEOUT);
+ this->sock->timeout = true;
+
+ /* NOTE: We must set this AFTER DelFd, as we added
+ * this socket whilst writeable. This means that we
+ * must DELETE the socket whilst writeable too!
+ */
+ this->sock->state = I_ERROR;
+
+ if (ServerInstance->SocketCull.find(this->sock) == ServerInstance->SocketCull.end())
+ ServerInstance->SocketCull[this->sock] = this->sock;
+ }
+
+ this->sock->Timeout = NULL;
+}
+
+bool InspSocket::Poll()
+{
+#ifdef WINDOWS
+ if(Instance->SE->GetRef(this->fd) != this)
+ return false;
+ int incoming = -1;
+#else
+ if (this->Instance->SE->GetRef(this->fd) != this)
+ return false;
+
+ int incoming = -1;
+
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+#endif
+ switch (this->state)
+ {
+ case I_CONNECTING:
+ /* Our socket was in write-state, so delete it and re-add it
+ * in read-state.
+ */
+#ifndef WINDOWS
+ if (this->fd > -1)
+ {
+ this->Instance->SE->DelFd(this);
+ this->SetState(I_CONNECTED);
+ if (!this->Instance->SE->AddFd(this))
+ return false;
+ }
+#else
+ this->SetState(I_CONNECTED);
+#endif
+ Instance->Log(DEBUG,"Inspsocket I_CONNECTING state");
+ if (Instance->Config->GetIOHook(this))
+ {
+ Instance->Log(DEBUG,"Hook for raw connect");
+ try
+ {
+ Instance->Config->GetIOHook(this)->OnRawSocketConnect(this->fd);
+ }
+ catch (CoreException& modexcept)
+ {
+ Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+ return this->OnConnected();
+ break;
+ case I_LISTENING:
+ {
+ sockaddr* client = new sockaddr[2];
+ length = sizeof (sockaddr_in);
+ std::string recvip;
+#ifdef IPV6
+ if ((!*this->host) || strchr(this->host, ':'))
+ length = sizeof(sockaddr_in6);
+#endif
+ incoming = _accept (this->fd, client, &length);
+#ifdef IPV6
+ if ((!*this->host) || strchr(this->host, ':'))
+ {
+ char buf[1024];
+ recvip = inet_ntop(AF_INET6, &((sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
+ }
+ else
+#endif
+ recvip = inet_ntoa(((sockaddr_in*)client)->sin_addr);
+ this->OnIncomingConnection(incoming, (char*)recvip.c_str());
+
+ if (this->IsIOHooked)
+ {
+ try
+ {
+ Instance->Config->GetIOHook(this)->OnRawSocketAccept(incoming, recvip.c_str(), this->port);
+ }
+ catch (CoreException& modexcept)
+ {
+ Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+
+ this->SetQueues(incoming);
+
+ delete[] client;
+ return true;
+ }
+ break;
+ case I_CONNECTED:
+ /* Process the read event */
+ return this->OnDataReady();
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+void InspSocket::SetState(InspSocketState s)
+{
+ this->state = s;
+}
+
+InspSocketState InspSocket::GetState()
+{
+ return this->state;
+}
+
+int InspSocket::GetFd()
+{
+ return this->fd;
+}
+
+bool InspSocket::OnConnected() { return true; }
+void InspSocket::OnError(InspSocketError e) { return; }
+int InspSocket::OnDisconnect() { return 0; }
+int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; }
+bool InspSocket::OnDataReady() { return true; }
+bool InspSocket::OnWriteReady() { return true; }
+void InspSocket::OnTimeout() { return; }
+void InspSocket::OnClose() { return; }
+
+InspSocket::~InspSocket()
+{
+ this->Close();
+ if (Timeout)
+ {
+ Instance->Timers->DelTimer(Timeout);
+ Timeout = NULL;
+ }
+}
+
+void InspSocket::HandleEvent(EventType et, int errornum)
+{
+ switch (et)
+ {
+ case EVENT_ERROR:
+ switch (errornum)
+ {
+ case ETIMEDOUT:
+ this->OnError(I_ERR_TIMEOUT);
+ break;
+ case ECONNREFUSED:
+ case 0:
+ this->OnError(this->state == I_CONNECTING ? I_ERR_CONNECT : I_ERR_WRITE);
+ break;
+ case EADDRINUSE:
+ this->OnError(I_ERR_BIND);
+ break;
+ case EPIPE:
+ case EIO:
+ this->OnError(I_ERR_WRITE);
+ break;
+ }
+ if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
+ this->Instance->SocketCull[this] = this;
+ return;
+ break;
+ case EVENT_READ:
+ if (!this->Poll())
+ {
+ if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
+ this->Instance->SocketCull[this] = this;
+ return;
+ }
+ break;
+ case EVENT_WRITE:
+ if (this->WaitingForWriteEvent)
+ {
+ this->WaitingForWriteEvent = false;
+ if (!this->OnWriteReady())
+ {
+ if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
+ this->Instance->SocketCull[this] = this;
+ return;
+ }
+ }
+ if (this->state == I_CONNECTING)
+ {
+ /* This might look wrong as if we should be actually calling
+ * with EVENT_WRITE, but trust me it is correct. There are some
+ * writeability-state things in the read code, because of how
+ * InspSocket used to work regarding write buffering in previous
+ * versions of InspIRCd. - Brain
+ */
+ this->HandleEvent(EVENT_READ);
+ return;
+ }
+ else
+ {
+ if (this->FlushWriteBuffer())
+ {
+ if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
+ this->Instance->SocketCull[this] = this;
+ return;
+ }
+ }
+ break;
+ }
+}
+
diff --git a/src/inspstring.cpp b/src/inspstring.cpp
index b5278a6ad..98e7228d5 100644
--- a/src/inspstring.cpp
+++ b/src/inspstring.cpp
@@ -1 +1,137 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspstring.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; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspstring.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;
+}
+
diff --git a/src/mode.cpp b/src/mode.cpp
index cfb009273..db120cfe5 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -1 +1,1066 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" #include "inspstring.h" #include "mode.h" /* +s (secret) */ #include "modes/cmode_s.h" /* +p (private) */ #include "modes/cmode_p.h" /* +b (bans) */ #include "modes/cmode_b.h" /* +m (moderated) */ #include "modes/cmode_m.h" /* +t (only (half) ops can change topic) */ #include "modes/cmode_t.h" /* +n (no external messages) */ #include "modes/cmode_n.h" /* +i (invite only) */ #include "modes/cmode_i.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" /* +h (channel halfop) */ #include "modes/cmode_h.h" /* +v (channel voice) */ #include "modes/cmode_v.h" /* +s (server notices) */ #include "modes/umode_s.h" /* +w (see wallops) */ #include "modes/umode_w.h" /* +i (invisible) */ #include "modes/umode_i.h" /* +o (operator) */ #include "modes/umode_o.h" /* +n (notice mask - our implementation of snomasks) */ #include "modes/umode_n.h" ModeHandler::ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix) : ServerInstance(Instance), mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly), prefix(mprefix), count(0) { } ModeHandler::~ModeHandler() { } bool ModeHandler::IsListMode() { return list; } unsigned int ModeHandler::GetPrefixRank() { return 0; } unsigned int ModeHandler::GetCount() { return 0; } void ModeHandler::ChangeCount(int modifier) { count += modifier; } ModeType ModeHandler::GetModeType() { return m_type; } bool ModeHandler::NeedsOper() { return oper; } char ModeHandler::GetPrefix() { return prefix; } int ModeHandler::GetNumParams(bool adding) { return adding ? n_params_on : n_params_off; } char ModeHandler::GetModeChar() { return mode; } ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { return MODEACTION_DENY; } ModePair ModeHandler::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { if (dest) { return std::make_pair(dest->IsModeSet(this->mode), ""); } else { return std::make_pair(channel->IsModeSet(this->mode), ""); } } void ModeHandler::DisplayList(userrec* user, chanrec* channel) { } bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { return (ours < theirs); } ModeWatcher::ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type) : ServerInstance(Instance), mode(modeletter), m_type(type) { } ModeWatcher::~ModeWatcher() { } char ModeWatcher::GetModeChar() { return mode; } ModeType ModeWatcher::GetModeType() { return m_type; } bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type) { return true; } void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type) { } userrec* ModeParser::SanityChecks(userrec *user,const char *dest,chanrec *chan,int status) { userrec *d; if ((!user) || (!dest) || (!chan) || (!*dest)) { return NULL; } d = ServerInstance->FindNick(dest); if (!d) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, dest); return NULL; } return d; } const char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK) { if (!chan) return ""; UCListIter n = d->chans.find(chan); if (n != d->chans.end()) { if (n->second & MASK) { return ""; } n->second = n->second | MASK; switch (MASK) { case UCMODE_OP: n->first->AddOppedUser(d); break; case UCMODE_HOP: n->first->AddHalfoppedUser(d); break; case UCMODE_VOICE: n->first->AddVoicedUser(d); break; } return d->nick; } return ""; } const char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK) { if (!chan) return ""; UCListIter n = d->chans.find(chan); if (n != d->chans.end()) { if ((n->second & MASK) == 0) { return ""; } n->second ^= MASK; switch (MASK) { case UCMODE_OP: n->first->DelOppedUser(d); break; case UCMODE_HOP: n->first->DelHalfoppedUser(d); break; case UCMODE_VOICE: n->first->DelVoicedUser(d); break; } return d->nick; } return ""; } void ModeParser::DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text) { if (targetchannel) { /* Display channel's current mode string */ user->WriteServ("324 %s %s +%s",user->nick, targetchannel->name, targetchannel->ChanModes(targetchannel->HasUser(user))); user->WriteServ("329 %s %s %lu", user->nick, targetchannel->name, (unsigned long)targetchannel->age); return; } else if (targetuser) { if (targetuser->Visibility && !targetuser->Visibility->VisibleTo(user)) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, text); return; } if ((targetuser == user) || (IS_OPER(user))) { /* Display user's current mode string */ user->WriteServ("221 %s :+%s",targetuser->nick,targetuser->FormatModes()); if (IS_OPER(targetuser)) user->WriteServ("008 %s +%s :Server notice mask", targetuser->nick, targetuser->FormatNoticeMasks()); return; } else { user->WriteServ("502 %s :Can't change mode for other users", user->nick); return; } } /* No such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, text); return; } void ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool servermode) { std::string target = parameters[0]; ModeType type = MODETYPE_USER; unsigned char mask = 0; chanrec* targetchannel = ServerInstance->FindChan(parameters[0]); userrec* targetuser = ServerInstance->FindNick(parameters[0]); LastParse.clear(); /* Special case for displaying the list for listmodes, * e.g. MODE #chan b, or MODE #chan +b without a parameter */ if ((targetchannel) && (pcnt == 2)) { const char* mode = parameters[1]; int nonlistmodes_found = 0; bool sent[256]; mask = MASK_CHANNEL; memset(&sent, 0, 256); while (mode && *mode) { unsigned char mletter = *mode; if (*mode == '+') { mode++; continue; } /* Ensure the user doesnt request the same mode twice, * so they cant flood themselves off out of idiocy. */ if (!sent[mletter]) { sent[mletter] = true; } else { mode++; continue; } ModeHandler *mh = this->FindMode(*mode, MODETYPE_CHANNEL); bool display = true; if ((mh) && (mh->IsListMode())) { if (ServerInstance->Config->HideModeLists[mletter] && (targetchannel->GetStatus(user) < STATUS_HOP)) { user->WriteServ("482 %s %s :Only half-operators and above may view the +%c list",user->nick, targetchannel->name, *mode++); continue; } /** See below for a description of what craq this is :D */ unsigned char handler_id = (*mode - 65) | mask; for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) { std::string dummyparam; if (!((*watchers)->BeforeMode(user, NULL, targetchannel, dummyparam, true, MODETYPE_CHANNEL))) display = false; } if (display) mh->DisplayList(user, targetchannel); } else nonlistmodes_found++; mode++; } /* We didnt have any modes that were non-list, we can return here */ if (!nonlistmodes_found) return; } if (pcnt == 1) { this->DisplayCurrentModes(user, targetuser, targetchannel, parameters[0]); } else if (pcnt > 1) { if (targetchannel) { type = MODETYPE_CHANNEL; mask = MASK_CHANNEL; /* Extra security checks on channel modes * (e.g. are they a (half)op? */ if ((IS_LOCAL(user)) && (targetchannel->GetStatus(user) < STATUS_HOP)) { /* We don't have halfop */ int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE)); if (MOD_RESULT == ACR_DENY) return; if (MOD_RESULT == ACR_DEFAULT) { /* Are we a uline or is it a servermode? */ if ((!ServerInstance->ULine(user->server)) && (!servermode)) { /* Not enough permission: * NOT a uline and NOT a servermode, * OR, NOT halfop or above. */ user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, targetchannel->name); return; } } } } else if (targetuser) { type = MODETYPE_USER; mask = MASK_USER; if ((user != targetuser) && (!ServerInstance->ULine(user->server))) { user->WriteServ("502 %s :Can't change mode for other users", user->nick); return; } } else { /* No such nick/channel */ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); return; } std::string mode_sequence = parameters[1]; std::string parameter; std::ostringstream parameter_list; std::string output_sequence; bool adding = true, state_change = false; unsigned char handler_id = 0; int parameter_counter = 2; /* Index of first parameter */ int parameter_count = 0; bool last_successful_state_change = false; /* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */ if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-')) mode_sequence.insert(0, "+"); for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++) { unsigned char modechar = *letter; switch (modechar) { /* NB: * For + and - mode characters, we don't just stick the character into the output sequence. * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this * appearing in the output sequence, we store a flag which says there was a state change, * which is set on any + or -, however, the + or - that we finish on is only appended to * the output stream in the event it is followed by a non "+ or -" character, such as o or v. */ case '+': /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick, * however, will allow the + if it is the first item in the sequence, regardless. */ if ((!adding) || (!output_sequence.length())) state_change = true; adding = true; if (!output_sequence.length()) last_successful_state_change = false; continue; break; case '-': if ((adding) || (!output_sequence.length())) state_change = true; adding = false; if (!output_sequence.length()) last_successful_state_change = true; continue; break; default: /** * Watch carefully for the sleight of hand trick. * 65 is the ascii value of 'A'. We take this from * the char we're looking at to get a number between * 1 and 127. We then logic-or it to get the hashed * position, dependent on wether its a channel or * a user mode. This is a little stranger, but a lot * faster, than using a map of pairs. */ handler_id = (modechar - 65) | mask; if (modehandlers[handler_id]) { bool abort = false; if (modehandlers[handler_id]->GetModeType() == type) { if (modehandlers[handler_id]->GetNumParams(adding)) { /* This mode expects a parameter, do we have any parameters left in our list to use? */ if (parameter_counter < pcnt) { parameter = parameters[parameter_counter++]; /* Yerk, invalid! */ if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos)) parameter.clear(); } else { /* No parameter, continue to the next mode */ continue; } bool had_parameter = !parameter.empty(); for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) { if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false) { abort = true; break; } /* A module whacked the parameter completely, and there was one. abort. */ if ((had_parameter) && (parameter.empty())) { abort = true; break; } } if (abort) continue; } else { /* Fix by brain: mode watchers not being called for parameterless modes */ for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) { if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false) { abort = true; break; } } if (abort) continue; } /* It's an oper only mode, check if theyre an oper. If they arent, * eat any parameter that came with the mode, and continue to next */ if ((IS_LOCAL(user)) && (modehandlers[handler_id]->NeedsOper()) && (!IS_OPER(user))) { user->WriteServ("481 %s :Permission Denied - Only IRC operators may %sset %s mode %c", user->nick, adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", modehandlers[handler_id]->GetModeChar()); continue; } /* Call the handler for the mode */ ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding); if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter.empty())) { /* The handler nuked the parameter and they are supposed to have one. * We CANT continue now, even if they actually returned MODEACTION_ALLOW, * so we bail to the next mode character. */ continue; } if (ma == MODEACTION_ALLOW) { /* We're about to output a valid mode letter - was there previously a pending state-change? */ if (state_change) { if (adding != last_successful_state_change) output_sequence.append(adding ? "+" : "-"); last_successful_state_change = adding; } /* Add the mode letter */ output_sequence.push_back(modechar); /* Is there a valid parameter for this mode? If so add it to the parameter list */ if ((modehandlers[handler_id]->GetNumParams(adding)) && (!parameter.empty())) { parameter_list << " " << parameter; parameter_count++; /* Does this mode have a prefix? */ if (modehandlers[handler_id]->GetPrefix() && targetchannel) { userrec* user_to_prefix = ServerInstance->FindNick(parameter); if (user_to_prefix) targetchannel->SetPrefix(user_to_prefix, modehandlers[handler_id]->GetPrefix(), modehandlers[handler_id]->GetPrefixRank(), adding); } } /* Call all the AfterMode events in the mode watchers for this mode */ for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type); /* Reset the state change flag */ state_change = false; if ((output_sequence.length() + parameter_list.str().length() > 450) || (output_sequence.length() > 100) || (parameter_count > MAXMODES)) { /* We cant have a mode sequence this long */ letter = mode_sequence.end() - 1; continue; } } } } else { /* No mode handler? Unknown mode character then. */ user->WriteServ("472 %s %c :is unknown mode char to me",user->nick, modechar); } break; } } /* Was there at least one valid mode in the sequence? */ if (!output_sequence.empty()) { if (servermode) { if (type == MODETYPE_CHANNEL) { targetchannel->WriteChannelWithServ(ServerInstance->Config->ServerName, "MODE %s %s%s", targetchannel->name, output_sequence.c_str(), parameter_list.str().c_str()); this->LastParse = targetchannel->name; } else { targetuser->WriteServ("MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str()); this->LastParse = targetuser->nick; } } else { if (type == MODETYPE_CHANNEL) { targetchannel->WriteChannel(user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str()); FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str())); this->LastParse = targetchannel->name; } else { user->WriteTo(targetuser,"MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str()); FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, output_sequence + parameter_list.str())); this->LastParse = targetuser->nick; } } LastParse.append(" "); LastParse.append(output_sequence); LastParse.append(parameter_list.str()); } } } const std::string& ModeParser::GetLastParse() { return LastParse; } void ModeParser::CleanMask(std::string &mask) { std::string::size_type pos_of_pling = mask.find_first_of('!'); std::string::size_type pos_of_at = mask.find_first_of('@'); std::string::size_type pos_of_dot = mask.find_first_of('.'); std::string::size_type pos_of_colon = mask.find_first_of(':'); /* Because ipv6 addresses are colon delimited */ if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos)) { /* Just a nick, or just a host */ if ((pos_of_dot == std::string::npos) && (pos_of_colon == std::string::npos)) { /* It has no '.' in it, it must be a nick. */ mask.append("!*@*"); } else { /* Got a dot in it? Has to be a host */ mask = "*!*@" + mask; } } else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos)) { /* Has an @ but no !, its a user@host */ mask = "*!" + mask; } else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos)) { /* Has a ! but no @, it must be a nick!ident */ mask.append("@*"); } } bool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter) { unsigned char mask = 0; unsigned char pos = 0; /* 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; /* 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; mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; pos = (mh->GetModeChar()-65) | mask; if (modehandlers[pos]) return false; modehandlers[pos] = mh; return true; } 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; if (!modehandlers[pos]) return false; switch (mh->GetModeType()) { case MODETYPE_USER: for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) { mh->RemoveMode(i->second); } break; case MODETYPE_CHANNEL: for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) { mh->RemoveMode(i->second); } break; } modehandlers[pos] = NULL; return true; } ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt) { unsigned char mask = 0; unsigned char pos = 0; if ((modeletter < 'A') || (modeletter > 'z')) return NULL; mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; pos = (modeletter-65) | mask; return modehandlers[pos]; } std::string ModeParser::UserModeList() { 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; } std::string ModeParser::ChannelModeList() { char modestr[256]; int pointer = 0; for (unsigned char mode = 'A'; mode <= 'z'; mode++) { if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) continue; unsigned char pos = (mode-65) | MASK_CHANNEL; if (modehandlers[pos]) modestr[pointer++] = mode; } modestr[pointer++] = 0; return modestr; } std::string ModeParser::ParaModeList() { char modestr[256]; int pointer = 0; for (unsigned char mode = 'A'; mode <= 'z'; mode++) { if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) continue; unsigned char pos = (mode-65) | MASK_CHANNEL; if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true))) modestr[pointer++] = mode; } modestr[pointer++] = 0; return modestr; } ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter) { for (unsigned char mode = 'A'; mode <= 'z'; mode++) { unsigned char pos = (mode-65) | MASK_CHANNEL; if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter)) { return modehandlers[pos]; } } return NULL; } std::string ModeParser::ModeString(userrec* user, chanrec* channel) { std::string types; std::string pars; if (!channel || !user) return ""; for (unsigned char mode = 'A'; mode <= 'z'; mode++) { unsigned char pos = (mode-65) | MASK_CHANNEL; ModeHandler* mh = modehandlers[pos]; if ((mh) && (mh->GetNumParams(true)) && (mh->GetNumParams(false))) { ModePair ret; ret = mh->ModeSet(NULL, user, channel, user->nick); if ((ret.first) && (ret.second == user->nick)) { pars.append(" "); pars.append(user->nick); types.push_back(mh->GetModeChar()); } } } return types+pars; } std::string ModeParser::ChanModes() { std::string type1; /* Listmodes EXCEPT those with a prefix */ std::string type2; /* Modes that take a param when adding or removing */ std::string type3; /* Modes that only take a param when adding */ std::string type4; /* Modes that dont take a param */ for (unsigned char mode = 'A'; mode <= 'z'; mode++) { if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) continue; unsigned char pos = (mode-65) | MASK_CHANNEL; /* One parameter when adding */ if (modehandlers[pos]) { if (modehandlers[pos]->GetNumParams(true)) { if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix())) { type1 += modehandlers[pos]->GetModeChar(); } else { /* ... and one parameter when removing */ if (modehandlers[pos]->GetNumParams(false)) { /* But not a list mode */ if (!modehandlers[pos]->GetPrefix()) { type2 += modehandlers[pos]->GetModeChar(); } } else { /* No parameters when removing */ type3 += modehandlers[pos]->GetModeChar(); } } } else { type4 += modehandlers[pos]->GetModeChar(); } } } return type1 + "," + type2 + "," + type3 + "," + type4; } bool ModeParser::PrefixComparison(prefixtype one, prefixtype two) { return one.second > two.second; } std::string ModeParser::BuildPrefixes() { std::string mletters; std::string mprefixes; pfxcontainer pfx; std::map<char,char> prefix_to_mode; for (unsigned char mode = 'A'; mode <= 'z'; mode++) { if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) continue; unsigned char pos = (mode-65) | MASK_CHANNEL; if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix())) { pfx.push_back(std::make_pair<char,unsigned int>(modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetPrefixRank())); prefix_to_mode[modehandlers[pos]->GetPrefix()] = modehandlers[pos]->GetModeChar(); } } sort(pfx.begin(), pfx.end(), ModeParser::PrefixComparison); for (pfxcontainer::iterator n = pfx.begin(); n != pfx.end(); n++) { mletters = mletters + n->first; mprefixes = mprefixes + prefix_to_mode.find(n->first)->second; } return "(" + mprefixes + ")" + mletters; } bool 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; } 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 = find(modewatchers[pos].begin(),modewatchers[pos].end(),mw); if (a == modewatchers[pos].end()) { return false; } modewatchers[pos].erase(a); return true; } /** This default implementation can remove simple user modes */ void ModeHandler::RemoveMode(userrec* user) { char moderemove[MAXBUF]; const char* parameters[] = { user->nick, moderemove }; if (user->IsModeSet(this->GetModeChar())) { sprintf(moderemove,"-%c",this->GetModeChar()); ServerInstance->Parser->CallHandler("MODE", parameters, 2, user); } } /** This default implementation can remove simple channel modes * (no parameters) */ void ModeHandler::RemoveMode(chanrec* channel) { char moderemove[MAXBUF]; const char* parameters[] = { channel->name, moderemove }; if (channel->IsModeSet(this->GetModeChar())) { userrec* n = new userrec(ServerInstance); sprintf(moderemove,"-%c",this->GetModeChar()); n->SetFd(FD_MAGIC_NUMBER); ServerInstance->SendMode(parameters, 2, n); delete n; } } ModeParser::ModeParser(InspIRCd* Instance) : ServerInstance(Instance) { struct Initializer { char modechar; ModeHandler* handler; }; Initializer modes[] = { { 's', new ModeChannelSecret(Instance) }, { 'p', new ModeChannelPrivate(Instance) }, { 'm', new ModeChannelModerated(Instance) }, { 't', new ModeChannelTopicOps(Instance) }, { 'n', new ModeChannelNoExternal(Instance) }, { 'i', new ModeChannelInviteOnly(Instance) }, { 'k', new ModeChannelKey(Instance) }, { 'l', new ModeChannelLimit(Instance) }, { 'b', new ModeChannelBan(Instance) }, { 'o', new ModeChannelOp(Instance) }, { 'h', new ModeChannelHalfOp(Instance) }, { 'v', new ModeChannelVoice(Instance) }, { 's', new ModeUserServerNotice(Instance) }, { 'w', new ModeUserWallops(Instance) }, { 'i', new ModeUserInvisible(Instance) }, { 'o', new ModeUserOperator(Instance) }, { 'n', new ModeUserServerNoticeMask(Instance) }, { 0, NULL } }; /* Clear mode list */ memset(modehandlers, 0, sizeof(modehandlers)); memset(modewatchers, 0, sizeof(modewatchers)); /* Last parse string */ LastParse.clear(); /* Initialise the RFC mode letters */ for (int index = 0; modes[index].modechar; index++) this->AddMode(modes[index].handler, modes[index].modechar); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+#include "inspstring.h"
+#include "mode.h"
+
+/* +s (secret) */
+#include "modes/cmode_s.h"
+/* +p (private) */
+#include "modes/cmode_p.h"
+/* +b (bans) */
+#include "modes/cmode_b.h"
+/* +m (moderated) */
+#include "modes/cmode_m.h"
+/* +t (only (half) ops can change topic) */
+#include "modes/cmode_t.h"
+/* +n (no external messages) */
+#include "modes/cmode_n.h"
+/* +i (invite only) */
+#include "modes/cmode_i.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"
+/* +h (channel halfop) */
+#include "modes/cmode_h.h"
+/* +v (channel voice) */
+#include "modes/cmode_v.h"
+/* +s (server notices) */
+#include "modes/umode_s.h"
+/* +w (see wallops) */
+#include "modes/umode_w.h"
+/* +i (invisible) */
+#include "modes/umode_i.h"
+/* +o (operator) */
+#include "modes/umode_o.h"
+/* +n (notice mask - our implementation of snomasks) */
+#include "modes/umode_n.h"
+
+ModeHandler::ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix)
+ : ServerInstance(Instance), mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly), prefix(mprefix), count(0)
+{
+}
+
+ModeHandler::~ModeHandler()
+{
+}
+
+bool ModeHandler::IsListMode()
+{
+ return list;
+}
+
+unsigned int ModeHandler::GetPrefixRank()
+{
+ return 0;
+}
+
+unsigned int ModeHandler::GetCount()
+{
+ return 0;
+}
+
+void ModeHandler::ChangeCount(int modifier)
+{
+ count += modifier;
+}
+
+ModeType ModeHandler::GetModeType()
+{
+ return m_type;
+}
+
+bool ModeHandler::NeedsOper()
+{
+ return oper;
+}
+
+char ModeHandler::GetPrefix()
+{
+ return prefix;
+}
+
+int ModeHandler::GetNumParams(bool adding)
+{
+ return adding ? n_params_on : n_params_off;
+}
+
+char ModeHandler::GetModeChar()
+{
+ return mode;
+}
+
+ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ return MODEACTION_DENY;
+}
+
+ModePair ModeHandler::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ if (dest)
+ {
+ return std::make_pair(dest->IsModeSet(this->mode), "");
+ }
+ else
+ {
+ return std::make_pair(channel->IsModeSet(this->mode), "");
+ }
+}
+
+void ModeHandler::DisplayList(userrec* user, chanrec* channel)
+{
+}
+
+bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+{
+ return (ours < theirs);
+}
+
+ModeWatcher::ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type) : ServerInstance(Instance), mode(modeletter), m_type(type)
+{
+}
+
+ModeWatcher::~ModeWatcher()
+{
+}
+
+char ModeWatcher::GetModeChar()
+{
+ return mode;
+}
+
+ModeType ModeWatcher::GetModeType()
+{
+ return m_type;
+}
+
+bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type)
+{
+ return true;
+}
+
+void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type)
+{
+}
+
+userrec* ModeParser::SanityChecks(userrec *user,const char *dest,chanrec *chan,int status)
+{
+ userrec *d;
+ if ((!user) || (!dest) || (!chan) || (!*dest))
+ {
+ return NULL;
+ }
+ d = ServerInstance->FindNick(dest);
+ if (!d)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, dest);
+ return NULL;
+ }
+ return d;
+}
+
+const char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK)
+{
+ if (!chan)
+ return "";
+
+ UCListIter n = d->chans.find(chan);
+ if (n != d->chans.end())
+ {
+ if (n->second & MASK)
+ {
+ return "";
+ }
+ n->second = n->second | MASK;
+ switch (MASK)
+ {
+ case UCMODE_OP:
+ n->first->AddOppedUser(d);
+ break;
+ case UCMODE_HOP:
+ n->first->AddHalfoppedUser(d);
+ break;
+ case UCMODE_VOICE:
+ n->first->AddVoicedUser(d);
+ break;
+ }
+ return d->nick;
+ }
+ return "";
+}
+
+const char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK)
+{
+ if (!chan)
+ return "";
+
+ UCListIter n = d->chans.find(chan);
+ if (n != d->chans.end())
+ {
+ if ((n->second & MASK) == 0)
+ {
+ return "";
+ }
+ n->second ^= MASK;
+ switch (MASK)
+ {
+ case UCMODE_OP:
+ n->first->DelOppedUser(d);
+ break;
+ case UCMODE_HOP:
+ n->first->DelHalfoppedUser(d);
+ break;
+ case UCMODE_VOICE:
+ n->first->DelVoicedUser(d);
+ break;
+ }
+ return d->nick;
+ }
+ return "";
+}
+
+void ModeParser::DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text)
+{
+ if (targetchannel)
+ {
+ /* Display channel's current mode string */
+ user->WriteServ("324 %s %s +%s",user->nick, targetchannel->name, targetchannel->ChanModes(targetchannel->HasUser(user)));
+ user->WriteServ("329 %s %s %lu", user->nick, targetchannel->name, (unsigned long)targetchannel->age);
+ return;
+ }
+ else if (targetuser)
+ {
+ if (targetuser->Visibility && !targetuser->Visibility->VisibleTo(user))
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);
+ return;
+ }
+
+ if ((targetuser == user) || (IS_OPER(user)))
+ {
+ /* Display user's current mode string */
+ user->WriteServ("221 %s :+%s",targetuser->nick,targetuser->FormatModes());
+ if (IS_OPER(targetuser))
+ user->WriteServ("008 %s +%s :Server notice mask", targetuser->nick, targetuser->FormatNoticeMasks());
+ return;
+ }
+ else
+ {
+ user->WriteServ("502 %s :Can't change mode for other users", user->nick);
+ return;
+ }
+ }
+
+ /* No such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);
+ return;
+}
+
+void ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool servermode)
+{
+ std::string target = parameters[0];
+ ModeType type = MODETYPE_USER;
+ unsigned char mask = 0;
+ chanrec* targetchannel = ServerInstance->FindChan(parameters[0]);
+ userrec* targetuser = ServerInstance->FindNick(parameters[0]);
+
+ LastParse.clear();
+
+ /* Special case for displaying the list for listmodes,
+ * e.g. MODE #chan b, or MODE #chan +b without a parameter
+ */
+ if ((targetchannel) && (pcnt == 2))
+ {
+ const char* mode = parameters[1];
+ int nonlistmodes_found = 0;
+ bool sent[256];
+
+ mask = MASK_CHANNEL;
+
+ memset(&sent, 0, 256);
+
+ while (mode && *mode)
+ {
+ unsigned char mletter = *mode;
+
+ if (*mode == '+')
+ {
+ mode++;
+ continue;
+ }
+
+ /* Ensure the user doesnt request the same mode twice,
+ * so they cant flood themselves off out of idiocy.
+ */
+ if (!sent[mletter])
+ {
+ sent[mletter] = true;
+ }
+ else
+ {
+ mode++;
+ continue;
+ }
+
+ ModeHandler *mh = this->FindMode(*mode, MODETYPE_CHANNEL);
+ bool display = true;
+
+ if ((mh) && (mh->IsListMode()))
+ {
+ if (ServerInstance->Config->HideModeLists[mletter] && (targetchannel->GetStatus(user) < STATUS_HOP))
+ {
+ user->WriteServ("482 %s %s :Only half-operators and above may view the +%c list",user->nick, targetchannel->name, *mode++);
+ continue;
+ }
+
+ /** See below for a description of what craq this is :D
+ */
+ unsigned char handler_id = (*mode - 65) | mask;
+
+ for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ {
+ std::string dummyparam;
+
+ if (!((*watchers)->BeforeMode(user, NULL, targetchannel, dummyparam, true, MODETYPE_CHANNEL)))
+ display = false;
+ }
+
+ if (display)
+ mh->DisplayList(user, targetchannel);
+ }
+ else
+ nonlistmodes_found++;
+
+ mode++;
+ }
+
+ /* We didnt have any modes that were non-list, we can return here */
+ if (!nonlistmodes_found)
+ return;
+ }
+
+ if (pcnt == 1)
+ {
+ this->DisplayCurrentModes(user, targetuser, targetchannel, parameters[0]);
+ }
+ else if (pcnt > 1)
+ {
+ if (targetchannel)
+ {
+ type = MODETYPE_CHANNEL;
+ mask = MASK_CHANNEL;
+
+ /* Extra security checks on channel modes
+ * (e.g. are they a (half)op?
+ */
+
+ if ((IS_LOCAL(user)) && (targetchannel->GetStatus(user) < STATUS_HOP))
+ {
+ /* We don't have halfop */
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE));
+ if (MOD_RESULT == ACR_DENY)
+ return;
+
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ /* Are we a uline or is it a servermode? */
+ if ((!ServerInstance->ULine(user->server)) && (!servermode))
+ {
+ /* Not enough permission:
+ * NOT a uline and NOT a servermode,
+ * OR, NOT halfop or above.
+ */
+ user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, targetchannel->name);
+ return;
+ }
+ }
+ }
+ }
+ else if (targetuser)
+ {
+ type = MODETYPE_USER;
+ mask = MASK_USER;
+ if ((user != targetuser) && (!ServerInstance->ULine(user->server)))
+ {
+ user->WriteServ("502 %s :Can't change mode for other users", user->nick);
+ return;
+ }
+ }
+ else
+ {
+ /* No such nick/channel */
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ return;
+ }
+
+ std::string mode_sequence = parameters[1];
+ std::string parameter;
+ std::ostringstream parameter_list;
+ std::string output_sequence;
+ bool adding = true, state_change = false;
+ unsigned char handler_id = 0;
+ int parameter_counter = 2; /* Index of first parameter */
+ int parameter_count = 0;
+ bool last_successful_state_change = false;
+
+ /* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */
+ if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-'))
+ mode_sequence.insert(0, "+");
+
+ for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
+ {
+ unsigned char modechar = *letter;
+
+ switch (modechar)
+ {
+ /* NB:
+ * For + and - mode characters, we don't just stick the character into the output sequence.
+ * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this
+ * appearing in the output sequence, we store a flag which says there was a state change,
+ * which is set on any + or -, however, the + or - that we finish on is only appended to
+ * the output stream in the event it is followed by a non "+ or -" character, such as o or v.
+ */
+ case '+':
+ /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,
+ * however, will allow the + if it is the first item in the sequence, regardless.
+ */
+ if ((!adding) || (!output_sequence.length()))
+ state_change = true;
+ adding = true;
+ if (!output_sequence.length())
+ last_successful_state_change = false;
+ continue;
+ break;
+ case '-':
+ if ((adding) || (!output_sequence.length()))
+ state_change = true;
+ adding = false;
+ if (!output_sequence.length())
+ last_successful_state_change = true;
+ continue;
+ break;
+ default:
+
+ /**
+ * Watch carefully for the sleight of hand trick.
+ * 65 is the ascii value of 'A'. We take this from
+ * the char we're looking at to get a number between
+ * 1 and 127. We then logic-or it to get the hashed
+ * position, dependent on wether its a channel or
+ * a user mode. This is a little stranger, but a lot
+ * faster, than using a map of pairs.
+ */
+ handler_id = (modechar - 65) | mask;
+
+ if (modehandlers[handler_id])
+ {
+ bool abort = false;
+
+ if (modehandlers[handler_id]->GetModeType() == type)
+ {
+ if (modehandlers[handler_id]->GetNumParams(adding))
+ {
+ /* This mode expects a parameter, do we have any parameters left in our list to use? */
+ if (parameter_counter < pcnt)
+ {
+ parameter = parameters[parameter_counter++];
+
+ /* Yerk, invalid! */
+ if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
+ parameter.clear();
+ }
+ else
+ {
+ /* No parameter, continue to the next mode */
+ continue;
+ }
+
+ bool had_parameter = !parameter.empty();
+
+ for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ {
+ if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)
+ {
+ abort = true;
+ break;
+ }
+ /* A module whacked the parameter completely, and there was one. abort. */
+ if ((had_parameter) && (parameter.empty()))
+ {
+ abort = true;
+ break;
+ }
+ }
+
+ if (abort)
+ continue;
+ }
+ else
+ {
+ /* Fix by brain: mode watchers not being called for parameterless modes */
+ for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ {
+ if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)
+ {
+ abort = true;
+ break;
+ }
+ }
+
+ if (abort)
+ continue;
+ }
+
+ /* It's an oper only mode, check if theyre an oper. If they arent,
+ * eat any parameter that came with the mode, and continue to next
+ */
+ if ((IS_LOCAL(user)) && (modehandlers[handler_id]->NeedsOper()) && (!IS_OPER(user)))
+ {
+ user->WriteServ("481 %s :Permission Denied - Only IRC operators may %sset %s mode %c", user->nick,
+ adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user",
+ modehandlers[handler_id]->GetModeChar());
+ continue;
+ }
+
+ /* Call the handler for the mode */
+ ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding);
+
+ if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter.empty()))
+ {
+ /* The handler nuked the parameter and they are supposed to have one.
+ * We CANT continue now, even if they actually returned MODEACTION_ALLOW,
+ * so we bail to the next mode character.
+ */
+ continue;
+ }
+
+ if (ma == MODEACTION_ALLOW)
+ {
+ /* We're about to output a valid mode letter - was there previously a pending state-change? */
+ if (state_change)
+ {
+ if (adding != last_successful_state_change)
+ output_sequence.append(adding ? "+" : "-");
+ last_successful_state_change = adding;
+ }
+
+ /* Add the mode letter */
+ output_sequence.push_back(modechar);
+
+ /* Is there a valid parameter for this mode? If so add it to the parameter list */
+ if ((modehandlers[handler_id]->GetNumParams(adding)) && (!parameter.empty()))
+ {
+ parameter_list << " " << parameter;
+ parameter_count++;
+ /* Does this mode have a prefix? */
+ if (modehandlers[handler_id]->GetPrefix() && targetchannel)
+ {
+ userrec* user_to_prefix = ServerInstance->FindNick(parameter);
+ if (user_to_prefix)
+ targetchannel->SetPrefix(user_to_prefix, modehandlers[handler_id]->GetPrefix(),
+ modehandlers[handler_id]->GetPrefixRank(), adding);
+ }
+ }
+
+ /* Call all the AfterMode events in the mode watchers for this mode */
+ for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type);
+
+ /* Reset the state change flag */
+ state_change = false;
+
+ if ((output_sequence.length() + parameter_list.str().length() > 450) || (output_sequence.length() > 100)
+ || (parameter_count > MAXMODES))
+ {
+ /* We cant have a mode sequence this long */
+ letter = mode_sequence.end() - 1;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* No mode handler? Unknown mode character then. */
+ user->WriteServ("472 %s %c :is unknown mode char to me",user->nick, modechar);
+ }
+ break;
+ }
+ }
+
+ /* Was there at least one valid mode in the sequence? */
+ if (!output_sequence.empty())
+ {
+ if (servermode)
+ {
+ if (type == MODETYPE_CHANNEL)
+ {
+ targetchannel->WriteChannelWithServ(ServerInstance->Config->ServerName, "MODE %s %s%s", targetchannel->name, output_sequence.c_str(), parameter_list.str().c_str());
+ this->LastParse = targetchannel->name;
+ }
+ else
+ {
+ targetuser->WriteServ("MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());
+ this->LastParse = targetuser->nick;
+ }
+ }
+ else
+ {
+ if (type == MODETYPE_CHANNEL)
+ {
+ targetchannel->WriteChannel(user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
+ FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str()));
+ this->LastParse = targetchannel->name;
+ }
+ else
+ {
+ user->WriteTo(targetuser,"MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());
+ FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, output_sequence + parameter_list.str()));
+ this->LastParse = targetuser->nick;
+ }
+ }
+
+ LastParse.append(" ");
+ LastParse.append(output_sequence);
+ LastParse.append(parameter_list.str());
+ }
+ }
+}
+
+const std::string& ModeParser::GetLastParse()
+{
+ return LastParse;
+}
+
+void ModeParser::CleanMask(std::string &mask)
+{
+ std::string::size_type pos_of_pling = mask.find_first_of('!');
+ std::string::size_type pos_of_at = mask.find_first_of('@');
+ std::string::size_type pos_of_dot = mask.find_first_of('.');
+ std::string::size_type pos_of_colon = mask.find_first_of(':'); /* Because ipv6 addresses are colon delimited */
+
+ if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos))
+ {
+ /* Just a nick, or just a host */
+ if ((pos_of_dot == std::string::npos) && (pos_of_colon == std::string::npos))
+ {
+ /* It has no '.' in it, it must be a nick. */
+ mask.append("!*@*");
+ }
+ else
+ {
+ /* Got a dot in it? Has to be a host */
+ mask = "*!*@" + mask;
+ }
+ }
+ else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos))
+ {
+ /* Has an @ but no !, its a user@host */
+ mask = "*!" + mask;
+ }
+ else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos))
+ {
+ /* Has a ! but no @, it must be a nick!ident */
+ mask.append("@*");
+ }
+}
+
+bool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter)
+{
+ unsigned char mask = 0;
+ unsigned char pos = 0;
+
+ /* 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;
+
+ /* 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;
+
+ mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
+ pos = (mh->GetModeChar()-65) | mask;
+
+ if (modehandlers[pos])
+ return false;
+
+ modehandlers[pos] = mh;
+ return true;
+}
+
+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;
+
+ if (!modehandlers[pos])
+ return false;
+
+ switch (mh->GetModeType())
+ {
+ case MODETYPE_USER:
+ for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
+ {
+ mh->RemoveMode(i->second);
+ }
+ break;
+ case MODETYPE_CHANNEL:
+ for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+ {
+ mh->RemoveMode(i->second);
+ }
+ break;
+ }
+
+ modehandlers[pos] = NULL;
+
+ return true;
+}
+
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
+{
+ unsigned char mask = 0;
+ unsigned char pos = 0;
+
+ if ((modeletter < 'A') || (modeletter > 'z'))
+ return NULL;
+
+ mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
+ pos = (modeletter-65) | mask;
+
+ return modehandlers[pos];
+}
+
+std::string ModeParser::UserModeList()
+{
+ 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;
+}
+
+std::string ModeParser::ChannelModeList()
+{
+ char modestr[256];
+ int pointer = 0;
+
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
+ continue;
+
+ unsigned char pos = (mode-65) | MASK_CHANNEL;
+
+ if (modehandlers[pos])
+ modestr[pointer++] = mode;
+ }
+ modestr[pointer++] = 0;
+ return modestr;
+}
+
+std::string ModeParser::ParaModeList()
+{
+ char modestr[256];
+ int pointer = 0;
+
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
+ continue;
+
+ unsigned char pos = (mode-65) | MASK_CHANNEL;
+
+ if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))
+ modestr[pointer++] = mode;
+ }
+ modestr[pointer++] = 0;
+ return modestr;
+}
+
+ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)
+{
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ unsigned char pos = (mode-65) | MASK_CHANNEL;
+
+ if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))
+ {
+ return modehandlers[pos];
+ }
+ }
+ return NULL;
+}
+
+std::string ModeParser::ModeString(userrec* user, chanrec* channel)
+{
+ std::string types;
+ std::string pars;
+
+ if (!channel || !user)
+ return "";
+
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ unsigned char pos = (mode-65) | MASK_CHANNEL;
+ ModeHandler* mh = modehandlers[pos];
+ if ((mh) && (mh->GetNumParams(true)) && (mh->GetNumParams(false)))
+ {
+ ModePair ret;
+ ret = mh->ModeSet(NULL, user, channel, user->nick);
+ if ((ret.first) && (ret.second == user->nick))
+ {
+ pars.append(" ");
+ pars.append(user->nick);
+ types.push_back(mh->GetModeChar());
+ }
+ }
+ }
+
+ return types+pars;
+}
+
+std::string ModeParser::ChanModes()
+{
+ std::string type1; /* Listmodes EXCEPT those with a prefix */
+ std::string type2; /* Modes that take a param when adding or removing */
+ std::string type3; /* Modes that only take a param when adding */
+ std::string type4; /* Modes that dont take a param */
+
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
+ continue;
+
+ unsigned char pos = (mode-65) | MASK_CHANNEL;
+ /* One parameter when adding */
+ if (modehandlers[pos])
+ {
+ if (modehandlers[pos]->GetNumParams(true))
+ {
+ if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))
+ {
+ type1 += modehandlers[pos]->GetModeChar();
+ }
+ else
+ {
+ /* ... and one parameter when removing */
+ if (modehandlers[pos]->GetNumParams(false))
+ {
+ /* But not a list mode */
+ if (!modehandlers[pos]->GetPrefix())
+ {
+ type2 += modehandlers[pos]->GetModeChar();
+ }
+ }
+ else
+ {
+ /* No parameters when removing */
+ type3 += modehandlers[pos]->GetModeChar();
+ }
+ }
+ }
+ else
+ {
+ type4 += modehandlers[pos]->GetModeChar();
+ }
+ }
+
+ }
+
+ return type1 + "," + type2 + "," + type3 + "," + type4;
+}
+
+bool ModeParser::PrefixComparison(prefixtype one, prefixtype two)
+{
+ return one.second > two.second;
+}
+
+std::string ModeParser::BuildPrefixes()
+{
+ std::string mletters;
+ std::string mprefixes;
+ pfxcontainer pfx;
+ std::map<char,char> prefix_to_mode;
+
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
+ continue;
+
+ unsigned char pos = (mode-65) | MASK_CHANNEL;
+
+ if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
+ {
+ pfx.push_back(std::make_pair<char,unsigned int>(modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetPrefixRank()));
+ prefix_to_mode[modehandlers[pos]->GetPrefix()] = modehandlers[pos]->GetModeChar();
+ }
+ }
+
+ sort(pfx.begin(), pfx.end(), ModeParser::PrefixComparison);
+
+ for (pfxcontainer::iterator n = pfx.begin(); n != pfx.end(); n++)
+ {
+ mletters = mletters + n->first;
+ mprefixes = mprefixes + prefix_to_mode.find(n->first)->second;
+ }
+
+ return "(" + mprefixes + ")" + mletters;
+}
+
+bool 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;
+}
+
+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 = find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);
+
+ if (a == modewatchers[pos].end())
+ {
+ return false;
+ }
+
+ modewatchers[pos].erase(a);
+
+ return true;
+}
+
+/** This default implementation can remove simple user modes
+ */
+void ModeHandler::RemoveMode(userrec* user)
+{
+ char moderemove[MAXBUF];
+ const char* parameters[] = { user->nick, moderemove };
+
+ if (user->IsModeSet(this->GetModeChar()))
+ {
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ ServerInstance->Parser->CallHandler("MODE", parameters, 2, user);
+ }
+}
+
+/** This default implementation can remove simple channel modes
+ * (no parameters)
+ */
+void ModeHandler::RemoveMode(chanrec* channel)
+{
+ char moderemove[MAXBUF];
+ const char* parameters[] = { channel->name, moderemove };
+
+ if (channel->IsModeSet(this->GetModeChar()))
+ {
+ userrec* n = new userrec(ServerInstance);
+
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ n->SetFd(FD_MAGIC_NUMBER);
+
+ ServerInstance->SendMode(parameters, 2, n);
+
+ delete n;
+ }
+}
+
+ModeParser::ModeParser(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ struct Initializer
+ {
+ char modechar;
+ ModeHandler* handler;
+ };
+
+ Initializer modes[] = {
+ { 's', new ModeChannelSecret(Instance) },
+ { 'p', new ModeChannelPrivate(Instance) },
+ { 'm', new ModeChannelModerated(Instance) },
+ { 't', new ModeChannelTopicOps(Instance) },
+ { 'n', new ModeChannelNoExternal(Instance) },
+ { 'i', new ModeChannelInviteOnly(Instance) },
+ { 'k', new ModeChannelKey(Instance) },
+ { 'l', new ModeChannelLimit(Instance) },
+ { 'b', new ModeChannelBan(Instance) },
+ { 'o', new ModeChannelOp(Instance) },
+ { 'h', new ModeChannelHalfOp(Instance) },
+ { 'v', new ModeChannelVoice(Instance) },
+ { 's', new ModeUserServerNotice(Instance) },
+ { 'w', new ModeUserWallops(Instance) },
+ { 'i', new ModeUserInvisible(Instance) },
+ { 'o', new ModeUserOperator(Instance) },
+ { 'n', new ModeUserServerNoticeMask(Instance) },
+ { 0, NULL }
+ };
+
+ /* Clear mode list */
+ memset(modehandlers, 0, sizeof(modehandlers));
+ memset(modewatchers, 0, sizeof(modewatchers));
+
+ /* Last parse string */
+ LastParse.clear();
+
+ /* Initialise the RFC mode letters */
+ for (int index = 0; modes[index].modechar; index++)
+ this->AddMode(modes[index].handler, modes[index].modechar);
+}
diff --git a/src/modes/Makefile b/src/modes/Makefile
index 1b7743649..8839168b7 100644
--- a/src/modes/Makefile
+++ b/src/modes/Makefile
@@ -1 +1,65 @@
-CC = i am cornholio CXXFLAGS = -I../../include ${FLAGS} all: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o modeclasses.a umode_w.o: umode_w.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_w.cpp umode_s.o: umode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_s.cpp umode_o.o: umode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_o.cpp umode_n.o: umode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_n.cpp umode_i.o: umode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_i.cpp cmode_v.o: cmode_v.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_v.cpp cmode_t.o: cmode_t.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_t.cpp cmode_s.o: cmode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_s.cpp cmode_p.o: cmode_p.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_p.cpp cmode_o.o: cmode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_o.cpp cmode_n.o: cmode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_n.cpp cmode_m.o: cmode_m.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_m.cpp cmode_l.o: cmode_l.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_l.cpp cmode_k.o: cmode_k.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_k.cpp cmode_i.o: cmode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_i.cpp cmode_h.o: cmode_h.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_h.cpp cmode_b.o: cmode_b.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_b.cpp modeclasses.a: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o @-rm -rf modeclasses.a ar r modeclasses.a *.o ranlib modeclasses.a clean: @-rm *.o @-rm modeclasses.a \ No newline at end of file
+CC = i am cornholio
+CXXFLAGS = -I../../include ${FLAGS}
+
+all: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o modeclasses.a
+
+umode_w.o: umode_w.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_w.cpp
+
+umode_s.o: umode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_s.cpp
+
+umode_o.o: umode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_o.cpp
+
+umode_n.o: umode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_n.cpp
+
+umode_i.o: umode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_i.cpp
+
+cmode_v.o: cmode_v.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_v.cpp
+
+cmode_t.o: cmode_t.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_t.cpp
+
+cmode_s.o: cmode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_s.cpp
+
+cmode_p.o: cmode_p.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_p.cpp
+
+cmode_o.o: cmode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_o.cpp
+
+cmode_n.o: cmode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_n.cpp
+
+cmode_m.o: cmode_m.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_m.cpp
+
+cmode_l.o: cmode_l.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_l.cpp
+
+cmode_k.o: cmode_k.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_k.cpp
+
+cmode_i.o: cmode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_i.cpp
+
+cmode_h.o: cmode_h.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_h.cpp
+
+cmode_b.o: cmode_b.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
+ $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_b.cpp
+
+modeclasses.a: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o
+ @-rm -rf modeclasses.a
+ ar r modeclasses.a *.o
+ ranlib modeclasses.a
+
+clean:
+ @-rm *.o
+ @-rm modeclasses.a
+
diff --git a/src/modes/cmode_b.cpp b/src/modes/cmode_b.cpp
index dbc2e925d..e306f31f6 100644
--- a/src/modes/cmode_b.cpp
+++ b/src/modes/cmode_b.cpp
@@ -1 +1,185 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #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(InspIRCd* Instance) : ModeHandler(Instance, 'b', 1, 1, true, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelBan::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { int status = channel->GetStatus(source); /* Call the correct method depending on wether we're adding or removing the mode */ if (adding) { parameter = this->AddBan(source, parameter, channel, status); } else { parameter = 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(chanrec* channel) { BanList copy; char moderemove[MAXBUF]; userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); 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++) { sprintf(moderemove,"-%c",this->GetModeChar()); const char* parameters[] = { channel->name, moderemove, i->data }; ServerInstance->SendMode(parameters, 3, n); } delete n; } void ModeChannelBan::RemoveMode(userrec* user) { } void ModeChannelBan::DisplayList(userrec* user, chanrec* 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 %d",user->nick, channel->name, i->data, i->set_by, i->set_time); } user->WriteServ("368 %s %s :End of channel ban list",user->nick, channel->name); return; } std::string& ModeChannelBan::AddBan(userrec *user,std::string &dest,chanrec *chan,int status) { if ((!user) || (!chan)) { ServerInstance->Log(DEFAULT,"*** BUG *** AddBan was given an invalid parameter"); dest = ""; return dest; } /* Attempt to tidy the mask */ ModeParser::CleanMask(dest); /* If the mask was invalid, we exit */ if (dest == "") return dest; long maxbans = chan->GetMaxBans(); if ((unsigned)chan->bans.size() > (unsigned)maxbans) { user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)",user->nick, chan->name,chan->name,maxbans); dest = ""; return dest; } int MOD_RESULT = 0; FOREACH_RESULT(I_OnAddBan,OnAddBan(user,chan,dest)); if (MOD_RESULT) { dest = ""; return dest; } for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) { if (!strcasecmp(i->data,dest.c_str())) { /* dont allow a user to set the same ban twice */ dest = ""; return dest; } } b.set_time = ServerInstance->Time(); strlcpy(b.data,dest.c_str(),MAXBUF); if (*user->nick) { strlcpy(b.set_by,user->nick,NICKMAX-1); } else { strlcpy(b.set_by,ServerInstance->Config->ServerName,NICKMAX-1); } chan->bans.push_back(b); return dest; } ModePair ModeChannelBan::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) { if (!strcasecmp(i->data,parameter.c_str())) { return std::make_pair(true, i->data); } } return std::make_pair(false, parameter); } std::string& ModeChannelBan::DelBan(userrec *user,std::string& dest,chanrec *chan,int status) { if ((!user) || (!chan)) { ServerInstance->Log(DEFAULT,"*** BUG *** TakeBan was given an invalid parameter"); dest = ""; return dest; } /* 'Clean' the mask, e.g. nick -> nick!*@* */ ModeParser::CleanMask(dest); for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) { if (!strcasecmp(i->data,dest.c_str())) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnDelBan,OnDelBan(user,chan,dest)); if (MOD_RESULT) { dest = ""; return dest; } chan->bans.erase(i); return dest; } } dest = ""; return dest; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#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(InspIRCd* Instance) : ModeHandler(Instance, 'b', 1, 1, true, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelBan::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ int status = channel->GetStatus(source);
+ /* Call the correct method depending on wether we're adding or removing the mode */
+ if (adding)
+ {
+ parameter = this->AddBan(source, parameter, channel, status);
+ }
+ else
+ {
+ parameter = 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(chanrec* channel)
+{
+ BanList copy;
+ char moderemove[MAXBUF];
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+
+ 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++)
+ {
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ const char* parameters[] = { channel->name, moderemove, i->data };
+ ServerInstance->SendMode(parameters, 3, n);
+ }
+
+ delete n;
+}
+
+void ModeChannelBan::RemoveMode(userrec* user)
+{
+}
+
+void ModeChannelBan::DisplayList(userrec* user, chanrec* 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 %d",user->nick, channel->name, i->data, i->set_by, i->set_time);
+ }
+ user->WriteServ("368 %s %s :End of channel ban list",user->nick, channel->name);
+ return;
+}
+
+std::string& ModeChannelBan::AddBan(userrec *user,std::string &dest,chanrec *chan,int status)
+{
+ if ((!user) || (!chan))
+ {
+ ServerInstance->Log(DEFAULT,"*** BUG *** AddBan was given an invalid parameter");
+ dest = "";
+ return dest;
+ }
+
+ /* Attempt to tidy the mask */
+ ModeParser::CleanMask(dest);
+ /* If the mask was invalid, we exit */
+ if (dest == "")
+ return dest;
+
+ long maxbans = chan->GetMaxBans();
+ if ((unsigned)chan->bans.size() > (unsigned)maxbans)
+ {
+ user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)",user->nick, chan->name,chan->name,maxbans);
+ dest = "";
+ return dest;
+ }
+
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAddBan,OnAddBan(user,chan,dest));
+ if (MOD_RESULT)
+ {
+ dest = "";
+ return dest;
+ }
+
+ for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
+ {
+ if (!strcasecmp(i->data,dest.c_str()))
+ {
+ /* dont allow a user to set the same ban twice */
+ dest = "";
+ return dest;
+ }
+ }
+
+ b.set_time = ServerInstance->Time();
+ strlcpy(b.data,dest.c_str(),MAXBUF);
+ if (*user->nick)
+ {
+ strlcpy(b.set_by,user->nick,NICKMAX-1);
+ }
+ else
+ {
+ strlcpy(b.set_by,ServerInstance->Config->ServerName,NICKMAX-1);
+ }
+ chan->bans.push_back(b);
+ return dest;
+}
+
+ModePair ModeChannelBan::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
+ {
+ if (!strcasecmp(i->data,parameter.c_str()))
+ {
+ return std::make_pair(true, i->data);
+ }
+ }
+ return std::make_pair(false, parameter);
+}
+
+std::string& ModeChannelBan::DelBan(userrec *user,std::string& dest,chanrec *chan,int status)
+{
+ if ((!user) || (!chan))
+ {
+ ServerInstance->Log(DEFAULT,"*** BUG *** TakeBan was given an invalid parameter");
+ dest = "";
+ return dest;
+ }
+
+ /* 'Clean' the mask, e.g. nick -> nick!*@* */
+ ModeParser::CleanMask(dest);
+
+ for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
+ {
+ if (!strcasecmp(i->data,dest.c_str()))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnDelBan,OnDelBan(user,chan,dest));
+ if (MOD_RESULT)
+ {
+ dest = "";
+ return dest;
+ }
+ chan->bans.erase(i);
+ return dest;
+ }
+ }
+ dest = "";
+ return dest;
+}
+
diff --git a/src/modes/cmode_h.cpp b/src/modes/cmode_h.cpp
index f2ffff665..ecee93388 100644
--- a/src/modes/cmode_h.cpp
+++ b/src/modes/cmode_h.cpp
@@ -1 +1,162 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modules.h" #include "modes/cmode_h.h" ModeChannelHalfOp::ModeChannelHalfOp(InspIRCd* Instance) : ModeHandler(Instance, 'h', 1, 1, true, MODETYPE_CHANNEL, false, '%') { } unsigned int ModeChannelHalfOp::GetPrefixRank() { return HALFOP_VALUE; } ModePair ModeChannelHalfOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { userrec* x = ServerInstance->FindNick(parameter); if (x) { if (channel->GetStatusFlags(x) & UCMODE_HOP) { return std::make_pair(true, x->nick); } else { return std::make_pair(false, parameter); } } return std::make_pair(false, parameter); } void ModeChannelHalfOp::RemoveMode(chanrec* channel) { CUList* list = channel->GetHalfoppedUsers(); CUList copy; char moderemove[MAXBUF]; userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); for (CUList::iterator i = list->begin(); i != list->end(); i++) { userrec* n = i->first; copy.insert(std::make_pair(n,n->nick)); } for (CUList::iterator i = copy.begin(); i != copy.end(); i++) { sprintf(moderemove,"-%c",this->GetModeChar()); const char* parameters[] = { channel->name, moderemove, i->first->nick }; ServerInstance->SendMode(parameters, 3, n); } delete n; } void ModeChannelHalfOp::RemoveMode(userrec* user) { } ModeAction ModeChannelHalfOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* If halfops are not enabled in the conf, we don't execute * anything in this class at all. */ if (!ServerInstance->Config->AllowHalfop) { parameter = ""; return MODEACTION_DENY; } int status = channel->GetStatus(source); /* Call the correct method depending on wether we're adding or removing the mode */ if (adding) { parameter = this->AddHalfOp(source, parameter.c_str(), channel, status); } else { parameter = this->DelHalfOp(source, parameter.c_str(), 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. */ if (parameter.length()) return MODEACTION_ALLOW; else return MODEACTION_DENY; } std::string ModeChannelHalfOp::AddHalfOp(userrec *user,const char* dest,chanrec *chan,int status) { userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); if (d) { if (IS_LOCAL(user)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_HALFOP)); if (MOD_RESULT == ACR_DENY) return ""; if (MOD_RESULT == ACR_DEFAULT) { if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server))) { user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name); return ""; } } } return ServerInstance->Modes->Grant(d,chan,UCMODE_HOP); } return ""; } std::string ModeChannelHalfOp::DelHalfOp(userrec *user,const char *dest,chanrec *chan,int status) { userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); if (d) { if (IS_LOCAL(user)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEHALFOP)); if (MOD_RESULT == ACR_DENY) return ""; if (MOD_RESULT == ACR_DEFAULT) { if ((user != d) && ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)))) { user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name); return ""; } } } return ServerInstance->Modes->Revoke(d,chan,UCMODE_HOP); } return ""; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modules.h"
+#include "modes/cmode_h.h"
+
+ModeChannelHalfOp::ModeChannelHalfOp(InspIRCd* Instance) : ModeHandler(Instance, 'h', 1, 1, true, MODETYPE_CHANNEL, false, '%')
+{
+}
+
+unsigned int ModeChannelHalfOp::GetPrefixRank()
+{
+ return HALFOP_VALUE;
+}
+
+ModePair ModeChannelHalfOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ userrec* x = ServerInstance->FindNick(parameter);
+ if (x)
+ {
+ if (channel->GetStatusFlags(x) & UCMODE_HOP)
+ {
+ return std::make_pair(true, x->nick);
+ }
+ else
+ {
+ return std::make_pair(false, parameter);
+ }
+ }
+ return std::make_pair(false, parameter);
+}
+
+void ModeChannelHalfOp::RemoveMode(chanrec* channel)
+{
+ CUList* list = channel->GetHalfoppedUsers();
+ CUList copy;
+ char moderemove[MAXBUF];
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+
+ for (CUList::iterator i = list->begin(); i != list->end(); i++)
+ {
+ userrec* n = i->first;
+ copy.insert(std::make_pair(n,n->nick));
+ }
+ for (CUList::iterator i = copy.begin(); i != copy.end(); i++)
+ {
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ const char* parameters[] = { channel->name, moderemove, i->first->nick };
+ ServerInstance->SendMode(parameters, 3, n);
+ }
+ delete n;
+}
+
+void ModeChannelHalfOp::RemoveMode(userrec* user)
+{
+}
+
+ModeAction ModeChannelHalfOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ /* If halfops are not enabled in the conf, we don't execute
+ * anything in this class at all.
+ */
+ if (!ServerInstance->Config->AllowHalfop)
+ {
+ parameter = "";
+ return MODEACTION_DENY;
+ }
+
+ int status = channel->GetStatus(source);
+
+ /* Call the correct method depending on wether we're adding or removing the mode */
+ if (adding)
+ {
+ parameter = this->AddHalfOp(source, parameter.c_str(), channel, status);
+ }
+ else
+ {
+ parameter = this->DelHalfOp(source, parameter.c_str(), 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.
+ */
+ if (parameter.length())
+ return MODEACTION_ALLOW;
+ else
+ return MODEACTION_DENY;
+}
+
+std::string ModeChannelHalfOp::AddHalfOp(userrec *user,const char* dest,chanrec *chan,int status)
+{
+ userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
+
+ if (d)
+ {
+ if (IS_LOCAL(user))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_HALFOP));
+
+ if (MOD_RESULT == ACR_DENY)
+ return "";
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)))
+ {
+ user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name);
+ return "";
+ }
+ }
+ }
+
+ return ServerInstance->Modes->Grant(d,chan,UCMODE_HOP);
+ }
+ return "";
+}
+
+std::string ModeChannelHalfOp::DelHalfOp(userrec *user,const char *dest,chanrec *chan,int status)
+{
+ userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
+
+ if (d)
+ {
+ if (IS_LOCAL(user))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEHALFOP));
+
+ if (MOD_RESULT == ACR_DENY)
+ return "";
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ if ((user != d) && ((status < STATUS_OP) && (!ServerInstance->ULine(user->server))))
+ {
+ user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name);
+ return "";
+ }
+ }
+ }
+
+ return ServerInstance->Modes->Revoke(d,chan,UCMODE_HOP);
+ }
+ return "";
+}
+
diff --git a/src/modes/cmode_i.cpp b/src/modes/cmode_i.cpp
index 1dd6dc857..12bdb18b8 100644
--- a/src/modes/cmode_i.cpp
+++ b/src/modes/cmode_i.cpp
@@ -1 +1,35 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_i.h" ModeChannelInviteOnly::ModeChannelInviteOnly(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelInviteOnly::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->modes[CM_INVITEONLY] != adding) { channel->modes[CM_INVITEONLY] = adding; return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_i.h"
+
+ModeChannelInviteOnly::ModeChannelInviteOnly(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelInviteOnly::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (channel->modes[CM_INVITEONLY] != adding)
+ {
+ channel->modes[CM_INVITEONLY] = adding;
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp
index e06c35e83..eb59714f7 100644
--- a/src/modes/cmode_k.cpp
+++ b/src/modes/cmode_k.cpp
@@ -1 +1,103 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_k.h" ModeChannelKey::ModeChannelKey(InspIRCd* Instance) : ModeHandler(Instance, 'k', 1, 1, false, MODETYPE_CHANNEL, false) { } ModePair ModeChannelKey::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { if (channel->modes[CM_KEY]) { return std::make_pair(true, channel->key); } else { return std::make_pair(false, parameter); } } void ModeChannelKey::RemoveMode(chanrec* channel) { /** +k needs a parameter when being removed, * so we have a special-case RemoveMode here for it */ char moderemove[MAXBUF]; const char* parameters[] = { channel->name, moderemove, channel->key }; if (channel->IsModeSet(this->GetModeChar())) { userrec* n = new userrec(ServerInstance); sprintf(moderemove,"-%c",this->GetModeChar()); n->SetFd(FD_MAGIC_NUMBER); ServerInstance->SendMode(parameters, 3, n); delete n; } } void ModeChannelKey::RemoveMode(userrec* user) { } bool ModeChannelKey::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { /* When TS is equal, the alphabetically later channel key wins */ return (their_param < our_param); } ModeAction ModeChannelKey::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if ((channel->modes[CM_KEY] != adding) || (!IS_LOCAL(source))) { if (((channel->modes[CM_KEY]) && (strcasecmp(parameter.c_str(),channel->key))) && (IS_LOCAL(source))) { /* Key is currently set and the correct key wasnt given */ return MODEACTION_DENY; } else if ((!channel->modes[CM_KEY]) || ((adding) && (!IS_LOCAL(source)))) { /* Key isnt currently set */ if ((parameter.length()) && (parameter.rfind(' ') == std::string::npos)) { strlcpy(channel->key,parameter.c_str(),32); channel->modes[CM_KEY] = adding; parameter = channel->key; return MODEACTION_ALLOW; } else return MODEACTION_DENY; } else if (((channel->modes[CM_KEY]) && (!strcasecmp(parameter.c_str(),channel->key))) || ((!adding) && (!IS_LOCAL(source)))) { /* Key is currently set, and correct key was given */ *channel->key = 0; channel->modes[CM_KEY] = adding; return MODEACTION_ALLOW; } return MODEACTION_DENY; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_k.h"
+
+ModeChannelKey::ModeChannelKey(InspIRCd* Instance) : ModeHandler(Instance, 'k', 1, 1, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModePair ModeChannelKey::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ if (channel->modes[CM_KEY])
+ {
+ return std::make_pair(true, channel->key);
+ }
+ else
+ {
+ return std::make_pair(false, parameter);
+ }
+}
+
+void ModeChannelKey::RemoveMode(chanrec* channel)
+{
+ /** +k needs a parameter when being removed,
+ * so we have a special-case RemoveMode here for it
+ */
+ char moderemove[MAXBUF];
+ const char* parameters[] = { channel->name, moderemove, channel->key };
+
+ if (channel->IsModeSet(this->GetModeChar()))
+ {
+ userrec* n = new userrec(ServerInstance);
+
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ n->SetFd(FD_MAGIC_NUMBER);
+
+ ServerInstance->SendMode(parameters, 3, n);
+
+ delete n;
+ }
+}
+
+void ModeChannelKey::RemoveMode(userrec* user)
+{
+}
+
+bool ModeChannelKey::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+{
+ /* When TS is equal, the alphabetically later channel key wins */
+ return (their_param < our_param);
+}
+
+ModeAction ModeChannelKey::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if ((channel->modes[CM_KEY] != adding) || (!IS_LOCAL(source)))
+ {
+ if (((channel->modes[CM_KEY]) && (strcasecmp(parameter.c_str(),channel->key))) && (IS_LOCAL(source)))
+ {
+ /* Key is currently set and the correct key wasnt given */
+ return MODEACTION_DENY;
+ }
+ else if ((!channel->modes[CM_KEY]) || ((adding) && (!IS_LOCAL(source))))
+ {
+ /* Key isnt currently set */
+ if ((parameter.length()) && (parameter.rfind(' ') == std::string::npos))
+ {
+ strlcpy(channel->key,parameter.c_str(),32);
+ channel->modes[CM_KEY] = adding;
+ parameter = channel->key;
+ return MODEACTION_ALLOW;
+ }
+ else
+ return MODEACTION_DENY;
+ }
+ else if (((channel->modes[CM_KEY]) && (!strcasecmp(parameter.c_str(),channel->key))) || ((!adding) && (!IS_LOCAL(source))))
+ {
+ /* Key is currently set, and correct key was given */
+ *channel->key = 0;
+ channel->modes[CM_KEY] = adding;
+ return MODEACTION_ALLOW;
+ }
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
+
diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp
index 5e1a8c26c..1a57a440d 100644
--- a/src/modes/cmode_l.cpp
+++ b/src/modes/cmode_l.cpp
@@ -1 +1,97 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_l.h" ModeChannelLimit::ModeChannelLimit(InspIRCd* Instance) : ModeHandler(Instance, 'l', 1, 0, false, MODETYPE_CHANNEL, false) { } ModePair ModeChannelLimit::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { if (channel->limit) { return std::make_pair(true, ConvToStr(channel->limit)); } else { return std::make_pair(false, parameter); } } bool ModeChannelLimit::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { /* When TS is equal, the higher channel limit wins */ return (atoi(their_param.c_str()) < atoi(our_param.c_str())); } ModeAction ModeChannelLimit::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { /* Setting a new limit, sanity check */ long limit = atoi(parameter.c_str()); /* Wrap low values at 32768 */ if (limit < 0) limit = 0x7FFF; /* If the new limit is the same as the old limit, * and the old limit isnt 0, disallow */ if ((limit == channel->limit) && (channel->limit > 0)) { parameter = ""; return MODEACTION_DENY; } /* They must have specified an invalid number. * Dont allow +l 0. */ if (!limit) { parameter = ""; return MODEACTION_DENY; } parameter = ConvToStr(limit); /* Set new limit */ channel->limit = limit; channel->modes[CM_LIMIT] = 1; return MODEACTION_ALLOW; } else { /* Check if theres a limit here to remove. * If there isnt, dont allow the -l */ if (!channel->limit) { parameter = ""; return MODEACTION_DENY; } /* Removing old limit, no checks here */ channel->limit = 0; channel->modes[CM_LIMIT] = 0; return MODEACTION_ALLOW; } return MODEACTION_DENY; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_l.h"
+
+ModeChannelLimit::ModeChannelLimit(InspIRCd* Instance) : ModeHandler(Instance, 'l', 1, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModePair ModeChannelLimit::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ if (channel->limit)
+ {
+ return std::make_pair(true, ConvToStr(channel->limit));
+ }
+ else
+ {
+ return std::make_pair(false, parameter);
+ }
+}
+
+bool ModeChannelLimit::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+{
+ /* When TS is equal, the higher channel limit wins */
+ return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
+}
+
+ModeAction ModeChannelLimit::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (adding)
+ {
+ /* Setting a new limit, sanity check */
+ long limit = atoi(parameter.c_str());
+
+ /* Wrap low values at 32768 */
+ if (limit < 0)
+ limit = 0x7FFF;
+
+ /* If the new limit is the same as the old limit,
+ * and the old limit isnt 0, disallow */
+ if ((limit == channel->limit) && (channel->limit > 0))
+ {
+ parameter = "";
+ return MODEACTION_DENY;
+ }
+
+ /* They must have specified an invalid number.
+ * Dont allow +l 0.
+ */
+ if (!limit)
+ {
+ parameter = "";
+ return MODEACTION_DENY;
+ }
+
+ parameter = ConvToStr(limit);
+
+ /* Set new limit */
+ channel->limit = limit;
+ channel->modes[CM_LIMIT] = 1;
+
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ /* Check if theres a limit here to remove.
+ * If there isnt, dont allow the -l
+ */
+ if (!channel->limit)
+ {
+ parameter = "";
+ return MODEACTION_DENY;
+ }
+
+ /* Removing old limit, no checks here */
+ channel->limit = 0;
+ channel->modes[CM_LIMIT] = 0;
+
+ return MODEACTION_ALLOW;
+ }
+
+ return MODEACTION_DENY;
+}
diff --git a/src/modes/cmode_m.cpp b/src/modes/cmode_m.cpp
index da882333b..520248fda 100644
--- a/src/modes/cmode_m.cpp
+++ b/src/modes/cmode_m.cpp
@@ -1 +1,36 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_m.h" ModeChannelModerated::ModeChannelModerated(InspIRCd* Instance) : ModeHandler(Instance, 'm', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelModerated::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->modes[CM_MODERATED] != adding) { channel->modes[CM_MODERATED] = adding; return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_m.h"
+
+ModeChannelModerated::ModeChannelModerated(InspIRCd* Instance) : ModeHandler(Instance, 'm', 0, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelModerated::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (channel->modes[CM_MODERATED] != adding)
+ {
+ channel->modes[CM_MODERATED] = adding;
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
+
diff --git a/src/modes/cmode_n.cpp b/src/modes/cmode_n.cpp
index 3ae7d538c..ddc2e1bbd 100644
--- a/src/modes/cmode_n.cpp
+++ b/src/modes/cmode_n.cpp
@@ -1 +1,36 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_n.h" ModeChannelNoExternal::ModeChannelNoExternal(InspIRCd* Instance) : ModeHandler(Instance, 'n', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelNoExternal::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->modes[CM_NOEXTERNAL] != adding) { channel->modes[CM_NOEXTERNAL] = adding; return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_n.h"
+
+ModeChannelNoExternal::ModeChannelNoExternal(InspIRCd* Instance) : ModeHandler(Instance, 'n', 0, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelNoExternal::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (channel->modes[CM_NOEXTERNAL] != adding)
+ {
+ channel->modes[CM_NOEXTERNAL] = adding;
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
+
diff --git a/src/modes/cmode_o.cpp b/src/modes/cmode_o.cpp
index 9ae18109e..47d191ff8 100644
--- a/src/modes/cmode_o.cpp
+++ b/src/modes/cmode_o.cpp
@@ -1 +1,153 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #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(InspIRCd* Instance) : ModeHandler(Instance, 'o', 1, 1, true, MODETYPE_CHANNEL, false, '@') { } unsigned int ModeChannelOp::GetPrefixRank() { return OP_VALUE; } ModePair ModeChannelOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { userrec* x = ServerInstance->FindNick(parameter); if (x) { if (channel->GetStatusFlags(x) & UCMODE_OP) { return std::make_pair(true, x->nick); } else { return std::make_pair(false, parameter); } } return std::make_pair(false, parameter); } void ModeChannelOp::RemoveMode(chanrec* channel) { CUList* list = channel->GetOppedUsers(); CUList copy; char moderemove[MAXBUF]; userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); for (CUList::iterator i = list->begin(); i != list->end(); i++) { userrec* n = i->first; copy.insert(std::make_pair(n,n->nick)); } for (CUList::iterator i = copy.begin(); i != copy.end(); i++) { sprintf(moderemove,"-%c",this->GetModeChar()); const char* parameters[] = { channel->name, moderemove, i->first->nick }; ServerInstance->SendMode(parameters, 3, n); } delete n; } void ModeChannelOp::RemoveMode(userrec* user) { } ModeAction ModeChannelOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { int status = channel->GetStatus(source); /* Call the correct method depending on wether we're adding or removing the mode */ if (adding) { parameter = this->AddOp(source, parameter.c_str(), channel, status); } else { parameter = this->DelOp(source, parameter.c_str(), 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. */ if (parameter.length()) return MODEACTION_ALLOW; else return MODEACTION_DENY; } std::string ModeChannelOp::AddOp(userrec *user,const char* dest,chanrec *chan,int status) { userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); if (d) { if (IS_LOCAL(user)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_OP)); if (MOD_RESULT == ACR_DENY) return ""; if (MOD_RESULT == ACR_DEFAULT) { if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server))) { user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name); return ""; } } } return ServerInstance->Modes->Grant(d,chan,UCMODE_OP); } return ""; } std::string ModeChannelOp::DelOp(userrec *user,const char *dest,chanrec *chan,int status) { userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); if (d) { if (IS_LOCAL(user)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEOP)); if (MOD_RESULT == ACR_DENY) return ""; if (MOD_RESULT == ACR_DEFAULT) { if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)) && (IS_LOCAL(user))) { user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name); return ""; } } } return ServerInstance->Modes->Revoke(d,chan,UCMODE_OP); } return ""; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#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(InspIRCd* Instance) : ModeHandler(Instance, 'o', 1, 1, true, MODETYPE_CHANNEL, false, '@')
+{
+}
+
+unsigned int ModeChannelOp::GetPrefixRank()
+{
+ return OP_VALUE;
+}
+
+ModePair ModeChannelOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ userrec* x = ServerInstance->FindNick(parameter);
+ if (x)
+ {
+ if (channel->GetStatusFlags(x) & UCMODE_OP)
+ {
+ return std::make_pair(true, x->nick);
+ }
+ else
+ {
+ return std::make_pair(false, parameter);
+ }
+ }
+ return std::make_pair(false, parameter);
+}
+
+
+void ModeChannelOp::RemoveMode(chanrec* channel)
+{
+ CUList* list = channel->GetOppedUsers();
+ CUList copy;
+ char moderemove[MAXBUF];
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+
+ for (CUList::iterator i = list->begin(); i != list->end(); i++)
+ {
+ userrec* n = i->first;
+ copy.insert(std::make_pair(n,n->nick));
+ }
+ for (CUList::iterator i = copy.begin(); i != copy.end(); i++)
+ {
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ const char* parameters[] = { channel->name, moderemove, i->first->nick };
+ ServerInstance->SendMode(parameters, 3, n);
+ }
+ delete n;
+}
+
+void ModeChannelOp::RemoveMode(userrec* user)
+{
+}
+
+ModeAction ModeChannelOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ int status = channel->GetStatus(source);
+
+ /* Call the correct method depending on wether we're adding or removing the mode */
+ if (adding)
+ {
+ parameter = this->AddOp(source, parameter.c_str(), channel, status);
+ }
+ else
+ {
+ parameter = this->DelOp(source, parameter.c_str(), 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.
+ */
+ if (parameter.length())
+ return MODEACTION_ALLOW;
+ else
+ return MODEACTION_DENY;
+}
+
+std::string ModeChannelOp::AddOp(userrec *user,const char* dest,chanrec *chan,int status)
+{
+ userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
+
+ if (d)
+ {
+ if (IS_LOCAL(user))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_OP));
+
+ if (MOD_RESULT == ACR_DENY)
+ return "";
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)))
+ {
+ user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name);
+ return "";
+ }
+ }
+ }
+
+ return ServerInstance->Modes->Grant(d,chan,UCMODE_OP);
+ }
+ return "";
+}
+
+std::string ModeChannelOp::DelOp(userrec *user,const char *dest,chanrec *chan,int status)
+{
+ userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
+
+ if (d)
+ {
+ if (IS_LOCAL(user))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEOP));
+
+ if (MOD_RESULT == ACR_DENY)
+ return "";
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)) && (IS_LOCAL(user)))
+ {
+ user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name);
+ return "";
+ }
+ }
+ }
+
+ return ServerInstance->Modes->Revoke(d,chan,UCMODE_OP);
+ }
+ return "";
+}
diff --git a/src/modes/cmode_p.cpp b/src/modes/cmode_p.cpp
index c762b7827..15c33222e 100644
--- a/src/modes/cmode_p.cpp
+++ b/src/modes/cmode_p.cpp
@@ -1 +1,35 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_p.h" ModeChannelPrivate::ModeChannelPrivate(InspIRCd* Instance) : ModeHandler(Instance, 'p', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelPrivate::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->modes[CM_PRIVATE] != adding) { channel->modes[CM_PRIVATE] = adding; return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_p.h"
+
+ModeChannelPrivate::ModeChannelPrivate(InspIRCd* Instance) : ModeHandler(Instance, 'p', 0, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelPrivate::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (channel->modes[CM_PRIVATE] != adding)
+ {
+ channel->modes[CM_PRIVATE] = adding;
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
diff --git a/src/modes/cmode_s.cpp b/src/modes/cmode_s.cpp
index 069591e6b..135291592 100644
--- a/src/modes/cmode_s.cpp
+++ b/src/modes/cmode_s.cpp
@@ -1 +1,35 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_s.h" ModeChannelSecret::ModeChannelSecret(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelSecret::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->modes[CM_SECRET] != adding) { channel->modes[CM_SECRET] = adding; return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_s.h"
+
+ModeChannelSecret::ModeChannelSecret(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelSecret::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (channel->modes[CM_SECRET] != adding)
+ {
+ channel->modes[CM_SECRET] = adding;
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
diff --git a/src/modes/cmode_t.cpp b/src/modes/cmode_t.cpp
index def48a781..2a6c06b42 100644
--- a/src/modes/cmode_t.cpp
+++ b/src/modes/cmode_t.cpp
@@ -1 +1,36 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/cmode_t.h" ModeChannelTopicOps::ModeChannelTopicOps(InspIRCd* Instance) : ModeHandler(Instance, 't', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction ModeChannelTopicOps::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->modes[CM_TOPICLOCK] != adding) { channel->modes[CM_TOPICLOCK] = adding; return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/cmode_t.h"
+
+ModeChannelTopicOps::ModeChannelTopicOps(InspIRCd* Instance) : ModeHandler(Instance, 't', 0, 0, false, MODETYPE_CHANNEL, false)
+{
+}
+
+ModeAction ModeChannelTopicOps::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ if (channel->modes[CM_TOPICLOCK] != adding)
+ {
+ channel->modes[CM_TOPICLOCK] = adding;
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+}
+
diff --git a/src/modes/cmode_v.cpp b/src/modes/cmode_v.cpp
index c055cca7f..1e244c606 100644
--- a/src/modes/cmode_v.cpp
+++ b/src/modes/cmode_v.cpp
@@ -1 +1,152 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #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(InspIRCd* Instance) : ModeHandler(Instance, 'v', 1, 1, true, MODETYPE_CHANNEL, false, '+') { } unsigned int ModeChannelVoice::GetPrefixRank() { return VOICE_VALUE; } ModePair ModeChannelVoice::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { userrec* x = ServerInstance->FindNick(parameter); if (x) { if (channel->GetStatusFlags(x) & UCMODE_VOICE) { return std::make_pair(true, x->nick); } else { return std::make_pair(false, parameter); } } return std::make_pair(false, parameter); } void ModeChannelVoice::RemoveMode(chanrec* channel) { CUList* list = channel->GetVoicedUsers(); CUList copy; char moderemove[MAXBUF]; userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); for (CUList::iterator i = list->begin(); i != list->end(); i++) { userrec* n = i->first; copy.insert(std::make_pair(n,n->nick)); } for (CUList::iterator i = copy.begin(); i != copy.end(); i++) { sprintf(moderemove,"-%c",this->GetModeChar()); const char* parameters[] = { channel->name, moderemove, i->first->nick }; ServerInstance->SendMode(parameters, 3, n); } delete n; } void ModeChannelVoice::RemoveMode(userrec* user) { } ModeAction ModeChannelVoice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { int status = channel->GetStatus(source); /* Call the correct method depending on wether we're adding or removing the mode */ if (adding) { parameter = this->AddVoice(source, parameter.c_str(), channel, status); } else { parameter = this->DelVoice(source, parameter.c_str(), 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. */ if (parameter.length()) return MODEACTION_ALLOW; else return MODEACTION_DENY; } std::string ModeChannelVoice::AddVoice(userrec *user,const char* dest,chanrec *chan,int status) { userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); if (d) { if (IS_LOCAL(user)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_VOICE)); if (MOD_RESULT == ACR_DENY) return ""; if (MOD_RESULT == ACR_DEFAULT) { if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server))) { user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, chan->name); return ""; } } } return ServerInstance->Modes->Grant(d,chan,UCMODE_VOICE); } return ""; } std::string ModeChannelVoice::DelVoice(userrec *user,const char *dest,chanrec *chan,int status) { userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); if (d) { if (IS_LOCAL(user)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEVOICE)); if (MOD_RESULT == ACR_DENY) return ""; if (MOD_RESULT == ACR_DEFAULT) { if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server))) { user->WriteServ("482 %s %s :You are not a channel (half)operator",user->nick, chan->name); return ""; } } } return ServerInstance->Modes->Revoke(d,chan,UCMODE_VOICE); } return ""; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#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(InspIRCd* Instance) : ModeHandler(Instance, 'v', 1, 1, true, MODETYPE_CHANNEL, false, '+')
+{
+}
+
+unsigned int ModeChannelVoice::GetPrefixRank()
+{
+ return VOICE_VALUE;
+}
+
+ModePair ModeChannelVoice::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+{
+ userrec* x = ServerInstance->FindNick(parameter);
+ if (x)
+ {
+ if (channel->GetStatusFlags(x) & UCMODE_VOICE)
+ {
+ return std::make_pair(true, x->nick);
+ }
+ else
+ {
+ return std::make_pair(false, parameter);
+ }
+ }
+ return std::make_pair(false, parameter);
+}
+
+void ModeChannelVoice::RemoveMode(chanrec* channel)
+{
+ CUList* list = channel->GetVoicedUsers();
+ CUList copy;
+ char moderemove[MAXBUF];
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+
+ for (CUList::iterator i = list->begin(); i != list->end(); i++)
+ {
+ userrec* n = i->first;
+ copy.insert(std::make_pair(n,n->nick));
+ }
+ for (CUList::iterator i = copy.begin(); i != copy.end(); i++)
+ {
+ sprintf(moderemove,"-%c",this->GetModeChar());
+ const char* parameters[] = { channel->name, moderemove, i->first->nick };
+ ServerInstance->SendMode(parameters, 3, n);
+ }
+ delete n;
+}
+
+void ModeChannelVoice::RemoveMode(userrec* user)
+{
+}
+
+ModeAction ModeChannelVoice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ int status = channel->GetStatus(source);
+
+ /* Call the correct method depending on wether we're adding or removing the mode */
+ if (adding)
+ {
+ parameter = this->AddVoice(source, parameter.c_str(), channel, status);
+ }
+ else
+ {
+ parameter = this->DelVoice(source, parameter.c_str(), 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.
+ */
+ if (parameter.length())
+ return MODEACTION_ALLOW;
+ else
+ return MODEACTION_DENY;
+}
+
+std::string ModeChannelVoice::AddVoice(userrec *user,const char* dest,chanrec *chan,int status)
+{
+ userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
+
+ if (d)
+ {
+ if (IS_LOCAL(user))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_VOICE));
+
+ if (MOD_RESULT == ACR_DENY)
+ return "";
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server)))
+ {
+ user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, chan->name);
+ return "";
+ }
+ }
+ }
+
+ return ServerInstance->Modes->Grant(d,chan,UCMODE_VOICE);
+ }
+ return "";
+}
+
+std::string ModeChannelVoice::DelVoice(userrec *user,const char *dest,chanrec *chan,int status)
+{
+ userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
+
+ if (d)
+ {
+ if (IS_LOCAL(user))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEVOICE));
+
+ if (MOD_RESULT == ACR_DENY)
+ return "";
+ if (MOD_RESULT == ACR_DEFAULT)
+ {
+ if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server)))
+ {
+ user->WriteServ("482 %s %s :You are not a channel (half)operator",user->nick, chan->name);
+ return "";
+ }
+ }
+ }
+
+ return ServerInstance->Modes->Revoke(d,chan,UCMODE_VOICE);
+ }
+ return "";
+}
diff --git a/src/modes/umode_i.cpp b/src/modes/umode_i.cpp
index 6bd769a9a..5a9327375 100644
--- a/src/modes/umode_i.cpp
+++ b/src/modes/umode_i.cpp
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/umode_i.h" ModeUserInvisible::ModeUserInvisible(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_USER, false) { } ModeAction ModeUserInvisible::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if ((source != dest) && (!*source->oper)) return MODEACTION_DENY; /* Set the bitfields */ if (dest->modes[UM_INVISIBLE] != adding) { dest->modes[UM_INVISIBLE] = adding; this->count += (adding ? 1: -1); return MODEACTION_ALLOW; } /* Allow the change */ return MODEACTION_DENY; } unsigned int ModeUserInvisible::GetCount() { return count; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/umode_i.h"
+
+ModeUserInvisible::ModeUserInvisible(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_USER, false)
+{
+}
+
+ModeAction ModeUserInvisible::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ /* Only opers can change other users modes */
+ if ((source != dest) && (!*source->oper))
+ return MODEACTION_DENY;
+
+ /* Set the bitfields */
+ if (dest->modes[UM_INVISIBLE] != adding)
+ {
+ dest->modes[UM_INVISIBLE] = adding;
+ this->count += (adding ? 1: -1);
+ return MODEACTION_ALLOW;
+ }
+
+ /* Allow the change */
+ return MODEACTION_DENY;
+}
+
+unsigned int ModeUserInvisible::GetCount()
+{
+ return count;
+}
diff --git a/src/modes/umode_n.cpp b/src/modes/umode_n.cpp
index c5d2599d8..c9c9e312e 100644
--- a/src/modes/umode_n.cpp
+++ b/src/modes/umode_n.cpp
@@ -1 +1,58 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/umode_n.h" ModeUserServerNoticeMask::ModeUserServerNoticeMask(InspIRCd* Instance) : ModeHandler(Instance, 'n', 1, 0, false, MODETYPE_USER, true) { } ModeAction ModeUserServerNoticeMask::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if ((source != dest) && (!*source->oper)) return MODEACTION_DENY; /* Set the bitfields */ if (adding) { /* Fix for bug #310 reported by Smartys */ if (!dest->modes[UM_SNOMASK]) memset(dest->snomasks, 0, sizeof(dest->snomasks)); parameter = dest->ProcessNoticeMasks(parameter.c_str()); dest->modes[UM_SNOMASK] = true; if (!dest->modes[UM_SERVERNOTICE]) { const char* newmodes[] = { dest->nick, "+s" }; ServerInstance->Modes->Process(newmodes, 2, source, true); } return MODEACTION_ALLOW; } else { if (dest->modes[UM_SNOMASK] != false) { dest->modes[UM_SNOMASK] = false; return MODEACTION_ALLOW; } } /* Allow the change */ return MODEACTION_DENY; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/umode_n.h"
+
+ModeUserServerNoticeMask::ModeUserServerNoticeMask(InspIRCd* Instance) : ModeHandler(Instance, 'n', 1, 0, false, MODETYPE_USER, true)
+{
+}
+
+ModeAction ModeUserServerNoticeMask::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ /* Only opers can change other users modes */
+ if ((source != dest) && (!*source->oper))
+ return MODEACTION_DENY;
+
+ /* Set the bitfields */
+ if (adding)
+ {
+ /* Fix for bug #310 reported by Smartys */
+ if (!dest->modes[UM_SNOMASK])
+ memset(dest->snomasks, 0, sizeof(dest->snomasks));
+
+ parameter = dest->ProcessNoticeMasks(parameter.c_str());
+ dest->modes[UM_SNOMASK] = true;
+ if (!dest->modes[UM_SERVERNOTICE])
+ {
+ const char* newmodes[] = { dest->nick, "+s" };
+ ServerInstance->Modes->Process(newmodes, 2, source, true);
+ }
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ if (dest->modes[UM_SNOMASK] != false)
+ {
+ dest->modes[UM_SNOMASK] = false;
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ /* Allow the change */
+ return MODEACTION_DENY;
+}
+
diff --git a/src/modes/umode_o.cpp b/src/modes/umode_o.cpp
index 1b47fe8b2..30ed089f6 100644
--- a/src/modes/umode_o.cpp
+++ b/src/modes/umode_o.cpp
@@ -1 +1,49 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/umode_o.h" ModeUserOperator::ModeUserOperator(InspIRCd* Instance) : ModeHandler(Instance, 'o', 0, 0, false, MODETYPE_USER, true) { } ModeAction ModeUserOperator::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can execute this class at all */ if (!*source->oper) return MODEACTION_DENY; /* Not even opers can GIVE the +o mode, only take it away */ if (adding) return MODEACTION_DENY; /* Set the bitfields. * Note that oper status is only given in cmd_oper.cpp * NOT here. It is impossible to directly set +o without * verifying as an oper and getting an opertype assigned * to your userrec! */ ServerInstance->SNO->WriteToSnoMask('o', "User %s de-opered (by %s)", dest->nick, source->nick); dest->UnOper(); return MODEACTION_ALLOW; } unsigned int ModeUserOperator::GetCount() { return ServerInstance->all_opers.size(); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/umode_o.h"
+
+ModeUserOperator::ModeUserOperator(InspIRCd* Instance) : ModeHandler(Instance, 'o', 0, 0, false, MODETYPE_USER, true)
+{
+}
+
+ModeAction ModeUserOperator::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ /* Only opers can execute this class at all */
+ if (!*source->oper)
+ return MODEACTION_DENY;
+
+ /* Not even opers can GIVE the +o mode, only take it away */
+ if (adding)
+ return MODEACTION_DENY;
+
+ /* Set the bitfields.
+ * Note that oper status is only given in cmd_oper.cpp
+ * NOT here. It is impossible to directly set +o without
+ * verifying as an oper and getting an opertype assigned
+ * to your userrec!
+ */
+ ServerInstance->SNO->WriteToSnoMask('o', "User %s de-opered (by %s)", dest->nick, source->nick);
+ dest->UnOper();
+
+ return MODEACTION_ALLOW;
+}
+
+unsigned int ModeUserOperator::GetCount()
+{
+ return ServerInstance->all_opers.size();
+}
diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp
index f0441b85f..4b3179001 100644
--- a/src/modes/umode_s.cpp
+++ b/src/modes/umode_s.cpp
@@ -1 +1,45 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/umode_s.h" ModeUserServerNotice::ModeUserServerNotice(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_USER, false) { } ModeAction ModeUserServerNotice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if ((source != dest) && (!*source->oper)) return MODEACTION_DENY; /* Set the bitfields */ if (dest->modes[UM_SERVERNOTICE] != adding) { dest->modes[UM_SERVERNOTICE] = adding; this->count += (adding ? 1: -1); return MODEACTION_ALLOW; } /* Allow the change */ return MODEACTION_DENY; } unsigned int ModeUserServerNotice::GetCount() { return count; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/umode_s.h"
+
+ModeUserServerNotice::ModeUserServerNotice(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_USER, false)
+{
+}
+
+ModeAction ModeUserServerNotice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ /* Only opers can change other users modes */
+ if ((source != dest) && (!*source->oper))
+ return MODEACTION_DENY;
+
+ /* Set the bitfields */
+ if (dest->modes[UM_SERVERNOTICE] != adding)
+ {
+ dest->modes[UM_SERVERNOTICE] = adding;
+ this->count += (adding ? 1: -1);
+ return MODEACTION_ALLOW;
+ }
+
+ /* Allow the change */
+ return MODEACTION_DENY;
+}
+
+unsigned int ModeUserServerNotice::GetCount()
+{
+ return count;
+}
diff --git a/src/modes/umode_w.cpp b/src/modes/umode_w.cpp
index 21c007655..383c91f6e 100644
--- a/src/modes/umode_w.cpp
+++ b/src/modes/umode_w.cpp
@@ -1 +1,46 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "channels.h" #include "users.h" #include "modes/umode_w.h" ModeUserWallops::ModeUserWallops(InspIRCd* Instance) : ModeHandler(Instance, 'w', 0, 0, false, MODETYPE_USER, false) { } ModeAction ModeUserWallops::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if ((source != dest) && (!*source->oper)) return MODEACTION_DENY; /* Set the bitfields */ if (dest->modes[UM_WALLOPS] != adding) { dest->modes[UM_WALLOPS] = adding; this->count += (adding ? 1: -1); return MODEACTION_ALLOW; } /* Allow the change */ return MODEACTION_DENY; } unsigned int ModeUserWallops::GetCount() { return count; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "channels.h"
+#include "users.h"
+#include "modes/umode_w.h"
+
+ModeUserWallops::ModeUserWallops(InspIRCd* Instance) : ModeHandler(Instance, 'w', 0, 0, false, MODETYPE_USER, false)
+{
+}
+
+ModeAction ModeUserWallops::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+{
+ /* Only opers can change other users modes */
+ if ((source != dest) && (!*source->oper))
+ return MODEACTION_DENY;
+
+ /* Set the bitfields */
+ if (dest->modes[UM_WALLOPS] != adding)
+ {
+ dest->modes[UM_WALLOPS] = adding;
+ this->count += (adding ? 1: -1);
+ return MODEACTION_ALLOW;
+ }
+
+ /* Allow the change */
+ return MODEACTION_DENY;
+}
+
+unsigned int ModeUserWallops::GetCount()
+{
+ return count;
+}
+
diff --git a/src/modules.cpp b/src/modules.cpp
index 2c34a034c..9deaa7954 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -1 +1,732 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "mode.h" #include "xline.h" #include "socket.h" #include "socketengine.h" #include "command_parse.h" #include "dns.h" // version is a simple class for holding a modules version number Version::Version(int major, int minor, int revision, int build, int flags, int api_ver) : Major(major), Minor(minor), Revision(revision), Build(build), Flags(flags), API(api_ver) { } Request::Request(char* anydata, Module* src, Module* dst) : data(anydata), source(src), dest(dst) { /* Ensure that because this module doesnt support ID strings, it doesnt break modules that do * by passing them uninitialized pointers (could happen) */ id = '\0'; } Request::Request(Module* src, Module* dst, const char* idstr) : id(idstr), source(src), dest(dst) { } char* Request::GetData() { return this->data; } const char* Request::GetId() { return this->id; } Module* Request::GetSource() { return this->source; } Module* Request::GetDest() { return this->dest; } char* Request::Send() { if (this->dest) { return dest->OnRequest(this); } else { return NULL; } } Event::Event(char* anydata, Module* src, const std::string &eventid) : data(anydata), source(src), id(eventid) { } char* Event::GetData() { return (char*)this->data; } Module* Event::GetSource() { return this->source; } char* Event::Send(InspIRCd* ServerInstance) { FOREACH_MOD(I_OnEvent,OnEvent(this)); return NULL; } std::string Event::GetEventID() { return this->id; } // These declarations define the behavours of the base class Module (which does nothing at all) Module::Module(InspIRCd* Me) : ServerInstance(Me) { } Module::~Module() { } void Module::OnUserConnect(userrec* user) { } void Module::OnUserQuit(userrec* user, const std::string& message, const std::string &oper_message) { } void Module::OnUserDisconnect(userrec* user) { } void Module::OnUserJoin(userrec* user, chanrec* channel, bool &silent) { } void Module::OnPostJoin(userrec* user, chanrec* channel) { } void Module::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { } void Module::OnRehash(userrec* user, const std::string &parameter) { } void Module::OnServerRaw(std::string &raw, bool inbound, userrec* user) { } int Module::OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { return 0; } void Module::OnMode(userrec* user, void* dest, int target_type, const std::string &text) { } Version Module::GetVersion() { return Version(1,0,0,0,VF_VENDOR,-1); } void Module::OnOper(userrec* user, const std::string &opertype) { } void Module::OnPostOper(userrec* user, const std::string &opertype) { } void Module::OnInfo(userrec* user) { } void Module::OnWhois(userrec* source, userrec* dest) { } int Module::OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) { return 0; } int Module::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; } int Module::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; } int Module::OnUserPreNick(userrec* user, const std::string &newnick) { return 0; } void Module::OnUserPostNick(userrec* user, const std::string &oldnick) { } int Module::OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { return ACR_DEFAULT; } void Module::On005Numeric(std::string &output) { } int Module::OnKill(userrec* source, userrec* dest, const std::string &reason) { return 0; } void Module::OnLoadModule(Module* mod,const std::string &name) { } void Module::OnUnloadModule(Module* mod,const std::string &name) { } void Module::OnBackgroundTimer(time_t curtime) { } int Module::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { return 0; } void Module::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { } bool Module::OnCheckReady(userrec* user) { return true; } int Module::OnUserRegister(userrec* user) { return 0; } int Module::OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) { return 0; } void Module::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { } int Module::OnCheckInvite(userrec* user, chanrec* chan) { return 0; } int Module::OnCheckKey(userrec* user, chanrec* chan, const std::string &keygiven) { return 0; } int Module::OnCheckLimit(userrec* user, chanrec* chan) { return 0; } int Module::OnCheckBan(userrec* user, chanrec* chan) { return 0; } int Module::OnStats(char symbol, userrec* user, string_list &results) { return 0; } int Module::OnChangeLocalUserHost(userrec* user, const std::string &newhost) { return 0; } int Module::OnChangeLocalUserGECOS(userrec* user, const std::string &newhost) { return 0; } int Module::OnLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { return 0; } void Module::OnEvent(Event* event) { return; } char* Module::OnRequest(Request* request) { return NULL; } int Module::OnOperCompare(const std::string &password, const std::string &input, int tagnumber) { return 0; } void Module::OnGlobalOper(userrec* user) { } void Module::OnPostConnect(userrec* user) { } int Module::OnAddBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; } int Module::OnDelBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; } void Module::OnRawSocketAccept(int fd, const std::string &ip, int localport) { } int Module::OnRawSocketWrite(int fd, const char* buffer, int count) { return 0; } void Module::OnRawSocketClose(int fd) { } void Module::OnRawSocketConnect(int fd) { } int Module::OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { return 0; } void Module::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { } void Module::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { } void Module::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason) { } void Module::OnUserInvite(userrec* source,userrec* dest,chanrec* channel) { } void Module::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { } void Module::OnGetServerDescription(const std::string &servername,std::string &description) { } void Module::OnSyncUser(userrec* user, Module* proto, void* opaque) { } void Module::OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { } void Module::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) { } void Module::OnSyncChannelMetaData(chanrec* chan, Module* proto,void* opaque, const std::string &extname, bool displayable) { } void Module::OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { } void Module::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { } void Module::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { } void Module::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { } void Module::OnWallops(userrec* user, const std::string &text) { } void Module::OnChangeHost(userrec* user, const std::string &newhost) { } void Module::OnChangeName(userrec* user, const std::string &gecos) { } void Module::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { } void Module::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask) { } void Module::OnAddKLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { } void Module::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask) { } void Module::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { } void Module::OnDelGLine(userrec* source, const std::string &hostmask) { } void Module::OnDelZLine(userrec* source, const std::string &ipmask) { } void Module::OnDelKLine(userrec* source, const std::string &hostmask) { } void Module::OnDelQLine(userrec* source, const std::string &nickmask) { } void Module::OnDelELine(userrec* source, const std::string &hostmask) { } void Module::OnCleanup(int target_type, void* item) { } void Module::Implements(char* Implements) { for (int j = 0; j < 255; j++) Implements[j] = 0; } void Module::OnChannelDelete(chanrec* chan) { } Priority Module::Prioritize() { return PRIORITY_DONTCARE; } void Module::OnSetAway(userrec* user) { } void Module::OnCancelAway(userrec* user) { } int Module::OnUserList(userrec* user, chanrec* Ptr, CUList* &userlist) { return 0; } int Module::OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { return 0; } void Module::OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) { } void Module::OnGarbageCollect() { } void Module::OnBufferFlushed(userrec* user) { } long InspIRCd::PriorityAfter(const std::string &modulename) { for (unsigned int j = 0; j < this->Config->module_names.size(); j++) { if (this->Config->module_names[j] == modulename) { return ((j << 8) | PRIORITY_AFTER); } } return PRIORITY_DONTCARE; } long InspIRCd::PriorityBefore(const std::string &modulename) { for (unsigned int j = 0; j < this->Config->module_names.size(); j++) { if (this->Config->module_names[j] == modulename) { return ((j << 8) | PRIORITY_BEFORE); } } return PRIORITY_DONTCARE; } bool InspIRCd::PublishFeature(const std::string &FeatureName, Module* Mod) { if (Features.find(FeatureName) == Features.end()) { Features[FeatureName] = Mod; return true; } return false; } bool InspIRCd::UnpublishFeature(const std::string &FeatureName) { featurelist::iterator iter = Features.find(FeatureName); if (iter == Features.end()) return false; Features.erase(iter); return true; } Module* InspIRCd::FindFeature(const std::string &FeatureName) { featurelist::iterator iter = Features.find(FeatureName); if (iter == Features.end()) return NULL; return iter->second; } bool InspIRCd::PublishInterface(const std::string &InterfaceName, Module* Mod) { interfacelist::iterator iter = Interfaces.find(InterfaceName); if (iter == Interfaces.end()) { modulelist ml; ml.push_back(Mod); Interfaces[InterfaceName] = std::make_pair(0, ml); return true; } else { iter->second.second.push_back(Mod); return true; } return false; } bool InspIRCd::UnpublishInterface(const std::string &InterfaceName, Module* Mod) { interfacelist::iterator iter = Interfaces.find(InterfaceName); if (iter == Interfaces.end()) return false; for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++) { if (*x == Mod) { iter->second.second.erase(x); if (iter->second.second.empty()) Interfaces.erase(InterfaceName); return true; } } return false; } modulelist* InspIRCd::FindInterface(const std::string &InterfaceName) { interfacelist::iterator iter = Interfaces.find(InterfaceName); if (iter == Interfaces.end()) return NULL; else return &(iter->second.second); } void InspIRCd::UseInterface(const std::string &InterfaceName) { interfacelist::iterator iter = Interfaces.find(InterfaceName); if (iter != Interfaces.end()) iter->second.first++; } void InspIRCd::DoneWithInterface(const std::string &InterfaceName) { interfacelist::iterator iter = Interfaces.find(InterfaceName); if (iter != Interfaces.end()) iter->second.first--; } std::pair<int,std::string> InspIRCd::GetInterfaceInstanceCount(Module* m) { for (interfacelist::iterator iter = Interfaces.begin(); iter != Interfaces.end(); iter++) { for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++) { if (*x == m) { return std::make_pair(iter->second.first, iter->first); } } } return std::make_pair(0, ""); } const std::string& InspIRCd::GetModuleName(Module* m) { static std::string nothing; /* Prevent compiler warning */ if (!this->GetModuleCount()) return nothing; for (int i = 0; i <= this->GetModuleCount(); i++) { if (this->modules[i] == m) { return this->Config->module_names[i]; } } return nothing; /* As above */ } void InspIRCd::RehashServer() { this->WriteOpers("*** Rehashing config file"); this->RehashUsersAndChans(); this->Config->Read(false,NULL); this->ResetMaxBans(); this->Res->Rehash(); } /* This is ugly, yes, but hash_map's arent designed to be * addressed in this manner, and this is a bit of a kludge. * Luckily its a specialist function and rarely used by * many modules (in fact, it was specially created to make * m_safelist possible, initially). */ chanrec* InspIRCd::GetChannelIndex(long index) { int target = 0; for (chan_hash::iterator n = this->chanlist->begin(); n != this->chanlist->end(); n++, target++) { if (index == target) return n->second; } return NULL; } bool InspIRCd::MatchText(const std::string &sliteral, const std::string &spattern) { return match(sliteral.c_str(),spattern.c_str()); } CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const char** parameters, int pcnt, userrec* user) { return this->Parser->CallHandler(commandname,parameters,pcnt,user); } bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, userrec* user) { return this->Parser->IsValidCommand(commandname, pcnt, user); } void InspIRCd::AddCommand(command_t *f) { if (!this->Parser->CreateCommand(f)) { ModuleException err("Command "+std::string(f->command)+" already exists."); throw (err); } } void InspIRCd::SendMode(const char** parameters, int pcnt, userrec *user) { this->Modes->Process(parameters,pcnt,user,true); } void InspIRCd::DumpText(userrec* User, const std::string &LinePrefix, stringstream &TextStream) { std::string CompleteLine = LinePrefix; std::string Word; while (TextStream >> Word) { if (CompleteLine.length() + Word.length() + 3 > 500) { User->WriteServ(CompleteLine); CompleteLine = LinePrefix; } CompleteLine = CompleteLine + Word + " "; } User->WriteServ(CompleteLine); } userrec* InspIRCd::FindDescriptor(int socket) { return reinterpret_cast<userrec*>(this->SE->GetRef(socket)); } bool InspIRCd::AddMode(ModeHandler* mh, const unsigned char mode) { return this->Modes->AddMode(mh,mode); } bool InspIRCd::AddModeWatcher(ModeWatcher* mw) { return this->Modes->AddModeWatcher(mw); } bool InspIRCd::DelModeWatcher(ModeWatcher* mw) { return this->Modes->DelModeWatcher(mw); } bool InspIRCd::AddResolver(Resolver* r, bool cached) { if (!cached) return this->Res->AddResolverClass(r); else { r->TriggerCachedResult(); delete r; return true; } } void InspIRCd::AddGLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask) { XLines->add_gline(duration, source.c_str(), reason.c_str(), hostmask.c_str()); XLines->apply_lines(APPLY_GLINES); } void InspIRCd::AddQLine(long duration, const std::string &source, const std::string &reason, const std::string &nickname) { XLines->add_qline(duration, source.c_str(), reason.c_str(), nickname.c_str()); XLines->apply_lines(APPLY_QLINES); } void InspIRCd::AddZLine(long duration, const std::string &source, const std::string &reason, const std::string &ipaddr) { XLines->add_zline(duration, source.c_str(), reason.c_str(), ipaddr.c_str()); XLines->apply_lines(APPLY_ZLINES); } void InspIRCd::AddKLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask) { XLines->add_kline(duration, source.c_str(), reason.c_str(), hostmask.c_str()); XLines->apply_lines(APPLY_KLINES); } void InspIRCd::AddELine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask) { XLines->add_eline(duration, source.c_str(), reason.c_str(), hostmask.c_str()); } bool InspIRCd::DelGLine(const std::string &hostmask) { return XLines->del_gline(hostmask.c_str()); } bool InspIRCd::DelQLine(const std::string &nickname) { return XLines->del_qline(nickname.c_str()); } bool InspIRCd::DelZLine(const std::string &ipaddr) { return XLines->del_zline(ipaddr.c_str()); } bool InspIRCd::DelKLine(const std::string &hostmask) { return XLines->del_kline(hostmask.c_str()); } bool InspIRCd::DelELine(const std::string &hostmask) { return XLines->del_eline(hostmask.c_str()); } /* * XXX why on *earth* is this in modules.cpp...? I think * perhaps we need a server.cpp for InspIRCd:: stuff where possible. -- w00t */ bool InspIRCd::IsValidMask(const std::string &mask) { char* dest = (char*)mask.c_str(); if (strchr(dest,'!')==0) return false; if (strchr(dest,'@')==0) return false; for (char* i = dest; *i; i++) if (*i < 32) return false; for (char* i = dest; *i; i++) if (*i > 126) return false; unsigned int c = 0; for (char* i = dest; *i; i++) if (*i == '!') c++; if (c>1) return false; c = 0; for (char* i = dest; *i; i++) if (*i == '@') c++; if (c>1) return false; return true; } Module* InspIRCd::FindModule(const std::string &name) { for (int i = 0; i <= this->GetModuleCount(); i++) { if (this->Config->module_names[i] == name) { return this->modules[i]; } } return NULL; } ConfigReader::ConfigReader(InspIRCd* Instance) : ServerInstance(Instance) { /* Is there any reason to load the entire config file again here? * it's needed if they specify another config file, but using the * default one we can just use the global config data - pre-parsed! */ this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out); this->data = &ServerInstance->Config->config_data; this->privatehash = false; } ConfigReader::~ConfigReader() { if (this->errorlog) DELETE(this->errorlog); if(this->privatehash) DELETE(this->data); } ConfigReader::ConfigReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance) { ServerInstance->Config->ClearStack(); this->data = new ConfigDataHash; this->privatehash = true; this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out); this->readerror = ServerInstance->Config->LoadConf(*this->data, filename, *this->errorlog); if (!this->readerror) this->error = CONF_FILE_NOT_FOUND; } std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds) { /* Don't need to strlcpy() tag and name anymore, ReadConf() takes const char* */ std::string result; if (!ServerInstance->Config->ConfValue(*this->data, tag, name, default_value, index, 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) { return ServerInstance->Config->ConfValueBool(*this->data, tag, name, default_value, index); } bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index) { return ReadFlag(tag, name, "", index); } long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool needs_unsigned) { int result; if(!ServerInstance->Config->ConfValueInteger(*this->data, tag, name, default_value, index, result)) { this->error = CONF_VALUE_NOT_FOUND; return 0; } if ((needs_unsigned) && (result < 0)) { this->error = CONF_NOT_UNSIGNED; return 0; } return result; } long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool needs_unsigned) { return ReadInteger(tag, name, "", index, needs_unsigned); } long ConfigReader::GetError() { long olderr = this->error; this->error = 0; return olderr; } void ConfigReader::DumpErrors(bool bail, userrec* user) { ServerInstance->Config->ReportConfigError(this->errorlog->str(), bail, user); } int ConfigReader::Enumerate(const std::string &tag) { return ServerInstance->Config->ConfValueEnum(*this->data, tag); } int ConfigReader::EnumerateValues(const std::string &tag, int index) { return ServerInstance->Config->ConfVarEnum(*this->data, tag, index); } bool ConfigReader::Verify() { return this->readerror; } FileReader::FileReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance) { LoadFile(filename); } FileReader::FileReader(InspIRCd* Instance) : ServerInstance(Instance) { } std::string FileReader::Contents() { std::string x; for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++) { x.append(*a); x.append("\r\n"); } return x; } unsigned long FileReader::ContentSize() { return this->contentsize; } void FileReader::CalcSize() { unsigned long n = 0; for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++) n += (a->length() + 2); this->contentsize = n; } void FileReader::LoadFile(const std::string &filename) { file_cache c; c.clear(); if (ServerInstance->Config->ReadFile(c,filename.c_str())) { this->fc = c; this->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(); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "mode.h"
+#include "xline.h"
+#include "socket.h"
+#include "socketengine.h"
+#include "command_parse.h"
+#include "dns.h"
+
+// version is a simple class for holding a modules version number
+Version::Version(int major, int minor, int revision, int build, int flags, int api_ver)
+: Major(major), Minor(minor), Revision(revision), Build(build), Flags(flags), API(api_ver)
+{
+}
+
+Request::Request(char* anydata, Module* src, Module* dst)
+: data(anydata), source(src), dest(dst)
+{
+ /* Ensure that because this module doesnt support ID strings, it doesnt break modules that do
+ * by passing them uninitialized pointers (could happen)
+ */
+ id = '\0';
+}
+
+Request::Request(Module* src, Module* dst, const char* idstr)
+: id(idstr), source(src), dest(dst)
+{
+}
+
+char* Request::GetData()
+{
+ return this->data;
+}
+
+const char* Request::GetId()
+{
+ return this->id;
+}
+
+Module* Request::GetSource()
+{
+ return this->source;
+}
+
+Module* Request::GetDest()
+{
+ return this->dest;
+}
+
+char* Request::Send()
+{
+ if (this->dest)
+ {
+ return dest->OnRequest(this);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+Event::Event(char* anydata, Module* src, const std::string &eventid) : data(anydata), source(src), id(eventid) { }
+
+char* Event::GetData()
+{
+ return (char*)this->data;
+}
+
+Module* Event::GetSource()
+{
+ return this->source;
+}
+
+char* Event::Send(InspIRCd* ServerInstance)
+{
+ FOREACH_MOD(I_OnEvent,OnEvent(this));
+ return NULL;
+}
+
+std::string Event::GetEventID()
+{
+ return this->id;
+}
+
+
+// These declarations define the behavours of the base class Module (which does nothing at all)
+
+ Module::Module(InspIRCd* Me) : ServerInstance(Me) { }
+ Module::~Module() { }
+void Module::OnUserConnect(userrec* user) { }
+void Module::OnUserQuit(userrec* user, const std::string& message, const std::string &oper_message) { }
+void Module::OnUserDisconnect(userrec* user) { }
+void Module::OnUserJoin(userrec* user, chanrec* channel, bool &silent) { }
+void Module::OnPostJoin(userrec* user, chanrec* channel) { }
+void Module::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { }
+void Module::OnRehash(userrec* user, const std::string &parameter) { }
+void Module::OnServerRaw(std::string &raw, bool inbound, userrec* user) { }
+int Module::OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { return 0; }
+void Module::OnMode(userrec* user, void* dest, int target_type, const std::string &text) { }
+Version Module::GetVersion() { return Version(1,0,0,0,VF_VENDOR,-1); }
+void Module::OnOper(userrec* user, const std::string &opertype) { }
+void Module::OnPostOper(userrec* user, const std::string &opertype) { }
+void Module::OnInfo(userrec* user) { }
+void Module::OnWhois(userrec* source, userrec* dest) { }
+int Module::OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) { return 0; }
+int Module::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; }
+int Module::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; }
+int Module::OnUserPreNick(userrec* user, const std::string &newnick) { return 0; }
+void Module::OnUserPostNick(userrec* user, const std::string &oldnick) { }
+int Module::OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { return ACR_DEFAULT; }
+void Module::On005Numeric(std::string &output) { }
+int Module::OnKill(userrec* source, userrec* dest, const std::string &reason) { return 0; }
+void Module::OnLoadModule(Module* mod,const std::string &name) { }
+void Module::OnUnloadModule(Module* mod,const std::string &name) { }
+void Module::OnBackgroundTimer(time_t curtime) { }
+int Module::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { return 0; }
+void Module::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { }
+bool Module::OnCheckReady(userrec* user) { return true; }
+int Module::OnUserRegister(userrec* user) { return 0; }
+int Module::OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) { return 0; }
+void Module::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { }
+int Module::OnCheckInvite(userrec* user, chanrec* chan) { return 0; }
+int Module::OnCheckKey(userrec* user, chanrec* chan, const std::string &keygiven) { return 0; }
+int Module::OnCheckLimit(userrec* user, chanrec* chan) { return 0; }
+int Module::OnCheckBan(userrec* user, chanrec* chan) { return 0; }
+int Module::OnStats(char symbol, userrec* user, string_list &results) { return 0; }
+int Module::OnChangeLocalUserHost(userrec* user, const std::string &newhost) { return 0; }
+int Module::OnChangeLocalUserGECOS(userrec* user, const std::string &newhost) { return 0; }
+int Module::OnLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { return 0; }
+void Module::OnEvent(Event* event) { return; }
+char* Module::OnRequest(Request* request) { return NULL; }
+int Module::OnOperCompare(const std::string &password, const std::string &input, int tagnumber) { return 0; }
+void Module::OnGlobalOper(userrec* user) { }
+void Module::OnPostConnect(userrec* user) { }
+int Module::OnAddBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; }
+int Module::OnDelBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; }
+void Module::OnRawSocketAccept(int fd, const std::string &ip, int localport) { }
+int Module::OnRawSocketWrite(int fd, const char* buffer, int count) { return 0; }
+void Module::OnRawSocketClose(int fd) { }
+void Module::OnRawSocketConnect(int fd) { }
+int Module::OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { return 0; }
+void Module::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { }
+void Module::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { }
+void Module::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason) { }
+void Module::OnUserInvite(userrec* source,userrec* dest,chanrec* channel) { }
+void Module::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { }
+void Module::OnGetServerDescription(const std::string &servername,std::string &description) { }
+void Module::OnSyncUser(userrec* user, Module* proto, void* opaque) { }
+void Module::OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { }
+void Module::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) { }
+void Module::OnSyncChannelMetaData(chanrec* chan, Module* proto,void* opaque, const std::string &extname, bool displayable) { }
+void Module::OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { }
+void Module::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { }
+void Module::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { }
+void Module::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { }
+void Module::OnWallops(userrec* user, const std::string &text) { }
+void Module::OnChangeHost(userrec* user, const std::string &newhost) { }
+void Module::OnChangeName(userrec* user, const std::string &gecos) { }
+void Module::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { }
+void Module::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask) { }
+void Module::OnAddKLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { }
+void Module::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask) { }
+void Module::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { }
+void Module::OnDelGLine(userrec* source, const std::string &hostmask) { }
+void Module::OnDelZLine(userrec* source, const std::string &ipmask) { }
+void Module::OnDelKLine(userrec* source, const std::string &hostmask) { }
+void Module::OnDelQLine(userrec* source, const std::string &nickmask) { }
+void Module::OnDelELine(userrec* source, const std::string &hostmask) { }
+void Module::OnCleanup(int target_type, void* item) { }
+void Module::Implements(char* Implements) { for (int j = 0; j < 255; j++) Implements[j] = 0; }
+void Module::OnChannelDelete(chanrec* chan) { }
+Priority Module::Prioritize() { return PRIORITY_DONTCARE; }
+void Module::OnSetAway(userrec* user) { }
+void Module::OnCancelAway(userrec* user) { }
+int Module::OnUserList(userrec* user, chanrec* Ptr, CUList* &userlist) { return 0; }
+int Module::OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { return 0; }
+void Module::OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) { }
+void Module::OnGarbageCollect() { }
+void Module::OnBufferFlushed(userrec* user) { }
+
+long InspIRCd::PriorityAfter(const std::string &modulename)
+{
+ for (unsigned int j = 0; j < this->Config->module_names.size(); j++)
+ {
+ if (this->Config->module_names[j] == modulename)
+ {
+ return ((j << 8) | PRIORITY_AFTER);
+ }
+ }
+ return PRIORITY_DONTCARE;
+}
+
+long InspIRCd::PriorityBefore(const std::string &modulename)
+{
+ for (unsigned int j = 0; j < this->Config->module_names.size(); j++)
+ {
+ if (this->Config->module_names[j] == modulename)
+ {
+ return ((j << 8) | PRIORITY_BEFORE);
+ }
+ }
+ return PRIORITY_DONTCARE;
+}
+
+bool InspIRCd::PublishFeature(const std::string &FeatureName, Module* Mod)
+{
+ if (Features.find(FeatureName) == Features.end())
+ {
+ Features[FeatureName] = Mod;
+ return true;
+ }
+ return false;
+}
+
+bool InspIRCd::UnpublishFeature(const std::string &FeatureName)
+{
+ featurelist::iterator iter = Features.find(FeatureName);
+
+ if (iter == Features.end())
+ return false;
+
+ Features.erase(iter);
+ return true;
+}
+
+Module* InspIRCd::FindFeature(const std::string &FeatureName)
+{
+ featurelist::iterator iter = Features.find(FeatureName);
+
+ if (iter == Features.end())
+ return NULL;
+
+ return iter->second;
+}
+
+bool InspIRCd::PublishInterface(const std::string &InterfaceName, Module* Mod)
+{
+ interfacelist::iterator iter = Interfaces.find(InterfaceName);
+
+ if (iter == Interfaces.end())
+ {
+ modulelist ml;
+ ml.push_back(Mod);
+ Interfaces[InterfaceName] = std::make_pair(0, ml);
+ return true;
+ }
+ else
+ {
+ iter->second.second.push_back(Mod);
+ return true;
+ }
+ return false;
+}
+
+bool InspIRCd::UnpublishInterface(const std::string &InterfaceName, Module* Mod)
+{
+ interfacelist::iterator iter = Interfaces.find(InterfaceName);
+
+ if (iter == Interfaces.end())
+ return false;
+
+ for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++)
+ {
+ if (*x == Mod)
+ {
+ iter->second.second.erase(x);
+ if (iter->second.second.empty())
+ Interfaces.erase(InterfaceName);
+ return true;
+ }
+ }
+ return false;
+}
+
+modulelist* InspIRCd::FindInterface(const std::string &InterfaceName)
+{
+ interfacelist::iterator iter = Interfaces.find(InterfaceName);
+ if (iter == Interfaces.end())
+ return NULL;
+ else
+ return &(iter->second.second);
+}
+
+void InspIRCd::UseInterface(const std::string &InterfaceName)
+{
+ interfacelist::iterator iter = Interfaces.find(InterfaceName);
+ if (iter != Interfaces.end())
+ iter->second.first++;
+
+}
+
+void InspIRCd::DoneWithInterface(const std::string &InterfaceName)
+{
+ interfacelist::iterator iter = Interfaces.find(InterfaceName);
+ if (iter != Interfaces.end())
+ iter->second.first--;
+}
+
+std::pair<int,std::string> InspIRCd::GetInterfaceInstanceCount(Module* m)
+{
+ for (interfacelist::iterator iter = Interfaces.begin(); iter != Interfaces.end(); iter++)
+ {
+ for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++)
+ {
+ if (*x == m)
+ {
+ return std::make_pair(iter->second.first, iter->first);
+ }
+ }
+ }
+ return std::make_pair(0, "");
+}
+
+const std::string& InspIRCd::GetModuleName(Module* m)
+{
+ static std::string nothing; /* Prevent compiler warning */
+
+ if (!this->GetModuleCount())
+ return nothing;
+
+ for (int i = 0; i <= this->GetModuleCount(); i++)
+ {
+ if (this->modules[i] == m)
+ {
+ return this->Config->module_names[i];
+ }
+ }
+ return nothing; /* As above */
+}
+
+void InspIRCd::RehashServer()
+{
+ this->WriteOpers("*** Rehashing config file");
+ this->RehashUsersAndChans();
+ this->Config->Read(false,NULL);
+ this->ResetMaxBans();
+ this->Res->Rehash();
+}
+
+/* This is ugly, yes, but hash_map's arent designed to be
+ * addressed in this manner, and this is a bit of a kludge.
+ * Luckily its a specialist function and rarely used by
+ * many modules (in fact, it was specially created to make
+ * m_safelist possible, initially).
+ */
+
+chanrec* InspIRCd::GetChannelIndex(long index)
+{
+ int target = 0;
+ for (chan_hash::iterator n = this->chanlist->begin(); n != this->chanlist->end(); n++, target++)
+ {
+ if (index == target)
+ return n->second;
+ }
+ return NULL;
+}
+
+bool InspIRCd::MatchText(const std::string &sliteral, const std::string &spattern)
+{
+ return match(sliteral.c_str(),spattern.c_str());
+}
+
+CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const char** parameters, int pcnt, userrec* user)
+{
+ return this->Parser->CallHandler(commandname,parameters,pcnt,user);
+}
+
+bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, userrec* user)
+{
+ return this->Parser->IsValidCommand(commandname, pcnt, user);
+}
+
+void InspIRCd::AddCommand(command_t *f)
+{
+ if (!this->Parser->CreateCommand(f))
+ {
+ ModuleException err("Command "+std::string(f->command)+" already exists.");
+ throw (err);
+ }
+}
+
+void InspIRCd::SendMode(const char** parameters, int pcnt, userrec *user)
+{
+ this->Modes->Process(parameters,pcnt,user,true);
+}
+
+void InspIRCd::DumpText(userrec* User, const std::string &LinePrefix, stringstream &TextStream)
+{
+ std::string CompleteLine = LinePrefix;
+ std::string Word;
+ while (TextStream >> Word)
+ {
+ if (CompleteLine.length() + Word.length() + 3 > 500)
+ {
+ User->WriteServ(CompleteLine);
+ CompleteLine = LinePrefix;
+ }
+ CompleteLine = CompleteLine + Word + " ";
+ }
+ User->WriteServ(CompleteLine);
+}
+
+userrec* InspIRCd::FindDescriptor(int socket)
+{
+ return reinterpret_cast<userrec*>(this->SE->GetRef(socket));
+}
+
+bool InspIRCd::AddMode(ModeHandler* mh, const unsigned char mode)
+{
+ return this->Modes->AddMode(mh,mode);
+}
+
+bool InspIRCd::AddModeWatcher(ModeWatcher* mw)
+{
+ return this->Modes->AddModeWatcher(mw);
+}
+
+bool InspIRCd::DelModeWatcher(ModeWatcher* mw)
+{
+ return this->Modes->DelModeWatcher(mw);
+}
+
+bool InspIRCd::AddResolver(Resolver* r, bool cached)
+{
+ if (!cached)
+ return this->Res->AddResolverClass(r);
+ else
+ {
+ r->TriggerCachedResult();
+ delete r;
+ return true;
+ }
+}
+
+void InspIRCd::AddGLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask)
+{
+ XLines->add_gline(duration, source.c_str(), reason.c_str(), hostmask.c_str());
+ XLines->apply_lines(APPLY_GLINES);
+}
+
+void InspIRCd::AddQLine(long duration, const std::string &source, const std::string &reason, const std::string &nickname)
+{
+ XLines->add_qline(duration, source.c_str(), reason.c_str(), nickname.c_str());
+ XLines->apply_lines(APPLY_QLINES);
+}
+
+void InspIRCd::AddZLine(long duration, const std::string &source, const std::string &reason, const std::string &ipaddr)
+{
+ XLines->add_zline(duration, source.c_str(), reason.c_str(), ipaddr.c_str());
+ XLines->apply_lines(APPLY_ZLINES);
+}
+
+void InspIRCd::AddKLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask)
+{
+ XLines->add_kline(duration, source.c_str(), reason.c_str(), hostmask.c_str());
+ XLines->apply_lines(APPLY_KLINES);
+}
+
+void InspIRCd::AddELine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask)
+{
+ XLines->add_eline(duration, source.c_str(), reason.c_str(), hostmask.c_str());
+}
+
+bool InspIRCd::DelGLine(const std::string &hostmask)
+{
+ return XLines->del_gline(hostmask.c_str());
+}
+
+bool InspIRCd::DelQLine(const std::string &nickname)
+{
+ return XLines->del_qline(nickname.c_str());
+}
+
+bool InspIRCd::DelZLine(const std::string &ipaddr)
+{
+ return XLines->del_zline(ipaddr.c_str());
+}
+
+bool InspIRCd::DelKLine(const std::string &hostmask)
+{
+ return XLines->del_kline(hostmask.c_str());
+}
+
+bool InspIRCd::DelELine(const std::string &hostmask)
+{
+ return XLines->del_eline(hostmask.c_str());
+}
+
+/*
+ * XXX why on *earth* is this in modules.cpp...? I think
+ * perhaps we need a server.cpp for InspIRCd:: stuff where possible. -- w00t
+ */
+bool InspIRCd::IsValidMask(const std::string &mask)
+{
+ char* dest = (char*)mask.c_str();
+ if (strchr(dest,'!')==0)
+ return false;
+ if (strchr(dest,'@')==0)
+ return false;
+ for (char* i = dest; *i; i++)
+ if (*i < 32)
+ return false;
+ for (char* i = dest; *i; i++)
+ if (*i > 126)
+ return false;
+ unsigned int c = 0;
+ for (char* i = dest; *i; i++)
+ if (*i == '!')
+ c++;
+ if (c>1)
+ return false;
+ c = 0;
+ for (char* i = dest; *i; i++)
+ if (*i == '@')
+ c++;
+ if (c>1)
+ return false;
+
+ return true;
+}
+
+Module* InspIRCd::FindModule(const std::string &name)
+{
+ for (int i = 0; i <= this->GetModuleCount(); i++)
+ {
+ if (this->Config->module_names[i] == name)
+ {
+ return this->modules[i];
+ }
+ }
+ return NULL;
+}
+
+ConfigReader::ConfigReader(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ /* Is there any reason to load the entire config file again here?
+ * it's needed if they specify another config file, but using the
+ * default one we can just use the global config data - pre-parsed!
+ */
+ this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out);
+
+ this->data = &ServerInstance->Config->config_data;
+ this->privatehash = false;
+}
+
+
+ConfigReader::~ConfigReader()
+{
+ if (this->errorlog)
+ DELETE(this->errorlog);
+ if(this->privatehash)
+ DELETE(this->data);
+}
+
+
+ConfigReader::ConfigReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance)
+{
+ ServerInstance->Config->ClearStack();
+
+ this->data = new ConfigDataHash;
+ this->privatehash = true;
+ this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out);
+ this->readerror = ServerInstance->Config->LoadConf(*this->data, filename, *this->errorlog);
+ if (!this->readerror)
+ this->error = CONF_FILE_NOT_FOUND;
+}
+
+
+std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
+{
+ /* Don't need to strlcpy() tag and name anymore, ReadConf() takes const char* */
+ std::string result;
+
+ if (!ServerInstance->Config->ConfValue(*this->data, tag, name, default_value, index, 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)
+{
+ return ServerInstance->Config->ConfValueBool(*this->data, tag, name, default_value, index);
+}
+
+bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
+{
+ return ReadFlag(tag, name, "", index);
+}
+
+
+long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool needs_unsigned)
+{
+ int result;
+
+ if(!ServerInstance->Config->ConfValueInteger(*this->data, tag, name, default_value, index, result))
+ {
+ this->error = CONF_VALUE_NOT_FOUND;
+ return 0;
+ }
+
+ if ((needs_unsigned) && (result < 0))
+ {
+ this->error = CONF_NOT_UNSIGNED;
+ return 0;
+ }
+
+ return result;
+}
+
+long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool needs_unsigned)
+{
+ return ReadInteger(tag, name, "", index, needs_unsigned);
+}
+
+long ConfigReader::GetError()
+{
+ long olderr = this->error;
+ this->error = 0;
+ return olderr;
+}
+
+void ConfigReader::DumpErrors(bool bail, userrec* user)
+{
+ ServerInstance->Config->ReportConfigError(this->errorlog->str(), bail, user);
+}
+
+
+int ConfigReader::Enumerate(const std::string &tag)
+{
+ return ServerInstance->Config->ConfValueEnum(*this->data, tag);
+}
+
+int ConfigReader::EnumerateValues(const std::string &tag, int index)
+{
+ return ServerInstance->Config->ConfVarEnum(*this->data, tag, index);
+}
+
+bool ConfigReader::Verify()
+{
+ return this->readerror;
+}
+
+
+FileReader::FileReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance)
+{
+ LoadFile(filename);
+}
+
+FileReader::FileReader(InspIRCd* Instance) : ServerInstance(Instance)
+{
+}
+
+std::string FileReader::Contents()
+{
+ std::string x;
+ for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
+ {
+ x.append(*a);
+ x.append("\r\n");
+ }
+ return x;
+}
+
+unsigned long FileReader::ContentSize()
+{
+ return this->contentsize;
+}
+
+void FileReader::CalcSize()
+{
+ unsigned long n = 0;
+ for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
+ n += (a->length() + 2);
+ this->contentsize = n;
+}
+
+void FileReader::LoadFile(const std::string &filename)
+{
+ file_cache c;
+ c.clear();
+ if (ServerInstance->Config->ReadFile(c,filename.c_str()))
+ {
+ this->fc = c;
+ this->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();
+}
+
+
diff --git a/src/modules/extra/README b/src/modules/extra/README
index 4c4beef9d..7e3096b34 100644
--- a/src/modules/extra/README
+++ b/src/modules/extra/README
@@ -1 +1,7 @@
-This directory stores modules which require external libraries to compile. For example, m_filter_pcre requires the PCRE libraries. To compile any of these modules first ensure you have the required dependencies (read the online documentation at http://www.inspircd.org/wiki/) and then cp the .cpp file from this directory into the parent directory (src/modules/) and re-configure your inspircd with ./configure -update to detect the new module. \ No newline at end of file
+This directory stores modules which require external libraries to compile.
+For example, m_filter_pcre requires the PCRE libraries.
+
+To compile any of these modules first ensure you have the required dependencies
+(read the online documentation at http://www.inspircd.org/wiki/) and then cp
+the .cpp file from this directory into the parent directory (src/modules/) and
+re-configure your inspircd with ./configure -update to detect the new module.
diff --git a/src/modules/extra/m_filter_pcre.cpp b/src/modules/extra/m_filter_pcre.cpp
index 0c6c05c8c..6fe79a981 100644
--- a/src/modules/extra/m_filter_pcre.cpp
+++ b/src/modules/extra/m_filter_pcre.cpp
@@ -1 +1,182 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <pcre.h> #include "users.h" #include "channels.h" #include "modules.h" #include "m_filter.h" /* $ModDesc: m_filter with regexps */ /* $CompileFlags: exec("pcre-config --cflags") */ /* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */ /* $ModDep: m_filter.h */ #ifdef WINDOWS #pragma comment(lib, "pcre.lib") #endif class PCREFilter : public FilterResult { public: pcre* regexp; PCREFilter(pcre* r, const std::string &rea, const std::string &act, long gline_time, const std::string &pat, const std::string &flags) : FilterResult(pat, rea, act, gline_time, flags), regexp(r) { } PCREFilter() { } }; class ModuleFilterPCRE : public FilterBase { std::vector<PCREFilter> filters; pcre *re; const char *error; int erroffset; PCREFilter fr; public: ModuleFilterPCRE(InspIRCd* Me) : FilterBase(Me, "m_filter_pcre.so") { OnRehash(NULL,""); } virtual ~ModuleFilterPCRE() { } virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) { for (std::vector<PCREFilter>::iterator index = filters.begin(); index != filters.end(); index++) { /* Skip ones that dont apply to us */ if (!FilterBase::AppliesToMe(user, dynamic_cast<FilterResult*>(&(*index)), flags)) continue; if (pcre_exec(index->regexp, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1) { fr = *index; if (index != filters.begin()) { filters.erase(index); filters.insert(filters.begin(), fr); } return &fr; } } return NULL; } virtual bool DeleteFilter(const std::string &freeform) { for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) { if (i->freeform == freeform) { pcre_free((*i).regexp); filters.erase(i); return true; } } return false; } virtual void SyncFilters(Module* proto, void* opaque) { for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) { this->SendFilter(proto, opaque, &(*i)); } } virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) { for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) { if (i->freeform == freeform) { return std::make_pair(false, "Filter already exists"); } } re = pcre_compile(freeform.c_str(),0,&error,&erroffset,NULL); if (!re) { ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", freeform.c_str(), erroffset, error); ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", freeform.c_str()); return std::make_pair(false, "Error in regular expression at offset " + ConvToStr(erroffset) + ": "+error); } else { filters.push_back(PCREFilter(re, reason, type, duration, freeform, flags)); return std::make_pair(true, ""); } } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader MyConf(ServerInstance); for (int index = 0; index < MyConf.Enumerate("keyword"); index++) { this->DeleteFilter(MyConf.ReadValue("keyword", "pattern", index)); std::string pattern = MyConf.ReadValue("keyword", "pattern", index); std::string reason = MyConf.ReadValue("keyword", "reason", index); std::string action = MyConf.ReadValue("keyword", "action", index); std::string flags = MyConf.ReadValue("keyword", "flags", index); long gline_time = ServerInstance->Duration(MyConf.ReadValue("keyword", "duration", index)); if (action.empty()) action = "none"; if (flags.empty()) flags = "*"; re = pcre_compile(pattern.c_str(),0,&error,&erroffset,NULL); if (!re) { ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", pattern.c_str(), erroffset, error); ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", pattern.c_str()); } else { filters.push_back(PCREFilter(re, reason, action, gline_time, pattern, flags)); ServerInstance->Log(DEFAULT,"Regular expression %s loaded.", pattern.c_str()); } } } virtual int OnStats(char symbol, userrec* user, string_list &results) { if (symbol == 's') { std::string sn = ServerInstance->Config->ServerName; for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) { results.push_back(sn+" 223 "+user->nick+" :REGEXP:"+i->freeform+" "+i->flags+" "+i->action+" "+ConvToStr(i->gline_time)+" :"+i->reason); } } return 0; } }; MODULE_INIT(ModuleFilterPCRE); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <pcre.h>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_filter.h"
+
+/* $ModDesc: m_filter with regexps */
+/* $CompileFlags: exec("pcre-config --cflags") */
+/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */
+/* $ModDep: m_filter.h */
+
+#ifdef WINDOWS
+#pragma comment(lib, "pcre.lib")
+#endif
+
+class PCREFilter : public FilterResult
+{
+ public:
+ pcre* regexp;
+
+ PCREFilter(pcre* r, const std::string &rea, const std::string &act, long gline_time, const std::string &pat, const std::string &flags)
+ : FilterResult(pat, rea, act, gline_time, flags), regexp(r)
+ {
+ }
+
+ PCREFilter()
+ {
+ }
+};
+
+class ModuleFilterPCRE : public FilterBase
+{
+ std::vector<PCREFilter> filters;
+ pcre *re;
+ const char *error;
+ int erroffset;
+ PCREFilter fr;
+
+ public:
+ ModuleFilterPCRE(InspIRCd* Me)
+ : FilterBase(Me, "m_filter_pcre.so")
+ {
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleFilterPCRE()
+ {
+ }
+
+ virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags)
+ {
+ for (std::vector<PCREFilter>::iterator index = filters.begin(); index != filters.end(); index++)
+ {
+ /* Skip ones that dont apply to us */
+
+ if (!FilterBase::AppliesToMe(user, dynamic_cast<FilterResult*>(&(*index)), flags))
+ continue;
+
+ if (pcre_exec(index->regexp, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1)
+ {
+ fr = *index;
+ if (index != filters.begin())
+ {
+ filters.erase(index);
+ filters.insert(filters.begin(), fr);
+ }
+ return &fr;
+ }
+ }
+ return NULL;
+ }
+
+ virtual bool DeleteFilter(const std::string &freeform)
+ {
+ for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ if (i->freeform == freeform)
+ {
+ pcre_free((*i).regexp);
+ filters.erase(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual void SyncFilters(Module* proto, void* opaque)
+ {
+ for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ this->SendFilter(proto, opaque, &(*i));
+ }
+ }
+
+ virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags)
+ {
+ for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ if (i->freeform == freeform)
+ {
+ return std::make_pair(false, "Filter already exists");
+ }
+ }
+
+ re = pcre_compile(freeform.c_str(),0,&error,&erroffset,NULL);
+
+ if (!re)
+ {
+ ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", freeform.c_str(), erroffset, error);
+ ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", freeform.c_str());
+ return std::make_pair(false, "Error in regular expression at offset " + ConvToStr(erroffset) + ": "+error);
+ }
+ else
+ {
+ filters.push_back(PCREFilter(re, reason, type, duration, freeform, flags));
+ return std::make_pair(true, "");
+ }
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader MyConf(ServerInstance);
+
+ for (int index = 0; index < MyConf.Enumerate("keyword"); index++)
+ {
+ this->DeleteFilter(MyConf.ReadValue("keyword", "pattern", index));
+
+ std::string pattern = MyConf.ReadValue("keyword", "pattern", index);
+ std::string reason = MyConf.ReadValue("keyword", "reason", index);
+ std::string action = MyConf.ReadValue("keyword", "action", index);
+ std::string flags = MyConf.ReadValue("keyword", "flags", index);
+ long gline_time = ServerInstance->Duration(MyConf.ReadValue("keyword", "duration", index));
+ if (action.empty())
+ action = "none";
+ if (flags.empty())
+ flags = "*";
+
+ re = pcre_compile(pattern.c_str(),0,&error,&erroffset,NULL);
+
+ if (!re)
+ {
+ ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", pattern.c_str(), erroffset, error);
+ ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", pattern.c_str());
+ }
+ else
+ {
+ filters.push_back(PCREFilter(re, reason, action, gline_time, pattern, flags));
+ ServerInstance->Log(DEFAULT,"Regular expression %s loaded.", pattern.c_str());
+ }
+ }
+ }
+
+ virtual int OnStats(char symbol, userrec* user, string_list &results)
+ {
+ if (symbol == 's')
+ {
+ std::string sn = ServerInstance->Config->ServerName;
+ for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ results.push_back(sn+" 223 "+user->nick+" :REGEXP:"+i->freeform+" "+i->flags+" "+i->action+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+ }
+ }
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleFilterPCRE);
+
diff --git a/src/modules/extra/m_httpclienttest.cpp b/src/modules/extra/m_httpclienttest.cpp
index 3f74b549b..90e7a5159 100644
--- a/src/modules/extra/m_httpclienttest.cpp
+++ b/src/modules/extra/m_httpclienttest.cpp
@@ -1 +1,81 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "httpclient.h" /* $ModDep: httpclient.h */ class MyModule : public Module { public: MyModule(InspIRCd* Me) : Module::Module(Me) { } virtual ~MyModule() { } virtual void Implements(char* List) { List[I_OnRequest] = List[I_OnUserJoin] = List[I_OnUserPart] = 1; } virtual Version GetVersion() { return Version(1,0,0,1,VF_VENDOR,API_VERSION); } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { // method called when a user joins a channel std::string chan = channel->name; std::string nick = user->nick; ServerInstance->Log(DEBUG,"User " + nick + " joined " + chan); Module* target = ServerInstance->FindModule("m_http_client.so"); if(target) { HTTPClientRequest req(ServerInstance, this, target, "http://znc.in/~psychon"); req.Send(); } else ServerInstance->Log(DEBUG,"module not found, load it!!"); } char* OnRequest(Request* req) { HTTPClientResponse* resp = (HTTPClientResponse*)req; if(!strcmp(resp->GetId(), HTTP_CLIENT_RESPONSE)) { ServerInstance->Log(DEBUG, resp->GetData()); } return NULL; } virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { } }; MODULE_INIT(MyModule); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "httpclient.h"
+
+/* $ModDep: httpclient.h */
+
+class MyModule : public Module
+{
+
+public:
+
+ MyModule(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ }
+
+ virtual ~MyModule()
+ {
+ }
+
+ virtual void Implements(char* List)
+ {
+ List[I_OnRequest] = List[I_OnUserJoin] = List[I_OnUserPart] = 1;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,0,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ // method called when a user joins a channel
+
+ std::string chan = channel->name;
+ std::string nick = user->nick;
+ ServerInstance->Log(DEBUG,"User " + nick + " joined " + chan);
+
+ Module* target = ServerInstance->FindModule("m_http_client.so");
+ if(target)
+ {
+ HTTPClientRequest req(ServerInstance, this, target, "http://znc.in/~psychon");
+ req.Send();
+ }
+ else
+ ServerInstance->Log(DEBUG,"module not found, load it!!");
+ }
+
+ char* OnRequest(Request* req)
+ {
+ HTTPClientResponse* resp = (HTTPClientResponse*)req;
+ if(!strcmp(resp->GetId(), HTTP_CLIENT_RESPONSE))
+ {
+ ServerInstance->Log(DEBUG, resp->GetData());
+ }
+ return NULL;
+ }
+
+ virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
+ {
+ }
+
+};
+
+MODULE_INIT(MyModule);
+
diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp
index eeabe5d48..6605bed3c 100644
--- a/src/modules/extra/m_mysql.cpp
+++ b/src/modules/extra/m_mysql.cpp
@@ -1 +1,889 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <mysql.h> #include <pthread.h> #include "users.h" #include "channels.h" #include "modules.h" #include "m_sqlv2.h" /* VERSION 2 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") */ /* $ModDep: m_sqlv2.h */ /* THE NONBLOCKING MYSQL API! * * MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend * that instead, you should thread your program. This is what i've done here to allow for * asyncronous SQL requests via mysql. The way this works is as follows: * * The module spawns a thread via pthreads, and performs its mysql queries in this thread, * using a queue with priorities. There is a mutex on either end which prevents two threads * adjusting the queue at the same time, and crashing the ircd. Every 50 milliseconds, the * worker thread wakes up, and checks if there is a request at the head of its queue. * If there is, it processes this request, blocking the worker thread but leaving the ircd * thread to go about its business as usual. During this period, the ircd thread is able * to insert futher pending requests into the queue. * * Once the processing of a request is complete, it is removed from the incoming queue to * an outgoing queue, and initialized as a 'response'. The worker thread then signals the * ircd thread (via a loopback socket) of the fact a result is available, by sending the * connection ID through the connection. * * The ircd thread then mutexes the queue once more, reads the outbound response off the head * of the queue, and sends it on its way to the original calling module. * * XXX: You might be asking "why doesnt he just send the response from within the worker thread?" * The answer to this is simple. The majority of InspIRCd, and in fact most ircd's are not * threadsafe. This module is designed to be threadsafe and is careful with its use of threads, * however, if we were to call a module's OnRequest even from within a thread which was not the * one the module was originally instantiated upon, there is a chance of all hell breaking loose * if a module is ever put in a re-enterant state (stack corruption could occur, crashes, data * corruption, and worse, so DONT think about it until the day comes when InspIRCd is 100% * gauranteed threadsafe!) * * For a diagram of this system please see http://www.inspircd.org/wiki/Mysql2 */ class SQLConnection; class Notifier; typedef std::map<std::string, SQLConnection*> ConnMap; bool giveup = false; static Module* SQLModule = NULL; static Notifier* MessagePipe = NULL; int QueueFD = -1; #if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224 #define mysql_field_count mysql_num_fields #endif typedef std::deque<SQLresult*> ResultQueue; /* A mutex to wrap around queue accesses */ pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t results_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t logging_mutex = PTHREAD_MUTEX_INITIALIZER; /** Represents a mysql result set */ class MySQLresult : public SQLresult { int currentrow; std::vector<std::string> colnames; std::vector<SQLfieldList> fieldlists; SQLfieldMap* fieldmap; SQLfieldMap fieldmap2; SQLfieldList emptyfieldlist; int rows; public: MySQLresult(Module* self, Module* to, MYSQL_RES* res, int affected_rows, unsigned int id) : SQLresult(self, to, id), currentrow(0), fieldmap(NULL) { /* A number of affected rows from from mysql_affected_rows. */ fieldlists.clear(); rows = 0; if (affected_rows >= 1) { rows = affected_rows; fieldlists.resize(rows); } unsigned int field_count = 0; if (res) { MYSQL_ROW row; int n = 0; while ((row = mysql_fetch_row(res))) { if (fieldlists.size() < (unsigned int)rows+1) { fieldlists.resize(fieldlists.size()+1); } field_count = 0; MYSQL_FIELD *fields = mysql_fetch_fields(res); if(mysql_num_fields(res) == 0) break; if (fields && mysql_num_fields(res)) { colnames.clear(); while (field_count < mysql_num_fields(res)) { std::string a = (fields[field_count].name ? fields[field_count].name : ""); std::string b = (row[field_count] ? row[field_count] : ""); SQLfield sqlf(b, !row[field_count]); colnames.push_back(a); fieldlists[n].push_back(sqlf); field_count++; } n++; } rows++; } mysql_free_result(res); } } MySQLresult(Module* self, Module* to, SQLerror e, unsigned int id) : SQLresult(self, to, id), currentrow(0) { rows = 0; error = e; } ~MySQLresult() { } virtual int Rows() { return rows; } virtual int Cols() { return colnames.size(); } virtual std::string ColName(int column) { if (column < (int)colnames.size()) { return colnames[column]; } else { throw SQLbadColName(); } return ""; } virtual int ColNum(const std::string &column) { for (unsigned int i = 0; i < colnames.size(); i++) { if (column == colnames[i]) return i; } throw SQLbadColName(); return 0; } virtual SQLfield GetValue(int row, int column) { if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols())) { return fieldlists[row][column]; } throw SQLbadColName(); /* XXX: We never actually get here because of the throw */ return SQLfield("",true); } virtual SQLfieldList& GetRow() { if (currentrow < rows) return fieldlists[currentrow]; else return emptyfieldlist; } virtual SQLfieldMap& GetRowMap() { fieldmap2.clear(); if (currentrow < rows) { for (int i = 0; i < Cols(); i++) { fieldmap2.insert(std::make_pair(colnames[i],GetValue(currentrow, i))); } currentrow++; } return fieldmap2; } virtual SQLfieldList* GetRowPtr() { SQLfieldList* fieldlist = new SQLfieldList(); if (currentrow < rows) { for (int i = 0; i < Rows(); i++) { fieldlist->push_back(fieldlists[currentrow][i]); } currentrow++; } return fieldlist; } virtual SQLfieldMap* GetRowMapPtr() { fieldmap = new SQLfieldMap(); if (currentrow < rows) { for (int i = 0; i < Cols(); i++) { fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i))); } currentrow++; } return fieldmap; } virtual void Free(SQLfieldMap* fm) { delete fm; } virtual void Free(SQLfieldList* fl) { delete fl; } }; class SQLConnection; void NotifyMainThread(SQLConnection* connection_with_new_result); /** Represents a connection to a mysql database */ class SQLConnection : public classbase { protected: MYSQL connection; MYSQL_RES *res; MYSQL_ROW row; SQLhost host; std::map<std::string,std::string> thisrow; bool Enabled; public: QueryQueue queue; ResultQueue rq; // This constructor creates an SQLConnection object with the given credentials, but does not connect yet. SQLConnection(const SQLhost &hi) : host(hi), Enabled(false) { } ~SQLConnection() { Close(); } // This method connects to the database using the credentials supplied to the constructor, and returns // true upon success. bool Connect() { unsigned int timeout = 1; mysql_init(&connection); mysql_options(&connection,MYSQL_OPT_CONNECT_TIMEOUT,(char*)&timeout); return mysql_real_connect(&connection, host.host.c_str(), host.user.c_str(), host.pass.c_str(), host.name.c_str(), host.port, NULL, 0); } void DoLeadingQuery() { if (!CheckConnection()) return; /* Parse the command string and dispatch it to mysql */ SQLrequest& req = queue.front(); /* Pointer to the buffer we screw around with substitution in */ char* query; /* Pointer to the current end of query, where we append new stuff */ char* queryend; /* Total length of the unescaped parameters */ unsigned long paramlen; /* Total length of query, used for binary-safety in mysql_real_query */ unsigned long querylength = 0; paramlen = 0; for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++) { paramlen += i->size(); } /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. * sizeofquery + (totalparamlength*2) + 1 * * The +1 is for null-terminating the string for mysql_real_escape_string */ query = new char[req.query.q.length() + (paramlen*2) + 1]; queryend = query; /* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting * the parameters into it... */ for(unsigned long i = 0; i < req.query.q.length(); i++) { if(req.query.q[i] == '?') { /* We found a place to substitute..what fun. * use mysql calls to escape and write the * escaped string onto the end of our query buffer, * then we "just" need to make sure queryend is * pointing at the right place. */ if(req.query.p.size()) { unsigned long len = mysql_real_escape_string(&connection, queryend, req.query.p.front().c_str(), req.query.p.front().length()); queryend += len; req.query.p.pop_front(); } else break; } else { *queryend = req.query.q[i]; queryend++; } querylength++; } *queryend = 0; pthread_mutex_lock(&queue_mutex); req.query.q = query; pthread_mutex_unlock(&queue_mutex); if (!mysql_real_query(&connection, req.query.q.data(), req.query.q.length())) { /* Successfull query */ res = mysql_use_result(&connection); unsigned long rows = mysql_affected_rows(&connection); MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), res, rows, req.id); r->dbid = this->GetID(); r->query = req.query.q; /* Put this new result onto the results queue. * XXX: Remember to mutex the queue! */ pthread_mutex_lock(&results_mutex); rq.push_back(r); pthread_mutex_unlock(&results_mutex); } else { /* XXX: See /usr/include/mysql/mysqld_error.h for a list of * possible error numbers and error messages */ SQLerror e(QREPLY_FAIL, ConvToStr(mysql_errno(&connection)) + std::string(": ") + mysql_error(&connection)); MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), e, req.id); r->dbid = this->GetID(); r->query = req.query.q; pthread_mutex_lock(&results_mutex); rq.push_back(r); pthread_mutex_unlock(&results_mutex); } /* Now signal the main thread that we've got a result to process. * Pass them this connection id as what to examine */ delete[] query; NotifyMainThread(this); } bool ConnectionLost() { if (&connection) { return (mysql_ping(&connection) != 0); } else return false; } bool CheckConnection() { if (ConnectionLost()) { return Connect(); } else return true; } std::string GetError() { return mysql_error(&connection); } const std::string& GetID() { return host.id; } std::string GetHost() { return host.host; } void SetEnable(bool Enable) { Enabled = Enable; } bool IsEnabled() { return Enabled; } void Close() { mysql_close(&connection); } const SQLhost& GetConfHost() { return host; } }; ConnMap Connections; bool HasHost(const SQLhost &host) { for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); iter++) { if (host == iter->second->GetConfHost()) return true; } return false; } bool HostInConf(ConfigReader* conf, const SQLhost &h) { for(int i = 0; i < conf->Enumerate("database"); i++) { SQLhost host; host.id = conf->ReadValue("database", "id", i); host.host = conf->ReadValue("database", "hostname", i); host.port = conf->ReadInteger("database", "port", i, true); host.name = conf->ReadValue("database", "name", i); host.user = conf->ReadValue("database", "username", i); host.pass = conf->ReadValue("database", "password", i); host.ssl = conf->ReadFlag("database", "ssl", i); if (h == host) return true; } return false; } void ClearOldConnections(ConfigReader* conf) { ConnMap::iterator i,safei; for (i = Connections.begin(); i != Connections.end(); i++) { if (!HostInConf(conf, i->second->GetConfHost())) { DELETE(i->second); safei = i; --i; Connections.erase(safei); } } } void ClearAllConnections() { ConnMap::iterator i; while ((i = Connections.begin()) != Connections.end()) { Connections.erase(i); DELETE(i->second); } } void ConnectDatabases(InspIRCd* ServerInstance) { for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++) { if (i->second->IsEnabled()) continue; i->second->SetEnable(true); if (!i->second->Connect()) { /* XXX: MUTEX */ pthread_mutex_lock(&logging_mutex); ServerInstance->Log(DEFAULT,"SQL: Failed to connect database "+i->second->GetHost()+": Error: "+i->second->GetError()); i->second->SetEnable(false); pthread_mutex_unlock(&logging_mutex); } } } void LoadDatabases(ConfigReader* conf, InspIRCd* ServerInstance) { ClearOldConnections(conf); for (int j =0; j < conf->Enumerate("database"); j++) { SQLhost host; host.id = conf->ReadValue("database", "id", j); host.host = conf->ReadValue("database", "hostname", j); host.port = conf->ReadInteger("database", "port", j, true); host.name = conf->ReadValue("database", "name", j); host.user = conf->ReadValue("database", "username", j); host.pass = conf->ReadValue("database", "password", j); host.ssl = conf->ReadFlag("database", "ssl", j); if (HasHost(host)) continue; if (!host.id.empty() && !host.host.empty() && !host.name.empty() && !host.user.empty() && !host.pass.empty()) { SQLConnection* ThisSQL = new SQLConnection(host); Connections[host.id] = ThisSQL; } } ConnectDatabases(ServerInstance); } char FindCharId(const std::string &id) { char i = 1; for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i) { if (iter->first == id) { return i; } } return 0; } ConnMap::iterator GetCharId(char id) { char i = 1; for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i) { if (i == id) return iter; } return Connections.end(); } void NotifyMainThread(SQLConnection* connection_with_new_result) { /* Here we write() to the socket the main thread has open * and we connect()ed back to before our thread became active. * The main thread is using a nonblocking socket tied into * the socket engine, so they wont block and they'll receive * nearly instant notification. Because we're in a seperate * thread, we can just use standard connect(), and we can * block if we like. We just send the connection id of the * connection back. * * NOTE: We only send a single char down the connection, this * way we know it wont get a partial read at the other end if * the system is especially congested (see bug #263). * The function FindCharId translates a connection name into a * one character id, and GetCharId translates a character id * back into an iterator. */ char id = FindCharId(connection_with_new_result->GetID()); send(QueueFD, &id, 1, 0); } void* DispatcherThread(void* arg); /** Used by m_mysql to notify one thread when the other has a result */ class Notifier : public InspSocket { insp_sockaddr sock_us; socklen_t uslen; public: /* Create a socket on a random port. Let the tcp stack allocate us an available port */ #ifdef IPV6 Notifier(InspIRCd* SI) : InspSocket(SI, "::1", 0, true, 3000) #else Notifier(InspIRCd* SI) : InspSocket(SI, "127.0.0.1", 0, true, 3000) #endif { uslen = sizeof(sock_us); if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen)) { throw ModuleException("Could not create random listening port on localhost"); } } Notifier(InspIRCd* SI, int newfd, char* ip) : InspSocket(SI, newfd, ip) { } /* Using getsockname and ntohs, we can determine which port number we were allocated */ int GetPort() { #ifdef IPV6 return ntohs(sock_us.sin6_port); #else return ntohs(sock_us.sin_port); #endif } virtual int OnIncomingConnection(int newsock, char* ip) { Notifier* n = new Notifier(this->Instance, newsock, ip); n = n; /* Stop bitching at me, GCC */ return true; } virtual bool OnDataReady() { char data = 0; /* NOTE: Only a single character is read so we know we * cant get a partial read. (We've been told that theres * data waiting, so we wont ever get EAGAIN) * The function GetCharId translates a single character * back into an iterator. */ if (read(this->GetFd(), &data, 1) > 0) { ConnMap::iterator iter = GetCharId(data); if (iter != Connections.end()) { /* Lock the mutex, send back the data */ pthread_mutex_lock(&results_mutex); ResultQueue::iterator n = iter->second->rq.begin(); (*n)->Send(); iter->second->rq.pop_front(); pthread_mutex_unlock(&results_mutex); return true; } /* No error, but unknown id */ return true; } /* Erk, error on descriptor! */ return false; } }; /** MySQL module */ class ModuleSQL : public Module { public: ConfigReader *Conf; InspIRCd* PublicServerInstance; pthread_t Dispatcher; int currid; bool rehashing; ModuleSQL(InspIRCd* Me) : Module::Module(Me), rehashing(false) { ServerInstance->UseInterface("SQLutils"); Conf = new ConfigReader(ServerInstance); PublicServerInstance = ServerInstance; currid = 0; SQLModule = this; MessagePipe = new Notifier(ServerInstance); pthread_attr_t attribs; pthread_attr_init(&attribs); pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED); if (pthread_create(&this->Dispatcher, &attribs, DispatcherThread, (void *)this) != 0) { throw ModuleException("m_mysql: Failed to create dispatcher thread: " + std::string(strerror(errno))); } if (!ServerInstance->PublishFeature("SQL", this)) { /* Tell worker thread to exit NOW */ giveup = true; throw ModuleException("m_mysql: Unable to publish feature 'SQL'"); } ServerInstance->PublishInterface("SQL", this); } virtual ~ModuleSQL() { giveup = true; ClearAllConnections(); DELETE(Conf); ServerInstance->UnpublishInterface("SQL", this); ServerInstance->UnpublishFeature("SQL"); ServerInstance->DoneWithInterface("SQLutils"); } void Implements(char* List) { List[I_OnRehash] = List[I_OnRequest] = 1; } unsigned long NewID() { if (currid+1 == 0) currid++; return ++currid; } char* OnRequest(Request* request) { if(strcmp(SQLREQID, request->GetId()) == 0) { SQLrequest* req = (SQLrequest*)request; /* XXX: Lock */ pthread_mutex_lock(&queue_mutex); ConnMap::iterator iter; char* returnval = NULL; if((iter = Connections.find(req->dbid)) != Connections.end()) { req->id = NewID(); iter->second->queue.push(*req); returnval = SQLSUCCESS; } else { req->error.Id(BAD_DBID); } pthread_mutex_unlock(&queue_mutex); /* XXX: Unlock */ return returnval; } return NULL; } virtual void OnRehash(userrec* user, const std::string &parameter) { rehashing = true; } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); } }; void* DispatcherThread(void* arg) { ModuleSQL* thismodule = (ModuleSQL*)arg; LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance); /* Connect back to the Notifier */ if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1) { /* crap, we're out of sockets... */ return NULL; } insp_sockaddr addr; #ifdef IPV6 insp_aton("::1", &addr.sin6_addr); addr.sin6_family = AF_FAMILY; addr.sin6_port = htons(MessagePipe->GetPort()); #else insp_inaddr ia; insp_aton("127.0.0.1", &ia); addr.sin_family = AF_FAMILY; addr.sin_addr = ia; addr.sin_port = htons(MessagePipe->GetPort()); #endif if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1) { /* wtf, we cant connect to it, but we just created it! */ return NULL; } while (!giveup) { if (thismodule->rehashing) { /* XXX: Lock */ pthread_mutex_lock(&queue_mutex); thismodule->rehashing = false; LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance); pthread_mutex_unlock(&queue_mutex); /* XXX: Unlock */ } SQLConnection* conn = NULL; /* XXX: Lock here for safety */ pthread_mutex_lock(&queue_mutex); for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++) { if (i->second->queue.totalsize()) { conn = i->second; break; } } pthread_mutex_unlock(&queue_mutex); /* XXX: Unlock */ /* Theres an item! */ if (conn) { conn->DoLeadingQuery(); /* XXX: Lock */ pthread_mutex_lock(&queue_mutex); conn->queue.pop(); pthread_mutex_unlock(&queue_mutex); /* XXX: Unlock */ } usleep(50); } return NULL; } MODULE_INIT(ModuleSQL); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <mysql.h>
+#include <pthread.h>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_sqlv2.h"
+
+/* VERSION 2 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") */
+/* $ModDep: m_sqlv2.h */
+
+/* THE NONBLOCKING MYSQL API!
+ *
+ * MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend
+ * that instead, you should thread your program. This is what i've done here to allow for
+ * asyncronous SQL requests via mysql. The way this works is as follows:
+ *
+ * The module spawns a thread via pthreads, and performs its mysql queries in this thread,
+ * using a queue with priorities. There is a mutex on either end which prevents two threads
+ * adjusting the queue at the same time, and crashing the ircd. Every 50 milliseconds, the
+ * worker thread wakes up, and checks if there is a request at the head of its queue.
+ * If there is, it processes this request, blocking the worker thread but leaving the ircd
+ * thread to go about its business as usual. During this period, the ircd thread is able
+ * to insert futher pending requests into the queue.
+ *
+ * Once the processing of a request is complete, it is removed from the incoming queue to
+ * an outgoing queue, and initialized as a 'response'. The worker thread then signals the
+ * ircd thread (via a loopback socket) of the fact a result is available, by sending the
+ * connection ID through the connection.
+ *
+ * The ircd thread then mutexes the queue once more, reads the outbound response off the head
+ * of the queue, and sends it on its way to the original calling module.
+ *
+ * XXX: You might be asking "why doesnt he just send the response from within the worker thread?"
+ * The answer to this is simple. The majority of InspIRCd, and in fact most ircd's are not
+ * threadsafe. This module is designed to be threadsafe and is careful with its use of threads,
+ * however, if we were to call a module's OnRequest even from within a thread which was not the
+ * one the module was originally instantiated upon, there is a chance of all hell breaking loose
+ * if a module is ever put in a re-enterant state (stack corruption could occur, crashes, data
+ * corruption, and worse, so DONT think about it until the day comes when InspIRCd is 100%
+ * gauranteed threadsafe!)
+ *
+ * For a diagram of this system please see http://www.inspircd.org/wiki/Mysql2
+ */
+
+
+class SQLConnection;
+class Notifier;
+
+
+typedef std::map<std::string, SQLConnection*> ConnMap;
+bool giveup = false;
+static Module* SQLModule = NULL;
+static Notifier* MessagePipe = NULL;
+int QueueFD = -1;
+
+
+#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
+#define mysql_field_count mysql_num_fields
+#endif
+
+typedef std::deque<SQLresult*> ResultQueue;
+
+/* A mutex to wrap around queue accesses */
+pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_mutex_t results_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_mutex_t logging_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/** Represents a mysql result set
+ */
+class MySQLresult : public SQLresult
+{
+ int currentrow;
+ std::vector<std::string> colnames;
+ std::vector<SQLfieldList> fieldlists;
+ SQLfieldMap* fieldmap;
+ SQLfieldMap fieldmap2;
+ SQLfieldList emptyfieldlist;
+ int rows;
+ public:
+
+ MySQLresult(Module* self, Module* to, MYSQL_RES* res, int affected_rows, unsigned int id) : SQLresult(self, to, id), currentrow(0), fieldmap(NULL)
+ {
+ /* A number of affected rows from from mysql_affected_rows.
+ */
+ fieldlists.clear();
+ rows = 0;
+ if (affected_rows >= 1)
+ {
+ rows = affected_rows;
+ fieldlists.resize(rows);
+ }
+ unsigned int field_count = 0;
+ if (res)
+ {
+ MYSQL_ROW row;
+ int n = 0;
+ while ((row = mysql_fetch_row(res)))
+ {
+ if (fieldlists.size() < (unsigned int)rows+1)
+ {
+ fieldlists.resize(fieldlists.size()+1);
+ }
+ field_count = 0;
+ MYSQL_FIELD *fields = mysql_fetch_fields(res);
+ if(mysql_num_fields(res) == 0)
+ break;
+ if (fields && mysql_num_fields(res))
+ {
+ colnames.clear();
+ while (field_count < mysql_num_fields(res))
+ {
+ std::string a = (fields[field_count].name ? fields[field_count].name : "");
+ std::string b = (row[field_count] ? row[field_count] : "");
+ SQLfield sqlf(b, !row[field_count]);
+ colnames.push_back(a);
+ fieldlists[n].push_back(sqlf);
+ field_count++;
+ }
+ n++;
+ }
+ rows++;
+ }
+ mysql_free_result(res);
+ }
+ }
+
+ MySQLresult(Module* self, Module* to, SQLerror e, unsigned int id) : SQLresult(self, to, id), currentrow(0)
+ {
+ rows = 0;
+ error = e;
+ }
+
+ ~MySQLresult()
+ {
+ }
+
+ virtual int Rows()
+ {
+ return rows;
+ }
+
+ virtual int Cols()
+ {
+ return colnames.size();
+ }
+
+ virtual std::string ColName(int column)
+ {
+ if (column < (int)colnames.size())
+ {
+ return colnames[column];
+ }
+ else
+ {
+ throw SQLbadColName();
+ }
+ return "";
+ }
+
+ virtual int ColNum(const std::string &column)
+ {
+ for (unsigned int i = 0; i < colnames.size(); i++)
+ {
+ if (column == colnames[i])
+ return i;
+ }
+ throw SQLbadColName();
+ return 0;
+ }
+
+ virtual SQLfield GetValue(int row, int column)
+ {
+ if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
+ {
+ return fieldlists[row][column];
+ }
+
+ throw SQLbadColName();
+
+ /* XXX: We never actually get here because of the throw */
+ return SQLfield("",true);
+ }
+
+ virtual SQLfieldList& GetRow()
+ {
+ if (currentrow < rows)
+ return fieldlists[currentrow];
+ else
+ return emptyfieldlist;
+ }
+
+ virtual SQLfieldMap& GetRowMap()
+ {
+ fieldmap2.clear();
+
+ if (currentrow < rows)
+ {
+ for (int i = 0; i < Cols(); i++)
+ {
+ fieldmap2.insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
+ }
+ currentrow++;
+ }
+
+ return fieldmap2;
+ }
+
+ virtual SQLfieldList* GetRowPtr()
+ {
+ SQLfieldList* fieldlist = new SQLfieldList();
+
+ if (currentrow < rows)
+ {
+ for (int i = 0; i < Rows(); i++)
+ {
+ fieldlist->push_back(fieldlists[currentrow][i]);
+ }
+ currentrow++;
+ }
+ return fieldlist;
+ }
+
+ virtual SQLfieldMap* GetRowMapPtr()
+ {
+ fieldmap = new SQLfieldMap();
+
+ if (currentrow < rows)
+ {
+ for (int i = 0; i < Cols(); i++)
+ {
+ fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
+ }
+ currentrow++;
+ }
+
+ return fieldmap;
+ }
+
+ virtual void Free(SQLfieldMap* fm)
+ {
+ delete fm;
+ }
+
+ virtual void Free(SQLfieldList* fl)
+ {
+ delete fl;
+ }
+};
+
+class SQLConnection;
+
+void NotifyMainThread(SQLConnection* connection_with_new_result);
+
+/** Represents a connection to a mysql database
+ */
+class SQLConnection : public classbase
+{
+ protected:
+
+ MYSQL connection;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ SQLhost host;
+ std::map<std::string,std::string> thisrow;
+ bool Enabled;
+
+ public:
+
+ QueryQueue queue;
+ ResultQueue rq;
+
+ // This constructor creates an SQLConnection object with the given credentials, but does not connect yet.
+ SQLConnection(const SQLhost &hi) : host(hi), Enabled(false)
+ {
+ }
+
+ ~SQLConnection()
+ {
+ Close();
+ }
+
+ // This method connects to the database using the credentials supplied to the constructor, and returns
+ // true upon success.
+ bool Connect()
+ {
+ unsigned int timeout = 1;
+ mysql_init(&connection);
+ mysql_options(&connection,MYSQL_OPT_CONNECT_TIMEOUT,(char*)&timeout);
+ return mysql_real_connect(&connection, host.host.c_str(), host.user.c_str(), host.pass.c_str(), host.name.c_str(), host.port, NULL, 0);
+ }
+
+ void DoLeadingQuery()
+ {
+ if (!CheckConnection())
+ return;
+
+ /* Parse the command string and dispatch it to mysql */
+ SQLrequest& req = queue.front();
+
+ /* Pointer to the buffer we screw around with substitution in */
+ char* query;
+
+ /* Pointer to the current end of query, where we append new stuff */
+ char* queryend;
+
+ /* Total length of the unescaped parameters */
+ unsigned long paramlen;
+
+ /* Total length of query, used for binary-safety in mysql_real_query */
+ unsigned long querylength = 0;
+
+ paramlen = 0;
+
+ for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
+ {
+ paramlen += i->size();
+ }
+
+ /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
+ * sizeofquery + (totalparamlength*2) + 1
+ *
+ * The +1 is for null-terminating the string for mysql_real_escape_string
+ */
+
+ query = new char[req.query.q.length() + (paramlen*2) + 1];
+ queryend = query;
+
+ /* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting
+ * the parameters into it...
+ */
+
+ for(unsigned long i = 0; i < req.query.q.length(); i++)
+ {
+ if(req.query.q[i] == '?')
+ {
+ /* We found a place to substitute..what fun.
+ * use mysql calls to escape and write the
+ * escaped string onto the end of our query buffer,
+ * then we "just" need to make sure queryend is
+ * pointing at the right place.
+ */
+ if(req.query.p.size())
+ {
+ unsigned long len = mysql_real_escape_string(&connection, queryend, req.query.p.front().c_str(), req.query.p.front().length());
+
+ queryend += len;
+ req.query.p.pop_front();
+ }
+ else
+ break;
+ }
+ else
+ {
+ *queryend = req.query.q[i];
+ queryend++;
+ }
+ querylength++;
+ }
+
+ *queryend = 0;
+
+ pthread_mutex_lock(&queue_mutex);
+ req.query.q = query;
+ pthread_mutex_unlock(&queue_mutex);
+
+ if (!mysql_real_query(&connection, req.query.q.data(), req.query.q.length()))
+ {
+ /* Successfull query */
+ res = mysql_use_result(&connection);
+ unsigned long rows = mysql_affected_rows(&connection);
+ MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), res, rows, req.id);
+ r->dbid = this->GetID();
+ r->query = req.query.q;
+ /* Put this new result onto the results queue.
+ * XXX: Remember to mutex the queue!
+ */
+ pthread_mutex_lock(&results_mutex);
+ rq.push_back(r);
+ pthread_mutex_unlock(&results_mutex);
+ }
+ else
+ {
+ /* XXX: See /usr/include/mysql/mysqld_error.h for a list of
+ * possible error numbers and error messages */
+ SQLerror e(QREPLY_FAIL, ConvToStr(mysql_errno(&connection)) + std::string(": ") + mysql_error(&connection));
+ MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), e, req.id);
+ r->dbid = this->GetID();
+ r->query = req.query.q;
+
+ pthread_mutex_lock(&results_mutex);
+ rq.push_back(r);
+ pthread_mutex_unlock(&results_mutex);
+ }
+
+ /* Now signal the main thread that we've got a result to process.
+ * Pass them this connection id as what to examine
+ */
+
+ delete[] query;
+
+ NotifyMainThread(this);
+ }
+
+ bool ConnectionLost()
+ {
+ if (&connection) {
+ return (mysql_ping(&connection) != 0);
+ }
+ else return false;
+ }
+
+ bool CheckConnection()
+ {
+ if (ConnectionLost()) {
+ return Connect();
+ }
+ else return true;
+ }
+
+ std::string GetError()
+ {
+ return mysql_error(&connection);
+ }
+
+ const std::string& GetID()
+ {
+ return host.id;
+ }
+
+ std::string GetHost()
+ {
+ return host.host;
+ }
+
+ void SetEnable(bool Enable)
+ {
+ Enabled = Enable;
+ }
+
+ bool IsEnabled()
+ {
+ return Enabled;
+ }
+
+ void Close()
+ {
+ mysql_close(&connection);
+ }
+
+ const SQLhost& GetConfHost()
+ {
+ return host;
+ }
+
+};
+
+ConnMap Connections;
+
+bool HasHost(const SQLhost &host)
+{
+ for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); iter++)
+ {
+ if (host == iter->second->GetConfHost())
+ return true;
+ }
+ return false;
+}
+
+bool HostInConf(ConfigReader* conf, const SQLhost &h)
+{
+ for(int i = 0; i < conf->Enumerate("database"); i++)
+ {
+ SQLhost host;
+ host.id = conf->ReadValue("database", "id", i);
+ host.host = conf->ReadValue("database", "hostname", i);
+ host.port = conf->ReadInteger("database", "port", i, true);
+ host.name = conf->ReadValue("database", "name", i);
+ host.user = conf->ReadValue("database", "username", i);
+ host.pass = conf->ReadValue("database", "password", i);
+ host.ssl = conf->ReadFlag("database", "ssl", i);
+ if (h == host)
+ return true;
+ }
+ return false;
+}
+
+void ClearOldConnections(ConfigReader* conf)
+{
+ ConnMap::iterator i,safei;
+ for (i = Connections.begin(); i != Connections.end(); i++)
+ {
+ if (!HostInConf(conf, i->second->GetConfHost()))
+ {
+ DELETE(i->second);
+ safei = i;
+ --i;
+ Connections.erase(safei);
+ }
+ }
+}
+
+void ClearAllConnections()
+{
+ ConnMap::iterator i;
+ while ((i = Connections.begin()) != Connections.end())
+ {
+ Connections.erase(i);
+ DELETE(i->second);
+ }
+}
+
+void ConnectDatabases(InspIRCd* ServerInstance)
+{
+ for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++)
+ {
+ if (i->second->IsEnabled())
+ continue;
+
+ i->second->SetEnable(true);
+ if (!i->second->Connect())
+ {
+ /* XXX: MUTEX */
+ pthread_mutex_lock(&logging_mutex);
+ ServerInstance->Log(DEFAULT,"SQL: Failed to connect database "+i->second->GetHost()+": Error: "+i->second->GetError());
+ i->second->SetEnable(false);
+ pthread_mutex_unlock(&logging_mutex);
+ }
+ }
+}
+
+void LoadDatabases(ConfigReader* conf, InspIRCd* ServerInstance)
+{
+ ClearOldConnections(conf);
+ for (int j =0; j < conf->Enumerate("database"); j++)
+ {
+ SQLhost host;
+ host.id = conf->ReadValue("database", "id", j);
+ host.host = conf->ReadValue("database", "hostname", j);
+ host.port = conf->ReadInteger("database", "port", j, true);
+ host.name = conf->ReadValue("database", "name", j);
+ host.user = conf->ReadValue("database", "username", j);
+ host.pass = conf->ReadValue("database", "password", j);
+ host.ssl = conf->ReadFlag("database", "ssl", j);
+
+ if (HasHost(host))
+ continue;
+
+ if (!host.id.empty() && !host.host.empty() && !host.name.empty() && !host.user.empty() && !host.pass.empty())
+ {
+ SQLConnection* ThisSQL = new SQLConnection(host);
+ Connections[host.id] = ThisSQL;
+ }
+ }
+ ConnectDatabases(ServerInstance);
+}
+
+char FindCharId(const std::string &id)
+{
+ char i = 1;
+ for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i)
+ {
+ if (iter->first == id)
+ {
+ return i;
+ }
+ }
+ return 0;
+}
+
+ConnMap::iterator GetCharId(char id)
+{
+ char i = 1;
+ for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i)
+ {
+ if (i == id)
+ return iter;
+ }
+ return Connections.end();
+}
+
+void NotifyMainThread(SQLConnection* connection_with_new_result)
+{
+ /* Here we write() to the socket the main thread has open
+ * and we connect()ed back to before our thread became active.
+ * The main thread is using a nonblocking socket tied into
+ * the socket engine, so they wont block and they'll receive
+ * nearly instant notification. Because we're in a seperate
+ * thread, we can just use standard connect(), and we can
+ * block if we like. We just send the connection id of the
+ * connection back.
+ *
+ * NOTE: We only send a single char down the connection, this
+ * way we know it wont get a partial read at the other end if
+ * the system is especially congested (see bug #263).
+ * The function FindCharId translates a connection name into a
+ * one character id, and GetCharId translates a character id
+ * back into an iterator.
+ */
+ char id = FindCharId(connection_with_new_result->GetID());
+ send(QueueFD, &id, 1, 0);
+}
+
+void* DispatcherThread(void* arg);
+
+/** Used by m_mysql to notify one thread when the other has a result
+ */
+class Notifier : public InspSocket
+{
+ insp_sockaddr sock_us;
+ socklen_t uslen;
+
+
+ public:
+
+ /* Create a socket on a random port. Let the tcp stack allocate us an available port */
+#ifdef IPV6
+ Notifier(InspIRCd* SI) : InspSocket(SI, "::1", 0, true, 3000)
+#else
+ Notifier(InspIRCd* SI) : InspSocket(SI, "127.0.0.1", 0, true, 3000)
+#endif
+ {
+ uslen = sizeof(sock_us);
+ if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen))
+ {
+ throw ModuleException("Could not create random listening port on localhost");
+ }
+ }
+
+ Notifier(InspIRCd* SI, int newfd, char* ip) : InspSocket(SI, newfd, ip)
+ {
+ }
+
+ /* Using getsockname and ntohs, we can determine which port number we were allocated */
+ int GetPort()
+ {
+#ifdef IPV6
+ return ntohs(sock_us.sin6_port);
+#else
+ return ntohs(sock_us.sin_port);
+#endif
+ }
+
+ virtual int OnIncomingConnection(int newsock, char* ip)
+ {
+ Notifier* n = new Notifier(this->Instance, newsock, ip);
+ n = n; /* Stop bitching at me, GCC */
+ return true;
+ }
+
+ virtual bool OnDataReady()
+ {
+ char data = 0;
+ /* NOTE: Only a single character is read so we know we
+ * cant get a partial read. (We've been told that theres
+ * data waiting, so we wont ever get EAGAIN)
+ * The function GetCharId translates a single character
+ * back into an iterator.
+ */
+ if (read(this->GetFd(), &data, 1) > 0)
+ {
+ ConnMap::iterator iter = GetCharId(data);
+ if (iter != Connections.end())
+ {
+ /* Lock the mutex, send back the data */
+ pthread_mutex_lock(&results_mutex);
+ ResultQueue::iterator n = iter->second->rq.begin();
+ (*n)->Send();
+ iter->second->rq.pop_front();
+ pthread_mutex_unlock(&results_mutex);
+ return true;
+ }
+ /* No error, but unknown id */
+ return true;
+ }
+
+ /* Erk, error on descriptor! */
+ return false;
+ }
+};
+
+/** MySQL module
+ */
+class ModuleSQL : public Module
+{
+ public:
+
+ ConfigReader *Conf;
+ InspIRCd* PublicServerInstance;
+ pthread_t Dispatcher;
+ int currid;
+ bool rehashing;
+
+ ModuleSQL(InspIRCd* Me)
+ : Module::Module(Me), rehashing(false)
+ {
+ ServerInstance->UseInterface("SQLutils");
+
+ Conf = new ConfigReader(ServerInstance);
+ PublicServerInstance = ServerInstance;
+ currid = 0;
+ SQLModule = this;
+
+ MessagePipe = new Notifier(ServerInstance);
+
+ pthread_attr_t attribs;
+ pthread_attr_init(&attribs);
+ pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(&this->Dispatcher, &attribs, DispatcherThread, (void *)this) != 0)
+ {
+ throw ModuleException("m_mysql: Failed to create dispatcher thread: " + std::string(strerror(errno)));
+ }
+
+ if (!ServerInstance->PublishFeature("SQL", this))
+ {
+ /* Tell worker thread to exit NOW */
+ giveup = true;
+ throw ModuleException("m_mysql: Unable to publish feature 'SQL'");
+ }
+
+ ServerInstance->PublishInterface("SQL", this);
+ }
+
+ virtual ~ModuleSQL()
+ {
+ giveup = true;
+ ClearAllConnections();
+ DELETE(Conf);
+ ServerInstance->UnpublishInterface("SQL", this);
+ ServerInstance->UnpublishFeature("SQL");
+ ServerInstance->DoneWithInterface("SQLutils");
+ }
+
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnRequest] = 1;
+ }
+
+ unsigned long NewID()
+ {
+ if (currid+1 == 0)
+ currid++;
+ return ++currid;
+ }
+
+ char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLREQID, request->GetId()) == 0)
+ {
+ SQLrequest* req = (SQLrequest*)request;
+
+ /* XXX: Lock */
+ pthread_mutex_lock(&queue_mutex);
+
+ ConnMap::iterator iter;
+
+ char* returnval = NULL;
+
+ if((iter = Connections.find(req->dbid)) != Connections.end())
+ {
+ req->id = NewID();
+ iter->second->queue.push(*req);
+ returnval = SQLSUCCESS;
+ }
+ else
+ {
+ req->error.Id(BAD_DBID);
+ }
+
+ pthread_mutex_unlock(&queue_mutex);
+ /* XXX: Unlock */
+
+ return returnval;
+ }
+
+ return NULL;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ rehashing = true;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
+ }
+
+};
+
+void* DispatcherThread(void* arg)
+{
+ ModuleSQL* thismodule = (ModuleSQL*)arg;
+ LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance);
+
+ /* Connect back to the Notifier */
+
+ if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1)
+ {
+ /* crap, we're out of sockets... */
+ return NULL;
+ }
+
+ insp_sockaddr addr;
+
+#ifdef IPV6
+ insp_aton("::1", &addr.sin6_addr);
+ addr.sin6_family = AF_FAMILY;
+ addr.sin6_port = htons(MessagePipe->GetPort());
+#else
+ insp_inaddr ia;
+ insp_aton("127.0.0.1", &ia);
+ addr.sin_family = AF_FAMILY;
+ addr.sin_addr = ia;
+ addr.sin_port = htons(MessagePipe->GetPort());
+#endif
+
+ if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1)
+ {
+ /* wtf, we cant connect to it, but we just created it! */
+ return NULL;
+ }
+
+ while (!giveup)
+ {
+ if (thismodule->rehashing)
+ {
+ /* XXX: Lock */
+ pthread_mutex_lock(&queue_mutex);
+ thismodule->rehashing = false;
+ LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance);
+ pthread_mutex_unlock(&queue_mutex);
+ /* XXX: Unlock */
+ }
+
+ SQLConnection* conn = NULL;
+ /* XXX: Lock here for safety */
+ pthread_mutex_lock(&queue_mutex);
+ for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++)
+ {
+ if (i->second->queue.totalsize())
+ {
+ conn = i->second;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&queue_mutex);
+ /* XXX: Unlock */
+
+ /* Theres an item! */
+ if (conn)
+ {
+ conn->DoLeadingQuery();
+
+ /* XXX: Lock */
+ pthread_mutex_lock(&queue_mutex);
+ conn->queue.pop();
+ pthread_mutex_unlock(&queue_mutex);
+ /* XXX: Unlock */
+ }
+
+ usleep(50);
+ }
+
+ return NULL;
+}
+
+MODULE_INIT(ModuleSQL);
+
diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp
index 9e85a40de..5d267fc1a 100644
--- a/src/modules/extra/m_pgsql.cpp
+++ b/src/modules/extra/m_pgsql.cpp
@@ -1 +1,984 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <cstdlib> #include <sstream> #include <libpq-fe.h> #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" #include "m_sqlv2.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 */ /* $ModDep: m_sqlv2.h */ /* SQLConn rewritten by peavey to * use EventHandler instead of * InspSocket. This is much neater * and gives total control of destroy * and delete of resources. */ /* Forward declare, so we can have the typedef neatly at the top */ class SQLConn; typedef std::map<std::string, SQLConn*> ConnMap; /* CREAD, Connecting and wants read event * CWRITE, Connecting and wants write event * WREAD, Connected/Working and wants read event * WWRITE, Connected/Working and wants write event * RREAD, Resetting and wants read event * RWRITE, Resetting and wants write event */ enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE, RREAD, RWRITE }; /** SQLhost::GetDSN() - Overload to return correct DSN for PostgreSQL */ std::string SQLhost::GetDSN() { std::ostringstream conninfo("connect_timeout = '2'"); if (ip.length()) conninfo << " hostaddr = '" << ip << "'"; if (port) conninfo << " port = '" << port << "'"; if (name.length()) conninfo << " dbname = '" << name << "'"; if (user.length()) conninfo << " user = '" << user << "'"; if (pass.length()) conninfo << " password = '" << pass << "'"; if (ssl) { conninfo << " sslmode = 'require'"; } else { conninfo << " sslmode = 'disable'"; } return conninfo.str(); } class ReconnectTimer : public InspTimer { private: Module* mod; public: ReconnectTimer(InspIRCd* SI, Module* m) : InspTimer(5, SI->Time(), false), mod(m) { } virtual void Tick(time_t TIME); }; /** Used to resolve sql server hostnames */ class SQLresolver : public Resolver { private: SQLhost host; Module* mod; public: SQLresolver(Module* m, InspIRCd* Instance, const SQLhost& hi, bool &cached) : Resolver(Instance, hi.host, DNS_QUERY_FORWARD, cached, (Module*)m), host(hi), mod(m) { } virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); virtual void OnError(ResolverError e, const std::string &errormessage) { ServerInstance->Log(DEBUG, "PgSQL: DNS lookup failed (%s), dying horribly", errormessage.c_str()); } }; /** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult. * All SQL providers must create their own subclass and define it's methods using that * database library's data retriveal functions. The aim is to avoid a slow and inefficient process * of converting all data to a common format before it reaches the result structure. This way * data is passes to the module nearly as directly as if it was using the API directly itself. */ class PgSQLresult : public SQLresult { PGresult* res; int currentrow; int rows; int cols; SQLfieldList* fieldlist; SQLfieldMap* fieldmap; public: PgSQLresult(Module* self, Module* to, unsigned long id, PGresult* result) : SQLresult(self, to, id), res(result), currentrow(0), fieldlist(NULL), fieldmap(NULL) { rows = PQntuples(res); cols = PQnfields(res); } ~PgSQLresult() { /* If we allocated these, free them... */ if(fieldlist) DELETE(fieldlist); if(fieldmap) DELETE(fieldmap); PQclear(res); } virtual int Rows() { if(!cols && !rows) { return atoi(PQcmdTuples(res)); } else { return rows; } } virtual int Cols() { return PQnfields(res); } virtual std::string ColName(int column) { char* name = PQfname(res, column); return (name) ? name : ""; } virtual int ColNum(const std::string &column) { int n = PQfnumber(res, column.c_str()); if(n == -1) { throw SQLbadColName(); } else { return n; } } virtual SQLfield GetValue(int row, int column) { char* v = PQgetvalue(res, row, column); if(v) { return SQLfield(std::string(v, PQgetlength(res, row, column)), PQgetisnull(res, row, column)); } else { throw SQLbadColName(); } } virtual SQLfieldList& GetRow() { /* In an effort to reduce overhead we don't actually allocate the list * until the first time it's needed...so... */ if(fieldlist) { fieldlist->clear(); } else { fieldlist = new SQLfieldList; } if(currentrow < PQntuples(res)) { int cols = PQnfields(res); for(int i = 0; i < cols; i++) { fieldlist->push_back(GetValue(currentrow, i)); } currentrow++; } return *fieldlist; } virtual SQLfieldMap& GetRowMap() { /* In an effort to reduce overhead we don't actually allocate the map * until the first time it's needed...so... */ if(fieldmap) { fieldmap->clear(); } else { fieldmap = new SQLfieldMap; } if(currentrow < PQntuples(res)) { int cols = PQnfields(res); for(int i = 0; i < cols; i++) { fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); } currentrow++; } return *fieldmap; } virtual SQLfieldList* GetRowPtr() { SQLfieldList* fl = new SQLfieldList; if(currentrow < PQntuples(res)) { int cols = PQnfields(res); for(int i = 0; i < cols; i++) { fl->push_back(GetValue(currentrow, i)); } currentrow++; } return fl; } virtual SQLfieldMap* GetRowMapPtr() { SQLfieldMap* fm = new SQLfieldMap; if(currentrow < PQntuples(res)) { int cols = PQnfields(res); for(int i = 0; i < cols; i++) { fm->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); } currentrow++; } return fm; } virtual void Free(SQLfieldMap* fm) { DELETE(fm); } virtual void Free(SQLfieldList* fl) { DELETE(fl); } }; /** SQLConn represents one SQL session. */ class SQLConn : public EventHandler { private: InspIRCd* Instance; SQLhost confhost; /* The <database> entry */ Module* us; /* Pointer to the SQL provider itself */ PGconn* sql; /* PgSQL database connection handle */ SQLstatus status; /* PgSQL database connection status */ bool qinprog; /* If there is currently a query in progress */ QueryQueue queue; /* Queue of queries waiting to be executed on this connection */ time_t idle; /* Time we last heard from the database */ public: SQLConn(InspIRCd* SI, Module* self, const SQLhost& hi) : EventHandler(), Instance(SI), confhost(hi), us(self), sql(NULL), status(CWRITE), qinprog(false) { idle = this->Instance->Time(); if(!DoConnect()) { Instance->Log(DEFAULT, "WARNING: Could not connect to database with id: " + ConvToStr(hi.id)); DelayReconnect(); } } ~SQLConn() { Close(); } virtual void HandleEvent(EventType et, int errornum) { switch (et) { case EVENT_READ: OnDataReady(); break; case EVENT_WRITE: OnWriteReady(); break; case EVENT_ERROR: DelayReconnect(); break; default: break; } } bool DoConnect() { if(!(sql = PQconnectStart(confhost.GetDSN().c_str()))) return false; if(PQstatus(sql) == CONNECTION_BAD) return false; if(PQsetnonblocking(sql, 1) == -1) return false; /* OK, we've initalised the connection, now to get it hooked into the socket engine * and then start polling it. */ this->fd = PQsocket(sql); if(this->fd <= -1) return false; if (!this->Instance->SE->AddFd(this)) { Instance->Log(DEBUG, "BUG: Couldn't add pgsql socket to socket engine"); return false; } /* Socket all hooked into the engine, now to tell PgSQL to start connecting */ return DoPoll(); } bool DoPoll() { switch(PQconnectPoll(sql)) { case PGRES_POLLING_WRITING: Instance->SE->WantWrite(this); status = CWRITE; return true; case PGRES_POLLING_READING: status = CREAD; return true; case PGRES_POLLING_FAILED: return false; case PGRES_POLLING_OK: status = WWRITE; return DoConnectedPoll(); default: return true; } } bool DoConnectedPoll() { if(!qinprog && queue.totalsize()) { /* There's no query currently in progress, and there's queries in the queue. */ SQLrequest& query = queue.front(); DoQuery(query); } if(PQconsumeInput(sql)) { /* We just read stuff from the server, that counts as it being alive * so update the idle-since time :p */ idle = this->Instance->Time(); if (PQisBusy(sql)) { /* Nothing happens here */ } else if (qinprog) { /* Grab the request we're processing */ SQLrequest& query = queue.front(); /* Get a pointer to the module we're about to return the result to */ Module* to = query.GetSource(); /* Fetch the result.. */ PGresult* result = PQgetResult(sql); /* PgSQL would allow a query string to be sent which has multiple * queries in it, this isn't portable across database backends and * we don't want modules doing it. But just in case we make sure we * drain any results there are and just use the last one. * If the module devs are behaving there will only be one result. */ while (PGresult* temp = PQgetResult(sql)) { PQclear(result); result = temp; } if(to) { /* ..and the result */ PgSQLresult reply(us, to, query.id, result); /* Fix by brain, make sure the original query gets sent back in the reply */ reply.query = query.query.q; switch(PQresultStatus(result)) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: case PGRES_FATAL_ERROR: reply.error.Id(QREPLY_FAIL); reply.error.Str(PQresultErrorMessage(result)); default:; /* No action, other values are not errors */ } reply.Send(); /* PgSQLresult's destructor will free the PGresult */ } else { /* If the client module is unloaded partway through a query then the provider will set * the pointer to NULL. We cannot just cancel the query as the result will still come * through at some point...and it could get messy if we play with invalid pointers... */ PQclear(result); } qinprog = false; queue.pop(); DoConnectedPoll(); } return true; } else { /* I think we'll assume this means the server died...it might not, * but I think that any error serious enough we actually get here * deserves to reconnect [/excuse] * Returning true so the core doesn't try and close the connection. */ DelayReconnect(); return true; } } bool DoResetPoll() { switch(PQresetPoll(sql)) { case PGRES_POLLING_WRITING: Instance->SE->WantWrite(this); status = CWRITE; return DoPoll(); case PGRES_POLLING_READING: status = CREAD; return true; case PGRES_POLLING_FAILED: return false; case PGRES_POLLING_OK: status = WWRITE; return DoConnectedPoll(); default: return true; } } bool OnDataReady() { /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */ return DoEvent(); } bool OnWriteReady() { /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */ return DoEvent(); } bool OnConnected() { return DoEvent(); } void DelayReconnect(); bool DoEvent() { bool ret; if((status == CREAD) || (status == CWRITE)) { ret = DoPoll(); } else if((status == RREAD) || (status == RWRITE)) { ret = DoResetPoll(); } else { ret = DoConnectedPoll(); } return ret; } SQLerror DoQuery(SQLrequest &req) { if((status == WREAD) || (status == WWRITE)) { if(!qinprog) { /* Parse the command string and dispatch it */ /* Pointer to the buffer we screw around with substitution in */ char* query; /* Pointer to the current end of query, where we append new stuff */ char* queryend; /* Total length of the unescaped parameters */ unsigned int paramlen; paramlen = 0; for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++) { paramlen += i->size(); } /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. * sizeofquery + (totalparamlength*2) + 1 * * The +1 is for null-terminating the string for PQsendQuery() */ query = new char[req.query.q.length() + (paramlen*2) + 1]; queryend = query; /* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting * the parameters into it... */ for(unsigned int i = 0; i < req.query.q.length(); i++) { if(req.query.q[i] == '?') { /* We found a place to substitute..what fun. * Use the PgSQL calls to escape and write the * escaped string onto the end of our query buffer, * then we "just" need to make sure queryend is * pointing at the right place. */ if(req.query.p.size()) { int error = 0; size_t len = 0; #ifdef PGSQL_HAS_ESCAPECONN len = PQescapeStringConn(sql, queryend, req.query.p.front().c_str(), req.query.p.front().length(), &error); #else len = PQescapeString (queryend, req.query.p.front().c_str(), req.query.p.front().length()); #endif if(error) { Instance->Log(DEBUG, "BUG: Apparently PQescapeStringConn() failed somehow...don't know how or what to do..."); } /* Incremenet queryend to the end of the newly escaped parameter */ queryend += len; /* Remove the parameter we just substituted in */ req.query.p.pop_front(); } else { Instance->Log(DEBUG, "BUG: Found a substitution location but no parameter to substitute :|"); break; } } else { *queryend = req.query.q[i]; queryend++; } } /* Null-terminate the query */ *queryend = 0; req.query.q = query; if(PQsendQuery(sql, query)) { qinprog = true; delete[] query; return SQLerror(); } else { delete[] query; return SQLerror(QSEND_FAIL, PQerrorMessage(sql)); } } } return SQLerror(BAD_CONN, "Can't query until connection is complete"); } SQLerror Query(const SQLrequest &req) { queue.push(req); if(!qinprog && queue.totalsize()) { /* There's no query currently in progress, and there's queries in the queue. */ SQLrequest& query = queue.front(); return DoQuery(query); } else { return SQLerror(); } } void OnUnloadModule(Module* mod) { queue.PurgeModule(mod); } const SQLhost GetConfHost() { return confhost; } void Close() { if (!this->Instance->SE->DelFd(this)) { if (sql && PQstatus(sql) == CONNECTION_BAD) { this->Instance->SE->DelFd(this, true); } else { Instance->Log(DEBUG, "BUG: PQsocket cant be removed from socket engine!"); } } if(sql) { PQfinish(sql); sql = NULL; } } }; class ModulePgSQL : public Module { private: ConnMap connections; unsigned long currid; char* sqlsuccess; ReconnectTimer* retimer; public: ModulePgSQL(InspIRCd* Me) : Module::Module(Me), currid(0) { ServerInstance->UseInterface("SQLutils"); sqlsuccess = new char[strlen(SQLSUCCESS)+1]; strlcpy(sqlsuccess, SQLSUCCESS, strlen(SQLSUCCESS)); if (!ServerInstance->PublishFeature("SQL", this)) { throw ModuleException("BUG: PgSQL Unable to publish feature 'SQL'"); } ReadConf(); ServerInstance->PublishInterface("SQL", this); } virtual ~ModulePgSQL() { if (retimer) ServerInstance->Timers->DelTimer(retimer); ClearAllConnections(); delete[] sqlsuccess; ServerInstance->UnpublishInterface("SQL", this); ServerInstance->UnpublishFeature("SQL"); ServerInstance->DoneWithInterface("SQLutils"); } void Implements(char* List) { List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadConf(); } bool HasHost(const SQLhost &host) { for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) { if (host == iter->second->GetConfHost()) return true; } return false; } bool HostInConf(const SQLhost &h) { ConfigReader conf(ServerInstance); for(int i = 0; i < conf.Enumerate("database"); i++) { SQLhost host; host.id = conf.ReadValue("database", "id", i); host.host = conf.ReadValue("database", "hostname", i); host.port = conf.ReadInteger("database", "port", i, true); host.name = conf.ReadValue("database", "name", i); host.user = conf.ReadValue("database", "username", i); host.pass = conf.ReadValue("database", "password", i); host.ssl = conf.ReadFlag("database", "ssl", "0", i); if (h == host) return true; } return false; } void ReadConf() { ClearOldConnections(); ConfigReader conf(ServerInstance); for(int i = 0; i < conf.Enumerate("database"); i++) { SQLhost host; int ipvalid; host.id = conf.ReadValue("database", "id", i); host.host = conf.ReadValue("database", "hostname", i); host.port = conf.ReadInteger("database", "port", i, true); host.name = conf.ReadValue("database", "name", i); host.user = conf.ReadValue("database", "username", i); host.pass = conf.ReadValue("database", "password", i); host.ssl = conf.ReadFlag("database", "ssl", "0", i); if (HasHost(host)) continue; #ifdef IPV6 if (strchr(host.host.c_str(),':')) { in6_addr blargle; ipvalid = inet_pton(AF_INET6, host.host.c_str(), &blargle); } else #endif { in_addr blargle; ipvalid = inet_aton(host.host.c_str(), &blargle); } if(ipvalid > 0) { /* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */ host.ip = host.host; this->AddConn(host); } else if(ipvalid == 0) { /* Conversion failed, assume it's a host */ SQLresolver* resolver; try { bool cached; resolver = new SQLresolver(this, ServerInstance, host, cached); ServerInstance->AddResolver(resolver, cached); } catch(...) { /* THE WORLD IS COMING TO AN END! */ } } else { /* Invalid address family, die horribly. */ ServerInstance->Log(DEBUG, "BUG: insp_aton failed returning -1, oh noes."); } } } void ClearOldConnections() { ConnMap::iterator iter,safei; for (iter = connections.begin(); iter != connections.end(); iter++) { if (!HostInConf(iter->second->GetConfHost())) { DELETE(iter->second); safei = iter; --iter; connections.erase(safei); } } } void ClearAllConnections() { ConnMap::iterator i; while ((i = connections.begin()) != connections.end()) { connections.erase(i); DELETE(i->second); } } void AddConn(const SQLhost& hi) { if (HasHost(hi)) { ServerInstance->Log(DEFAULT, "WARNING: A pgsql connection with id: %s already exists, possibly due to DNS delay. Aborting connection attempt.", hi.id.c_str()); return; } SQLConn* newconn; /* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */ newconn = new SQLConn(ServerInstance, this, hi); connections.insert(std::make_pair(hi.id, newconn)); } void ReconnectConn(SQLConn* conn) { for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) { if (conn == iter->second) { DELETE(iter->second); connections.erase(iter); break; } } retimer = new ReconnectTimer(ServerInstance, this); ServerInstance->Timers->AddTimer(retimer); } virtual char* OnRequest(Request* request) { if(strcmp(SQLREQID, request->GetId()) == 0) { SQLrequest* req = (SQLrequest*)request; ConnMap::iterator iter; if((iter = connections.find(req->dbid)) != connections.end()) { /* Execute query */ req->id = NewID(); req->error = iter->second->Query(*req); return (req->error.Id() == NO_ERROR) ? sqlsuccess : NULL; } else { req->error.Id(BAD_DBID); return NULL; } } return NULL; } virtual void OnUnloadModule(Module* mod, const std::string& name) { /* When a module unloads we have to check all the pending queries for all our connections * and set the Module* specifying where the query came from to NULL. If the query has already * been dispatched then when it is processed it will be dropped if the pointer is NULL. * * If the queries we find are not already being executed then we can simply remove them immediately. */ for(ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) { iter->second->OnUnloadModule(mod); } } unsigned long NewID() { if (currid+1 == 0) currid++; return ++currid; } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION); } }; /* move this here to use AddConn, rather that than having the whole * module above SQLConn, since this is buggin me right now :/ */ void SQLresolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) { host.ip = result; ((ModulePgSQL*)mod)->AddConn(host); ((ModulePgSQL*)mod)->ClearOldConnections(); } void ReconnectTimer::Tick(time_t time) { ((ModulePgSQL*)mod)->ReadConf(); } void SQLConn::DelayReconnect() { ((ModulePgSQL*)us)->ReconnectConn(this); } MODULE_INIT(ModulePgSQL); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <cstdlib>
+#include <sstream>
+#include <libpq-fe.h>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+#include "m_sqlv2.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 */
+/* $ModDep: m_sqlv2.h */
+
+
+/* SQLConn rewritten by peavey to
+ * use EventHandler instead of
+ * InspSocket. This is much neater
+ * and gives total control of destroy
+ * and delete of resources.
+ */
+
+/* Forward declare, so we can have the typedef neatly at the top */
+class SQLConn;
+
+typedef std::map<std::string, SQLConn*> ConnMap;
+
+/* CREAD, Connecting and wants read event
+ * CWRITE, Connecting and wants write event
+ * WREAD, Connected/Working and wants read event
+ * WWRITE, Connected/Working and wants write event
+ * RREAD, Resetting and wants read event
+ * RWRITE, Resetting and wants write event
+ */
+enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE, RREAD, RWRITE };
+
+/** SQLhost::GetDSN() - Overload to return correct DSN for PostgreSQL
+ */
+std::string SQLhost::GetDSN()
+{
+ std::ostringstream conninfo("connect_timeout = '2'");
+
+ if (ip.length())
+ conninfo << " hostaddr = '" << ip << "'";
+
+ if (port)
+ conninfo << " port = '" << port << "'";
+
+ if (name.length())
+ conninfo << " dbname = '" << name << "'";
+
+ if (user.length())
+ conninfo << " user = '" << user << "'";
+
+ if (pass.length())
+ conninfo << " password = '" << pass << "'";
+
+ if (ssl)
+ {
+ conninfo << " sslmode = 'require'";
+ }
+ else
+ {
+ conninfo << " sslmode = 'disable'";
+ }
+
+ return conninfo.str();
+}
+
+class ReconnectTimer : public InspTimer
+{
+ private:
+ Module* mod;
+ public:
+ ReconnectTimer(InspIRCd* SI, Module* m)
+ : InspTimer(5, SI->Time(), false), mod(m)
+ {
+ }
+ virtual void Tick(time_t TIME);
+};
+
+
+/** Used to resolve sql server hostnames
+ */
+class SQLresolver : public Resolver
+{
+ private:
+ SQLhost host;
+ Module* mod;
+ public:
+ SQLresolver(Module* m, InspIRCd* Instance, const SQLhost& hi, bool &cached)
+ : Resolver(Instance, hi.host, DNS_QUERY_FORWARD, cached, (Module*)m), host(hi), mod(m)
+ {
+ }
+
+ virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
+
+ virtual void OnError(ResolverError e, const std::string &errormessage)
+ {
+ ServerInstance->Log(DEBUG, "PgSQL: DNS lookup failed (%s), dying horribly", errormessage.c_str());
+ }
+};
+
+/** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
+ * All SQL providers must create their own subclass and define it's methods using that
+ * database library's data retriveal functions. The aim is to avoid a slow and inefficient process
+ * of converting all data to a common format before it reaches the result structure. This way
+ * data is passes to the module nearly as directly as if it was using the API directly itself.
+ */
+
+class PgSQLresult : public SQLresult
+{
+ PGresult* res;
+ int currentrow;
+ int rows;
+ int cols;
+
+ SQLfieldList* fieldlist;
+ SQLfieldMap* fieldmap;
+public:
+ PgSQLresult(Module* self, Module* to, unsigned long id, PGresult* result)
+ : SQLresult(self, to, id), res(result), currentrow(0), fieldlist(NULL), fieldmap(NULL)
+ {
+ rows = PQntuples(res);
+ cols = PQnfields(res);
+ }
+
+ ~PgSQLresult()
+ {
+ /* If we allocated these, free them... */
+ if(fieldlist)
+ DELETE(fieldlist);
+
+ if(fieldmap)
+ DELETE(fieldmap);
+
+ PQclear(res);
+ }
+
+ virtual int Rows()
+ {
+ if(!cols && !rows)
+ {
+ return atoi(PQcmdTuples(res));
+ }
+ else
+ {
+ return rows;
+ }
+ }
+
+ virtual int Cols()
+ {
+ return PQnfields(res);
+ }
+
+ virtual std::string ColName(int column)
+ {
+ char* name = PQfname(res, column);
+
+ return (name) ? name : "";
+ }
+
+ virtual int ColNum(const std::string &column)
+ {
+ int n = PQfnumber(res, column.c_str());
+
+ if(n == -1)
+ {
+ throw SQLbadColName();
+ }
+ else
+ {
+ return n;
+ }
+ }
+
+ virtual SQLfield GetValue(int row, int column)
+ {
+ char* v = PQgetvalue(res, row, column);
+
+ if(v)
+ {
+ return SQLfield(std::string(v, PQgetlength(res, row, column)), PQgetisnull(res, row, column));
+ }
+ else
+ {
+ throw SQLbadColName();
+ }
+ }
+
+ virtual SQLfieldList& GetRow()
+ {
+ /* In an effort to reduce overhead we don't actually allocate the list
+ * until the first time it's needed...so...
+ */
+ if(fieldlist)
+ {
+ fieldlist->clear();
+ }
+ else
+ {
+ fieldlist = new SQLfieldList;
+ }
+
+ if(currentrow < PQntuples(res))
+ {
+ int cols = PQnfields(res);
+
+ for(int i = 0; i < cols; i++)
+ {
+ fieldlist->push_back(GetValue(currentrow, i));
+ }
+
+ currentrow++;
+ }
+
+ return *fieldlist;
+ }
+
+ virtual SQLfieldMap& GetRowMap()
+ {
+ /* In an effort to reduce overhead we don't actually allocate the map
+ * until the first time it's needed...so...
+ */
+ if(fieldmap)
+ {
+ fieldmap->clear();
+ }
+ else
+ {
+ fieldmap = new SQLfieldMap;
+ }
+
+ if(currentrow < PQntuples(res))
+ {
+ int cols = PQnfields(res);
+
+ for(int i = 0; i < cols; i++)
+ {
+ fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
+ }
+
+ currentrow++;
+ }
+
+ return *fieldmap;
+ }
+
+ virtual SQLfieldList* GetRowPtr()
+ {
+ SQLfieldList* fl = new SQLfieldList;
+
+ if(currentrow < PQntuples(res))
+ {
+ int cols = PQnfields(res);
+
+ for(int i = 0; i < cols; i++)
+ {
+ fl->push_back(GetValue(currentrow, i));
+ }
+
+ currentrow++;
+ }
+
+ return fl;
+ }
+
+ virtual SQLfieldMap* GetRowMapPtr()
+ {
+ SQLfieldMap* fm = new SQLfieldMap;
+
+ if(currentrow < PQntuples(res))
+ {
+ int cols = PQnfields(res);
+
+ for(int i = 0; i < cols; i++)
+ {
+ fm->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
+ }
+
+ currentrow++;
+ }
+
+ return fm;
+ }
+
+ virtual void Free(SQLfieldMap* fm)
+ {
+ DELETE(fm);
+ }
+
+ virtual void Free(SQLfieldList* fl)
+ {
+ DELETE(fl);
+ }
+};
+
+/** SQLConn represents one SQL session.
+ */
+class SQLConn : public EventHandler
+{
+ private:
+ InspIRCd* Instance;
+ SQLhost confhost; /* The <database> entry */
+ Module* us; /* Pointer to the SQL provider itself */
+ PGconn* sql; /* PgSQL database connection handle */
+ SQLstatus status; /* PgSQL database connection status */
+ bool qinprog; /* If there is currently a query in progress */
+ QueryQueue queue; /* Queue of queries waiting to be executed on this connection */
+ time_t idle; /* Time we last heard from the database */
+
+ public:
+ SQLConn(InspIRCd* SI, Module* self, const SQLhost& hi)
+ : EventHandler(), Instance(SI), confhost(hi), us(self), sql(NULL), status(CWRITE), qinprog(false)
+ {
+ idle = this->Instance->Time();
+ if(!DoConnect())
+ {
+ Instance->Log(DEFAULT, "WARNING: Could not connect to database with id: " + ConvToStr(hi.id));
+ DelayReconnect();
+ }
+ }
+
+ ~SQLConn()
+ {
+ Close();
+ }
+
+ virtual void HandleEvent(EventType et, int errornum)
+ {
+ switch (et)
+ {
+ case EVENT_READ:
+ OnDataReady();
+ break;
+
+ case EVENT_WRITE:
+ OnWriteReady();
+ break;
+
+ case EVENT_ERROR:
+ DelayReconnect();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ bool DoConnect()
+ {
+ if(!(sql = PQconnectStart(confhost.GetDSN().c_str())))
+ return false;
+
+ if(PQstatus(sql) == CONNECTION_BAD)
+ return false;
+
+ if(PQsetnonblocking(sql, 1) == -1)
+ return false;
+
+ /* OK, we've initalised the connection, now to get it hooked into the socket engine
+ * and then start polling it.
+ */
+ this->fd = PQsocket(sql);
+
+ if(this->fd <= -1)
+ return false;
+
+ if (!this->Instance->SE->AddFd(this))
+ {
+ Instance->Log(DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
+ return false;
+ }
+
+ /* Socket all hooked into the engine, now to tell PgSQL to start connecting */
+ return DoPoll();
+ }
+
+ bool DoPoll()
+ {
+ switch(PQconnectPoll(sql))
+ {
+ case PGRES_POLLING_WRITING:
+ Instance->SE->WantWrite(this);
+ status = CWRITE;
+ return true;
+ case PGRES_POLLING_READING:
+ status = CREAD;
+ return true;
+ case PGRES_POLLING_FAILED:
+ return false;
+ case PGRES_POLLING_OK:
+ status = WWRITE;
+ return DoConnectedPoll();
+ default:
+ return true;
+ }
+ }
+
+ bool DoConnectedPoll()
+ {
+ if(!qinprog && queue.totalsize())
+ {
+ /* There's no query currently in progress, and there's queries in the queue. */
+ SQLrequest& query = queue.front();
+ DoQuery(query);
+ }
+
+ if(PQconsumeInput(sql))
+ {
+ /* We just read stuff from the server, that counts as it being alive
+ * so update the idle-since time :p
+ */
+ idle = this->Instance->Time();
+
+ if (PQisBusy(sql))
+ {
+ /* Nothing happens here */
+ }
+ else if (qinprog)
+ {
+ /* Grab the request we're processing */
+ SQLrequest& query = queue.front();
+
+ /* Get a pointer to the module we're about to return the result to */
+ Module* to = query.GetSource();
+
+ /* Fetch the result.. */
+ PGresult* result = PQgetResult(sql);
+
+ /* PgSQL would allow a query string to be sent which has multiple
+ * queries in it, this isn't portable across database backends and
+ * we don't want modules doing it. But just in case we make sure we
+ * drain any results there are and just use the last one.
+ * If the module devs are behaving there will only be one result.
+ */
+ while (PGresult* temp = PQgetResult(sql))
+ {
+ PQclear(result);
+ result = temp;
+ }
+
+ if(to)
+ {
+ /* ..and the result */
+ PgSQLresult reply(us, to, query.id, result);
+
+ /* Fix by brain, make sure the original query gets sent back in the reply */
+ reply.query = query.query.q;
+
+ switch(PQresultStatus(result))
+ {
+ case PGRES_EMPTY_QUERY:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_FATAL_ERROR:
+ reply.error.Id(QREPLY_FAIL);
+ reply.error.Str(PQresultErrorMessage(result));
+ default:;
+ /* No action, other values are not errors */
+ }
+
+ reply.Send();
+
+ /* PgSQLresult's destructor will free the PGresult */
+ }
+ else
+ {
+ /* If the client module is unloaded partway through a query then the provider will set
+ * the pointer to NULL. We cannot just cancel the query as the result will still come
+ * through at some point...and it could get messy if we play with invalid pointers...
+ */
+ PQclear(result);
+ }
+ qinprog = false;
+ queue.pop();
+ DoConnectedPoll();
+ }
+ return true;
+ }
+ else
+ {
+ /* I think we'll assume this means the server died...it might not,
+ * but I think that any error serious enough we actually get here
+ * deserves to reconnect [/excuse]
+ * Returning true so the core doesn't try and close the connection.
+ */
+ DelayReconnect();
+ return true;
+ }
+ }
+
+ bool DoResetPoll()
+ {
+ switch(PQresetPoll(sql))
+ {
+ case PGRES_POLLING_WRITING:
+ Instance->SE->WantWrite(this);
+ status = CWRITE;
+ return DoPoll();
+ case PGRES_POLLING_READING:
+ status = CREAD;
+ return true;
+ case PGRES_POLLING_FAILED:
+ return false;
+ case PGRES_POLLING_OK:
+ status = WWRITE;
+ return DoConnectedPoll();
+ default:
+ return true;
+ }
+ }
+
+ bool OnDataReady()
+ {
+ /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */
+ return DoEvent();
+ }
+
+ bool OnWriteReady()
+ {
+ /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */
+ return DoEvent();
+ }
+
+ bool OnConnected()
+ {
+ return DoEvent();
+ }
+
+ void DelayReconnect();
+
+ bool DoEvent()
+ {
+ bool ret;
+
+ if((status == CREAD) || (status == CWRITE))
+ {
+ ret = DoPoll();
+ }
+ else if((status == RREAD) || (status == RWRITE))
+ {
+ ret = DoResetPoll();
+ }
+ else
+ {
+ ret = DoConnectedPoll();
+ }
+ return ret;
+ }
+
+ SQLerror DoQuery(SQLrequest &req)
+ {
+ if((status == WREAD) || (status == WWRITE))
+ {
+ if(!qinprog)
+ {
+ /* Parse the command string and dispatch it */
+
+ /* Pointer to the buffer we screw around with substitution in */
+ char* query;
+ /* Pointer to the current end of query, where we append new stuff */
+ char* queryend;
+ /* Total length of the unescaped parameters */
+ unsigned int paramlen;
+
+ paramlen = 0;
+
+ for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
+ {
+ paramlen += i->size();
+ }
+
+ /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
+ * sizeofquery + (totalparamlength*2) + 1
+ *
+ * The +1 is for null-terminating the string for PQsendQuery()
+ */
+
+ query = new char[req.query.q.length() + (paramlen*2) + 1];
+ queryend = query;
+
+ /* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting
+ * the parameters into it...
+ */
+
+ for(unsigned int i = 0; i < req.query.q.length(); i++)
+ {
+ if(req.query.q[i] == '?')
+ {
+ /* We found a place to substitute..what fun.
+ * Use the PgSQL calls to escape and write the
+ * escaped string onto the end of our query buffer,
+ * then we "just" need to make sure queryend is
+ * pointing at the right place.
+ */
+
+ if(req.query.p.size())
+ {
+ int error = 0;
+ size_t len = 0;
+
+#ifdef PGSQL_HAS_ESCAPECONN
+ len = PQescapeStringConn(sql, queryend, req.query.p.front().c_str(), req.query.p.front().length(), &error);
+#else
+ len = PQescapeString (queryend, req.query.p.front().c_str(), req.query.p.front().length());
+#endif
+ if(error)
+ {
+ Instance->Log(DEBUG, "BUG: Apparently PQescapeStringConn() failed somehow...don't know how or what to do...");
+ }
+
+ /* Incremenet queryend to the end of the newly escaped parameter */
+ queryend += len;
+
+ /* Remove the parameter we just substituted in */
+ req.query.p.pop_front();
+ }
+ else
+ {
+ Instance->Log(DEBUG, "BUG: Found a substitution location but no parameter to substitute :|");
+ break;
+ }
+ }
+ else
+ {
+ *queryend = req.query.q[i];
+ queryend++;
+ }
+ }
+
+ /* Null-terminate the query */
+ *queryend = 0;
+ req.query.q = query;
+
+ if(PQsendQuery(sql, query))
+ {
+ qinprog = true;
+ delete[] query;
+ return SQLerror();
+ }
+ else
+ {
+ delete[] query;
+ return SQLerror(QSEND_FAIL, PQerrorMessage(sql));
+ }
+ }
+ }
+ return SQLerror(BAD_CONN, "Can't query until connection is complete");
+ }
+
+ SQLerror Query(const SQLrequest &req)
+ {
+ queue.push(req);
+
+ if(!qinprog && queue.totalsize())
+ {
+ /* There's no query currently in progress, and there's queries in the queue. */
+ SQLrequest& query = queue.front();
+ return DoQuery(query);
+ }
+ else
+ {
+ return SQLerror();
+ }
+ }
+
+ void OnUnloadModule(Module* mod)
+ {
+ queue.PurgeModule(mod);
+ }
+
+ const SQLhost GetConfHost()
+ {
+ return confhost;
+ }
+
+ void Close() {
+ if (!this->Instance->SE->DelFd(this))
+ {
+ if (sql && PQstatus(sql) == CONNECTION_BAD)
+ {
+ this->Instance->SE->DelFd(this, true);
+ }
+ else
+ {
+ Instance->Log(DEBUG, "BUG: PQsocket cant be removed from socket engine!");
+ }
+ }
+
+ if(sql)
+ {
+ PQfinish(sql);
+ sql = NULL;
+ }
+ }
+
+};
+
+class ModulePgSQL : public Module
+{
+ private:
+ ConnMap connections;
+ unsigned long currid;
+ char* sqlsuccess;
+ ReconnectTimer* retimer;
+
+ public:
+ ModulePgSQL(InspIRCd* Me)
+ : Module::Module(Me), currid(0)
+ {
+ ServerInstance->UseInterface("SQLutils");
+
+ sqlsuccess = new char[strlen(SQLSUCCESS)+1];
+
+ strlcpy(sqlsuccess, SQLSUCCESS, strlen(SQLSUCCESS));
+
+ if (!ServerInstance->PublishFeature("SQL", this))
+ {
+ throw ModuleException("BUG: PgSQL Unable to publish feature 'SQL'");
+ }
+
+ ReadConf();
+
+ ServerInstance->PublishInterface("SQL", this);
+ }
+
+ virtual ~ModulePgSQL()
+ {
+ if (retimer)
+ ServerInstance->Timers->DelTimer(retimer);
+ ClearAllConnections();
+ delete[] sqlsuccess;
+ ServerInstance->UnpublishInterface("SQL", this);
+ ServerInstance->UnpublishFeature("SQL");
+ ServerInstance->DoneWithInterface("SQLutils");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadConf();
+ }
+
+ bool HasHost(const SQLhost &host)
+ {
+ for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ if (host == iter->second->GetConfHost())
+ return true;
+ }
+ return false;
+ }
+
+ bool HostInConf(const SQLhost &h)
+ {
+ ConfigReader conf(ServerInstance);
+ for(int i = 0; i < conf.Enumerate("database"); i++)
+ {
+ SQLhost host;
+ host.id = conf.ReadValue("database", "id", i);
+ host.host = conf.ReadValue("database", "hostname", i);
+ host.port = conf.ReadInteger("database", "port", i, true);
+ host.name = conf.ReadValue("database", "name", i);
+ host.user = conf.ReadValue("database", "username", i);
+ host.pass = conf.ReadValue("database", "password", i);
+ host.ssl = conf.ReadFlag("database", "ssl", "0", i);
+ if (h == host)
+ return true;
+ }
+ return false;
+ }
+
+ void ReadConf()
+ {
+ ClearOldConnections();
+
+ ConfigReader conf(ServerInstance);
+ for(int i = 0; i < conf.Enumerate("database"); i++)
+ {
+ SQLhost host;
+ int ipvalid;
+
+ host.id = conf.ReadValue("database", "id", i);
+ host.host = conf.ReadValue("database", "hostname", i);
+ host.port = conf.ReadInteger("database", "port", i, true);
+ host.name = conf.ReadValue("database", "name", i);
+ host.user = conf.ReadValue("database", "username", i);
+ host.pass = conf.ReadValue("database", "password", i);
+ host.ssl = conf.ReadFlag("database", "ssl", "0", i);
+
+ if (HasHost(host))
+ continue;
+
+#ifdef IPV6
+ if (strchr(host.host.c_str(),':'))
+ {
+ in6_addr blargle;
+ ipvalid = inet_pton(AF_INET6, host.host.c_str(), &blargle);
+ }
+ else
+#endif
+ {
+ in_addr blargle;
+ ipvalid = inet_aton(host.host.c_str(), &blargle);
+ }
+
+ if(ipvalid > 0)
+ {
+ /* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */
+ host.ip = host.host;
+ this->AddConn(host);
+ }
+ else if(ipvalid == 0)
+ {
+ /* Conversion failed, assume it's a host */
+ SQLresolver* resolver;
+
+ try
+ {
+ bool cached;
+ resolver = new SQLresolver(this, ServerInstance, host, cached);
+ ServerInstance->AddResolver(resolver, cached);
+ }
+ catch(...)
+ {
+ /* THE WORLD IS COMING TO AN END! */
+ }
+ }
+ else
+ {
+ /* Invalid address family, die horribly. */
+ ServerInstance->Log(DEBUG, "BUG: insp_aton failed returning -1, oh noes.");
+ }
+ }
+ }
+
+ void ClearOldConnections()
+ {
+ ConnMap::iterator iter,safei;
+ for (iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ if (!HostInConf(iter->second->GetConfHost()))
+ {
+ DELETE(iter->second);
+ safei = iter;
+ --iter;
+ connections.erase(safei);
+ }
+ }
+ }
+
+ void ClearAllConnections()
+ {
+ ConnMap::iterator i;
+ while ((i = connections.begin()) != connections.end())
+ {
+ connections.erase(i);
+ DELETE(i->second);
+ }
+ }
+
+ void AddConn(const SQLhost& hi)
+ {
+ if (HasHost(hi))
+ {
+ ServerInstance->Log(DEFAULT, "WARNING: A pgsql connection with id: %s already exists, possibly due to DNS delay. Aborting connection attempt.", hi.id.c_str());
+ return;
+ }
+
+ SQLConn* newconn;
+
+ /* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */
+ newconn = new SQLConn(ServerInstance, this, hi);
+
+ connections.insert(std::make_pair(hi.id, newconn));
+ }
+
+ void ReconnectConn(SQLConn* conn)
+ {
+ for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ if (conn == iter->second)
+ {
+ DELETE(iter->second);
+ connections.erase(iter);
+ break;
+ }
+ }
+ retimer = new ReconnectTimer(ServerInstance, this);
+ ServerInstance->Timers->AddTimer(retimer);
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLREQID, request->GetId()) == 0)
+ {
+ SQLrequest* req = (SQLrequest*)request;
+ ConnMap::iterator iter;
+ if((iter = connections.find(req->dbid)) != connections.end())
+ {
+ /* Execute query */
+ req->id = NewID();
+ req->error = iter->second->Query(*req);
+
+ return (req->error.Id() == NO_ERROR) ? sqlsuccess : NULL;
+ }
+ else
+ {
+ req->error.Id(BAD_DBID);
+ return NULL;
+ }
+ }
+ return NULL;
+ }
+
+ virtual void OnUnloadModule(Module* mod, const std::string& name)
+ {
+ /* When a module unloads we have to check all the pending queries for all our connections
+ * and set the Module* specifying where the query came from to NULL. If the query has already
+ * been dispatched then when it is processed it will be dropped if the pointer is NULL.
+ *
+ * If the queries we find are not already being executed then we can simply remove them immediately.
+ */
+ for(ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ iter->second->OnUnloadModule(mod);
+ }
+ }
+
+ unsigned long NewID()
+ {
+ if (currid+1 == 0)
+ currid++;
+
+ return ++currid;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION);
+ }
+};
+
+/* move this here to use AddConn, rather that than having the whole
+ * module above SQLConn, since this is buggin me right now :/
+ */
+void SQLresolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+{
+ host.ip = result;
+ ((ModulePgSQL*)mod)->AddConn(host);
+ ((ModulePgSQL*)mod)->ClearOldConnections();
+}
+
+void ReconnectTimer::Tick(time_t time)
+{
+ ((ModulePgSQL*)mod)->ReadConf();
+}
+
+void SQLConn::DelayReconnect()
+{
+ ((ModulePgSQL*)us)->ReconnectConn(this);
+}
+
+MODULE_INIT(ModulePgSQL);
+
diff --git a/src/modules/extra/m_sqlauth.cpp b/src/modules/extra/m_sqlauth.cpp
index 862929919..6b05ee521 100644
--- a/src/modules/extra/m_sqlauth.cpp
+++ b/src/modules/extra/m_sqlauth.cpp
@@ -1 +1,194 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "m_sqlv2.h" #include "m_sqlutils.h" /* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */ /* $ModDep: m_sqlv2.h m_sqlutils.h */ class ModuleSQLAuth : public Module { Module* SQLutils; Module* SQLprovider; std::string usertable; std::string userfield; std::string passfield; std::string encryption; std::string killreason; std::string allowpattern; std::string databaseid; bool verbose; public: ModuleSQLAuth(InspIRCd* Me) : Module::Module(Me) { ServerInstance->UseInterface("SQLutils"); ServerInstance->UseInterface("SQL"); SQLutils = ServerInstance->FindModule("m_sqlutils.so"); if (!SQLutils) throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so."); SQLprovider = ServerInstance->FindFeature("SQL"); if (!SQLprovider) throw ModuleException("Can't find an SQL provider module. Please load one before attempting to load m_sqlauth."); OnRehash(NULL,""); } virtual ~ModuleSQLAuth() { ServerInstance->DoneWithInterface("SQL"); ServerInstance->DoneWithInterface("SQLutils"); } void Implements(char* List) { List[I_OnUserDisconnect] = List[I_OnCheckReady] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); usertable = Conf.ReadValue("sqlauth", "usertable", 0); /* User table name */ databaseid = Conf.ReadValue("sqlauth", "dbid", 0); /* Database ID, given to the SQL service provider */ userfield = Conf.ReadValue("sqlauth", "userfield", 0); /* Field name where username can be found */ passfield = Conf.ReadValue("sqlauth", "passfield", 0); /* Field name where password can be found */ killreason = Conf.ReadValue("sqlauth", "killreason", 0); /* Reason to give when access is denied to a user (put your reg details here) */ allowpattern= Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */ encryption = Conf.ReadValue("sqlauth", "encryption", 0); /* Name of sql function used to encrypt password, e.g. "md5" or "passwd". * define, but leave blank if no encryption is to be used. */ verbose = Conf.ReadFlag("sqlauth", "verbose", 0); /* Set to true if failed connects should be reported to operators */ if (encryption.find("(") == std::string::npos) { encryption.append("("); } } virtual int OnUserRegister(userrec* user) { if ((!allowpattern.empty()) && (ServerInstance->MatchText(user->nick,allowpattern))) { user->Extend("sqlauthed"); return 0; } if (!CheckCredentials(user)) { userrec::QuitUser(ServerInstance,user,killreason); return 1; } return 0; } bool CheckCredentials(userrec* user) { SQLrequest req = SQLreq(this, SQLprovider, databaseid, "SELECT ? FROM ? WHERE ? = '?' AND ? = ?'?')", userfield, usertable, userfield, user->nick, passfield, encryption, user->password); if(req.Send()) { /* When we get the query response from the service provider we will be given an ID to play with, * just an ID number which is unique to this query. We need a way of associating that ID with a userrec * so we insert it into a map mapping the IDs to users. * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling * us to discard the query. */ AssociateUser(this, SQLutils, req.id, user).Send(); return true; } else { if (verbose) ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, req.error.Str()); return false; } } virtual char* OnRequest(Request* request) { if(strcmp(SQLRESID, request->GetId()) == 0) { SQLresult* res = static_cast<SQLresult*>(request); userrec* user = GetAssocUser(this, SQLutils, res->id).S().user; UnAssociate(this, SQLutils, res->id).S(); if(user) { if(res->error.Id() == NO_ERROR) { if(res->Rows()) { /* We got a row in the result, this is enough really */ user->Extend("sqlauthed"); } else if (verbose) { /* No rows in result, this means there was no record matching the user */ ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick, user->ident, user->host); user->Extend("sqlauth_failed"); } } else if (verbose) { ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, res->error.Str()); user->Extend("sqlauth_failed"); } } else { return NULL; } if (!user->GetExt("sqlauthed")) { userrec::QuitUser(ServerInstance,user,killreason); } return SQLSUCCESS; } return NULL; } virtual void OnUserDisconnect(userrec* user) { user->Shrink("sqlauthed"); user->Shrink("sqlauth_failed"); } virtual bool OnCheckReady(userrec* user) { return user->GetExt("sqlauthed"); } virtual Version GetVersion() { return Version(1,1,1,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSQLAuth); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_sqlv2.h"
+#include "m_sqlutils.h"
+
+/* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */
+/* $ModDep: m_sqlv2.h m_sqlutils.h */
+
+class ModuleSQLAuth : public Module
+{
+ Module* SQLutils;
+ Module* SQLprovider;
+
+ std::string usertable;
+ std::string userfield;
+ std::string passfield;
+ std::string encryption;
+ std::string killreason;
+ std::string allowpattern;
+ std::string databaseid;
+
+ bool verbose;
+
+public:
+ ModuleSQLAuth(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ ServerInstance->UseInterface("SQLutils");
+ ServerInstance->UseInterface("SQL");
+
+ SQLutils = ServerInstance->FindModule("m_sqlutils.so");
+ if (!SQLutils)
+ throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so.");
+
+ SQLprovider = ServerInstance->FindFeature("SQL");
+ if (!SQLprovider)
+ throw ModuleException("Can't find an SQL provider module. Please load one before attempting to load m_sqlauth.");
+
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleSQLAuth()
+ {
+ ServerInstance->DoneWithInterface("SQL");
+ ServerInstance->DoneWithInterface("SQLutils");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserDisconnect] = List[I_OnCheckReady] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+
+ usertable = Conf.ReadValue("sqlauth", "usertable", 0); /* User table name */
+ databaseid = Conf.ReadValue("sqlauth", "dbid", 0); /* Database ID, given to the SQL service provider */
+ userfield = Conf.ReadValue("sqlauth", "userfield", 0); /* Field name where username can be found */
+ passfield = Conf.ReadValue("sqlauth", "passfield", 0); /* Field name where password can be found */
+ killreason = Conf.ReadValue("sqlauth", "killreason", 0); /* Reason to give when access is denied to a user (put your reg details here) */
+ allowpattern= Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */
+ encryption = Conf.ReadValue("sqlauth", "encryption", 0); /* Name of sql function used to encrypt password, e.g. "md5" or "passwd".
+ * define, but leave blank if no encryption is to be used.
+ */
+ verbose = Conf.ReadFlag("sqlauth", "verbose", 0); /* Set to true if failed connects should be reported to operators */
+
+ if (encryption.find("(") == std::string::npos)
+ {
+ encryption.append("(");
+ }
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ if ((!allowpattern.empty()) && (ServerInstance->MatchText(user->nick,allowpattern)))
+ {
+ user->Extend("sqlauthed");
+ return 0;
+ }
+
+ if (!CheckCredentials(user))
+ {
+ userrec::QuitUser(ServerInstance,user,killreason);
+ return 1;
+ }
+ return 0;
+ }
+
+ bool CheckCredentials(userrec* user)
+ {
+ SQLrequest req = SQLreq(this, SQLprovider, databaseid, "SELECT ? FROM ? WHERE ? = '?' AND ? = ?'?')", userfield, usertable, userfield, user->nick, passfield, encryption, user->password);
+
+ if(req.Send())
+ {
+ /* When we get the query response from the service provider we will be given an ID to play with,
+ * just an ID number which is unique to this query. We need a way of associating that ID with a userrec
+ * so we insert it into a map mapping the IDs to users.
+ * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the
+ * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling
+ * us to discard the query.
+ */
+ AssociateUser(this, SQLutils, req.id, user).Send();
+
+ return true;
+ }
+ else
+ {
+ if (verbose)
+ ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, req.error.Str());
+ return false;
+ }
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLRESID, request->GetId()) == 0)
+ {
+ SQLresult* res = static_cast<SQLresult*>(request);
+
+ userrec* user = GetAssocUser(this, SQLutils, res->id).S().user;
+ UnAssociate(this, SQLutils, res->id).S();
+
+ if(user)
+ {
+ if(res->error.Id() == NO_ERROR)
+ {
+ if(res->Rows())
+ {
+ /* We got a row in the result, this is enough really */
+ user->Extend("sqlauthed");
+ }
+ else if (verbose)
+ {
+ /* No rows in result, this means there was no record matching the user */
+ ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick, user->ident, user->host);
+ user->Extend("sqlauth_failed");
+ }
+ }
+ else if (verbose)
+ {
+ ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, res->error.Str());
+ user->Extend("sqlauth_failed");
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+
+ if (!user->GetExt("sqlauthed"))
+ {
+ userrec::QuitUser(ServerInstance,user,killreason);
+ }
+ return SQLSUCCESS;
+ }
+ return NULL;
+ }
+
+ virtual void OnUserDisconnect(userrec* user)
+ {
+ user->Shrink("sqlauthed");
+ user->Shrink("sqlauth_failed");
+ }
+
+ virtual bool OnCheckReady(userrec* user)
+ {
+ return user->GetExt("sqlauthed");
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,1,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSQLAuth);
+
diff --git a/src/modules/extra/m_sqlite3.cpp b/src/modules/extra/m_sqlite3.cpp
index 6741d7745..66955de07 100644
--- a/src/modules/extra/m_sqlite3.cpp
+++ b/src/modules/extra/m_sqlite3.cpp
@@ -1 +1,660 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <sqlite3.h> #include "users.h" #include "channels.h" #include "modules.h" #include "m_sqlv2.h" /* $ModDesc: sqlite3 provider */ /* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */ /* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */ /* $ModDep: m_sqlv2.h */ class SQLConn; class SQLite3Result; class ResultNotifier; typedef std::map<std::string, SQLConn*> ConnMap; typedef std::deque<classbase*> paramlist; typedef std::deque<SQLite3Result*> ResultQueue; ResultNotifier* resultnotify = NULL; class ResultNotifier : public InspSocket { Module* mod; insp_sockaddr sock_us; socklen_t uslen; public: /* Create a socket on a random port. Let the tcp stack allocate us an available port */ #ifdef IPV6 ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "::1", 0, true, 3000), mod(m) #else ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "127.0.0.1", 0, true, 3000), mod(m) #endif { uslen = sizeof(sock_us); if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen)) { throw ModuleException("Could not create random listening port on localhost"); } } ResultNotifier(InspIRCd* SI, Module* m, int newfd, char* ip) : InspSocket(SI, newfd, ip), mod(m) { } /* Using getsockname and ntohs, we can determine which port number we were allocated */ int GetPort() { #ifdef IPV6 return ntohs(sock_us.sin6_port); #else return ntohs(sock_us.sin_port); #endif } virtual int OnIncomingConnection(int newsock, char* ip) { Dispatch(); return false; } void Dispatch(); }; class SQLite3Result : public SQLresult { private: int currentrow; int rows; int cols; std::vector<std::string> colnames; std::vector<SQLfieldList> fieldlists; SQLfieldList emptyfieldlist; SQLfieldList* fieldlist; SQLfieldMap* fieldmap; public: SQLite3Result(Module* self, Module* to, unsigned int id) : SQLresult(self, to, id), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL) { } ~SQLite3Result() { } void AddRow(int colsnum, char **data, char **colname) { colnames.clear(); cols = colsnum; for (int i = 0; i < colsnum; i++) { fieldlists.resize(fieldlists.size()+1); colnames.push_back(colname[i]); SQLfield sf(data[i] ? data[i] : "", data[i] ? false : true); fieldlists[rows].push_back(sf); } rows++; } void UpdateAffectedCount() { rows++; } virtual int Rows() { return rows; } virtual int Cols() { return cols; } virtual std::string ColName(int column) { if (column < (int)colnames.size()) { return colnames[column]; } else { throw SQLbadColName(); } return ""; } virtual int ColNum(const std::string &column) { for (unsigned int i = 0; i < colnames.size(); i++) { if (column == colnames[i]) return i; } throw SQLbadColName(); return 0; } virtual SQLfield GetValue(int row, int column) { if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols())) { return fieldlists[row][column]; } throw SQLbadColName(); /* XXX: We never actually get here because of the throw */ return SQLfield("",true); } virtual SQLfieldList& GetRow() { if (currentrow < rows) return fieldlists[currentrow]; else return emptyfieldlist; } virtual SQLfieldMap& GetRowMap() { /* In an effort to reduce overhead we don't actually allocate the map * until the first time it's needed...so... */ if(fieldmap) { fieldmap->clear(); } else { fieldmap = new SQLfieldMap; } if (currentrow < rows) { for (int i = 0; i < Cols(); i++) { fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); } currentrow++; } return *fieldmap; } virtual SQLfieldList* GetRowPtr() { fieldlist = new SQLfieldList(); if (currentrow < rows) { for (int i = 0; i < Rows(); i++) { fieldlist->push_back(fieldlists[currentrow][i]); } currentrow++; } return fieldlist; } virtual SQLfieldMap* GetRowMapPtr() { fieldmap = new SQLfieldMap(); if (currentrow < rows) { for (int i = 0; i < Cols(); i++) { fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i))); } currentrow++; } return fieldmap; } virtual void Free(SQLfieldMap* fm) { delete fm; } virtual void Free(SQLfieldList* fl) { delete fl; } }; class SQLConn : public classbase { private: ResultQueue results; InspIRCd* Instance; Module* mod; SQLhost host; sqlite3* conn; public: SQLConn(InspIRCd* SI, Module* m, const SQLhost& hi) : Instance(SI), mod(m), host(hi) { if (OpenDB() != SQLITE_OK) { Instance->Log(DEFAULT, "WARNING: Could not open DB with id: " + host.id); CloseDB(); } } ~SQLConn() { CloseDB(); } SQLerror Query(SQLrequest &req) { /* Pointer to the buffer we screw around with substitution in */ char* query; /* Pointer to the current end of query, where we append new stuff */ char* queryend; /* Total length of the unescaped parameters */ unsigned long paramlen; /* Total length of query, used for binary-safety in mysql_real_query */ unsigned long querylength = 0; paramlen = 0; for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++) { paramlen += i->size(); } /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. * sizeofquery + (totalparamlength*2) + 1 * * The +1 is for null-terminating the string for mysql_real_escape_string */ query = new char[req.query.q.length() + (paramlen*2) + 1]; queryend = query; for(unsigned long i = 0; i < req.query.q.length(); i++) { if(req.query.q[i] == '?') { if(req.query.p.size()) { char* escaped; escaped = sqlite3_mprintf("%q", req.query.p.front().c_str()); for (char* n = escaped; *n; n++) { *queryend = *n; queryend++; } sqlite3_free(escaped); req.query.p.pop_front(); } else break; } else { *queryend = req.query.q[i]; queryend++; } querylength++; } *queryend = 0; req.query.q = query; SQLite3Result* res = new SQLite3Result(mod, req.GetSource(), req.id); res->dbid = host.id; res->query = req.query.q; paramlist params; params.push_back(this); params.push_back(res); char *errmsg = 0; sqlite3_update_hook(conn, QueryUpdateHook, &params); if (sqlite3_exec(conn, req.query.q.data(), QueryResult, &params, &errmsg) != SQLITE_OK) { std::string error(errmsg); sqlite3_free(errmsg); delete[] query; delete res; return SQLerror(QSEND_FAIL, error); } delete[] query; results.push_back(res); SendNotify(); return SQLerror(); } static int QueryResult(void *params, int argc, char **argv, char **azColName) { paramlist* p = (paramlist*)params; ((SQLConn*)(*p)[0])->ResultReady(((SQLite3Result*)(*p)[1]), argc, argv, azColName); return 0; } static void QueryUpdateHook(void *params, int eventid, char const * azSQLite, char const * azColName, sqlite_int64 rowid) { paramlist* p = (paramlist*)params; ((SQLConn*)(*p)[0])->AffectedReady(((SQLite3Result*)(*p)[1])); } void ResultReady(SQLite3Result *res, int cols, char **data, char **colnames) { res->AddRow(cols, data, colnames); } void AffectedReady(SQLite3Result *res) { res->UpdateAffectedCount(); } int OpenDB() { return sqlite3_open(host.host.c_str(), &conn); } void CloseDB() { sqlite3_interrupt(conn); sqlite3_close(conn); } SQLhost GetConfHost() { return host; } void SendResults() { while (results.size()) { SQLite3Result* res = results[0]; if (res->GetDest()) { res->Send(); } else { /* If the client module is unloaded partway through a query then the provider will set * the pointer to NULL. We cannot just cancel the query as the result will still come * through at some point...and it could get messy if we play with invalid pointers... */ delete res; } results.pop_front(); } } void ClearResults() { while (results.size()) { SQLite3Result* res = results[0]; delete res; results.pop_front(); } } void SendNotify() { int QueueFD; if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1) { /* crap, we're out of sockets... */ return; } insp_sockaddr addr; #ifdef IPV6 insp_aton("::1", &addr.sin6_addr); addr.sin6_family = AF_FAMILY; addr.sin6_port = htons(resultnotify->GetPort()); #else insp_inaddr ia; insp_aton("127.0.0.1", &ia); addr.sin_family = AF_FAMILY; addr.sin_addr = ia; addr.sin_port = htons(resultnotify->GetPort()); #endif if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1) { /* wtf, we cant connect to it, but we just created it! */ return; } } }; class ModuleSQLite3 : public Module { private: ConnMap connections; unsigned long currid; public: ModuleSQLite3(InspIRCd* Me) : Module::Module(Me), currid(0) { ServerInstance->UseInterface("SQLutils"); if (!ServerInstance->PublishFeature("SQL", this)) { throw ModuleException("m_sqlite3: Unable to publish feature 'SQL'"); } resultnotify = new ResultNotifier(ServerInstance, this); ReadConf(); ServerInstance->PublishInterface("SQL", this); } virtual ~ModuleSQLite3() { ClearQueue(); ClearAllConnections(); resultnotify->SetFd(-1); resultnotify->state = I_ERROR; resultnotify->OnError(I_ERR_SOCKET); resultnotify->ClosePending = true; delete resultnotify; ServerInstance->UnpublishInterface("SQL", this); ServerInstance->UnpublishFeature("SQL"); ServerInstance->DoneWithInterface("SQLutils"); } void Implements(char* List) { List[I_OnRequest] = List[I_OnRehash] = 1; } void SendQueue() { for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) { iter->second->SendResults(); } } void ClearQueue() { for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) { iter->second->ClearResults(); } } bool HasHost(const SQLhost &host) { for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) { if (host == iter->second->GetConfHost()) return true; } return false; } bool HostInConf(const SQLhost &h) { ConfigReader conf(ServerInstance); for(int i = 0; i < conf.Enumerate("database"); i++) { SQLhost host; host.id = conf.ReadValue("database", "id", i); host.host = conf.ReadValue("database", "hostname", i); host.port = conf.ReadInteger("database", "port", i, true); host.name = conf.ReadValue("database", "name", i); host.user = conf.ReadValue("database", "username", i); host.pass = conf.ReadValue("database", "password", i); host.ssl = conf.ReadFlag("database", "ssl", "0", i); if (h == host) return true; } return false; } void ReadConf() { ClearOldConnections(); ConfigReader conf(ServerInstance); for(int i = 0; i < conf.Enumerate("database"); i++) { SQLhost host; host.id = conf.ReadValue("database", "id", i); host.host = conf.ReadValue("database", "hostname", i); host.port = conf.ReadInteger("database", "port", i, true); host.name = conf.ReadValue("database", "name", i); host.user = conf.ReadValue("database", "username", i); host.pass = conf.ReadValue("database", "password", i); host.ssl = conf.ReadFlag("database", "ssl", "0", i); if (HasHost(host)) continue; this->AddConn(host); } } void AddConn(const SQLhost& hi) { if (HasHost(hi)) { ServerInstance->Log(DEFAULT, "WARNING: A sqlite connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str()); return; } SQLConn* newconn; newconn = new SQLConn(ServerInstance, this, hi); connections.insert(std::make_pair(hi.id, newconn)); } void ClearOldConnections() { ConnMap::iterator iter,safei; for (iter = connections.begin(); iter != connections.end(); iter++) { if (!HostInConf(iter->second->GetConfHost())) { DELETE(iter->second); safei = iter; --iter; connections.erase(safei); } } } void ClearAllConnections() { ConnMap::iterator i; while ((i = connections.begin()) != connections.end()) { connections.erase(i); DELETE(i->second); } } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadConf(); } virtual char* OnRequest(Request* request) { if(strcmp(SQLREQID, request->GetId()) == 0) { SQLrequest* req = (SQLrequest*)request; ConnMap::iterator iter; if((iter = connections.find(req->dbid)) != connections.end()) { req->id = NewID(); req->error = iter->second->Query(*req); return SQLSUCCESS; } else { req->error.Id(BAD_DBID); return NULL; } } return NULL; } unsigned long NewID() { if (currid+1 == 0) currid++; return ++currid; } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); } }; void ResultNotifier::Dispatch() { ((ModuleSQLite3*)mod)->SendQueue(); } MODULE_INIT(ModuleSQLite3); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <sqlite3.h>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+#include "m_sqlv2.h"
+
+/* $ModDesc: sqlite3 provider */
+/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */
+/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */
+/* $ModDep: m_sqlv2.h */
+
+
+class SQLConn;
+class SQLite3Result;
+class ResultNotifier;
+
+typedef std::map<std::string, SQLConn*> ConnMap;
+typedef std::deque<classbase*> paramlist;
+typedef std::deque<SQLite3Result*> ResultQueue;
+
+ResultNotifier* resultnotify = NULL;
+
+
+class ResultNotifier : public InspSocket
+{
+ Module* mod;
+ insp_sockaddr sock_us;
+ socklen_t uslen;
+
+ public:
+ /* Create a socket on a random port. Let the tcp stack allocate us an available port */
+#ifdef IPV6
+ ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "::1", 0, true, 3000), mod(m)
+#else
+ ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "127.0.0.1", 0, true, 3000), mod(m)
+#endif
+ {
+ uslen = sizeof(sock_us);
+ if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen))
+ {
+ throw ModuleException("Could not create random listening port on localhost");
+ }
+ }
+
+ ResultNotifier(InspIRCd* SI, Module* m, int newfd, char* ip) : InspSocket(SI, newfd, ip), mod(m)
+ {
+ }
+
+ /* Using getsockname and ntohs, we can determine which port number we were allocated */
+ int GetPort()
+ {
+#ifdef IPV6
+ return ntohs(sock_us.sin6_port);
+#else
+ return ntohs(sock_us.sin_port);
+#endif
+ }
+
+ virtual int OnIncomingConnection(int newsock, char* ip)
+ {
+ Dispatch();
+ return false;
+ }
+
+ void Dispatch();
+};
+
+
+class SQLite3Result : public SQLresult
+{
+ private:
+ int currentrow;
+ int rows;
+ int cols;
+
+ std::vector<std::string> colnames;
+ std::vector<SQLfieldList> fieldlists;
+ SQLfieldList emptyfieldlist;
+
+ SQLfieldList* fieldlist;
+ SQLfieldMap* fieldmap;
+
+ public:
+ SQLite3Result(Module* self, Module* to, unsigned int id)
+ : SQLresult(self, to, id), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL)
+ {
+ }
+
+ ~SQLite3Result()
+ {
+ }
+
+ void AddRow(int colsnum, char **data, char **colname)
+ {
+ colnames.clear();
+ cols = colsnum;
+ for (int i = 0; i < colsnum; i++)
+ {
+ fieldlists.resize(fieldlists.size()+1);
+ colnames.push_back(colname[i]);
+ SQLfield sf(data[i] ? data[i] : "", data[i] ? false : true);
+ fieldlists[rows].push_back(sf);
+ }
+ rows++;
+ }
+
+ void UpdateAffectedCount()
+ {
+ rows++;
+ }
+
+ virtual int Rows()
+ {
+ return rows;
+ }
+
+ virtual int Cols()
+ {
+ return cols;
+ }
+
+ virtual std::string ColName(int column)
+ {
+ if (column < (int)colnames.size())
+ {
+ return colnames[column];
+ }
+ else
+ {
+ throw SQLbadColName();
+ }
+ return "";
+ }
+
+ virtual int ColNum(const std::string &column)
+ {
+ for (unsigned int i = 0; i < colnames.size(); i++)
+ {
+ if (column == colnames[i])
+ return i;
+ }
+ throw SQLbadColName();
+ return 0;
+ }
+
+ virtual SQLfield GetValue(int row, int column)
+ {
+ if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
+ {
+ return fieldlists[row][column];
+ }
+
+ throw SQLbadColName();
+
+ /* XXX: We never actually get here because of the throw */
+ return SQLfield("",true);
+ }
+
+ virtual SQLfieldList& GetRow()
+ {
+ if (currentrow < rows)
+ return fieldlists[currentrow];
+ else
+ return emptyfieldlist;
+ }
+
+ virtual SQLfieldMap& GetRowMap()
+ {
+ /* In an effort to reduce overhead we don't actually allocate the map
+ * until the first time it's needed...so...
+ */
+ if(fieldmap)
+ {
+ fieldmap->clear();
+ }
+ else
+ {
+ fieldmap = new SQLfieldMap;
+ }
+
+ if (currentrow < rows)
+ {
+ for (int i = 0; i < Cols(); i++)
+ {
+ fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
+ }
+ currentrow++;
+ }
+
+ return *fieldmap;
+ }
+
+ virtual SQLfieldList* GetRowPtr()
+ {
+ fieldlist = new SQLfieldList();
+
+ if (currentrow < rows)
+ {
+ for (int i = 0; i < Rows(); i++)
+ {
+ fieldlist->push_back(fieldlists[currentrow][i]);
+ }
+ currentrow++;
+ }
+ return fieldlist;
+ }
+
+ virtual SQLfieldMap* GetRowMapPtr()
+ {
+ fieldmap = new SQLfieldMap();
+
+ if (currentrow < rows)
+ {
+ for (int i = 0; i < Cols(); i++)
+ {
+ fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
+ }
+ currentrow++;
+ }
+
+ return fieldmap;
+ }
+
+ virtual void Free(SQLfieldMap* fm)
+ {
+ delete fm;
+ }
+
+ virtual void Free(SQLfieldList* fl)
+ {
+ delete fl;
+ }
+
+
+};
+
+class SQLConn : public classbase
+{
+ private:
+ ResultQueue results;
+ InspIRCd* Instance;
+ Module* mod;
+ SQLhost host;
+ sqlite3* conn;
+
+ public:
+ SQLConn(InspIRCd* SI, Module* m, const SQLhost& hi)
+ : Instance(SI), mod(m), host(hi)
+ {
+ if (OpenDB() != SQLITE_OK)
+ {
+ Instance->Log(DEFAULT, "WARNING: Could not open DB with id: " + host.id);
+ CloseDB();
+ }
+ }
+
+ ~SQLConn()
+ {
+ CloseDB();
+ }
+
+ SQLerror Query(SQLrequest &req)
+ {
+ /* Pointer to the buffer we screw around with substitution in */
+ char* query;
+
+ /* Pointer to the current end of query, where we append new stuff */
+ char* queryend;
+
+ /* Total length of the unescaped parameters */
+ unsigned long paramlen;
+
+ /* Total length of query, used for binary-safety in mysql_real_query */
+ unsigned long querylength = 0;
+
+ paramlen = 0;
+ for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
+ {
+ paramlen += i->size();
+ }
+
+ /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
+ * sizeofquery + (totalparamlength*2) + 1
+ *
+ * The +1 is for null-terminating the string for mysql_real_escape_string
+ */
+ query = new char[req.query.q.length() + (paramlen*2) + 1];
+ queryend = query;
+
+ for(unsigned long i = 0; i < req.query.q.length(); i++)
+ {
+ if(req.query.q[i] == '?')
+ {
+ if(req.query.p.size())
+ {
+ char* escaped;
+ escaped = sqlite3_mprintf("%q", req.query.p.front().c_str());
+ for (char* n = escaped; *n; n++)
+ {
+ *queryend = *n;
+ queryend++;
+ }
+ sqlite3_free(escaped);
+ req.query.p.pop_front();
+ }
+ else
+ break;
+ }
+ else
+ {
+ *queryend = req.query.q[i];
+ queryend++;
+ }
+ querylength++;
+ }
+ *queryend = 0;
+ req.query.q = query;
+
+ SQLite3Result* res = new SQLite3Result(mod, req.GetSource(), req.id);
+ res->dbid = host.id;
+ res->query = req.query.q;
+ paramlist params;
+ params.push_back(this);
+ params.push_back(res);
+
+ char *errmsg = 0;
+ sqlite3_update_hook(conn, QueryUpdateHook, &params);
+ if (sqlite3_exec(conn, req.query.q.data(), QueryResult, &params, &errmsg) != SQLITE_OK)
+ {
+ std::string error(errmsg);
+ sqlite3_free(errmsg);
+ delete[] query;
+ delete res;
+ return SQLerror(QSEND_FAIL, error);
+ }
+ delete[] query;
+
+ results.push_back(res);
+ SendNotify();
+ return SQLerror();
+ }
+
+ static int QueryResult(void *params, int argc, char **argv, char **azColName)
+ {
+ paramlist* p = (paramlist*)params;
+ ((SQLConn*)(*p)[0])->ResultReady(((SQLite3Result*)(*p)[1]), argc, argv, azColName);
+ return 0;
+ }
+
+ static void QueryUpdateHook(void *params, int eventid, char const * azSQLite, char const * azColName, sqlite_int64 rowid)
+ {
+ paramlist* p = (paramlist*)params;
+ ((SQLConn*)(*p)[0])->AffectedReady(((SQLite3Result*)(*p)[1]));
+ }
+
+ void ResultReady(SQLite3Result *res, int cols, char **data, char **colnames)
+ {
+ res->AddRow(cols, data, colnames);
+ }
+
+ void AffectedReady(SQLite3Result *res)
+ {
+ res->UpdateAffectedCount();
+ }
+
+ int OpenDB()
+ {
+ return sqlite3_open(host.host.c_str(), &conn);
+ }
+
+ void CloseDB()
+ {
+ sqlite3_interrupt(conn);
+ sqlite3_close(conn);
+ }
+
+ SQLhost GetConfHost()
+ {
+ return host;
+ }
+
+ void SendResults()
+ {
+ while (results.size())
+ {
+ SQLite3Result* res = results[0];
+ if (res->GetDest())
+ {
+ res->Send();
+ }
+ else
+ {
+ /* If the client module is unloaded partway through a query then the provider will set
+ * the pointer to NULL. We cannot just cancel the query as the result will still come
+ * through at some point...and it could get messy if we play with invalid pointers...
+ */
+ delete res;
+ }
+ results.pop_front();
+ }
+ }
+
+ void ClearResults()
+ {
+ while (results.size())
+ {
+ SQLite3Result* res = results[0];
+ delete res;
+ results.pop_front();
+ }
+ }
+
+ void SendNotify()
+ {
+ int QueueFD;
+ if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1)
+ {
+ /* crap, we're out of sockets... */
+ return;
+ }
+
+ insp_sockaddr addr;
+
+#ifdef IPV6
+ insp_aton("::1", &addr.sin6_addr);
+ addr.sin6_family = AF_FAMILY;
+ addr.sin6_port = htons(resultnotify->GetPort());
+#else
+ insp_inaddr ia;
+ insp_aton("127.0.0.1", &ia);
+ addr.sin_family = AF_FAMILY;
+ addr.sin_addr = ia;
+ addr.sin_port = htons(resultnotify->GetPort());
+#endif
+
+ if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1)
+ {
+ /* wtf, we cant connect to it, but we just created it! */
+ return;
+ }
+ }
+
+};
+
+
+class ModuleSQLite3 : public Module
+{
+ private:
+ ConnMap connections;
+ unsigned long currid;
+
+ public:
+ ModuleSQLite3(InspIRCd* Me)
+ : Module::Module(Me), currid(0)
+ {
+ ServerInstance->UseInterface("SQLutils");
+
+ if (!ServerInstance->PublishFeature("SQL", this))
+ {
+ throw ModuleException("m_sqlite3: Unable to publish feature 'SQL'");
+ }
+
+ resultnotify = new ResultNotifier(ServerInstance, this);
+
+ ReadConf();
+
+ ServerInstance->PublishInterface("SQL", this);
+ }
+
+ virtual ~ModuleSQLite3()
+ {
+ ClearQueue();
+ ClearAllConnections();
+ resultnotify->SetFd(-1);
+ resultnotify->state = I_ERROR;
+ resultnotify->OnError(I_ERR_SOCKET);
+ resultnotify->ClosePending = true;
+ delete resultnotify;
+ ServerInstance->UnpublishInterface("SQL", this);
+ ServerInstance->UnpublishFeature("SQL");
+ ServerInstance->DoneWithInterface("SQLutils");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRequest] = List[I_OnRehash] = 1;
+ }
+
+ void SendQueue()
+ {
+ for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ iter->second->SendResults();
+ }
+ }
+
+ void ClearQueue()
+ {
+ for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ iter->second->ClearResults();
+ }
+ }
+
+ bool HasHost(const SQLhost &host)
+ {
+ for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ if (host == iter->second->GetConfHost())
+ return true;
+ }
+ return false;
+ }
+
+ bool HostInConf(const SQLhost &h)
+ {
+ ConfigReader conf(ServerInstance);
+ for(int i = 0; i < conf.Enumerate("database"); i++)
+ {
+ SQLhost host;
+ host.id = conf.ReadValue("database", "id", i);
+ host.host = conf.ReadValue("database", "hostname", i);
+ host.port = conf.ReadInteger("database", "port", i, true);
+ host.name = conf.ReadValue("database", "name", i);
+ host.user = conf.ReadValue("database", "username", i);
+ host.pass = conf.ReadValue("database", "password", i);
+ host.ssl = conf.ReadFlag("database", "ssl", "0", i);
+ if (h == host)
+ return true;
+ }
+ return false;
+ }
+
+ void ReadConf()
+ {
+ ClearOldConnections();
+
+ ConfigReader conf(ServerInstance);
+ for(int i = 0; i < conf.Enumerate("database"); i++)
+ {
+ SQLhost host;
+
+ host.id = conf.ReadValue("database", "id", i);
+ host.host = conf.ReadValue("database", "hostname", i);
+ host.port = conf.ReadInteger("database", "port", i, true);
+ host.name = conf.ReadValue("database", "name", i);
+ host.user = conf.ReadValue("database", "username", i);
+ host.pass = conf.ReadValue("database", "password", i);
+ host.ssl = conf.ReadFlag("database", "ssl", "0", i);
+
+ if (HasHost(host))
+ continue;
+
+ this->AddConn(host);
+ }
+ }
+
+ void AddConn(const SQLhost& hi)
+ {
+ if (HasHost(hi))
+ {
+ ServerInstance->Log(DEFAULT, "WARNING: A sqlite connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
+ return;
+ }
+
+ SQLConn* newconn;
+
+ newconn = new SQLConn(ServerInstance, this, hi);
+
+ connections.insert(std::make_pair(hi.id, newconn));
+ }
+
+ void ClearOldConnections()
+ {
+ ConnMap::iterator iter,safei;
+ for (iter = connections.begin(); iter != connections.end(); iter++)
+ {
+ if (!HostInConf(iter->second->GetConfHost()))
+ {
+ DELETE(iter->second);
+ safei = iter;
+ --iter;
+ connections.erase(safei);
+ }
+ }
+ }
+
+ void ClearAllConnections()
+ {
+ ConnMap::iterator i;
+ while ((i = connections.begin()) != connections.end())
+ {
+ connections.erase(i);
+ DELETE(i->second);
+ }
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadConf();
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLREQID, request->GetId()) == 0)
+ {
+ SQLrequest* req = (SQLrequest*)request;
+ ConnMap::iterator iter;
+ if((iter = connections.find(req->dbid)) != connections.end())
+ {
+ req->id = NewID();
+ req->error = iter->second->Query(*req);
+ return SQLSUCCESS;
+ }
+ else
+ {
+ req->error.Id(BAD_DBID);
+ return NULL;
+ }
+ }
+ return NULL;
+ }
+
+ unsigned long NewID()
+ {
+ if (currid+1 == 0)
+ currid++;
+
+ return ++currid;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
+ }
+
+};
+
+void ResultNotifier::Dispatch()
+{
+ ((ModuleSQLite3*)mod)->SendQueue();
+}
+
+MODULE_INIT(ModuleSQLite3);
+
diff --git a/src/modules/extra/m_sqllog.cpp b/src/modules/extra/m_sqllog.cpp
index 04eb1fef1..391e4bbba 100644
--- a/src/modules/extra/m_sqllog.cpp
+++ b/src/modules/extra/m_sqllog.cpp
@@ -1 +1,310 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" #include "m_sqlv2.h" static Module* SQLModule; static Module* MyMod; static std::string dbid; enum LogTypes { LT_OPER = 1, LT_KILL, LT_SERVLINK, LT_XLINE, LT_CONNECT, LT_DISCONNECT, LT_FLOOD, LT_LOADMODULE }; enum QueryState { FIND_SOURCE, FIND_NICK, FIND_HOST, DONE}; class QueryInfo; std::map<unsigned long,QueryInfo*> active_queries; class QueryInfo { public: QueryState qs; unsigned long id; std::string nick; std::string source; std::string hostname; int sourceid; int nickid; int hostid; int category; time_t date; bool insert; QueryInfo(const std::string &n, const std::string &s, const std::string &h, unsigned long i, int cat) { qs = FIND_SOURCE; nick = n; source = s; hostname = h; id = i; category = cat; sourceid = nickid = hostid = -1; date = time(NULL); insert = false; } void Go(SQLresult* res) { SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "", ""); switch (qs) { case FIND_SOURCE: if (res->Rows() && sourceid == -1 && !insert) { sourceid = atoi(res->GetValue(0,0).d.c_str()); req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick); if(req.Send()) { insert = false; qs = FIND_NICK; active_queries[req.id] = this; } } else if (res->Rows() && sourceid == -1 && insert) { req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source); if(req.Send()) { insert = false; qs = FIND_SOURCE; active_queries[req.id] = this; } } else { req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')", source); if(req.Send()) { insert = true; qs = FIND_SOURCE; active_queries[req.id] = this; } } break; case FIND_NICK: if (res->Rows() && nickid == -1 && !insert) { nickid = atoi(res->GetValue(0,0).d.c_str()); req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname); if(req.Send()) { insert = false; qs = FIND_HOST; active_queries[req.id] = this; } } else if (res->Rows() && nickid == -1 && insert) { req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick); if(req.Send()) { insert = false; qs = FIND_NICK; active_queries[req.id] = this; } } else { req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')",nick); if(req.Send()) { insert = true; qs = FIND_NICK; active_queries[req.id] = this; } } break; case FIND_HOST: if (res->Rows() && hostid == -1 && !insert) { hostid = atoi(res->GetValue(0,0).d.c_str()); req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log (category_id,nick,host,source,dtime) VALUES("+ConvToStr(category)+","+ConvToStr(nickid)+","+ConvToStr(hostid)+","+ConvToStr(sourceid)+","+ConvToStr(date)+")"); if(req.Send()) { insert = true; qs = DONE; active_queries[req.id] = this; } } else if (res->Rows() && hostid == -1 && insert) { req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname); if(req.Send()) { insert = false; qs = FIND_HOST; active_queries[req.id] = this; } } else { req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_hosts (hostname) VALUES('?')", hostname); if(req.Send()) { insert = true; qs = FIND_HOST; active_queries[req.id] = this; } } break; case DONE: delete active_queries[req.id]; active_queries[req.id] = NULL; break; } } }; /* $ModDesc: Logs network-wide data to an SQL database */ class ModuleSQLLog : public Module { ConfigReader* Conf; public: ModuleSQLLog(InspIRCd* Me) : Module::Module(Me) { ServerInstance->UseInterface("SQLutils"); ServerInstance->UseInterface("SQL"); Module* SQLutils = ServerInstance->FindModule("m_sqlutils.so"); if (!SQLutils) throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so."); SQLModule = ServerInstance->FindFeature("SQL"); OnRehash(NULL,""); MyMod = this; active_queries.clear(); } virtual ~ModuleSQLLog() { ServerInstance->DoneWithInterface("SQL"); ServerInstance->DoneWithInterface("SQLutils"); } void Implements(char* List) { List[I_OnRehash] = List[I_OnOper] = List[I_OnGlobalOper] = List[I_OnKill] = 1; List[I_OnPreCommand] = List[I_OnUserConnect] = 1; List[I_OnUserQuit] = List[I_OnLoadModule] = List[I_OnRequest] = 1; } void ReadConfig() { ConfigReader Conf(ServerInstance); dbid = Conf.ReadValue("sqllog","dbid",0); // database id of a database configured in sql module } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadConfig(); } virtual char* OnRequest(Request* request) { if(strcmp(SQLRESID, request->GetId()) == 0) { SQLresult* res; std::map<unsigned long, QueryInfo*>::iterator n; res = static_cast<SQLresult*>(request); n = active_queries.find(res->id); if (n != active_queries.end()) { n->second->Go(res); std::map<unsigned long, QueryInfo*>::iterator n = active_queries.find(res->id); active_queries.erase(n); } return SQLSUCCESS; } return NULL; } void AddLogEntry(int category, const std::string &nick, const std::string &host, const std::string &source) { // is the sql module loaded? If not, we don't attempt to do anything. if (!SQLModule) return; SQLrequest req = SQLreq(this, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source); if(req.Send()) { QueryInfo* i = new QueryInfo(nick, source, host, req.id, category); i->qs = FIND_SOURCE; active_queries[req.id] = i; } } virtual void OnOper(userrec* user, const std::string &opertype) { AddLogEntry(LT_OPER,user->nick,user->host,user->server); } virtual void OnGlobalOper(userrec* user) { AddLogEntry(LT_OPER,user->nick,user->host,user->server); } virtual int OnKill(userrec* source, userrec* dest, const std::string &reason) { AddLogEntry(LT_KILL,dest->nick,dest->host,source->nick); return 0; } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { if ((command == "GLINE" || command == "KLINE" || command == "ELINE" || command == "ZLINE") && validated) { AddLogEntry(LT_XLINE,user->nick,command[0]+std::string(":")+std::string(parameters[0]),user->server); } return 0; } virtual void OnUserConnect(userrec* user) { AddLogEntry(LT_CONNECT,user->nick,user->host,user->server); } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { AddLogEntry(LT_DISCONNECT,user->nick,user->host,user->server); } virtual void OnLoadModule(Module* mod, const std::string &name) { AddLogEntry(LT_LOADMODULE,name,ServerInstance->Config->ServerName, ServerInstance->Config->ServerName); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSQLLog); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+#include "m_sqlv2.h"
+
+static Module* SQLModule;
+static Module* MyMod;
+static std::string dbid;
+
+enum LogTypes { LT_OPER = 1, LT_KILL, LT_SERVLINK, LT_XLINE, LT_CONNECT, LT_DISCONNECT, LT_FLOOD, LT_LOADMODULE };
+
+enum QueryState { FIND_SOURCE, FIND_NICK, FIND_HOST, DONE};
+
+class QueryInfo;
+
+std::map<unsigned long,QueryInfo*> active_queries;
+
+class QueryInfo
+{
+public:
+ QueryState qs;
+ unsigned long id;
+ std::string nick;
+ std::string source;
+ std::string hostname;
+ int sourceid;
+ int nickid;
+ int hostid;
+ int category;
+ time_t date;
+ bool insert;
+
+ QueryInfo(const std::string &n, const std::string &s, const std::string &h, unsigned long i, int cat)
+ {
+ qs = FIND_SOURCE;
+ nick = n;
+ source = s;
+ hostname = h;
+ id = i;
+ category = cat;
+ sourceid = nickid = hostid = -1;
+ date = time(NULL);
+ insert = false;
+ }
+
+ void Go(SQLresult* res)
+ {
+ SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "", "");
+ switch (qs)
+ {
+ case FIND_SOURCE:
+ if (res->Rows() && sourceid == -1 && !insert)
+ {
+ sourceid = atoi(res->GetValue(0,0).d.c_str());
+ req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick);
+ if(req.Send())
+ {
+ insert = false;
+ qs = FIND_NICK;
+ active_queries[req.id] = this;
+ }
+ }
+ else if (res->Rows() && sourceid == -1 && insert)
+ {
+ req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source);
+ if(req.Send())
+ {
+ insert = false;
+ qs = FIND_SOURCE;
+ active_queries[req.id] = this;
+ }
+ }
+ else
+ {
+ req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')", source);
+ if(req.Send())
+ {
+ insert = true;
+ qs = FIND_SOURCE;
+ active_queries[req.id] = this;
+ }
+ }
+ break;
+
+ case FIND_NICK:
+ if (res->Rows() && nickid == -1 && !insert)
+ {
+ nickid = atoi(res->GetValue(0,0).d.c_str());
+ req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname);
+ if(req.Send())
+ {
+ insert = false;
+ qs = FIND_HOST;
+ active_queries[req.id] = this;
+ }
+ }
+ else if (res->Rows() && nickid == -1 && insert)
+ {
+ req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick);
+ if(req.Send())
+ {
+ insert = false;
+ qs = FIND_NICK;
+ active_queries[req.id] = this;
+ }
+ }
+ else
+ {
+ req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')",nick);
+ if(req.Send())
+ {
+ insert = true;
+ qs = FIND_NICK;
+ active_queries[req.id] = this;
+ }
+ }
+ break;
+
+ case FIND_HOST:
+ if (res->Rows() && hostid == -1 && !insert)
+ {
+ hostid = atoi(res->GetValue(0,0).d.c_str());
+ req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log (category_id,nick,host,source,dtime) VALUES("+ConvToStr(category)+","+ConvToStr(nickid)+","+ConvToStr(hostid)+","+ConvToStr(sourceid)+","+ConvToStr(date)+")");
+ if(req.Send())
+ {
+ insert = true;
+ qs = DONE;
+ active_queries[req.id] = this;
+ }
+ }
+ else if (res->Rows() && hostid == -1 && insert)
+ {
+ req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname);
+ if(req.Send())
+ {
+ insert = false;
+ qs = FIND_HOST;
+ active_queries[req.id] = this;
+ }
+ }
+ else
+ {
+ req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_hosts (hostname) VALUES('?')", hostname);
+ if(req.Send())
+ {
+ insert = true;
+ qs = FIND_HOST;
+ active_queries[req.id] = this;
+ }
+ }
+ break;
+
+ case DONE:
+ delete active_queries[req.id];
+ active_queries[req.id] = NULL;
+ break;
+ }
+ }
+};
+
+/* $ModDesc: Logs network-wide data to an SQL database */
+
+class ModuleSQLLog : public Module
+{
+ ConfigReader* Conf;
+
+ public:
+ ModuleSQLLog(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ ServerInstance->UseInterface("SQLutils");
+ ServerInstance->UseInterface("SQL");
+
+ Module* SQLutils = ServerInstance->FindModule("m_sqlutils.so");
+ if (!SQLutils)
+ throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so.");
+
+ SQLModule = ServerInstance->FindFeature("SQL");
+
+ OnRehash(NULL,"");
+ MyMod = this;
+ active_queries.clear();
+ }
+
+ virtual ~ModuleSQLLog()
+ {
+ ServerInstance->DoneWithInterface("SQL");
+ ServerInstance->DoneWithInterface("SQLutils");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnOper] = List[I_OnGlobalOper] = List[I_OnKill] = 1;
+ List[I_OnPreCommand] = List[I_OnUserConnect] = 1;
+ List[I_OnUserQuit] = List[I_OnLoadModule] = List[I_OnRequest] = 1;
+ }
+
+ void ReadConfig()
+ {
+ ConfigReader Conf(ServerInstance);
+ dbid = Conf.ReadValue("sqllog","dbid",0); // database id of a database configured in sql module
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadConfig();
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLRESID, request->GetId()) == 0)
+ {
+ SQLresult* res;
+ std::map<unsigned long, QueryInfo*>::iterator n;
+
+ res = static_cast<SQLresult*>(request);
+ n = active_queries.find(res->id);
+
+ if (n != active_queries.end())
+ {
+ n->second->Go(res);
+ std::map<unsigned long, QueryInfo*>::iterator n = active_queries.find(res->id);
+ active_queries.erase(n);
+ }
+
+ return SQLSUCCESS;
+ }
+
+ return NULL;
+ }
+
+ void AddLogEntry(int category, const std::string &nick, const std::string &host, const std::string &source)
+ {
+ // is the sql module loaded? If not, we don't attempt to do anything.
+ if (!SQLModule)
+ return;
+
+ SQLrequest req = SQLreq(this, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source);
+ if(req.Send())
+ {
+ QueryInfo* i = new QueryInfo(nick, source, host, req.id, category);
+ i->qs = FIND_SOURCE;
+ active_queries[req.id] = i;
+ }
+ }
+
+ virtual void OnOper(userrec* user, const std::string &opertype)
+ {
+ AddLogEntry(LT_OPER,user->nick,user->host,user->server);
+ }
+
+ virtual void OnGlobalOper(userrec* user)
+ {
+ AddLogEntry(LT_OPER,user->nick,user->host,user->server);
+ }
+
+ virtual int OnKill(userrec* source, userrec* dest, const std::string &reason)
+ {
+ AddLogEntry(LT_KILL,dest->nick,dest->host,source->nick);
+ return 0;
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ if ((command == "GLINE" || command == "KLINE" || command == "ELINE" || command == "ZLINE") && validated)
+ {
+ AddLogEntry(LT_XLINE,user->nick,command[0]+std::string(":")+std::string(parameters[0]),user->server);
+ }
+ return 0;
+ }
+
+ virtual void OnUserConnect(userrec* user)
+ {
+ AddLogEntry(LT_CONNECT,user->nick,user->host,user->server);
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ AddLogEntry(LT_DISCONNECT,user->nick,user->host,user->server);
+ }
+
+ virtual void OnLoadModule(Module* mod, const std::string &name)
+ {
+ AddLogEntry(LT_LOADMODULE,name,ServerInstance->Config->ServerName, ServerInstance->Config->ServerName);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSQLLog);
+
diff --git a/src/modules/extra/m_sqloper.cpp b/src/modules/extra/m_sqloper.cpp
index 4b09ac26e..520869e21 100644
--- a/src/modules/extra/m_sqloper.cpp
+++ b/src/modules/extra/m_sqloper.cpp
@@ -1 +1,283 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" #include "m_sqlv2.h" #include "m_sqlutils.h" #include "m_hash.h" #include "commands/cmd_oper.h" /* $ModDesc: Allows storage of oper credentials in an SQL table */ /* $ModDep: m_sqlv2.h m_sqlutils.h */ class ModuleSQLOper : public Module { Module* SQLutils; Module* HashModule; std::string databaseid; public: ModuleSQLOper(InspIRCd* Me) : Module::Module(Me) { ServerInstance->UseInterface("SQLutils"); ServerInstance->UseInterface("SQL"); ServerInstance->UseInterface("HashRequest"); /* Attempt to locate the md5 service provider, bail if we can't find it */ HashModule = ServerInstance->FindModule("m_md5.so"); if (!HashModule) throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_sqloper.so."); SQLutils = ServerInstance->FindModule("m_sqlutils.so"); if (!SQLutils) throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqloper.so."); OnRehash(NULL,""); } virtual ~ModuleSQLOper() { ServerInstance->DoneWithInterface("SQL"); ServerInstance->DoneWithInterface("SQLutils"); ServerInstance->DoneWithInterface("HashRequest"); } void Implements(char* List) { List[I_OnRequest] = List[I_OnRehash] = List[I_OnPreCommand] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); databaseid = Conf.ReadValue("sqloper", "dbid", 0); /* Database ID of a database configured for the service provider module */ } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { if ((validated) && (command == "OPER")) { if (LookupOper(user, parameters[0], parameters[1])) { /* Returning true here just means the query is in progress, or on it's way to being * in progress. Nothing about the /oper actually being successful.. * If the oper lookup fails later, we pass the command to the original handler * for /oper by calling its Handle method directly. */ return 1; } } return 0; } bool LookupOper(userrec* user, const std::string &username, const std::string &password) { Module* target; target = ServerInstance->FindFeature("SQL"); if (target) { /* Reset hash module first back to MD5 standard state */ HashResetRequest(this, HashModule).Send(); /* Make an MD5 hash of the password for using in the query */ std::string md5_pass_hash = HashSumRequest(this, HashModule, password.c_str()).Send(); /* We generate our own MD5 sum here because some database providers (e.g. SQLite) dont have a builtin md5 function, * also hashing it in the module and only passing a remote query containing a hash is more secure. */ SQLrequest req = SQLreq(this, target, databaseid, "SELECT username, password, hostname, type FROM ircd_opers WHERE username = '?' AND password='?'", username, md5_pass_hash); if (req.Send()) { /* When we get the query response from the service provider we will be given an ID to play with, * just an ID number which is unique to this query. We need a way of associating that ID with a userrec * so we insert it into a map mapping the IDs to users. * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling * us to discard the query. */ AssociateUser(this, SQLutils, req.id, user).Send(); user->Extend("oper_user", strdup(username.c_str())); user->Extend("oper_pass", strdup(password.c_str())); return true; } else { return false; } } else { ServerInstance->Log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be able to oper up unless their o:line is statically configured"); return false; } } virtual char* OnRequest(Request* request) { if (strcmp(SQLRESID, request->GetId()) == 0) { SQLresult* res = static_cast<SQLresult*>(request); userrec* user = GetAssocUser(this, SQLutils, res->id).S().user; UnAssociate(this, SQLutils, res->id).S(); char* tried_user = NULL; char* tried_pass = NULL; user->GetExt("oper_user", tried_user); user->GetExt("oper_pass", tried_pass); if (user) { if (res->error.Id() == NO_ERROR) { if (res->Rows()) { /* We got a row in the result, this means there was a record for the oper.. * now we just need to check if their host matches, and if it does then * oper them up. * * We now (previous versions of the module didn't) support multiple SQL * rows per-oper in the same way the config file does, all rows will be tried * until one is found which matches. This is useful to define several different * hosts for a single oper. * * The for() loop works as SQLresult::GetRowMap() returns an empty map when there * are no more rows to return. */ for (SQLfieldMap& row = res->GetRowMap(); row.size(); row = res->GetRowMap()) { if (OperUser(user, row["username"].d, row["password"].d, row["hostname"].d, row["type"].d)) { /* If/when one of the rows matches, stop checking and return */ return SQLSUCCESS; } if (tried_user && tried_pass) { LoginFail(user, tried_user, tried_pass); free(tried_user); free(tried_pass); user->Shrink("oper_user"); user->Shrink("oper_pass"); } } } else { /* No rows in result, this means there was no oper line for the user, * we should have already checked the o:lines so now we need an * "insufficient awesomeness" (invalid credentials) error */ if (tried_user && tried_pass) { LoginFail(user, tried_user, tried_pass); free(tried_user); free(tried_pass); user->Shrink("oper_user"); user->Shrink("oper_pass"); } } } else { /* This one shouldn't happen, the query failed for some reason. * We have to fail the /oper request and give them the same error * as above. */ if (tried_user && tried_pass) { LoginFail(user, tried_user, tried_pass); free(tried_user); free(tried_pass); user->Shrink("oper_user"); user->Shrink("oper_pass"); } } } return SQLSUCCESS; } return NULL; } void LoginFail(userrec* user, const std::string &username, const std::string &pass) { command_t* oper_command = ServerInstance->Parser->GetHandler("OPER"); if (oper_command) { const char* params[] = { username.c_str(), pass.c_str() }; oper_command->Handle(params, 2, user); } else { ServerInstance->Log(DEBUG, "BUG: WHAT?! Why do we have no OPER command?!"); } } bool OperUser(userrec* user, const std::string &username, const std::string &password, const std::string &pattern, const std::string &type) { ConfigReader Conf(ServerInstance); for (int j = 0; j < Conf.Enumerate("type"); j++) { std::string tname = Conf.ReadValue("type","name",j); std::string hostname(user->ident); hostname.append("@").append(user->host); if ((tname == type) && OneOfMatches(hostname.c_str(), user->GetIPString(), pattern.c_str())) { /* Opertype and host match, looks like this is it. */ std::string operhost = Conf.ReadValue("type", "host", j); if (operhost.size()) user->ChangeDisplayedHost(operhost.c_str()); ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s", user->nick, user->ident, user->host, type.c_str()); user->WriteServ("381 %s :You are now an IRC operator of type %s", user->nick, type.c_str()); if (!user->modes[UM_OPERATOR]) user->Oper(type); return true; } } return false; } virtual Version GetVersion() { return Version(1,1,1,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSQLOper); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+
+#include "m_sqlv2.h"
+#include "m_sqlutils.h"
+#include "m_hash.h"
+#include "commands/cmd_oper.h"
+
+/* $ModDesc: Allows storage of oper credentials in an SQL table */
+/* $ModDep: m_sqlv2.h m_sqlutils.h */
+
+class ModuleSQLOper : public Module
+{
+ Module* SQLutils;
+ Module* HashModule;
+ std::string databaseid;
+
+public:
+ ModuleSQLOper(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ ServerInstance->UseInterface("SQLutils");
+ ServerInstance->UseInterface("SQL");
+ ServerInstance->UseInterface("HashRequest");
+
+ /* Attempt to locate the md5 service provider, bail if we can't find it */
+ HashModule = ServerInstance->FindModule("m_md5.so");
+ if (!HashModule)
+ throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_sqloper.so.");
+
+ SQLutils = ServerInstance->FindModule("m_sqlutils.so");
+ if (!SQLutils)
+ throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqloper.so.");
+
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleSQLOper()
+ {
+ ServerInstance->DoneWithInterface("SQL");
+ ServerInstance->DoneWithInterface("SQLutils");
+ ServerInstance->DoneWithInterface("HashRequest");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRequest] = List[I_OnRehash] = List[I_OnPreCommand] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+
+ databaseid = Conf.ReadValue("sqloper", "dbid", 0); /* Database ID of a database configured for the service provider module */
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ if ((validated) && (command == "OPER"))
+ {
+ if (LookupOper(user, parameters[0], parameters[1]))
+ {
+ /* Returning true here just means the query is in progress, or on it's way to being
+ * in progress. Nothing about the /oper actually being successful..
+ * If the oper lookup fails later, we pass the command to the original handler
+ * for /oper by calling its Handle method directly.
+ */
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ bool LookupOper(userrec* user, const std::string &username, const std::string &password)
+ {
+ Module* target;
+
+ target = ServerInstance->FindFeature("SQL");
+
+ if (target)
+ {
+ /* Reset hash module first back to MD5 standard state */
+ HashResetRequest(this, HashModule).Send();
+ /* Make an MD5 hash of the password for using in the query */
+ std::string md5_pass_hash = HashSumRequest(this, HashModule, password.c_str()).Send();
+
+ /* We generate our own MD5 sum here because some database providers (e.g. SQLite) dont have a builtin md5 function,
+ * also hashing it in the module and only passing a remote query containing a hash is more secure.
+ */
+
+ SQLrequest req = SQLreq(this, target, databaseid, "SELECT username, password, hostname, type FROM ircd_opers WHERE username = '?' AND password='?'", username, md5_pass_hash);
+
+ if (req.Send())
+ {
+ /* When we get the query response from the service provider we will be given an ID to play with,
+ * just an ID number which is unique to this query. We need a way of associating that ID with a userrec
+ * so we insert it into a map mapping the IDs to users.
+ * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the
+ * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling
+ * us to discard the query.
+ */
+ AssociateUser(this, SQLutils, req.id, user).Send();
+
+ user->Extend("oper_user", strdup(username.c_str()));
+ user->Extend("oper_pass", strdup(password.c_str()));
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ ServerInstance->Log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be able to oper up unless their o:line is statically configured");
+ return false;
+ }
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if (strcmp(SQLRESID, request->GetId()) == 0)
+ {
+ SQLresult* res = static_cast<SQLresult*>(request);
+
+ userrec* user = GetAssocUser(this, SQLutils, res->id).S().user;
+ UnAssociate(this, SQLutils, res->id).S();
+
+ char* tried_user = NULL;
+ char* tried_pass = NULL;
+
+ user->GetExt("oper_user", tried_user);
+ user->GetExt("oper_pass", tried_pass);
+
+ if (user)
+ {
+ if (res->error.Id() == NO_ERROR)
+ {
+ if (res->Rows())
+ {
+ /* We got a row in the result, this means there was a record for the oper..
+ * now we just need to check if their host matches, and if it does then
+ * oper them up.
+ *
+ * We now (previous versions of the module didn't) support multiple SQL
+ * rows per-oper in the same way the config file does, all rows will be tried
+ * until one is found which matches. This is useful to define several different
+ * hosts for a single oper.
+ *
+ * The for() loop works as SQLresult::GetRowMap() returns an empty map when there
+ * are no more rows to return.
+ */
+
+ for (SQLfieldMap& row = res->GetRowMap(); row.size(); row = res->GetRowMap())
+ {
+ if (OperUser(user, row["username"].d, row["password"].d, row["hostname"].d, row["type"].d))
+ {
+ /* If/when one of the rows matches, stop checking and return */
+ return SQLSUCCESS;
+ }
+ if (tried_user && tried_pass)
+ {
+ LoginFail(user, tried_user, tried_pass);
+ free(tried_user);
+ free(tried_pass);
+ user->Shrink("oper_user");
+ user->Shrink("oper_pass");
+ }
+ }
+ }
+ else
+ {
+ /* No rows in result, this means there was no oper line for the user,
+ * we should have already checked the o:lines so now we need an
+ * "insufficient awesomeness" (invalid credentials) error
+ */
+ if (tried_user && tried_pass)
+ {
+ LoginFail(user, tried_user, tried_pass);
+ free(tried_user);
+ free(tried_pass);
+ user->Shrink("oper_user");
+ user->Shrink("oper_pass");
+ }
+ }
+ }
+ else
+ {
+ /* This one shouldn't happen, the query failed for some reason.
+ * We have to fail the /oper request and give them the same error
+ * as above.
+ */
+ if (tried_user && tried_pass)
+ {
+ LoginFail(user, tried_user, tried_pass);
+ free(tried_user);
+ free(tried_pass);
+ user->Shrink("oper_user");
+ user->Shrink("oper_pass");
+ }
+
+ }
+ }
+
+ return SQLSUCCESS;
+ }
+
+ return NULL;
+ }
+
+ void LoginFail(userrec* user, const std::string &username, const std::string &pass)
+ {
+ command_t* oper_command = ServerInstance->Parser->GetHandler("OPER");
+
+ if (oper_command)
+ {
+ const char* params[] = { username.c_str(), pass.c_str() };
+ oper_command->Handle(params, 2, user);
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG, "BUG: WHAT?! Why do we have no OPER command?!");
+ }
+ }
+
+ bool OperUser(userrec* user, const std::string &username, const std::string &password, const std::string &pattern, const std::string &type)
+ {
+ ConfigReader Conf(ServerInstance);
+
+ for (int j = 0; j < Conf.Enumerate("type"); j++)
+ {
+ std::string tname = Conf.ReadValue("type","name",j);
+ std::string hostname(user->ident);
+
+ hostname.append("@").append(user->host);
+
+ if ((tname == type) && OneOfMatches(hostname.c_str(), user->GetIPString(), pattern.c_str()))
+ {
+ /* Opertype and host match, looks like this is it. */
+ std::string operhost = Conf.ReadValue("type", "host", j);
+
+ if (operhost.size())
+ user->ChangeDisplayedHost(operhost.c_str());
+
+ ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s", user->nick, user->ident, user->host, type.c_str());
+ user->WriteServ("381 %s :You are now an IRC operator of type %s", user->nick, type.c_str());
+
+ if (!user->modes[UM_OPERATOR])
+ user->Oper(type);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,1,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSQLOper);
+
diff --git a/src/modules/extra/m_sqlutils.cpp b/src/modules/extra/m_sqlutils.cpp
index 6cd09252b..b470f99af 100644
--- a/src/modules/extra/m_sqlutils.cpp
+++ b/src/modules/extra/m_sqlutils.cpp
@@ -1 +1,238 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <sstream> #include <list> #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" #include "m_sqlutils.h" /* $ModDesc: Provides some utilities to SQL client modules, such as mapping queries to users and channels */ /* $ModDep: m_sqlutils.h */ typedef std::map<unsigned long, userrec*> IdUserMap; typedef std::map<unsigned long, chanrec*> IdChanMap; typedef std::list<unsigned long> AssocIdList; class ModuleSQLutils : public Module { private: IdUserMap iduser; IdChanMap idchan; public: ModuleSQLutils(InspIRCd* Me) : Module::Module(Me) { ServerInstance->PublishInterface("SQLutils", this); } virtual ~ModuleSQLutils() { ServerInstance->UnpublishInterface("SQLutils", this); } void Implements(char* List) { List[I_OnChannelDelete] = List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnUserDisconnect] = 1; } virtual char* OnRequest(Request* request) { if(strcmp(SQLUTILAU, request->GetId()) == 0) { AssociateUser* req = (AssociateUser*)request; iduser.insert(std::make_pair(req->id, req->user)); AttachList(req->user, req->id); } else if(strcmp(SQLUTILAC, request->GetId()) == 0) { AssociateChan* req = (AssociateChan*)request; idchan.insert(std::make_pair(req->id, req->chan)); AttachList(req->chan, req->id); } else if(strcmp(SQLUTILUA, request->GetId()) == 0) { UnAssociate* req = (UnAssociate*)request; /* Unassociate a given query ID with all users and channels * it is associated with. */ DoUnAssociate(iduser, req->id); DoUnAssociate(idchan, req->id); } else if(strcmp(SQLUTILGU, request->GetId()) == 0) { GetAssocUser* req = (GetAssocUser*)request; IdUserMap::iterator iter = iduser.find(req->id); if(iter != iduser.end()) { req->user = iter->second; } } else if(strcmp(SQLUTILGC, request->GetId()) == 0) { GetAssocChan* req = (GetAssocChan*)request; IdChanMap::iterator iter = idchan.find(req->id); if(iter != idchan.end()) { req->chan = iter->second; } } return SQLUTILSUCCESS; } virtual void OnUserDisconnect(userrec* user) { /* A user is disconnecting, first we need to check if they have a list of queries associated with them. * Then, if they do, we need to erase each of them from our IdUserMap (iduser) so when the module that * associated them asks to look them up then it gets a NULL result and knows to discard the query. */ AssocIdList* il; if(user->GetExt("sqlutils_queryids", il)) { for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++) { IdUserMap::iterator iter; iter = iduser.find(*listiter); if(iter != iduser.end()) { if(iter->second != user) { ServerInstance->Log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map (erasing anyway)", user->nick); } iduser.erase(iter); } else { ServerInstance->Log(DEBUG, "BUG: user %s was extended with sqlutils_queryids but there was nothing matching in the map", user->nick); } } user->Shrink("sqlutils_queryids"); delete il; } } void AttachList(Extensible* obj, unsigned long id) { AssocIdList* il; if(!obj->GetExt("sqlutils_queryids", il)) { /* Doesn't already exist, create a new list and attach it. */ il = new AssocIdList; obj->Extend("sqlutils_queryids", il); } /* Now either way we have a valid list in il, attached. */ il->push_back(id); } void RemoveFromList(Extensible* obj, unsigned long id) { AssocIdList* il; if(obj->GetExt("sqlutils_queryids", il)) { /* Only do anything if the list exists... (which it ought to) */ il->remove(id); if(il->empty()) { /* If we just emptied it.. */ delete il; obj->Shrink("sqlutils_queryids"); } } } template <class T> void DoUnAssociate(T &map, unsigned long id) { /* For each occurence of 'id' (well, only one..it's not a multimap) in 'map' * remove it from the map, take an Extensible* value from the map and remove * 'id' from the list of query IDs attached to it. */ typename T::iterator iter = map.find(id); if(iter != map.end()) { /* Found a value indexed by 'id', call RemoveFromList() * on it with 'id' to remove 'id' from the list attached * to the value. */ RemoveFromList(iter->second, id); } } virtual void OnChannelDelete(chanrec* chan) { /* A channel is being destroyed, first we need to check if it has a list of queries associated with it. * Then, if it does, we need to erase each of them from our IdChanMap (idchan) so when the module that * associated them asks to look them up then it gets a NULL result and knows to discard the query. */ AssocIdList* il; if(chan->GetExt("sqlutils_queryids", il)) { for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++) { IdChanMap::iterator iter; iter = idchan.find(*listiter); if(iter != idchan.end()) { if(iter->second != chan) { ServerInstance->Log(DEBUG, "BUG: ID associated with channel %s doesn't have the same chanrec* associated with it in the map (erasing anyway)", chan->name); } idchan.erase(iter); } else { ServerInstance->Log(DEBUG, "BUG: channel %s was extended with sqlutils_queryids but there was nothing matching in the map", chan->name); } } chan->Shrink("sqlutils_queryids"); delete il; } } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION); } }; MODULE_INIT(ModuleSQLutils); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <sstream>
+#include <list>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+#include "m_sqlutils.h"
+
+/* $ModDesc: Provides some utilities to SQL client modules, such as mapping queries to users and channels */
+/* $ModDep: m_sqlutils.h */
+
+typedef std::map<unsigned long, userrec*> IdUserMap;
+typedef std::map<unsigned long, chanrec*> IdChanMap;
+typedef std::list<unsigned long> AssocIdList;
+
+class ModuleSQLutils : public Module
+{
+private:
+ IdUserMap iduser;
+ IdChanMap idchan;
+
+public:
+ ModuleSQLutils(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ ServerInstance->PublishInterface("SQLutils", this);
+ }
+
+ virtual ~ModuleSQLutils()
+ {
+ ServerInstance->UnpublishInterface("SQLutils", this);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnChannelDelete] = List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnUserDisconnect] = 1;
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLUTILAU, request->GetId()) == 0)
+ {
+ AssociateUser* req = (AssociateUser*)request;
+
+ iduser.insert(std::make_pair(req->id, req->user));
+
+ AttachList(req->user, req->id);
+ }
+ else if(strcmp(SQLUTILAC, request->GetId()) == 0)
+ {
+ AssociateChan* req = (AssociateChan*)request;
+
+ idchan.insert(std::make_pair(req->id, req->chan));
+
+ AttachList(req->chan, req->id);
+ }
+ else if(strcmp(SQLUTILUA, request->GetId()) == 0)
+ {
+ UnAssociate* req = (UnAssociate*)request;
+
+ /* Unassociate a given query ID with all users and channels
+ * it is associated with.
+ */
+
+ DoUnAssociate(iduser, req->id);
+ DoUnAssociate(idchan, req->id);
+ }
+ else if(strcmp(SQLUTILGU, request->GetId()) == 0)
+ {
+ GetAssocUser* req = (GetAssocUser*)request;
+
+ IdUserMap::iterator iter = iduser.find(req->id);
+
+ if(iter != iduser.end())
+ {
+ req->user = iter->second;
+ }
+ }
+ else if(strcmp(SQLUTILGC, request->GetId()) == 0)
+ {
+ GetAssocChan* req = (GetAssocChan*)request;
+
+ IdChanMap::iterator iter = idchan.find(req->id);
+
+ if(iter != idchan.end())
+ {
+ req->chan = iter->second;
+ }
+ }
+
+ return SQLUTILSUCCESS;
+ }
+
+ virtual void OnUserDisconnect(userrec* user)
+ {
+ /* A user is disconnecting, first we need to check if they have a list of queries associated with them.
+ * Then, if they do, we need to erase each of them from our IdUserMap (iduser) so when the module that
+ * associated them asks to look them up then it gets a NULL result and knows to discard the query.
+ */
+ AssocIdList* il;
+
+ if(user->GetExt("sqlutils_queryids", il))
+ {
+ for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++)
+ {
+ IdUserMap::iterator iter;
+
+ iter = iduser.find(*listiter);
+
+ if(iter != iduser.end())
+ {
+ if(iter->second != user)
+ {
+ ServerInstance->Log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map (erasing anyway)", user->nick);
+ }
+
+ iduser.erase(iter);
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG, "BUG: user %s was extended with sqlutils_queryids but there was nothing matching in the map", user->nick);
+ }
+ }
+
+ user->Shrink("sqlutils_queryids");
+ delete il;
+ }
+ }
+
+ void AttachList(Extensible* obj, unsigned long id)
+ {
+ AssocIdList* il;
+
+ if(!obj->GetExt("sqlutils_queryids", il))
+ {
+ /* Doesn't already exist, create a new list and attach it. */
+ il = new AssocIdList;
+ obj->Extend("sqlutils_queryids", il);
+ }
+
+ /* Now either way we have a valid list in il, attached. */
+ il->push_back(id);
+ }
+
+ void RemoveFromList(Extensible* obj, unsigned long id)
+ {
+ AssocIdList* il;
+
+ if(obj->GetExt("sqlutils_queryids", il))
+ {
+ /* Only do anything if the list exists... (which it ought to) */
+ il->remove(id);
+
+ if(il->empty())
+ {
+ /* If we just emptied it.. */
+ delete il;
+ obj->Shrink("sqlutils_queryids");
+ }
+ }
+ }
+
+ template <class T> void DoUnAssociate(T &map, unsigned long id)
+ {
+ /* For each occurence of 'id' (well, only one..it's not a multimap) in 'map'
+ * remove it from the map, take an Extensible* value from the map and remove
+ * 'id' from the list of query IDs attached to it.
+ */
+ typename T::iterator iter = map.find(id);
+
+ if(iter != map.end())
+ {
+ /* Found a value indexed by 'id', call RemoveFromList()
+ * on it with 'id' to remove 'id' from the list attached
+ * to the value.
+ */
+ RemoveFromList(iter->second, id);
+ }
+ }
+
+ virtual void OnChannelDelete(chanrec* chan)
+ {
+ /* A channel is being destroyed, first we need to check if it has a list of queries associated with it.
+ * Then, if it does, we need to erase each of them from our IdChanMap (idchan) so when the module that
+ * associated them asks to look them up then it gets a NULL result and knows to discard the query.
+ */
+ AssocIdList* il;
+
+ if(chan->GetExt("sqlutils_queryids", il))
+ {
+ for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++)
+ {
+ IdChanMap::iterator iter;
+
+ iter = idchan.find(*listiter);
+
+ if(iter != idchan.end())
+ {
+ if(iter->second != chan)
+ {
+ ServerInstance->Log(DEBUG, "BUG: ID associated with channel %s doesn't have the same chanrec* associated with it in the map (erasing anyway)", chan->name);
+ }
+ idchan.erase(iter);
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG, "BUG: channel %s was extended with sqlutils_queryids but there was nothing matching in the map", chan->name);
+ }
+ }
+
+ chan->Shrink("sqlutils_queryids");
+ delete il;
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSQLutils);
+
diff --git a/src/modules/extra/m_sqlutils.h b/src/modules/extra/m_sqlutils.h
index cdde51f67..92fbdf5c7 100644
--- a/src/modules/extra/m_sqlutils.h
+++ b/src/modules/extra/m_sqlutils.h
@@ -1 +1,143 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_SQLUTILS #define INSPIRCD_SQLUTILS #include "modules.h" #define SQLUTILAU "SQLutil AssociateUser" #define SQLUTILAC "SQLutil AssociateChan" #define SQLUTILUA "SQLutil UnAssociate" #define SQLUTILGU "SQLutil GetAssocUser" #define SQLUTILGC "SQLutil GetAssocChan" #define SQLUTILSUCCESS "You shouldn't be reading this (success)" /** Used to associate an SQL query with a user */ class AssociateUser : public Request { public: /** Query ID */ unsigned long id; /** User */ userrec* user; AssociateUser(Module* s, Module* d, unsigned long i, userrec* u) : Request(s, d, SQLUTILAU), id(i), user(u) { } AssociateUser& S() { Send(); return *this; } }; /** Used to associate an SQL query with a channel */ class AssociateChan : public Request { public: /** Query ID */ unsigned long id; /** Channel */ chanrec* chan; AssociateChan(Module* s, Module* d, unsigned long i, chanrec* u) : Request(s, d, SQLUTILAC), id(i), chan(u) { } AssociateChan& S() { Send(); return *this; } }; /** Unassociate a user or class from an SQL query */ class UnAssociate : public Request { public: /** The query ID */ unsigned long id; UnAssociate(Module* s, Module* d, unsigned long i) : Request(s, d, SQLUTILUA), id(i) { } UnAssociate& S() { Send(); return *this; } }; /** Get the user associated with an SQL query ID */ class GetAssocUser : public Request { public: /** The query id */ unsigned long id; /** The user */ userrec* user; GetAssocUser(Module* s, Module* d, unsigned long i) : Request(s, d, SQLUTILGU), id(i), user(NULL) { } GetAssocUser& S() { Send(); return *this; } }; /** Get the channel associated with an SQL query ID */ class GetAssocChan : public Request { public: /** The query id */ unsigned long id; /** The channel */ chanrec* chan; GetAssocChan(Module* s, Module* d, unsigned long i) : Request(s, d, SQLUTILGC), id(i), chan(NULL) { } GetAssocChan& S() { Send(); return *this; } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_SQLUTILS
+#define INSPIRCD_SQLUTILS
+
+#include "modules.h"
+
+#define SQLUTILAU "SQLutil AssociateUser"
+#define SQLUTILAC "SQLutil AssociateChan"
+#define SQLUTILUA "SQLutil UnAssociate"
+#define SQLUTILGU "SQLutil GetAssocUser"
+#define SQLUTILGC "SQLutil GetAssocChan"
+#define SQLUTILSUCCESS "You shouldn't be reading this (success)"
+
+/** Used to associate an SQL query with a user
+ */
+class AssociateUser : public Request
+{
+public:
+ /** Query ID
+ */
+ unsigned long id;
+ /** User
+ */
+ userrec* user;
+
+ AssociateUser(Module* s, Module* d, unsigned long i, userrec* u)
+ : Request(s, d, SQLUTILAU), id(i), user(u)
+ {
+ }
+
+ AssociateUser& S()
+ {
+ Send();
+ return *this;
+ }
+};
+
+/** Used to associate an SQL query with a channel
+ */
+class AssociateChan : public Request
+{
+public:
+ /** Query ID
+ */
+ unsigned long id;
+ /** Channel
+ */
+ chanrec* chan;
+
+ AssociateChan(Module* s, Module* d, unsigned long i, chanrec* u)
+ : Request(s, d, SQLUTILAC), id(i), chan(u)
+ {
+ }
+
+ AssociateChan& S()
+ {
+ Send();
+ return *this;
+ }
+};
+
+/** Unassociate a user or class from an SQL query
+ */
+class UnAssociate : public Request
+{
+public:
+ /** The query ID
+ */
+ unsigned long id;
+
+ UnAssociate(Module* s, Module* d, unsigned long i)
+ : Request(s, d, SQLUTILUA), id(i)
+ {
+ }
+
+ UnAssociate& S()
+ {
+ Send();
+ return *this;
+ }
+};
+
+/** Get the user associated with an SQL query ID
+ */
+class GetAssocUser : public Request
+{
+public:
+ /** The query id
+ */
+ unsigned long id;
+ /** The user
+ */
+ userrec* user;
+
+ GetAssocUser(Module* s, Module* d, unsigned long i)
+ : Request(s, d, SQLUTILGU), id(i), user(NULL)
+ {
+ }
+
+ GetAssocUser& S()
+ {
+ Send();
+ return *this;
+ }
+};
+
+/** Get the channel associated with an SQL query ID
+ */
+class GetAssocChan : public Request
+{
+public:
+ /** The query id
+ */
+ unsigned long id;
+ /** The channel
+ */
+ chanrec* chan;
+
+ GetAssocChan(Module* s, Module* d, unsigned long i)
+ : Request(s, d, SQLUTILGC), id(i), chan(NULL)
+ {
+ }
+
+ GetAssocChan& S()
+ {
+ Send();
+ return *this;
+ }
+};
+
+#endif
diff --git a/src/modules/extra/m_sqlv2.h b/src/modules/extra/m_sqlv2.h
index decac4b57..c7f6edbb9 100644
--- a/src/modules/extra/m_sqlv2.h
+++ b/src/modules/extra/m_sqlv2.h
@@ -1 +1,605 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef INSPIRCD_SQLAPI_2 #define INSPIRCD_SQLAPI_2 #include <string> #include <deque> #include <map> #include "modules.h" /** SQLreq define. * This is the voodoo magic which lets us pass multiple * parameters to the SQLrequest constructor... voodoo... */ #define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e)) /** Identifiers used to identify Request types */ #define SQLREQID "SQLv2 Request" #define SQLRESID "SQLv2 Result" #define SQLSUCCESS "You shouldn't be reading this (success)" /** Defines the error types which SQLerror may be set to */ enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL }; /** A list of format parameters for an SQLquery object. */ typedef std::deque<std::string> ParamL; /** The base class of SQL exceptions */ class SQLexception : public ModuleException { public: SQLexception(const std::string &reason) : ModuleException(reason) { } SQLexception() : ModuleException("SQLv2: Undefined exception") { } }; /** An exception thrown when a bad column or row name or id is requested */ class SQLbadColName : public SQLexception { public: SQLbadColName() : SQLexception("SQLv2: Bad column name") { } }; /** SQLerror holds the error state of any SQLrequest or SQLresult. * The error string varies from database software to database software * and should be used to display informational error messages to users. */ class SQLerror : public classbase { /** The error id */ SQLerrorNum id; /** The error string */ std::string str; public: /** Initialize an SQLerror * @param i The error ID to set * @param s The (optional) error string to set */ SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "") : id(i), str(s) { } /** Return the ID of the error */ SQLerrorNum Id() { return id; } /** Set the ID of an error * @param i The new error ID to set * @return the ID which was set */ SQLerrorNum Id(SQLerrorNum i) { id = i; return id; } /** Set the error string for an error * @param s The new error string to set */ void Str(const std::string &s) { str = s; } /** Return the error string for an error */ const char* Str() { if(str.length()) return str.c_str(); switch(id) { case NO_ERROR: return "No error"; case BAD_DBID: return "Invalid database ID"; case BAD_CONN: return "Invalid connection"; case QSEND_FAIL: return "Sending query failed"; case QREPLY_FAIL: return "Getting query result failed"; default: return "Unknown error"; } } }; /** SQLquery provides a way to represent a query string, and its parameters in a type-safe way. * C++ has no native type-safe way of having a variable number of arguments to a function, * the workaround for this isn't easy to describe simply, but in a nutshell what's really * happening when - from the above example - you do this: * * SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42"); * * what's actually happening is functionally this: * * SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42")); * * with 'query()' returning a reference to an object with a 'addparam()' member function which * in turn returns a reference to that object. There are actually four ways you can create a * SQLrequest..all have their disadvantages and advantages. In the real implementations the * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds * the query string and a ParamL (std::deque<std::string>) of query parameters. * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is: * * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter)); */ class SQLquery : public classbase { public: /** The query 'format string' */ std::string q; /** The query parameter list * There should be one parameter for every ? character * within the format string shown above. */ ParamL p; /** Initialize an SQLquery with a given format string only */ SQLquery(const std::string &query) : q(query) { } /** Initialize an SQLquery with a format string and parameters. * If you provide parameters, you must initialize the list yourself * if you choose to do it via this method, using std::deque::push_back(). */ SQLquery(const std::string &query, const ParamL &params) : q(query), p(params) { } /** An overloaded operator for pushing parameters onto the parameter list */ template<typename T> SQLquery& operator,(const T &foo) { p.push_back(ConvToStr(foo)); return *this; } /** An overloaded operator for pushing parameters onto the parameter list. * This has higher precedence than 'operator,' and can save on parenthesis. */ template<typename T> SQLquery& operator%(const T &foo) { p.push_back(ConvToStr(foo)); return *this; } }; /** SQLrequest is sent to the SQL API to command it to run a query and return the result. * You must instantiate this object with a valid SQLquery object and its parameters, then * send it using its Send() method to the module providing the 'SQL' feature. To find this * module, use Server::FindFeature(). */ class SQLrequest : public Request { public: /** The fully parsed and expanded query string * This is initialized from the SQLquery parameter given in the constructor. */ SQLquery query; /** The database ID to apply the request to */ std::string dbid; /** True if this is a priority query. * Priority queries may 'queue jump' in the request queue. */ bool pri; /** The query ID, assigned by the SQL api. * After your request is processed, this will * be initialized for you by the API to a valid request ID, * except in the case of an error. */ unsigned long id; /** If an error occured, error.id will be any other value than NO_ERROR. */ SQLerror error; /** Initialize an SQLrequest. * For example: * * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick); * * @param s A pointer to the sending module, where the result should be routed * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature * @param databaseid The database ID to perform the query on. This must match a valid * database ID from the configuration of the SQL module. * @param q A properly initialized SQLquery object. */ SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q) : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0) { } /** Set the priority of a request. */ void Priority(bool p = true) { pri = p; } /** Set the source of a request. You should not need to use this method. */ void SetSource(Module* mod) { source = mod; } }; /** * This class contains a field's data plus a way to determine if the field * is NULL or not without having to mess around with NULL pointers. */ class SQLfield { public: /** * The data itself */ std::string d; /** * If the field was null */ bool null; /** Initialize an SQLfield */ SQLfield(const std::string &data = "", bool n = false) : d(data), null(n) { } }; /** A list of items which make up a row of a result or table (tuple) * This does not include field names. */ typedef std::vector<SQLfield> SQLfieldList; /** A list of items which make up a row of a result or table (tuple) * This also includes the field names. */ typedef std::map<std::string, SQLfield> SQLfieldMap; /** SQLresult is a reply to a previous query. * If you send a query to the SQL api, the response will arrive at your * OnRequest method of your module at some later time, depending on the * congestion of the SQL server and complexity of the query. The ID of * this result will match the ID assigned to your original request. * SQLresult contains its own internal cursor (row counter) which is * incremented with each method call which retrieves a single row. */ class SQLresult : public Request { public: /** The original query string passed initially to the SQL API */ std::string query; /** The database ID the query was executed on */ std::string dbid; /** * The error (if any) which occured. * If an error occured the value of error.id will be any * other value than NO_ERROR. */ SQLerror error; /** * This will match query ID you were given when sending * the request at an earlier time. */ unsigned long id; /** Used by the SQL API to instantiate an SQLrequest */ SQLresult(Module* s, Module* d, unsigned long i) : Request(s, d, SQLRESID), id(i) { } /** * 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, and SQLresult::Cols() * will contain 0. In this case you SHOULD NEVER * access any of the result set rows, as there arent any! * @returns Number of rows in the result set. */ virtual int Rows() = 0; /** * Return the number of columns in the result. * If you performed an UPDATE or INSERT which * does not return a dataset, this value will * be 0. * @returns Number of columns in the result set. */ virtual int Cols() = 0; /** * Get a string name of the column by an index number * @param column The id number of a column * @returns The column name associated with the given ID */ virtual std::string ColName(int column) = 0; /** * Get an index number for a column from a string name. * An exception of type SQLbadColName will be thrown if * the name given is invalid. * @param column The column name to get the ID of * @returns The ID number of the column provided */ virtual int ColNum(const std::string &column) = 0; /** * Get a string value in a given row and column * This does not effect the internal cursor. * @returns The value stored at [row,column] in the table */ virtual SQLfield GetValue(int row, int column) = 0; /** * Return a list of values in a row, this should * increment an internal counter so you can repeatedly * call it until it returns an empty vector. * This returns a reference to an internal object, * the same object is used for all calls to this function * and therefore the return value is only valid until * you call this function again. It is also invalid if * the SQLresult object is destroyed. * The internal cursor (row counter) is incremented by one. * @returns A reference to the current row's SQLfieldList */ virtual SQLfieldList& GetRow() = 0; /** * As above, but return a map indexed by key name. * The internal cursor (row counter) is incremented by one. * @returns A reference to the current row's SQLfieldMap */ virtual SQLfieldMap& GetRowMap() = 0; /** * Like GetRow(), but returns a pointer to a dynamically * allocated object which must be explicitly freed. For * portability reasons this must be freed with SQLresult::Free() * The internal cursor (row counter) is incremented by one. * @returns A newly-allocated SQLfieldList */ virtual SQLfieldList* GetRowPtr() = 0; /** * As above, but return a map indexed by key name * The internal cursor (row counter) is incremented by one. * @returns A newly-allocated SQLfieldMap */ virtual SQLfieldMap* GetRowMapPtr() = 0; /** * Overloaded function for freeing the lists and maps * returned by GetRowPtr or GetRowMapPtr. * @param fm The SQLfieldMap to free */ virtual void Free(SQLfieldMap* fm) = 0; /** * Overloaded function for freeing the lists and maps * returned by GetRowPtr or GetRowMapPtr. * @param fl The SQLfieldList to free */ virtual void Free(SQLfieldList* fl) = 0; }; /** SQLHost represents a <database> config line and is useful * for storing in a map and iterating on rehash to see which * <database> tags was added/removed/unchanged. */ class SQLhost { public: std::string id; /* Database handle id */ std::string host; /* Database server hostname */ std::string ip; /* resolved IP, needed for at least pgsql.so */ unsigned int port; /* Database server port */ std::string name; /* Database name */ std::string user; /* Database username */ std::string pass; /* Database password */ bool ssl; /* If we should require SSL */ SQLhost() { } SQLhost(const std::string& i, const std::string& h, unsigned int p, const std::string& n, const std::string& u, const std::string& pa, bool s) : id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s) { } /** Overload this to return a correct Data source Name (DSN) for * the current SQL module. */ std::string GetDSN(); }; /** Overload operator== for two SQLhost objects for easy comparison. */ bool operator== (const SQLhost& l, const SQLhost& r) { return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == l.user && l.pass == r.pass && l.ssl == r.ssl); } /** QueryQueue, a queue of queries waiting to be executed. * This maintains two queues internally, one for 'priority' * queries and one for less important ones. Each queue has * new queries appended to it and ones to execute are popped * off the front. This keeps them flowing round nicely and no * query should ever get 'stuck' for too long. If there are * queries in the priority queue they will be executed first, * 'unimportant' queries will only be executed when the * priority queue is empty. * * We store lists of SQLrequest's here, by value as we want to avoid storing * any data allocated inside the client module (in case that module is unloaded * while the query is in progress). * * Because we want to work on the current SQLrequest in-situ, we need a way * of accessing the request we are currently processing, QueryQueue::front(), * but that call needs to always return the same request until that request * is removed from the queue, this is what the 'which' variable is. New queries are * always added to the back of one of the two queues, but if when front() * is first called then the priority queue is empty then front() will return * a query from the normal queue, but if a query is then added to the priority * queue then front() must continue to return the front of the *normal* queue * until pop() is called. */ class QueryQueue : public classbase { private: typedef std::deque<SQLrequest> ReqDeque; ReqDeque priority; /* The priority queue */ ReqDeque normal; /* The 'normal' queue */ enum { PRI, NOR, NON } which; /* Which queue the currently active element is at the front of */ public: QueryQueue() : which(NON) { } void push(const SQLrequest &q) { if(q.pri) priority.push_back(q); else normal.push_back(q); } void pop() { if((which == PRI) && priority.size()) { priority.pop_front(); } else if((which == NOR) && normal.size()) { normal.pop_front(); } /* Reset this */ which = NON; /* Silently do nothing if there was no element to pop() */ } SQLrequest& front() { switch(which) { case PRI: return priority.front(); case NOR: return normal.front(); default: if(priority.size()) { which = PRI; return priority.front(); } if(normal.size()) { which = NOR; return normal.front(); } /* This will probably result in a segfault, * but the caller should have checked totalsize() * first so..meh - moron :p */ return priority.front(); } } std::pair<int, int> size() { return std::make_pair(priority.size(), normal.size()); } int totalsize() { return priority.size() + normal.size(); } void PurgeModule(Module* mod) { DoPurgeModule(mod, priority); DoPurgeModule(mod, normal); } private: void DoPurgeModule(Module* mod, ReqDeque& q) { for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++) { if(iter->GetSource() == mod) { if(iter->id == front().id) { /* It's the currently active query.. :x */ iter->SetSource(NULL); } else { /* It hasn't been executed yet..just remove it */ iter = q.erase(iter); } } } } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_SQLAPI_2
+#define INSPIRCD_SQLAPI_2
+
+#include <string>
+#include <deque>
+#include <map>
+#include "modules.h"
+
+/** SQLreq define.
+ * This is the voodoo magic which lets us pass multiple
+ * parameters to the SQLrequest constructor... voodoo...
+ */
+#define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e))
+
+/** Identifiers used to identify Request types
+ */
+#define SQLREQID "SQLv2 Request"
+#define SQLRESID "SQLv2 Result"
+#define SQLSUCCESS "You shouldn't be reading this (success)"
+
+/** Defines the error types which SQLerror may be set to
+ */
+enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL };
+
+/** A list of format parameters for an SQLquery object.
+ */
+typedef std::deque<std::string> ParamL;
+
+/** The base class of SQL exceptions
+ */
+class SQLexception : public ModuleException
+{
+ public:
+ SQLexception(const std::string &reason) : ModuleException(reason)
+ {
+ }
+
+ SQLexception() : ModuleException("SQLv2: Undefined exception")
+ {
+ }
+};
+
+/** An exception thrown when a bad column or row name or id is requested
+ */
+class SQLbadColName : public SQLexception
+{
+public:
+ SQLbadColName() : SQLexception("SQLv2: Bad column name")
+ {
+ }
+};
+
+/** SQLerror holds the error state of any SQLrequest or SQLresult.
+ * The error string varies from database software to database software
+ * and should be used to display informational error messages to users.
+ */
+class SQLerror : public classbase
+{
+ /** The error id
+ */
+ SQLerrorNum id;
+ /** The error string
+ */
+ std::string str;
+public:
+ /** Initialize an SQLerror
+ * @param i The error ID to set
+ * @param s The (optional) error string to set
+ */
+ SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")
+ : id(i), str(s)
+ {
+ }
+
+ /** Return the ID of the error
+ */
+ SQLerrorNum Id()
+ {
+ return id;
+ }
+
+ /** Set the ID of an error
+ * @param i The new error ID to set
+ * @return the ID which was set
+ */
+ SQLerrorNum Id(SQLerrorNum i)
+ {
+ id = i;
+ return id;
+ }
+
+ /** Set the error string for an error
+ * @param s The new error string to set
+ */
+ void Str(const std::string &s)
+ {
+ str = s;
+ }
+
+ /** Return the error string for an error
+ */
+ const char* Str()
+ {
+ if(str.length())
+ return str.c_str();
+
+ switch(id)
+ {
+ case NO_ERROR:
+ return "No error";
+ case BAD_DBID:
+ return "Invalid database ID";
+ case BAD_CONN:
+ return "Invalid connection";
+ case QSEND_FAIL:
+ return "Sending query failed";
+ case QREPLY_FAIL:
+ return "Getting query result failed";
+ default:
+ return "Unknown error";
+ }
+ }
+};
+
+/** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.
+ * C++ has no native type-safe way of having a variable number of arguments to a function,
+ * the workaround for this isn't easy to describe simply, but in a nutshell what's really
+ * happening when - from the above example - you do this:
+ *
+ * SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42");
+ *
+ * what's actually happening is functionally this:
+ *
+ * SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
+ *
+ * with 'query()' returning a reference to an object with a 'addparam()' member function which
+ * in turn returns a reference to that object. There are actually four ways you can create a
+ * SQLrequest..all have their disadvantages and advantages. In the real implementations the
+ * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds
+ * the query string and a ParamL (std::deque<std::string>) of query parameters.
+ * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:
+ *
+ * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
+ */
+class SQLquery : public classbase
+{
+public:
+ /** The query 'format string'
+ */
+ std::string q;
+ /** The query parameter list
+ * There should be one parameter for every ? character
+ * within the format string shown above.
+ */
+ ParamL p;
+
+ /** Initialize an SQLquery with a given format string only
+ */
+ SQLquery(const std::string &query)
+ : q(query)
+ {
+ }
+
+ /** Initialize an SQLquery with a format string and parameters.
+ * If you provide parameters, you must initialize the list yourself
+ * if you choose to do it via this method, using std::deque::push_back().
+ */
+ SQLquery(const std::string &query, const ParamL &params)
+ : q(query), p(params)
+ {
+ }
+
+ /** An overloaded operator for pushing parameters onto the parameter list
+ */
+ template<typename T> SQLquery& operator,(const T &foo)
+ {
+ p.push_back(ConvToStr(foo));
+ return *this;
+ }
+
+ /** An overloaded operator for pushing parameters onto the parameter list.
+ * This has higher precedence than 'operator,' and can save on parenthesis.
+ */
+ template<typename T> SQLquery& operator%(const T &foo)
+ {
+ p.push_back(ConvToStr(foo));
+ return *this;
+ }
+};
+
+/** SQLrequest is sent to the SQL API to command it to run a query and return the result.
+ * You must instantiate this object with a valid SQLquery object and its parameters, then
+ * send it using its Send() method to the module providing the 'SQL' feature. To find this
+ * module, use Server::FindFeature().
+ */
+class SQLrequest : public Request
+{
+public:
+ /** The fully parsed and expanded query string
+ * This is initialized from the SQLquery parameter given in the constructor.
+ */
+ SQLquery query;
+ /** The database ID to apply the request to
+ */
+ std::string dbid;
+ /** True if this is a priority query.
+ * Priority queries may 'queue jump' in the request queue.
+ */
+ bool pri;
+ /** The query ID, assigned by the SQL api.
+ * After your request is processed, this will
+ * be initialized for you by the API to a valid request ID,
+ * except in the case of an error.
+ */
+ unsigned long id;
+ /** If an error occured, error.id will be any other value than NO_ERROR.
+ */
+ SQLerror error;
+
+ /** Initialize an SQLrequest.
+ * For example:
+ *
+ * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick);
+ *
+ * @param s A pointer to the sending module, where the result should be routed
+ * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature
+ * @param databaseid The database ID to perform the query on. This must match a valid
+ * database ID from the configuration of the SQL module.
+ * @param q A properly initialized SQLquery object.
+ */
+ SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)
+ : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)
+ {
+ }
+
+ /** Set the priority of a request.
+ */
+ void Priority(bool p = true)
+ {
+ pri = p;
+ }
+
+ /** Set the source of a request. You should not need to use this method.
+ */
+ void SetSource(Module* mod)
+ {
+ source = mod;
+ }
+};
+
+/**
+ * This class contains a field's data plus a way to determine if the field
+ * is NULL or not without having to mess around with NULL pointers.
+ */
+class SQLfield
+{
+public:
+ /**
+ * The data itself
+ */
+ std::string d;
+
+ /**
+ * If the field was null
+ */
+ bool null;
+
+ /** Initialize an SQLfield
+ */
+ SQLfield(const std::string &data = "", bool n = false)
+ : d(data), null(n)
+ {
+
+ }
+};
+
+/** A list of items which make up a row of a result or table (tuple)
+ * This does not include field names.
+ */
+typedef std::vector<SQLfield> SQLfieldList;
+/** A list of items which make up a row of a result or table (tuple)
+ * This also includes the field names.
+ */
+typedef std::map<std::string, SQLfield> SQLfieldMap;
+
+/** SQLresult is a reply to a previous query.
+ * If you send a query to the SQL api, the response will arrive at your
+ * OnRequest method of your module at some later time, depending on the
+ * congestion of the SQL server and complexity of the query. The ID of
+ * this result will match the ID assigned to your original request.
+ * SQLresult contains its own internal cursor (row counter) which is
+ * incremented with each method call which retrieves a single row.
+ */
+class SQLresult : public Request
+{
+public:
+ /** The original query string passed initially to the SQL API
+ */
+ std::string query;
+ /** The database ID the query was executed on
+ */
+ std::string dbid;
+ /**
+ * The error (if any) which occured.
+ * If an error occured the value of error.id will be any
+ * other value than NO_ERROR.
+ */
+ SQLerror error;
+ /**
+ * This will match query ID you were given when sending
+ * the request at an earlier time.
+ */
+ unsigned long id;
+
+ /** Used by the SQL API to instantiate an SQLrequest
+ */
+ SQLresult(Module* s, Module* d, unsigned long i)
+ : Request(s, d, SQLRESID), id(i)
+ {
+ }
+
+ /**
+ * 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, and SQLresult::Cols()
+ * will contain 0. In this case you SHOULD NEVER
+ * access any of the result set rows, as there arent any!
+ * @returns Number of rows in the result set.
+ */
+ virtual int Rows() = 0;
+
+ /**
+ * Return the number of columns in the result.
+ * If you performed an UPDATE or INSERT which
+ * does not return a dataset, this value will
+ * be 0.
+ * @returns Number of columns in the result set.
+ */
+ virtual int Cols() = 0;
+
+ /**
+ * Get a string name of the column by an index number
+ * @param column The id number of a column
+ * @returns The column name associated with the given ID
+ */
+ virtual std::string ColName(int column) = 0;
+
+ /**
+ * Get an index number for a column from a string name.
+ * An exception of type SQLbadColName will be thrown if
+ * the name given is invalid.
+ * @param column The column name to get the ID of
+ * @returns The ID number of the column provided
+ */
+ virtual int ColNum(const std::string &column) = 0;
+
+ /**
+ * Get a string value in a given row and column
+ * This does not effect the internal cursor.
+ * @returns The value stored at [row,column] in the table
+ */
+ virtual SQLfield GetValue(int row, int column) = 0;
+
+ /**
+ * Return a list of values in a row, this should
+ * increment an internal counter so you can repeatedly
+ * call it until it returns an empty vector.
+ * This returns a reference to an internal object,
+ * the same object is used for all calls to this function
+ * and therefore the return value is only valid until
+ * you call this function again. It is also invalid if
+ * the SQLresult object is destroyed.
+ * The internal cursor (row counter) is incremented by one.
+ * @returns A reference to the current row's SQLfieldList
+ */
+ virtual SQLfieldList& GetRow() = 0;
+
+ /**
+ * As above, but return a map indexed by key name.
+ * The internal cursor (row counter) is incremented by one.
+ * @returns A reference to the current row's SQLfieldMap
+ */
+ virtual SQLfieldMap& GetRowMap() = 0;
+
+ /**
+ * Like GetRow(), but returns a pointer to a dynamically
+ * allocated object which must be explicitly freed. For
+ * portability reasons this must be freed with SQLresult::Free()
+ * The internal cursor (row counter) is incremented by one.
+ * @returns A newly-allocated SQLfieldList
+ */
+ virtual SQLfieldList* GetRowPtr() = 0;
+
+ /**
+ * As above, but return a map indexed by key name
+ * The internal cursor (row counter) is incremented by one.
+ * @returns A newly-allocated SQLfieldMap
+ */
+ virtual SQLfieldMap* GetRowMapPtr() = 0;
+
+ /**
+ * Overloaded function for freeing the lists and maps
+ * returned by GetRowPtr or GetRowMapPtr.
+ * @param fm The SQLfieldMap to free
+ */
+ virtual void Free(SQLfieldMap* fm) = 0;
+
+ /**
+ * Overloaded function for freeing the lists and maps
+ * returned by GetRowPtr or GetRowMapPtr.
+ * @param fl The SQLfieldList to free
+ */
+ virtual void Free(SQLfieldList* fl) = 0;
+};
+
+
+/** SQLHost represents a <database> config line and is useful
+ * for storing in a map and iterating on rehash to see which
+ * <database> tags was added/removed/unchanged.
+ */
+class SQLhost
+{
+ public:
+ std::string id; /* Database handle id */
+ std::string host; /* Database server hostname */
+ std::string ip; /* resolved IP, needed for at least pgsql.so */
+ unsigned int port; /* Database server port */
+ std::string name; /* Database name */
+ std::string user; /* Database username */
+ std::string pass; /* Database password */
+ bool ssl; /* If we should require SSL */
+
+ SQLhost()
+ {
+ }
+
+ SQLhost(const std::string& i, const std::string& h, unsigned int p, const std::string& n, const std::string& u, const std::string& pa, bool s)
+ : id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s)
+ {
+ }
+
+ /** Overload this to return a correct Data source Name (DSN) for
+ * the current SQL module.
+ */
+ std::string GetDSN();
+};
+
+/** Overload operator== for two SQLhost objects for easy comparison.
+ */
+bool operator== (const SQLhost& l, const SQLhost& r)
+{
+ return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == l.user && l.pass == r.pass && l.ssl == r.ssl);
+}
+
+
+/** QueryQueue, a queue of queries waiting to be executed.
+ * This maintains two queues internally, one for 'priority'
+ * queries and one for less important ones. Each queue has
+ * new queries appended to it and ones to execute are popped
+ * off the front. This keeps them flowing round nicely and no
+ * query should ever get 'stuck' for too long. If there are
+ * queries in the priority queue they will be executed first,
+ * 'unimportant' queries will only be executed when the
+ * priority queue is empty.
+ *
+ * We store lists of SQLrequest's here, by value as we want to avoid storing
+ * any data allocated inside the client module (in case that module is unloaded
+ * while the query is in progress).
+ *
+ * Because we want to work on the current SQLrequest in-situ, we need a way
+ * of accessing the request we are currently processing, QueryQueue::front(),
+ * but that call needs to always return the same request until that request
+ * is removed from the queue, this is what the 'which' variable is. New queries are
+ * always added to the back of one of the two queues, but if when front()
+ * is first called then the priority queue is empty then front() will return
+ * a query from the normal queue, but if a query is then added to the priority
+ * queue then front() must continue to return the front of the *normal* queue
+ * until pop() is called.
+ */
+
+class QueryQueue : public classbase
+{
+private:
+ typedef std::deque<SQLrequest> ReqDeque;
+
+ ReqDeque priority; /* The priority queue */
+ ReqDeque normal; /* The 'normal' queue */
+ enum { PRI, NOR, NON } which; /* Which queue the currently active element is at the front of */
+
+public:
+ QueryQueue()
+ : which(NON)
+ {
+ }
+
+ void push(const SQLrequest &q)
+ {
+ if(q.pri)
+ priority.push_back(q);
+ else
+ normal.push_back(q);
+ }
+
+ void pop()
+ {
+ if((which == PRI) && priority.size())
+ {
+ priority.pop_front();
+ }
+ else if((which == NOR) && normal.size())
+ {
+ normal.pop_front();
+ }
+
+ /* Reset this */
+ which = NON;
+
+ /* Silently do nothing if there was no element to pop() */
+ }
+
+ SQLrequest& front()
+ {
+ switch(which)
+ {
+ case PRI:
+ return priority.front();
+ case NOR:
+ return normal.front();
+ default:
+ if(priority.size())
+ {
+ which = PRI;
+ return priority.front();
+ }
+
+ if(normal.size())
+ {
+ which = NOR;
+ return normal.front();
+ }
+
+ /* This will probably result in a segfault,
+ * but the caller should have checked totalsize()
+ * first so..meh - moron :p
+ */
+
+ return priority.front();
+ }
+ }
+
+ std::pair<int, int> size()
+ {
+ return std::make_pair(priority.size(), normal.size());
+ }
+
+ int totalsize()
+ {
+ return priority.size() + normal.size();
+ }
+
+ void PurgeModule(Module* mod)
+ {
+ DoPurgeModule(mod, priority);
+ DoPurgeModule(mod, normal);
+ }
+
+private:
+ void DoPurgeModule(Module* mod, ReqDeque& q)
+ {
+ for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++)
+ {
+ if(iter->GetSource() == mod)
+ {
+ if(iter->id == front().id)
+ {
+ /* It's the currently active query.. :x */
+ iter->SetSource(NULL);
+ }
+ else
+ {
+ /* It hasn't been executed yet..just remove it */
+ iter = q.erase(iter);
+ }
+ }
+ }
+ }
+};
+
+
+#endif
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp
index 037d2cf72..fd8b12d32 100644
--- a/src/modules/extra/m_ssl_gnutls.cpp
+++ b/src/modules/extra/m_ssl_gnutls.cpp
@@ -1 +1,843 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include "inspircd_config.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "socket.h" #include "hashcomp.h" #include "transport.h" #ifdef WINDOWS #pragma comment(lib, "libgnutls-13.lib") #undef MAX_DESCRIPTORS #define MAX_DESCRIPTORS 10000 #endif /* $ModDesc: Provides SSL support for clients */ /* $CompileFlags: exec("libgnutls-config --cflags") */ /* $LinkerFlags: rpath("libgnutls-config --libs") exec("libgnutls-config --libs") */ /* $ModDep: transport.h */ enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED }; bool isin(int port, const std::vector<int> &portlist) { for(unsigned int i = 0; i < portlist.size(); i++) if(portlist[i] == port) return true; return false; } /** Represents an SSL user's extra data */ class issl_session : public classbase { public: gnutls_session_t sess; issl_status status; std::string outbuf; int inbufoffset; char* inbuf; int fd; }; class ModuleSSLGnuTLS : public Module { ConfigReader* Conf; char* dummy; std::vector<int> listenports; int inbufsize; issl_session sessions[MAX_DESCRIPTORS]; gnutls_certificate_credentials x509_cred; gnutls_dh_params dh_params; std::string keyfile; std::string certfile; std::string cafile; std::string crlfile; std::string sslports; int dh_bits; int clientactive; public: ModuleSSLGnuTLS(InspIRCd* Me) : Module(Me) { ServerInstance->PublishInterface("InspSocketHook", this); // Not rehashable...because I cba to reduce all the sizes of existing buffers. inbufsize = ServerInstance->Config->NetBufferSize; gnutls_global_init(); // This must be called once in the program if(gnutls_certificate_allocate_credentials(&x509_cred) != 0) ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials"); // Guessing return meaning if(gnutls_dh_params_init(&dh_params) < 0) ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters"); // Needs the flag as it ignores a plain /rehash OnRehash(NULL,"ssl"); // Void return, guess we assume success gnutls_certificate_set_dh_params(x509_cred, dh_params); } virtual void OnRehash(userrec* user, const std::string &param) { if(param != "ssl") return; Conf = new ConfigReader(ServerInstance); for(unsigned int i = 0; i < listenports.size(); i++) { ServerInstance->Config->DelIOHook(listenports[i]); } listenports.clear(); clientactive = 0; sslports.clear(); for(int i = 0; i < Conf->Enumerate("bind"); i++) { // For each <bind> tag std::string x = Conf->ReadValue("bind", "type", i); if(((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "gnutls")) { // Get the port we're meant to be listening on with SSL std::string port = Conf->ReadValue("bind", "port", i); irc::portparser portrange(port, false); long portno = -1; while ((portno = portrange.GetToken())) { clientactive++; try { if (ServerInstance->Config->AddIOHook(portno, this)) { listenports.push_back(portno); for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) if (ServerInstance->Config->ports[i]->GetPort() == portno) ServerInstance->Config->ports[i]->SetDescription("ssl"); ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %d", portno); sslports.append("*:").append(ConvToStr(portno)).append(";"); } else { ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno); } } catch (ModuleException &e) { ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have an other SSL or similar module loaded?", portno, e.GetReason()); } } } } std::string confdir(ServerInstance->ConfigFileName); // +1 so we the path ends with a / confdir = confdir.substr(0, confdir.find_last_of('/') + 1); cafile = Conf->ReadValue("gnutls", "cafile", 0); crlfile = Conf->ReadValue("gnutls", "crlfile", 0); certfile = Conf->ReadValue("gnutls", "certfile", 0); keyfile = Conf->ReadValue("gnutls", "keyfile", 0); dh_bits = Conf->ReadInteger("gnutls", "dhbits", 0, false); // Set all the default values needed. if (cafile.empty()) cafile = "ca.pem"; if (crlfile.empty()) crlfile = "crl.pem"; if (certfile.empty()) certfile = "cert.pem"; if (keyfile.empty()) keyfile = "key.pem"; if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096)) dh_bits = 1024; // Prepend relative paths with the path to the config directory. if(cafile[0] != '/') cafile = confdir + cafile; if(crlfile[0] != '/') crlfile = confdir + crlfile; if(certfile[0] != '/') certfile = confdir + certfile; if(keyfile[0] != '/') keyfile = confdir + keyfile; int ret; if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret)); if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret)); if((ret = gnutls_certificate_set_x509_key_file (x509_cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) { // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException throw ModuleException("Unable to load GnuTLS server certificate: " + std::string(gnutls_strerror(ret))); } // This may be on a large (once a day or week) timer eventually. GenerateDHParams(); DELETE(Conf); } void GenerateDHParams() { // 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. int ret; if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0) ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret)); } virtual ~ModuleSSLGnuTLS() { gnutls_dh_params_deinit(dh_params); gnutls_certificate_free_credentials(x509_cred); gnutls_global_deinit(); } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_USER) { userrec* user = (userrec*)item; if(user->GetExt("ssl", dummy) && isin(user->GetPort(), listenports)) { // 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->GlobalCulls.AddItem(user, "SSL module unloading"); } if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports)) { ssl_cert* tofree; user->GetExt("ssl_cert", tofree); delete tofree; user->Shrink("ssl_cert"); } } } virtual void OnUnloadModule(Module* mod, const std::string &name) { if(mod == this) { for(unsigned int i = 0; i < listenports.size(); i++) { ServerInstance->Config->DelIOHook(listenports[i]); for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++) if (ServerInstance->Config->ports[j]->GetPort() == listenports[i]) ServerInstance->Config->ports[j]->SetDescription("plaintext"); } } } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_On005Numeric] = List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1; List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1; } virtual void On005Numeric(std::string &output) { output.append(" SSL=" + sslports); } virtual char* OnRequest(Request* request) { ISHRequest* ISR = (ISHRequest*)request; if (strcmp("IS_NAME", request->GetId()) == 0) { return "gnutls"; } else if (strcmp("IS_HOOK", request->GetId()) == 0) { char* ret = "OK"; try { ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; } catch (ModuleException &e) { return NULL; } return ret; } else if (strcmp("IS_UNHOOK", request->GetId()) == 0) { return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; } else if (strcmp("IS_HSDONE", request->GetId()) == 0) { if (ISR->Sock->GetFd() < 0) return (char*)"OK"; issl_session* session = &sessions[ISR->Sock->GetFd()]; return (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) ? NULL : (char*)"OK"; } else if (strcmp("IS_ATTACH", request->GetId()) == 0) { if (ISR->Sock->GetFd() > -1) { issl_session* session = &sessions[ISR->Sock->GetFd()]; if (session->sess) { if ((Extensible*)ServerInstance->FindDescriptor(ISR->Sock->GetFd()) == (Extensible*)(ISR->Sock)) { VerifyCertificate(session, (InspSocket*)ISR->Sock); return "OK"; } } } } return NULL; } virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport) { issl_session* session = &sessions[fd]; session->fd = fd; session->inbuf = new char[inbufsize]; session->inbufoffset = 0; gnutls_init(&session->sess, GNUTLS_SERVER); gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate. gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred); gnutls_dh_set_prime_bits(session->sess, dh_bits); /* This is an experimental change to avoid a warning on 64bit systems about casting between integer and pointer of different sizes * This needs testing, but it's easy enough to rollback if need be * Old: gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. * New: gnutls_transport_set_ptr(session->sess, &fd); // Give gnutls the fd for the socket. * * With testing this seems to...not work :/ */ gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any. Handshake(session); } virtual void OnRawSocketConnect(int fd) { issl_session* session = &sessions[fd]; session->fd = fd; session->inbuf = new char[inbufsize]; session->inbufoffset = 0; gnutls_init(&session->sess, GNUTLS_CLIENT); gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate. gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred); gnutls_dh_set_prime_bits(session->sess, dh_bits); gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. Handshake(session); } virtual void OnRawSocketClose(int fd) { CloseSession(&sessions[fd]); EventHandler* user = ServerInstance->SE->GetRef(fd); if ((user) && (user->GetExt("ssl_cert", dummy))) { ssl_cert* tofree; user->GetExt("ssl_cert", tofree); delete tofree; user->Shrink("ssl_cert"); } } virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { issl_session* session = &sessions[fd]; if (!session->sess) { readresult = 0; CloseSession(session); return 1; } if (session->status == ISSL_HANDSHAKING_READ) { // The handshake isn't finished, try to finish it. if(!Handshake(session)) { // Couldn't resume handshake. return -1; } } else if (session->status == ISSL_HANDSHAKING_WRITE) { errno = EAGAIN; return -1; } // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN. if (session->status == ISSL_HANDSHAKEN) { // Is this right? Not sure if the unencrypted data is garaunteed to be the same length. // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet. int ret = gnutls_record_recv(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset); if (ret == 0) { // Client closed connection. readresult = 0; CloseSession(session); return 1; } else if (ret < 0) { if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { errno = EAGAIN; return -1; } else { readresult = 0; CloseSession(session); } } else { // Read successfully 'ret' bytes into inbuf + inbufoffset // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf' // 'buffer' is 'count' long unsigned int length = ret + session->inbufoffset; if(count <= length) { memcpy(buffer, session->inbuf, count); // Move the stuff left in inbuf to the beginning of it memcpy(session->inbuf, session->inbuf + count, (length - count)); // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp. session->inbufoffset = length - count; // Insp uses readresult as the count of how much data there is in buffer, so: readresult = count; } else { // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing. memcpy(buffer, session->inbuf, length); // Zero the offset, as there's nothing there.. session->inbufoffset = 0; // As above readresult = length; } } } else if(session->status == ISSL_CLOSING) readresult = 0; return 1; } virtual int OnRawSocketWrite(int fd, const char* buffer, int count) { if (!count) return 0; issl_session* session = &sessions[fd]; const char* sendbuffer = buffer; if (!session->sess) { ServerInstance->Log(DEBUG,"No session"); CloseSession(session); return 1; } session->outbuf.append(sendbuffer, count); sendbuffer = session->outbuf.c_str(); count = session->outbuf.size(); if (session->status == ISSL_HANDSHAKING_WRITE) { // The handshake isn't finished, try to finish it. ServerInstance->Log(DEBUG,"Finishing handshake"); Handshake(session); errno = EAGAIN; return -1; } int ret = 0; if (session->status == ISSL_HANDSHAKEN) { ServerInstance->Log(DEBUG,"Send record"); ret = gnutls_record_send(session->sess, sendbuffer, count); ServerInstance->Log(DEBUG,"Return: %d", ret); if (ret == 0) { CloseSession(session); } else if (ret < 0) { if(ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { ServerInstance->Log(DEBUG,"Not egain or interrupt, close session"); CloseSession(session); } else { ServerInstance->Log(DEBUG,"Again please"); errno = EAGAIN; return -1; } } else { ServerInstance->Log(DEBUG,"Trim buffer"); session->outbuf = session->outbuf.substr(ret); } } /* Who's smart idea was it to return 1 when we havent written anything? * This fucks the buffer up in InspSocket :p */ return ret < 1 ? 0 : ret; } // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection virtual void OnWhois(userrec* source, userrec* dest) { if (!clientactive) return; // Bugfix, only send this numeric for *our* SSL users if(dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports))) { ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick); } } virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) { // check if the linking module wants to know about OUR metadata if(extname == "ssl") { // check if this user has an swhois field to send if(user->GetExt(extname, dummy)) { // call this function in the linking module, let it format the data how it // sees fit, and send it on its way. We dont need or want to know how. proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON"); } } } virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { // check if its our metadata key, and its associated with a user if ((target_type == TYPE_USER) && (extname == "ssl")) { userrec* dest = (userrec*)target; // if they dont already have an ssl flag, accept the remote server's if (!dest->GetExt(extname, dummy)) { dest->Extend(extname, "ON"); } } } bool Handshake(issl_session* session) { int ret = gnutls_handshake(session->sess); if (ret < 0) { if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { // Handshake needs resuming later, read() or write() would have blocked. if(gnutls_record_get_direction(session->sess) == 0) { // gnutls_handshake() wants to read() again. session->status = ISSL_HANDSHAKING_READ; } else { // gnutls_handshake() wants to write() again. session->status = ISSL_HANDSHAKING_WRITE; MakePollWrite(session); } } else { // Handshake failed. CloseSession(session); session->status = ISSL_CLOSING; } return false; } else { // Handshake complete. // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater. userrec* extendme = ServerInstance->FindDescriptor(session->fd); if (extendme) { if (!extendme->GetExt("ssl", dummy)) extendme->Extend("ssl", "ON"); } // Change the seesion state session->status = ISSL_HANDSHAKEN; // Finish writing, if any left MakePollWrite(session); return true; } } virtual void OnPostConnect(userrec* user) { // This occurs AFTER OnUserConnect so we can be sure the // protocol module has propogated the NICK message. if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user))) { // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW. std::deque<std::string>* metadata = new std::deque<std::string>; metadata->push_back(user->nick); metadata->push_back("ssl"); // The metadata id metadata->push_back("ON"); // The value to send Event* event = new Event((char*)metadata,(Module*)this,"send_metadata"); event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up. DELETE(event); DELETE(metadata); VerifyCertificate(&sessions[user->GetFd()],user); if (sessions[user->GetFd()].sess) { std::string cipher = gnutls_kx_get_name(gnutls_kx_get(sessions[user->GetFd()].sess)); cipher.append("-").append(gnutls_cipher_get_name(gnutls_cipher_get(sessions[user->GetFd()].sess))).append("-"); cipher.append(gnutls_mac_get_name(gnutls_mac_get(sessions[user->GetFd()].sess))); user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, cipher.c_str()); } } } void MakePollWrite(issl_session* session) { OnRawSocketWrite(session->fd, NULL, 0); } void CloseSession(issl_session* session) { if(session->sess) { gnutls_bye(session->sess, GNUTLS_SHUT_WR); gnutls_deinit(session->sess); } if(session->inbuf) { delete[] session->inbuf; } session->outbuf.clear(); session->inbuf = NULL; session->sess = NULL; session->status = ISSL_NONE; } void VerifyCertificate(issl_session* session, Extensible* user) { if (!session->sess || !user) return; 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; user->Extend("ssl_cert",certinfo); /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. */ ret = gnutls_certificate_verify_peers2(session->sess, &status); if (ret < 0) { certinfo->data.insert(std::make_pair("error",std::string(gnutls_strerror(ret)))); return; } if (status & GNUTLS_CERT_INVALID) { certinfo->data.insert(std::make_pair("invalid",ConvToStr(1))); } else { certinfo->data.insert(std::make_pair("invalid",ConvToStr(0))); } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1))); } else { certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0))); } if (status & GNUTLS_CERT_REVOKED) { certinfo->data.insert(std::make_pair("revoked",ConvToStr(1))); } else { certinfo->data.insert(std::make_pair("revoked",ConvToStr(0))); } if (status & GNUTLS_CERT_SIGNER_NOT_CA) { certinfo->data.insert(std::make_pair("trusted",ConvToStr(0))); } else { certinfo->data.insert(std::make_pair("trusted",ConvToStr(1))); } /* 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->data.insert(std::make_pair("error","No X509 keys sent")); return; } ret = gnutls_x509_crt_init(&cert); if (ret < 0) { certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); return; } cert_list_size = 0; cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size); if (cert_list == NULL) { certinfo->data.insert(std::make_pair("error","No certificate was found")); return; } /* 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->data.insert(std::make_pair("error",gnutls_strerror(ret))); return; } gnutls_x509_crt_get_dn(cert, name, &name_size); certinfo->data.insert(std::make_pair("dn",name)); gnutls_x509_crt_get_issuer_dn(cert, name, &name_size); certinfo->data.insert(std::make_pair("issuer",name)); if ((ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, digest, &digest_size)) < 0) { certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); } else { certinfo->data.insert(std::make_pair("fingerprint",irc::hex(digest, digest_size))); } /* Beware here we do not check for errors. */ if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0))) { certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate")); } gnutls_x509_crt_deinit(cert); return; } }; MODULE_INIT(ModuleSSLGnuTLS); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "inspircd_config.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "socket.h"
+#include "hashcomp.h"
+#include "transport.h"
+
+#ifdef WINDOWS
+#pragma comment(lib, "libgnutls-13.lib")
+#undef MAX_DESCRIPTORS
+#define MAX_DESCRIPTORS 10000
+#endif
+
+/* $ModDesc: Provides SSL support for clients */
+/* $CompileFlags: exec("libgnutls-config --cflags") */
+/* $LinkerFlags: rpath("libgnutls-config --libs") exec("libgnutls-config --libs") */
+/* $ModDep: transport.h */
+
+
+enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
+
+bool isin(int port, const std::vector<int> &portlist)
+{
+ for(unsigned int i = 0; i < portlist.size(); i++)
+ if(portlist[i] == port)
+ return true;
+
+ return false;
+}
+
+/** Represents an SSL user's extra data
+ */
+class issl_session : public classbase
+{
+public:
+ gnutls_session_t sess;
+ issl_status status;
+ std::string outbuf;
+ int inbufoffset;
+ char* inbuf;
+ int fd;
+};
+
+class ModuleSSLGnuTLS : public Module
+{
+
+ ConfigReader* Conf;
+
+ char* dummy;
+
+ std::vector<int> listenports;
+
+ int inbufsize;
+ issl_session sessions[MAX_DESCRIPTORS];
+
+ gnutls_certificate_credentials x509_cred;
+ gnutls_dh_params dh_params;
+
+ std::string keyfile;
+ std::string certfile;
+ std::string cafile;
+ std::string crlfile;
+ std::string sslports;
+ int dh_bits;
+
+ int clientactive;
+
+ public:
+
+ ModuleSSLGnuTLS(InspIRCd* Me)
+ : Module(Me)
+ {
+ ServerInstance->PublishInterface("InspSocketHook", this);
+
+ // Not rehashable...because I cba to reduce all the sizes of existing buffers.
+ inbufsize = ServerInstance->Config->NetBufferSize;
+
+ gnutls_global_init(); // This must be called once in the program
+
+ if(gnutls_certificate_allocate_credentials(&x509_cred) != 0)
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials");
+
+ // Guessing return meaning
+ if(gnutls_dh_params_init(&dh_params) < 0)
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters");
+
+ // Needs the flag as it ignores a plain /rehash
+ OnRehash(NULL,"ssl");
+
+ // Void return, guess we assume success
+ gnutls_certificate_set_dh_params(x509_cred, dh_params);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ if(param != "ssl")
+ return;
+
+ Conf = new ConfigReader(ServerInstance);
+
+ for(unsigned int i = 0; i < listenports.size(); i++)
+ {
+ ServerInstance->Config->DelIOHook(listenports[i]);
+ }
+
+ listenports.clear();
+ clientactive = 0;
+ sslports.clear();
+
+ for(int i = 0; i < Conf->Enumerate("bind"); i++)
+ {
+ // For each <bind> tag
+ std::string x = Conf->ReadValue("bind", "type", i);
+ if(((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "gnutls"))
+ {
+ // Get the port we're meant to be listening on with SSL
+ std::string port = Conf->ReadValue("bind", "port", i);
+ irc::portparser portrange(port, false);
+ long portno = -1;
+ while ((portno = portrange.GetToken()))
+ {
+ clientactive++;
+ try
+ {
+ if (ServerInstance->Config->AddIOHook(portno, this))
+ {
+ listenports.push_back(portno);
+ for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
+ if (ServerInstance->Config->ports[i]->GetPort() == portno)
+ ServerInstance->Config->ports[i]->SetDescription("ssl");
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %d", portno);
+ sslports.append("*:").append(ConvToStr(portno)).append(";");
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno);
+ }
+ }
+ catch (ModuleException &e)
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have an other SSL or similar module loaded?", portno, e.GetReason());
+ }
+ }
+ }
+ }
+
+ std::string confdir(ServerInstance->ConfigFileName);
+ // +1 so we the path ends with a /
+ confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
+
+ cafile = Conf->ReadValue("gnutls", "cafile", 0);
+ crlfile = Conf->ReadValue("gnutls", "crlfile", 0);
+ certfile = Conf->ReadValue("gnutls", "certfile", 0);
+ keyfile = Conf->ReadValue("gnutls", "keyfile", 0);
+ dh_bits = Conf->ReadInteger("gnutls", "dhbits", 0, false);
+
+ // Set all the default values needed.
+ if (cafile.empty())
+ cafile = "ca.pem";
+
+ if (crlfile.empty())
+ crlfile = "crl.pem";
+
+ if (certfile.empty())
+ certfile = "cert.pem";
+
+ if (keyfile.empty())
+ keyfile = "key.pem";
+
+ if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
+ dh_bits = 1024;
+
+ // Prepend relative paths with the path to the config directory.
+ if(cafile[0] != '/')
+ cafile = confdir + cafile;
+
+ if(crlfile[0] != '/')
+ crlfile = confdir + crlfile;
+
+ if(certfile[0] != '/')
+ certfile = confdir + certfile;
+
+ if(keyfile[0] != '/')
+ keyfile = confdir + keyfile;
+
+ int ret;
+
+ if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
+
+ if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
+
+ if((ret = gnutls_certificate_set_x509_key_file (x509_cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
+ {
+ // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
+ throw ModuleException("Unable to load GnuTLS server certificate: " + std::string(gnutls_strerror(ret)));
+ }
+
+ // This may be on a large (once a day or week) timer eventually.
+ GenerateDHParams();
+
+ DELETE(Conf);
+ }
+
+ void GenerateDHParams()
+ {
+ // 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.
+
+ int ret;
+
+ if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
+ ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
+ }
+
+ virtual ~ModuleSSLGnuTLS()
+ {
+ gnutls_dh_params_deinit(dh_params);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+
+ if(user->GetExt("ssl", dummy) && isin(user->GetPort(), listenports))
+ {
+ // 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->GlobalCulls.AddItem(user, "SSL module unloading");
+ }
+ if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports))
+ {
+ ssl_cert* tofree;
+ user->GetExt("ssl_cert", tofree);
+ delete tofree;
+ user->Shrink("ssl_cert");
+ }
+ }
+ }
+
+ virtual void OnUnloadModule(Module* mod, const std::string &name)
+ {
+ if(mod == this)
+ {
+ for(unsigned int i = 0; i < listenports.size(); i++)
+ {
+ ServerInstance->Config->DelIOHook(listenports[i]);
+ for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
+ if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
+ ServerInstance->Config->ports[j]->SetDescription("plaintext");
+ }
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_On005Numeric] = List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1;
+ List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" SSL=" + sslports);
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ ISHRequest* ISR = (ISHRequest*)request;
+ if (strcmp("IS_NAME", request->GetId()) == 0)
+ {
+ return "gnutls";
+ }
+ else if (strcmp("IS_HOOK", request->GetId()) == 0)
+ {
+ char* ret = "OK";
+ try
+ {
+ ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
+ }
+ catch (ModuleException &e)
+ {
+ return NULL;
+ }
+ return ret;
+ }
+ else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
+ {
+ return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
+ }
+ else if (strcmp("IS_HSDONE", request->GetId()) == 0)
+ {
+ if (ISR->Sock->GetFd() < 0)
+ return (char*)"OK";
+
+ issl_session* session = &sessions[ISR->Sock->GetFd()];
+ return (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) ? NULL : (char*)"OK";
+ }
+ else if (strcmp("IS_ATTACH", request->GetId()) == 0)
+ {
+ if (ISR->Sock->GetFd() > -1)
+ {
+ issl_session* session = &sessions[ISR->Sock->GetFd()];
+ if (session->sess)
+ {
+ if ((Extensible*)ServerInstance->FindDescriptor(ISR->Sock->GetFd()) == (Extensible*)(ISR->Sock))
+ {
+ VerifyCertificate(session, (InspSocket*)ISR->Sock);
+ return "OK";
+ }
+ }
+ }
+ }
+ return NULL;
+ }
+
+
+ virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
+ {
+ issl_session* session = &sessions[fd];
+
+ session->fd = fd;
+ session->inbuf = new char[inbufsize];
+ session->inbufoffset = 0;
+
+ gnutls_init(&session->sess, GNUTLS_SERVER);
+
+ gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.
+ gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_dh_set_prime_bits(session->sess, dh_bits);
+
+ /* This is an experimental change to avoid a warning on 64bit systems about casting between integer and pointer of different sizes
+ * This needs testing, but it's easy enough to rollback if need be
+ * Old: gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.
+ * New: gnutls_transport_set_ptr(session->sess, &fd); // Give gnutls the fd for the socket.
+ *
+ * With testing this seems to...not work :/
+ */
+
+ gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.
+
+ gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+
+ Handshake(session);
+ }
+
+ virtual void OnRawSocketConnect(int fd)
+ {
+ issl_session* session = &sessions[fd];
+
+ session->fd = fd;
+ session->inbuf = new char[inbufsize];
+ session->inbufoffset = 0;
+
+ gnutls_init(&session->sess, GNUTLS_CLIENT);
+
+ gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.
+ gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_dh_set_prime_bits(session->sess, dh_bits);
+ gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.
+
+ Handshake(session);
+ }
+
+ virtual void OnRawSocketClose(int fd)
+ {
+ CloseSession(&sessions[fd]);
+
+ EventHandler* user = ServerInstance->SE->GetRef(fd);
+
+ if ((user) && (user->GetExt("ssl_cert", dummy)))
+ {
+ ssl_cert* tofree;
+ user->GetExt("ssl_cert", tofree);
+ delete tofree;
+ user->Shrink("ssl_cert");
+ }
+ }
+
+ virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
+ {
+ issl_session* session = &sessions[fd];
+
+ if (!session->sess)
+ {
+ readresult = 0;
+ CloseSession(session);
+ return 1;
+ }
+
+ if (session->status == ISSL_HANDSHAKING_READ)
+ {
+ // The handshake isn't finished, try to finish it.
+
+ if(!Handshake(session))
+ {
+ // Couldn't resume handshake.
+ return -1;
+ }
+ }
+ else if (session->status == ISSL_HANDSHAKING_WRITE)
+ {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
+
+ if (session->status == ISSL_HANDSHAKEN)
+ {
+ // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
+ // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
+ int ret = gnutls_record_recv(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
+
+ if (ret == 0)
+ {
+ // Client closed connection.
+ readresult = 0;
+ CloseSession(session);
+ return 1;
+ }
+ else if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+ {
+ errno = EAGAIN;
+ return -1;
+ }
+ else
+ {
+ readresult = 0;
+ CloseSession(session);
+ }
+ }
+ else
+ {
+ // Read successfully 'ret' bytes into inbuf + inbufoffset
+ // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
+ // 'buffer' is 'count' long
+
+ unsigned int length = ret + session->inbufoffset;
+
+ if(count <= length)
+ {
+ memcpy(buffer, session->inbuf, count);
+ // Move the stuff left in inbuf to the beginning of it
+ memcpy(session->inbuf, session->inbuf + count, (length - count));
+ // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
+ session->inbufoffset = length - count;
+ // Insp uses readresult as the count of how much data there is in buffer, so:
+ readresult = count;
+ }
+ else
+ {
+ // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
+ memcpy(buffer, session->inbuf, length);
+ // Zero the offset, as there's nothing there..
+ session->inbufoffset = 0;
+ // As above
+ readresult = length;
+ }
+ }
+ }
+ else if(session->status == ISSL_CLOSING)
+ readresult = 0;
+
+ return 1;
+ }
+
+ virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
+ {
+ if (!count)
+ return 0;
+
+ issl_session* session = &sessions[fd];
+ const char* sendbuffer = buffer;
+
+ if (!session->sess)
+ {
+ ServerInstance->Log(DEBUG,"No session");
+ CloseSession(session);
+ return 1;
+ }
+
+ session->outbuf.append(sendbuffer, count);
+ sendbuffer = session->outbuf.c_str();
+ count = session->outbuf.size();
+
+ if (session->status == ISSL_HANDSHAKING_WRITE)
+ {
+ // The handshake isn't finished, try to finish it.
+ ServerInstance->Log(DEBUG,"Finishing handshake");
+ Handshake(session);
+ errno = EAGAIN;
+ return -1;
+ }
+
+ int ret = 0;
+
+ if (session->status == ISSL_HANDSHAKEN)
+ {
+ ServerInstance->Log(DEBUG,"Send record");
+ ret = gnutls_record_send(session->sess, sendbuffer, count);
+ ServerInstance->Log(DEBUG,"Return: %d", ret);
+
+ if (ret == 0)
+ {
+ CloseSession(session);
+ }
+ else if (ret < 0)
+ {
+ if(ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED)
+ {
+ ServerInstance->Log(DEBUG,"Not egain or interrupt, close session");
+ CloseSession(session);
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG,"Again please");
+ errno = EAGAIN;
+ return -1;
+ }
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG,"Trim buffer");
+ session->outbuf = session->outbuf.substr(ret);
+ }
+ }
+
+ /* Who's smart idea was it to return 1 when we havent written anything?
+ * This fucks the buffer up in InspSocket :p
+ */
+ return ret < 1 ? 0 : ret;
+ }
+
+ // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
+ virtual void OnWhois(userrec* source, userrec* dest)
+ {
+ if (!clientactive)
+ return;
+
+ // Bugfix, only send this numeric for *our* SSL users
+ if(dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports)))
+ {
+ ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
+ }
+ }
+
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
+ {
+ // check if the linking module wants to know about OUR metadata
+ if(extname == "ssl")
+ {
+ // check if this user has an swhois field to send
+ if(user->GetExt(extname, dummy))
+ {
+ // call this function in the linking module, let it format the data how it
+ // sees fit, and send it on its way. We dont need or want to know how.
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
+ }
+ }
+ }
+
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ // check if its our metadata key, and its associated with a user
+ if ((target_type == TYPE_USER) && (extname == "ssl"))
+ {
+ userrec* dest = (userrec*)target;
+ // if they dont already have an ssl flag, accept the remote server's
+ if (!dest->GetExt(extname, dummy))
+ {
+ dest->Extend(extname, "ON");
+ }
+ }
+ }
+
+ bool Handshake(issl_session* session)
+ {
+ int ret = gnutls_handshake(session->sess);
+
+ if (ret < 0)
+ {
+ if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+ {
+ // Handshake needs resuming later, read() or write() would have blocked.
+
+ if(gnutls_record_get_direction(session->sess) == 0)
+ {
+ // gnutls_handshake() wants to read() again.
+ session->status = ISSL_HANDSHAKING_READ;
+ }
+ else
+ {
+ // gnutls_handshake() wants to write() again.
+ session->status = ISSL_HANDSHAKING_WRITE;
+ MakePollWrite(session);
+ }
+ }
+ else
+ {
+ // Handshake failed.
+ CloseSession(session);
+ session->status = ISSL_CLOSING;
+ }
+
+ return false;
+ }
+ else
+ {
+ // Handshake complete.
+ // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
+ userrec* extendme = ServerInstance->FindDescriptor(session->fd);
+ if (extendme)
+ {
+ if (!extendme->GetExt("ssl", dummy))
+ extendme->Extend("ssl", "ON");
+ }
+
+ // Change the seesion state
+ session->status = ISSL_HANDSHAKEN;
+
+ // Finish writing, if any left
+ MakePollWrite(session);
+
+ return true;
+ }
+ }
+
+ virtual void OnPostConnect(userrec* user)
+ {
+ // This occurs AFTER OnUserConnect so we can be sure the
+ // protocol module has propogated the NICK message.
+ if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
+ {
+ // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
+ std::deque<std::string>* metadata = new std::deque<std::string>;
+ metadata->push_back(user->nick);
+ metadata->push_back("ssl"); // The metadata id
+ metadata->push_back("ON"); // The value to send
+ Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");
+ event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up.
+ DELETE(event);
+ DELETE(metadata);
+
+ VerifyCertificate(&sessions[user->GetFd()],user);
+ if (sessions[user->GetFd()].sess)
+ {
+ std::string cipher = gnutls_kx_get_name(gnutls_kx_get(sessions[user->GetFd()].sess));
+ cipher.append("-").append(gnutls_cipher_get_name(gnutls_cipher_get(sessions[user->GetFd()].sess))).append("-");
+ cipher.append(gnutls_mac_get_name(gnutls_mac_get(sessions[user->GetFd()].sess)));
+ user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, cipher.c_str());
+ }
+ }
+ }
+
+ void MakePollWrite(issl_session* session)
+ {
+ OnRawSocketWrite(session->fd, NULL, 0);
+ }
+
+ void CloseSession(issl_session* session)
+ {
+ if(session->sess)
+ {
+ gnutls_bye(session->sess, GNUTLS_SHUT_WR);
+ gnutls_deinit(session->sess);
+ }
+
+ if(session->inbuf)
+ {
+ delete[] session->inbuf;
+ }
+
+ session->outbuf.clear();
+ session->inbuf = NULL;
+ session->sess = NULL;
+ session->status = ISSL_NONE;
+ }
+
+ void VerifyCertificate(issl_session* session, Extensible* user)
+ {
+ if (!session->sess || !user)
+ return;
+
+ 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;
+
+ user->Extend("ssl_cert",certinfo);
+
+ /* This verification function uses the trusted CAs in the credentials
+ * structure. So you must have installed one or more CA certificates.
+ */
+ ret = gnutls_certificate_verify_peers2(session->sess, &status);
+
+ if (ret < 0)
+ {
+ certinfo->data.insert(std::make_pair("error",std::string(gnutls_strerror(ret))));
+ return;
+ }
+
+ if (status & GNUTLS_CERT_INVALID)
+ {
+ certinfo->data.insert(std::make_pair("invalid",ConvToStr(1)));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("invalid",ConvToStr(0)));
+ }
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ {
+ certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));
+ }
+ if (status & GNUTLS_CERT_REVOKED)
+ {
+ certinfo->data.insert(std::make_pair("revoked",ConvToStr(1)));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("revoked",ConvToStr(0)));
+ }
+ if (status & GNUTLS_CERT_SIGNER_NOT_CA)
+ {
+ certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));
+ }
+
+ /* 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->data.insert(std::make_pair("error","No X509 keys sent"));
+ return;
+ }
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
+ {
+ certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));
+ return;
+ }
+
+ cert_list_size = 0;
+ cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
+ if (cert_list == NULL)
+ {
+ certinfo->data.insert(std::make_pair("error","No certificate was found"));
+ return;
+ }
+
+ /* 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->data.insert(std::make_pair("error",gnutls_strerror(ret)));
+ return;
+ }
+
+ gnutls_x509_crt_get_dn(cert, name, &name_size);
+
+ certinfo->data.insert(std::make_pair("dn",name));
+
+ gnutls_x509_crt_get_issuer_dn(cert, name, &name_size);
+
+ certinfo->data.insert(std::make_pair("issuer",name));
+
+ if ((ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, digest, &digest_size)) < 0)
+ {
+ certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("fingerprint",irc::hex(digest, digest_size)));
+ }
+
+ /* Beware here we do not check for errors.
+ */
+ if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0)))
+ {
+ certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
+ }
+
+ gnutls_x509_crt_deinit(cert);
+
+ return;
+ }
+
+};
+
+MODULE_INIT(ModuleSSLGnuTLS);
+
diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp
index 43dc43aea..ffd9d4032 100644
--- a/src/modules/extra/m_ssl_openssl.cpp
+++ b/src/modules/extra/m_ssl_openssl.cpp
@@ -1 +1,901 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <openssl/ssl.h> #include <openssl/err.h> #ifdef WINDOWS #include <openssl/applink.c> #endif #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "socket.h" #include "hashcomp.h" #include "transport.h" #ifdef WINDOWS #pragma comment(lib, "libeay32MTd") #pragma comment(lib, "ssleay32MTd") #undef MAX_DESCRIPTORS #define MAX_DESCRIPTORS 10000 #endif /* $ModDesc: Provides SSL support for clients */ /* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */ /* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */ /* $ModDep: transport.h */ enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN }; enum issl_io_status { ISSL_WRITE, ISSL_READ }; static bool SelfSigned = false; bool isin(int port, const std::vector<int> &portlist) { for(unsigned int i = 0; i < portlist.size(); i++) if(portlist[i] == port) return true; return false; } char* get_error() { return ERR_error_string(ERR_get_error(), NULL); } static int error_callback(const char *str, size_t len, void *u); /** Represents an SSL user's extra data */ class issl_session : public classbase { public: SSL* sess; issl_status status; issl_io_status rstat; issl_io_status wstat; unsigned int inbufoffset; char* inbuf; // Buffer OpenSSL reads into. std::string outbuf; // Buffer for outgoing data that OpenSSL will not take. int fd; bool outbound; issl_session() { outbound = false; rstat = ISSL_READ; wstat = ISSL_WRITE; } }; 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 ModuleSSLOpenSSL : public Module { ConfigReader* Conf; std::vector<int> listenports; int inbufsize; issl_session sessions[MAX_DESCRIPTORS]; SSL_CTX* ctx; SSL_CTX* clictx; char* dummy; char cipher[MAXBUF]; std::string keyfile; std::string certfile; std::string cafile; // std::string crlfile; std::string dhfile; std::string sslports; int clientactive; public: InspIRCd* PublicInstance; ModuleSSLOpenSSL(InspIRCd* Me) : Module(Me), PublicInstance(Me) { ServerInstance->PublishInterface("InspSocketHook", this); // Not rehashable...because I cba to reduce all the sizes of existing buffers. inbufsize = ServerInstance->Config->NetBufferSize; /* Global SSL library initialization*/ SSL_library_init(); SSL_load_error_strings(); /* Build our SSL contexts: * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK. */ ctx = SSL_CTX_new( SSLv23_server_method() ); clictx = SSL_CTX_new( SSLv23_client_method() ); 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); // Needs the flag as it ignores a plain /rehash OnRehash(NULL,"ssl"); } virtual void OnRehash(userrec* user, const std::string &param) { if (param != "ssl") return; Conf = new ConfigReader(ServerInstance); for (unsigned int i = 0; i < listenports.size(); i++) { ServerInstance->Config->DelIOHook(listenports[i]); } listenports.clear(); clientactive = 0; sslports.clear(); for (int i = 0; i < Conf->Enumerate("bind"); i++) { // For each <bind> tag std::string x = Conf->ReadValue("bind", "type", i); if (((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "openssl")) { // Get the port we're meant to be listening on with SSL std::string port = Conf->ReadValue("bind", "port", i); irc::portparser portrange(port, false); long portno = -1; while ((portno = portrange.GetToken())) { clientactive++; try { if (ServerInstance->Config->AddIOHook(portno, this)) { listenports.push_back(portno); for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) if (ServerInstance->Config->ports[i]->GetPort() == portno) ServerInstance->Config->ports[i]->SetDescription("ssl"); ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %d", portno); sslports.append("*:").append(ConvToStr(portno)).append(";"); } else { ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno); } } catch (ModuleException &e) { ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another SSL or similar module loaded?", portno, e.GetReason()); } } } } if (!sslports.empty()) sslports.erase(sslports.end() - 1); std::string confdir(ServerInstance->ConfigFileName); // +1 so we the path ends with a / confdir = confdir.substr(0, confdir.find_last_of('/') + 1); cafile = Conf->ReadValue("openssl", "cafile", 0); certfile = Conf->ReadValue("openssl", "certfile", 0); keyfile = Conf->ReadValue("openssl", "keyfile", 0); dhfile = Conf->ReadValue("openssl", "dhfile", 0); // Set all the default values needed. if (cafile.empty()) cafile = "ca.pem"; if (certfile.empty()) certfile = "cert.pem"; if (keyfile.empty()) keyfile = "key.pem"; if (dhfile.empty()) dhfile = "dhparams.pem"; // Prepend relative paths with the path to the config directory. if (cafile[0] != '/') cafile = confdir + cafile; if (certfile[0] != '/') certfile = confdir + certfile; if (keyfile[0] != '/') keyfile = confdir + keyfile; if (dhfile[0] != '/') dhfile = confdir + dhfile; /* Load our keys and certificates * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck. */ if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str()))) { ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno)); ERR_print_errors_cb(error_callback, this); } 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))) { ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno)); ERR_print_errors_cb(error_callback, this); } /* Load the CAs we trust*/ if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0))) { ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno)); ERR_print_errors_cb(error_callback, this); } FILE* dhpfile = fopen(dhfile.c_str(), "r"); DH* ret; if (dhpfile == NULL) { ServerInstance->Log(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)); } else { ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL); if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0)) { ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str()); ERR_print_errors_cb(error_callback, this); } } fclose(dhpfile); DELETE(Conf); } virtual void On005Numeric(std::string &output) { output.append(" SSL=" + sslports); } virtual ~ModuleSSLOpenSSL() { SSL_CTX_free(ctx); SSL_CTX_free(clictx); } virtual void OnCleanup(int target_type, void* item) { if (target_type == TYPE_USER) { userrec* user = (userrec*)item; if (user->GetExt("ssl", dummy) && IS_LOCAL(user) && isin(user->GetPort(), listenports)) { // 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->GlobalCulls.AddItem(user, "SSL module unloading"); } if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports)) { ssl_cert* tofree; user->GetExt("ssl_cert", tofree); delete tofree; user->Shrink("ssl_cert"); } } } virtual void OnUnloadModule(Module* mod, const std::string &name) { if (mod == this) { for(unsigned int i = 0; i < listenports.size(); i++) { ServerInstance->Config->DelIOHook(listenports[i]); for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++) if (ServerInstance->Config->ports[j]->GetPort() == listenports[i]) ServerInstance->Config->ports[j]->SetDescription("plaintext"); } } } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = List[I_On005Numeric] = 1; List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1; } virtual char* OnRequest(Request* request) { ISHRequest* ISR = (ISHRequest*)request; if (strcmp("IS_NAME", request->GetId()) == 0) { return "openssl"; } else if (strcmp("IS_HOOK", request->GetId()) == 0) { char* ret = "OK"; try { ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; } catch (ModuleException &e) { return NULL; } return ret; } else if (strcmp("IS_UNHOOK", request->GetId()) == 0) { return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; } else if (strcmp("IS_HSDONE", request->GetId()) == 0) { ServerInstance->Log(DEBUG,"Module checking if handshake is done"); if (ISR->Sock->GetFd() < 0) return (char*)"OK"; issl_session* session = &sessions[ISR->Sock->GetFd()]; return (session->status == ISSL_HANDSHAKING) ? NULL : (char*)"OK"; } else if (strcmp("IS_ATTACH", request->GetId()) == 0) { issl_session* session = &sessions[ISR->Sock->GetFd()]; if (session->sess) { VerifyCertificate(session, (InspSocket*)ISR->Sock); return "OK"; } } return NULL; } virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport) { issl_session* session = &sessions[fd]; session->fd = fd; session->inbuf = new char[inbufsize]; session->inbufoffset = 0; session->sess = SSL_new(ctx); session->status = ISSL_NONE; session->outbound = false; if (session->sess == NULL) return; if (SSL_set_fd(session->sess, fd) == 0) { ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd); return; } Handshake(session); } virtual void OnRawSocketConnect(int fd) { ServerInstance->Log(DEBUG,"OnRawSocketConnect connecting"); issl_session* session = &sessions[fd]; session->fd = fd; session->inbuf = new char[inbufsize]; session->inbufoffset = 0; session->sess = SSL_new(clictx); session->status = ISSL_NONE; session->outbound = true; if (session->sess == NULL) return; if (SSL_set_fd(session->sess, fd) == 0) { ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd); return; } Handshake(session); ServerInstance->Log(DEBUG,"Exiting OnRawSocketConnect"); } virtual void OnRawSocketClose(int fd) { CloseSession(&sessions[fd]); EventHandler* user = ServerInstance->SE->GetRef(fd); if ((user) && (user->GetExt("ssl_cert", dummy))) { ssl_cert* tofree; user->GetExt("ssl_cert", tofree); delete tofree; user->Shrink("ssl_cert"); } } virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { issl_session* session = &sessions[fd]; ServerInstance->Log(DEBUG,"OnRawSocketRead"); if (!session->sess) { ServerInstance->Log(DEBUG,"OnRawSocketRead has no session"); readresult = 0; CloseSession(session); return 1; } if (session->status == ISSL_HANDSHAKING) { if (session->rstat == ISSL_READ || session->wstat == ISSL_READ) { ServerInstance->Log(DEBUG,"Resume handshake in read"); // The handshake isn't finished and it wants to read, try to finish it. if (!Handshake(session)) { ServerInstance->Log(DEBUG,"Cant resume handshake in read"); // Couldn't resume handshake. return -1; } } else { errno = EAGAIN; return -1; } } // If we resumed the handshake then session->status will be ISSL_OPEN if (session->status == ISSL_OPEN) { if (session->wstat == ISSL_READ) { if(DoWrite(session) == 0) return 0; } if (session->rstat == ISSL_READ) { int ret = DoRead(session); if (ret > 0) { if (count <= session->inbufoffset) { memcpy(buffer, session->inbuf, count); // Move the stuff left in inbuf to the beginning of it memcpy(session->inbuf, session->inbuf + count, (session->inbufoffset - count)); // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp. session->inbufoffset -= count; // Insp uses readresult as the count of how much data there is in buffer, so: readresult = count; } else { // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing. memcpy(buffer, session->inbuf, session->inbufoffset); readresult = session->inbufoffset; // Zero the offset, as there's nothing there.. session->inbufoffset = 0; } return 1; } else { return ret; } } } return -1; } virtual int OnRawSocketWrite(int fd, const char* buffer, int count) { issl_session* session = &sessions[fd]; if (!session->sess) { ServerInstance->Log(DEBUG,"Close session missing sess"); CloseSession(session); return -1; } session->outbuf.append(buffer, count); if (session->status == ISSL_HANDSHAKING) { // The handshake isn't finished, try to finish it. if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE) { ServerInstance->Log(DEBUG,"Handshake resume"); Handshake(session); } } if (session->status == ISSL_OPEN) { if (session->rstat == ISSL_WRITE) { ServerInstance->Log(DEBUG,"DoRead"); DoRead(session); } if (session->wstat == ISSL_WRITE) { ServerInstance->Log(DEBUG,"DoWrite"); return DoWrite(session); } } return 1; } int DoWrite(issl_session* session) { if (!session->outbuf.size()) return -1; int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size()); if (ret == 0) { ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_write"); CloseSession(session); return 0; } else if (ret < 0) { int err = SSL_get_error(session->sess, ret); if (err == SSL_ERROR_WANT_WRITE) { session->wstat = ISSL_WRITE; return -1; } else if (err == SSL_ERROR_WANT_READ) { session->wstat = ISSL_READ; return -1; } else { ServerInstance->Log(DEBUG,"Close due to returned -1 in SSL_Write"); CloseSession(session); return 0; } } else { session->outbuf = session->outbuf.substr(ret); return ret; } } int DoRead(issl_session* session) { // Is this right? Not sure if the unencrypted data is garaunteed to be the same length. // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet. ServerInstance->Log(DEBUG,"DoRead"); int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset); if (ret == 0) { // Client closed connection. ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_read"); CloseSession(session); return 0; } else if (ret < 0) { int err = SSL_get_error(session->sess, ret); if (err == SSL_ERROR_WANT_READ) { session->rstat = ISSL_READ; ServerInstance->Log(DEBUG,"Setting want_read"); return -1; } else if (err == SSL_ERROR_WANT_WRITE) { session->rstat = ISSL_WRITE; ServerInstance->Log(DEBUG,"Setting want_write"); return -1; } else { ServerInstance->Log(DEBUG,"Closed due to returned -1 in SSL_Read"); CloseSession(session); return 0; } } else { // Read successfully 'ret' bytes into inbuf + inbufoffset // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf' // 'buffer' is 'count' long session->inbufoffset += ret; return ret; } } // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection virtual void OnWhois(userrec* source, userrec* dest) { if (!clientactive) return; // Bugfix, only send this numeric for *our* SSL users if (dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports))) { ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick); } } virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) { // check if the linking module wants to know about OUR metadata if (extname == "ssl") { // check if this user has an swhois field to send if(user->GetExt(extname, dummy)) { // call this function in the linking module, let it format the data how it // sees fit, and send it on its way. We dont need or want to know how. proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON"); } } } virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { // check if its our metadata key, and its associated with a user if ((target_type == TYPE_USER) && (extname == "ssl")) { userrec* dest = (userrec*)target; // if they dont already have an ssl flag, accept the remote server's if (!dest->GetExt(extname, dummy)) { dest->Extend(extname, "ON"); } } } bool Handshake(issl_session* session) { ServerInstance->Log(DEBUG,"Handshake"); int ret; if (session->outbound) { ServerInstance->Log(DEBUG,"SSL_connect"); ret = SSL_connect(session->sess); } else ret = SSL_accept(session->sess); if (ret < 0) { int err = SSL_get_error(session->sess, ret); if (err == SSL_ERROR_WANT_READ) { ServerInstance->Log(DEBUG,"Want read, handshaking"); session->rstat = ISSL_READ; session->status = ISSL_HANDSHAKING; return true; } else if (err == SSL_ERROR_WANT_WRITE) { ServerInstance->Log(DEBUG,"Want write, handshaking"); session->wstat = ISSL_WRITE; session->status = ISSL_HANDSHAKING; MakePollWrite(session); return true; } else { ServerInstance->Log(DEBUG,"Handshake failed"); CloseSession(session); } return false; } else if (ret > 0) { // Handshake complete. // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater. userrec* u = ServerInstance->FindDescriptor(session->fd); if (u) { if (!u->GetExt("ssl", dummy)) u->Extend("ssl", "ON"); } session->status = ISSL_OPEN; MakePollWrite(session); return true; } else if (ret == 0) { int ssl_err = SSL_get_error(session->sess, ret); char buf[1024]; ERR_print_errors_fp(stderr); ServerInstance->Log(DEBUG,"Handshake fail 2: %d: %s", ssl_err, ERR_error_string(ssl_err,buf)); CloseSession(session); return true; } return true; } virtual void OnPostConnect(userrec* user) { // This occurs AFTER OnUserConnect so we can be sure the // protocol module has propogated the NICK message. if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user))) { // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW. std::deque<std::string>* metadata = new std::deque<std::string>; metadata->push_back(user->nick); metadata->push_back("ssl"); // The metadata id metadata->push_back("ON"); // The value to send Event* event = new Event((char*)metadata,(Module*)this,"send_metadata"); event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up. DELETE(event); DELETE(metadata); VerifyCertificate(&sessions[user->GetFd()], user); if (sessions[user->GetFd()].sess) user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, SSL_get_cipher(sessions[user->GetFd()].sess)); } } void MakePollWrite(issl_session* session) { OnRawSocketWrite(session->fd, NULL, 0); //EventHandler* eh = ServerInstance->FindDescriptor(session->fd); //if (eh) // ServerInstance->SE->WantWrite(eh); } void CloseSession(issl_session* session) { if (session->sess) { SSL_shutdown(session->sess); SSL_free(session->sess); } if (session->inbuf) { delete[] session->inbuf; } session->outbuf.clear(); session->inbuf = NULL; session->sess = NULL; session->status = ISSL_NONE; } void VerifyCertificate(issl_session* session, Extensible* user) { if (!session->sess || !user) return; X509* cert; ssl_cert* certinfo = new ssl_cert; unsigned int n; unsigned char md[EVP_MAX_MD_SIZE]; const EVP_MD *digest = EVP_md5(); user->Extend("ssl_cert",certinfo); cert = SSL_get_peer_certificate((SSL*)session->sess); if (!cert) { certinfo->data.insert(std::make_pair("error","Could not get peer certificate: "+std::string(get_error()))); return; } certinfo->data.insert(std::make_pair("invalid", SSL_get_verify_result(session->sess) != X509_V_OK ? ConvToStr(1) : ConvToStr(0))); if (SelfSigned) { certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0))); certinfo->data.insert(std::make_pair("trusted",ConvToStr(1))); } else { certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1))); certinfo->data.insert(std::make_pair("trusted",ConvToStr(0))); } certinfo->data.insert(std::make_pair("dn",std::string(X509_NAME_oneline(X509_get_subject_name(cert),0,0)))); certinfo->data.insert(std::make_pair("issuer",std::string(X509_NAME_oneline(X509_get_issuer_name(cert),0,0)))); if (!X509_digest(cert, digest, md, &n)) { certinfo->data.insert(std::make_pair("error","Out of memory generating fingerprint")); } else { certinfo->data.insert(std::make_pair("fingerprint",irc::hex(md, n))); } if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), time(NULL)) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), time(NULL)) == 0)) { certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate")); } X509_free(cert); } }; static int error_callback(const char *str, size_t len, void *u) { ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u; mssl->PublicInstance->Log(DEFAULT, "SSL error: " + std::string(str, len - 1)); return 0; } MODULE_INIT(ModuleSSLOpenSSL); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#ifdef WINDOWS
+#include <openssl/applink.c>
+#endif
+
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+#include "socket.h"
+#include "hashcomp.h"
+
+#include "transport.h"
+
+#ifdef WINDOWS
+#pragma comment(lib, "libeay32MTd")
+#pragma comment(lib, "ssleay32MTd")
+#undef MAX_DESCRIPTORS
+#define MAX_DESCRIPTORS 10000
+#endif
+
+/* $ModDesc: Provides SSL support for clients */
+/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
+/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
+/* $ModDep: transport.h */
+
+enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
+enum issl_io_status { ISSL_WRITE, ISSL_READ };
+
+static bool SelfSigned = false;
+
+bool isin(int port, const std::vector<int> &portlist)
+{
+ for(unsigned int i = 0; i < portlist.size(); i++)
+ if(portlist[i] == port)
+ return true;
+
+ return false;
+}
+
+char* get_error()
+{
+ return ERR_error_string(ERR_get_error(), NULL);
+}
+
+static int error_callback(const char *str, size_t len, void *u);
+
+/** Represents an SSL user's extra data
+ */
+class issl_session : public classbase
+{
+public:
+ SSL* sess;
+ issl_status status;
+ issl_io_status rstat;
+ issl_io_status wstat;
+
+ unsigned int inbufoffset;
+ char* inbuf; // Buffer OpenSSL reads into.
+ std::string outbuf; // Buffer for outgoing data that OpenSSL will not take.
+ int fd;
+ bool outbound;
+
+ issl_session()
+ {
+ outbound = false;
+ rstat = ISSL_READ;
+ wstat = ISSL_WRITE;
+ }
+};
+
+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 ModuleSSLOpenSSL : public Module
+{
+
+ ConfigReader* Conf;
+
+ std::vector<int> listenports;
+
+ int inbufsize;
+ issl_session sessions[MAX_DESCRIPTORS];
+
+ SSL_CTX* ctx;
+ SSL_CTX* clictx;
+
+ char* dummy;
+ char cipher[MAXBUF];
+
+ std::string keyfile;
+ std::string certfile;
+ std::string cafile;
+ // std::string crlfile;
+ std::string dhfile;
+ std::string sslports;
+
+ int clientactive;
+
+ public:
+
+ InspIRCd* PublicInstance;
+
+ ModuleSSLOpenSSL(InspIRCd* Me)
+ : Module(Me), PublicInstance(Me)
+ {
+ ServerInstance->PublishInterface("InspSocketHook", this);
+
+ // Not rehashable...because I cba to reduce all the sizes of existing buffers.
+ inbufsize = ServerInstance->Config->NetBufferSize;
+
+ /* Global SSL library initialization*/
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ /* Build our SSL contexts:
+ * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
+ */
+ ctx = SSL_CTX_new( SSLv23_server_method() );
+ clictx = SSL_CTX_new( SSLv23_client_method() );
+
+ 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);
+
+ // Needs the flag as it ignores a plain /rehash
+ OnRehash(NULL,"ssl");
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ if (param != "ssl")
+ return;
+
+ Conf = new ConfigReader(ServerInstance);
+
+ for (unsigned int i = 0; i < listenports.size(); i++)
+ {
+ ServerInstance->Config->DelIOHook(listenports[i]);
+ }
+
+ listenports.clear();
+ clientactive = 0;
+ sslports.clear();
+
+ for (int i = 0; i < Conf->Enumerate("bind"); i++)
+ {
+ // For each <bind> tag
+ std::string x = Conf->ReadValue("bind", "type", i);
+ if (((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "openssl"))
+ {
+ // Get the port we're meant to be listening on with SSL
+ std::string port = Conf->ReadValue("bind", "port", i);
+ irc::portparser portrange(port, false);
+ long portno = -1;
+ while ((portno = portrange.GetToken()))
+ {
+ clientactive++;
+ try
+ {
+ if (ServerInstance->Config->AddIOHook(portno, this))
+ {
+ listenports.push_back(portno);
+ for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
+ if (ServerInstance->Config->ports[i]->GetPort() == portno)
+ ServerInstance->Config->ports[i]->SetDescription("ssl");
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %d", portno);
+ sslports.append("*:").append(ConvToStr(portno)).append(";");
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno);
+ }
+ }
+ catch (ModuleException &e)
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another SSL or similar module loaded?", portno, e.GetReason());
+ }
+ }
+ }
+ }
+
+ if (!sslports.empty())
+ sslports.erase(sslports.end() - 1);
+
+ std::string confdir(ServerInstance->ConfigFileName);
+ // +1 so we the path ends with a /
+ confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
+
+ cafile = Conf->ReadValue("openssl", "cafile", 0);
+ certfile = Conf->ReadValue("openssl", "certfile", 0);
+ keyfile = Conf->ReadValue("openssl", "keyfile", 0);
+ dhfile = Conf->ReadValue("openssl", "dhfile", 0);
+
+ // Set all the default values needed.
+ if (cafile.empty())
+ cafile = "ca.pem";
+
+ if (certfile.empty())
+ certfile = "cert.pem";
+
+ if (keyfile.empty())
+ keyfile = "key.pem";
+
+ if (dhfile.empty())
+ dhfile = "dhparams.pem";
+
+ // Prepend relative paths with the path to the config directory.
+ if (cafile[0] != '/')
+ cafile = confdir + cafile;
+
+ if (certfile[0] != '/')
+ certfile = confdir + certfile;
+
+ if (keyfile[0] != '/')
+ keyfile = confdir + keyfile;
+
+ if (dhfile[0] != '/')
+ dhfile = confdir + dhfile;
+
+ /* Load our keys and certificates
+ * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
+ */
+ if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
+ ERR_print_errors_cb(error_callback, this);
+ }
+
+ 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)))
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
+ ERR_print_errors_cb(error_callback, this);
+ }
+
+ /* Load the CAs we trust*/
+ if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno));
+ ERR_print_errors_cb(error_callback, this);
+ }
+
+ FILE* dhpfile = fopen(dhfile.c_str(), "r");
+ DH* ret;
+
+ if (dhpfile == NULL)
+ {
+ ServerInstance->Log(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));
+ }
+ else
+ {
+ ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
+ if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
+ {
+ ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
+ ERR_print_errors_cb(error_callback, this);
+ }
+ }
+
+ fclose(dhpfile);
+
+ DELETE(Conf);
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" SSL=" + sslports);
+ }
+
+ virtual ~ModuleSSLOpenSSL()
+ {
+ SSL_CTX_free(ctx);
+ SSL_CTX_free(clictx);
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+
+ if (user->GetExt("ssl", dummy) && IS_LOCAL(user) && isin(user->GetPort(), listenports))
+ {
+ // 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->GlobalCulls.AddItem(user, "SSL module unloading");
+ }
+ if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports))
+ {
+ ssl_cert* tofree;
+ user->GetExt("ssl_cert", tofree);
+ delete tofree;
+ user->Shrink("ssl_cert");
+ }
+ }
+ }
+
+ virtual void OnUnloadModule(Module* mod, const std::string &name)
+ {
+ if (mod == this)
+ {
+ for(unsigned int i = 0; i < listenports.size(); i++)
+ {
+ ServerInstance->Config->DelIOHook(listenports[i]);
+ for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
+ if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
+ ServerInstance->Config->ports[j]->SetDescription("plaintext");
+ }
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = List[I_On005Numeric] = 1;
+ List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ ISHRequest* ISR = (ISHRequest*)request;
+ if (strcmp("IS_NAME", request->GetId()) == 0)
+ {
+ return "openssl";
+ }
+ else if (strcmp("IS_HOOK", request->GetId()) == 0)
+ {
+ char* ret = "OK";
+ try
+ {
+ ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
+ }
+ catch (ModuleException &e)
+ {
+ return NULL;
+ }
+
+ return ret;
+ }
+ else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
+ {
+ return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
+ }
+ else if (strcmp("IS_HSDONE", request->GetId()) == 0)
+ {
+ ServerInstance->Log(DEBUG,"Module checking if handshake is done");
+ if (ISR->Sock->GetFd() < 0)
+ return (char*)"OK";
+
+ issl_session* session = &sessions[ISR->Sock->GetFd()];
+ return (session->status == ISSL_HANDSHAKING) ? NULL : (char*)"OK";
+ }
+ else if (strcmp("IS_ATTACH", request->GetId()) == 0)
+ {
+ issl_session* session = &sessions[ISR->Sock->GetFd()];
+ if (session->sess)
+ {
+ VerifyCertificate(session, (InspSocket*)ISR->Sock);
+ return "OK";
+ }
+ }
+ return NULL;
+ }
+
+
+ virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
+ {
+ issl_session* session = &sessions[fd];
+
+ session->fd = fd;
+ session->inbuf = new char[inbufsize];
+ session->inbufoffset = 0;
+ session->sess = SSL_new(ctx);
+ session->status = ISSL_NONE;
+ session->outbound = false;
+
+ if (session->sess == NULL)
+ return;
+
+ if (SSL_set_fd(session->sess, fd) == 0)
+ {
+ ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
+ return;
+ }
+
+ Handshake(session);
+ }
+
+ virtual void OnRawSocketConnect(int fd)
+ {
+ ServerInstance->Log(DEBUG,"OnRawSocketConnect connecting");
+ issl_session* session = &sessions[fd];
+
+ session->fd = fd;
+ session->inbuf = new char[inbufsize];
+ session->inbufoffset = 0;
+ session->sess = SSL_new(clictx);
+ session->status = ISSL_NONE;
+ session->outbound = true;
+
+ if (session->sess == NULL)
+ return;
+
+ if (SSL_set_fd(session->sess, fd) == 0)
+ {
+ ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
+ return;
+ }
+
+ Handshake(session);
+ ServerInstance->Log(DEBUG,"Exiting OnRawSocketConnect");
+ }
+
+ virtual void OnRawSocketClose(int fd)
+ {
+ CloseSession(&sessions[fd]);
+
+ EventHandler* user = ServerInstance->SE->GetRef(fd);
+
+ if ((user) && (user->GetExt("ssl_cert", dummy)))
+ {
+ ssl_cert* tofree;
+ user->GetExt("ssl_cert", tofree);
+ delete tofree;
+ user->Shrink("ssl_cert");
+ }
+ }
+
+ virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
+ {
+ issl_session* session = &sessions[fd];
+
+ ServerInstance->Log(DEBUG,"OnRawSocketRead");
+
+ if (!session->sess)
+ {
+ ServerInstance->Log(DEBUG,"OnRawSocketRead has no session");
+ readresult = 0;
+ CloseSession(session);
+ return 1;
+ }
+
+ if (session->status == ISSL_HANDSHAKING)
+ {
+ if (session->rstat == ISSL_READ || session->wstat == ISSL_READ)
+ {
+ ServerInstance->Log(DEBUG,"Resume handshake in read");
+ // The handshake isn't finished and it wants to read, try to finish it.
+ if (!Handshake(session))
+ {
+ ServerInstance->Log(DEBUG,"Cant resume handshake in read");
+ // Couldn't resume handshake.
+ return -1;
+ }
+ }
+ else
+ {
+ errno = EAGAIN;
+ return -1;
+ }
+ }
+
+ // If we resumed the handshake then session->status will be ISSL_OPEN
+
+ if (session->status == ISSL_OPEN)
+ {
+ if (session->wstat == ISSL_READ)
+ {
+ if(DoWrite(session) == 0)
+ return 0;
+ }
+
+ if (session->rstat == ISSL_READ)
+ {
+ int ret = DoRead(session);
+
+ if (ret > 0)
+ {
+ if (count <= session->inbufoffset)
+ {
+ memcpy(buffer, session->inbuf, count);
+ // Move the stuff left in inbuf to the beginning of it
+ memcpy(session->inbuf, session->inbuf + count, (session->inbufoffset - count));
+ // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
+ session->inbufoffset -= count;
+ // Insp uses readresult as the count of how much data there is in buffer, so:
+ readresult = count;
+ }
+ else
+ {
+ // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
+ memcpy(buffer, session->inbuf, session->inbufoffset);
+
+ readresult = session->inbufoffset;
+ // Zero the offset, as there's nothing there..
+ session->inbufoffset = 0;
+ }
+
+ return 1;
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
+ {
+ issl_session* session = &sessions[fd];
+
+ if (!session->sess)
+ {
+ ServerInstance->Log(DEBUG,"Close session missing sess");
+ CloseSession(session);
+ return -1;
+ }
+
+ session->outbuf.append(buffer, count);
+
+ if (session->status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it.
+ if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE)
+ {
+ ServerInstance->Log(DEBUG,"Handshake resume");
+ Handshake(session);
+ }
+ }
+
+ if (session->status == ISSL_OPEN)
+ {
+ if (session->rstat == ISSL_WRITE)
+ {
+ ServerInstance->Log(DEBUG,"DoRead");
+ DoRead(session);
+ }
+
+ if (session->wstat == ISSL_WRITE)
+ {
+ ServerInstance->Log(DEBUG,"DoWrite");
+ return DoWrite(session);
+ }
+ }
+
+ return 1;
+ }
+
+ int DoWrite(issl_session* session)
+ {
+ if (!session->outbuf.size())
+ return -1;
+
+ int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size());
+
+ if (ret == 0)
+ {
+ ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_write");
+ CloseSession(session);
+ return 0;
+ }
+ else if (ret < 0)
+ {
+ int err = SSL_get_error(session->sess, ret);
+
+ if (err == SSL_ERROR_WANT_WRITE)
+ {
+ session->wstat = ISSL_WRITE;
+ return -1;
+ }
+ else if (err == SSL_ERROR_WANT_READ)
+ {
+ session->wstat = ISSL_READ;
+ return -1;
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG,"Close due to returned -1 in SSL_Write");
+ CloseSession(session);
+ return 0;
+ }
+ }
+ else
+ {
+ session->outbuf = session->outbuf.substr(ret);
+ return ret;
+ }
+ }
+
+ int DoRead(issl_session* session)
+ {
+ // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
+ // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
+
+ ServerInstance->Log(DEBUG,"DoRead");
+
+ int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
+
+ if (ret == 0)
+ {
+ // Client closed connection.
+ ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_read");
+ CloseSession(session);
+ return 0;
+ }
+ else if (ret < 0)
+ {
+ int err = SSL_get_error(session->sess, ret);
+
+ if (err == SSL_ERROR_WANT_READ)
+ {
+ session->rstat = ISSL_READ;
+ ServerInstance->Log(DEBUG,"Setting want_read");
+ return -1;
+ }
+ else if (err == SSL_ERROR_WANT_WRITE)
+ {
+ session->rstat = ISSL_WRITE;
+ ServerInstance->Log(DEBUG,"Setting want_write");
+ return -1;
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG,"Closed due to returned -1 in SSL_Read");
+ CloseSession(session);
+ return 0;
+ }
+ }
+ else
+ {
+ // Read successfully 'ret' bytes into inbuf + inbufoffset
+ // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
+ // 'buffer' is 'count' long
+
+ session->inbufoffset += ret;
+
+ return ret;
+ }
+ }
+
+ // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
+ virtual void OnWhois(userrec* source, userrec* dest)
+ {
+ if (!clientactive)
+ return;
+
+ // Bugfix, only send this numeric for *our* SSL users
+ if (dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports)))
+ {
+ ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
+ }
+ }
+
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
+ {
+ // check if the linking module wants to know about OUR metadata
+ if (extname == "ssl")
+ {
+ // check if this user has an swhois field to send
+ if(user->GetExt(extname, dummy))
+ {
+ // call this function in the linking module, let it format the data how it
+ // sees fit, and send it on its way. We dont need or want to know how.
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
+ }
+ }
+ }
+
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ // check if its our metadata key, and its associated with a user
+ if ((target_type == TYPE_USER) && (extname == "ssl"))
+ {
+ userrec* dest = (userrec*)target;
+ // if they dont already have an ssl flag, accept the remote server's
+ if (!dest->GetExt(extname, dummy))
+ {
+ dest->Extend(extname, "ON");
+ }
+ }
+ }
+
+ bool Handshake(issl_session* session)
+ {
+ ServerInstance->Log(DEBUG,"Handshake");
+ int ret;
+
+ if (session->outbound)
+ {
+ ServerInstance->Log(DEBUG,"SSL_connect");
+ ret = SSL_connect(session->sess);
+ }
+ else
+ ret = SSL_accept(session->sess);
+
+ if (ret < 0)
+ {
+ int err = SSL_get_error(session->sess, ret);
+
+ if (err == SSL_ERROR_WANT_READ)
+ {
+ ServerInstance->Log(DEBUG,"Want read, handshaking");
+ session->rstat = ISSL_READ;
+ session->status = ISSL_HANDSHAKING;
+ return true;
+ }
+ else if (err == SSL_ERROR_WANT_WRITE)
+ {
+ ServerInstance->Log(DEBUG,"Want write, handshaking");
+ session->wstat = ISSL_WRITE;
+ session->status = ISSL_HANDSHAKING;
+ MakePollWrite(session);
+ return true;
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG,"Handshake failed");
+ CloseSession(session);
+ }
+
+ return false;
+ }
+ else if (ret > 0)
+ {
+ // Handshake complete.
+ // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
+ userrec* u = ServerInstance->FindDescriptor(session->fd);
+ if (u)
+ {
+ if (!u->GetExt("ssl", dummy))
+ u->Extend("ssl", "ON");
+ }
+
+ session->status = ISSL_OPEN;
+
+ MakePollWrite(session);
+
+ return true;
+ }
+ else if (ret == 0)
+ {
+ int ssl_err = SSL_get_error(session->sess, ret);
+ char buf[1024];
+ ERR_print_errors_fp(stderr);
+ ServerInstance->Log(DEBUG,"Handshake fail 2: %d: %s", ssl_err, ERR_error_string(ssl_err,buf));
+ CloseSession(session);
+ return true;
+ }
+
+ return true;
+ }
+
+ virtual void OnPostConnect(userrec* user)
+ {
+ // This occurs AFTER OnUserConnect so we can be sure the
+ // protocol module has propogated the NICK message.
+ if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
+ {
+ // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
+ std::deque<std::string>* metadata = new std::deque<std::string>;
+ metadata->push_back(user->nick);
+ metadata->push_back("ssl"); // The metadata id
+ metadata->push_back("ON"); // The value to send
+ Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");
+ event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up.
+ DELETE(event);
+ DELETE(metadata);
+
+ VerifyCertificate(&sessions[user->GetFd()], user);
+ if (sessions[user->GetFd()].sess)
+ user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, SSL_get_cipher(sessions[user->GetFd()].sess));
+ }
+ }
+
+ void MakePollWrite(issl_session* session)
+ {
+ OnRawSocketWrite(session->fd, NULL, 0);
+ //EventHandler* eh = ServerInstance->FindDescriptor(session->fd);
+ //if (eh)
+ // ServerInstance->SE->WantWrite(eh);
+ }
+
+ void CloseSession(issl_session* session)
+ {
+ if (session->sess)
+ {
+ SSL_shutdown(session->sess);
+ SSL_free(session->sess);
+ }
+
+ if (session->inbuf)
+ {
+ delete[] session->inbuf;
+ }
+
+ session->outbuf.clear();
+ session->inbuf = NULL;
+ session->sess = NULL;
+ session->status = ISSL_NONE;
+ }
+
+ void VerifyCertificate(issl_session* session, Extensible* user)
+ {
+ if (!session->sess || !user)
+ return;
+
+ X509* cert;
+ ssl_cert* certinfo = new ssl_cert;
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ const EVP_MD *digest = EVP_md5();
+
+ user->Extend("ssl_cert",certinfo);
+
+ cert = SSL_get_peer_certificate((SSL*)session->sess);
+
+ if (!cert)
+ {
+ certinfo->data.insert(std::make_pair("error","Could not get peer certificate: "+std::string(get_error())));
+ return;
+ }
+
+ certinfo->data.insert(std::make_pair("invalid", SSL_get_verify_result(session->sess) != X509_V_OK ? ConvToStr(1) : ConvToStr(0)));
+
+ if (SelfSigned)
+ {
+ certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));
+ certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));
+ certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));
+ }
+
+ certinfo->data.insert(std::make_pair("dn",std::string(X509_NAME_oneline(X509_get_subject_name(cert),0,0))));
+ certinfo->data.insert(std::make_pair("issuer",std::string(X509_NAME_oneline(X509_get_issuer_name(cert),0,0))));
+
+ if (!X509_digest(cert, digest, md, &n))
+ {
+ certinfo->data.insert(std::make_pair("error","Out of memory generating fingerprint"));
+ }
+ else
+ {
+ certinfo->data.insert(std::make_pair("fingerprint",irc::hex(md, n)));
+ }
+
+ if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), time(NULL)) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), time(NULL)) == 0))
+ {
+ certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
+ }
+
+ X509_free(cert);
+ }
+};
+
+static int error_callback(const char *str, size_t len, void *u)
+{
+ ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u;
+ mssl->PublicInstance->Log(DEFAULT, "SSL error: " + std::string(str, len - 1));
+ return 0;
+}
+
+MODULE_INIT(ModuleSSLOpenSSL);
+
diff --git a/src/modules/extra/m_ssl_oper_cert.cpp b/src/modules/extra/m_ssl_oper_cert.cpp
index 7b1c90868..c67b50c8c 100644
--- a/src/modules/extra/m_ssl_oper_cert.cpp
+++ b/src/modules/extra/m_ssl_oper_cert.cpp
@@ -1 +1,180 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Allows for MD5 encrypted oper passwords */ /* $ModDep: transport.h */ #include "inspircd.h" #include "inspircd_config.h" #include "users.h" #include "channels.h" #include "modules.h" #include "transport.h" #include "wildcard.h" /** Handle /FINGERPRINT */ class cmd_fingerprint : public command_t { public: cmd_fingerprint (InspIRCd* Instance) : command_t(Instance,"FINGERPRINT", 0, 1) { this->source = "m_ssl_oper_cert.so"; syntax = "<nickname>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* target = ServerInstance->FindNick(parameters[0]); if (target) { ssl_cert* cert; if (target->GetExt("ssl_cert",cert)) { if (cert->GetFingerprint().length()) { user->WriteServ("NOTICE %s :Certificate fingerprint for %s is %s",user->nick,target->nick,cert->GetFingerprint().c_str()); return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick,target->nick); return CMD_FAILURE; } } else { user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick, target->nick); return CMD_FAILURE; } } else { user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]); return CMD_FAILURE; } } }; class ModuleOperSSLCert : public Module { ssl_cert* cert; bool HasCert; cmd_fingerprint* mycommand; ConfigReader* cf; public: ModuleOperSSLCert(InspIRCd* Me) : Module(Me) { mycommand = new cmd_fingerprint(ServerInstance); ServerInstance->AddCommand(mycommand); cf = new ConfigReader(ServerInstance); } virtual ~ModuleOperSSLCert() { delete cf; } void Implements(char* List) { List[I_OnPreCommand] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { delete cf; cf = new ConfigReader(ServerInstance); } bool OneOfMatches(const char* host, const char* ip, const char* hostlist) { std::stringstream hl(hostlist); std::string xhost; while (hl >> xhost) { if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true)) { return true; } } return false; } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { irc::string cmd = command.c_str(); if ((cmd == "OPER") && (validated)) { char TheHost[MAXBUF]; char TheIP[MAXBUF]; std::string LoginName; std::string Password; std::string OperType; std::string HostName; std::string FingerPrint; bool SSLOnly; char* dummy; snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host); snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString()); HasCert = user->GetExt("ssl_cert",cert); for (int i = 0; i < cf->Enumerate("oper"); i++) { LoginName = cf->ReadValue("oper", "name", i); Password = cf->ReadValue("oper", "password", i); OperType = cf->ReadValue("oper", "type", i); HostName = cf->ReadValue("oper", "host", i); FingerPrint = cf->ReadValue("oper", "fingerprint", i); SSLOnly = cf->ReadFlag("oper", "sslonly", i); if (SSLOnly || !FingerPrint.empty()) { if ((!strcmp(LoginName.c_str(),parameters[0])) && (!ServerInstance->OperPassCompare(Password.c_str(),parameters[1],i)) && (OneOfMatches(TheHost,TheIP,HostName.c_str()))) { if (SSLOnly && !user->GetExt("ssl", dummy)) { user->WriteServ("491 %s :This oper login name requires an SSL connection.", user->nick); return 1; } /* This oper would match */ if ((!cert) || (cert->GetFingerprint() != FingerPrint)) { user->WriteServ("491 %s :This oper login name requires a matching key fingerprint.",user->nick); ServerInstance->SNO->WriteToSnoMask('o',"'%s' cannot oper, does not match fingerprint", user->nick); ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but wrong fingerprint.",user->nick,user->ident,user->host); return 1; } } } } } return 0; } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleOperSSLCert); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Allows for MD5 encrypted oper passwords */
+/* $ModDep: transport.h */
+
+#include "inspircd.h"
+#include "inspircd_config.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "transport.h"
+#include "wildcard.h"
+
+/** Handle /FINGERPRINT
+ */
+class cmd_fingerprint : public command_t
+{
+ public:
+ cmd_fingerprint (InspIRCd* Instance) : command_t(Instance,"FINGERPRINT", 0, 1)
+ {
+ this->source = "m_ssl_oper_cert.so";
+ syntax = "<nickname>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* target = ServerInstance->FindNick(parameters[0]);
+ if (target)
+ {
+ ssl_cert* cert;
+ if (target->GetExt("ssl_cert",cert))
+ {
+ if (cert->GetFingerprint().length())
+ {
+ user->WriteServ("NOTICE %s :Certificate fingerprint for %s is %s",user->nick,target->nick,cert->GetFingerprint().c_str());
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick,target->nick);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick, target->nick);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+};
+
+
+
+class ModuleOperSSLCert : public Module
+{
+ ssl_cert* cert;
+ bool HasCert;
+ cmd_fingerprint* mycommand;
+ ConfigReader* cf;
+ public:
+
+ ModuleOperSSLCert(InspIRCd* Me)
+ : Module(Me)
+ {
+ mycommand = new cmd_fingerprint(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ cf = new ConfigReader(ServerInstance);
+ }
+
+ virtual ~ModuleOperSSLCert()
+ {
+ delete cf;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPreCommand] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ delete cf;
+ cf = new ConfigReader(ServerInstance);
+ }
+
+ bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
+ {
+ std::stringstream hl(hostlist);
+ std::string xhost;
+ while (hl >> xhost)
+ {
+ if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ irc::string cmd = command.c_str();
+
+ if ((cmd == "OPER") && (validated))
+ {
+ char TheHost[MAXBUF];
+ char TheIP[MAXBUF];
+ std::string LoginName;
+ std::string Password;
+ std::string OperType;
+ std::string HostName;
+ std::string FingerPrint;
+ bool SSLOnly;
+ char* dummy;
+
+ snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host);
+ snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString());
+
+ HasCert = user->GetExt("ssl_cert",cert);
+
+ for (int i = 0; i < cf->Enumerate("oper"); i++)
+ {
+ LoginName = cf->ReadValue("oper", "name", i);
+ Password = cf->ReadValue("oper", "password", i);
+ OperType = cf->ReadValue("oper", "type", i);
+ HostName = cf->ReadValue("oper", "host", i);
+ FingerPrint = cf->ReadValue("oper", "fingerprint", i);
+ SSLOnly = cf->ReadFlag("oper", "sslonly", i);
+
+ if (SSLOnly || !FingerPrint.empty())
+ {
+ if ((!strcmp(LoginName.c_str(),parameters[0])) && (!ServerInstance->OperPassCompare(Password.c_str(),parameters[1],i)) && (OneOfMatches(TheHost,TheIP,HostName.c_str())))
+ {
+ if (SSLOnly && !user->GetExt("ssl", dummy))
+ {
+ user->WriteServ("491 %s :This oper login name requires an SSL connection.", user->nick);
+ return 1;
+ }
+
+ /* This oper would match */
+ if ((!cert) || (cert->GetFingerprint() != FingerPrint))
+ {
+ user->WriteServ("491 %s :This oper login name requires a matching key fingerprint.",user->nick);
+ ServerInstance->SNO->WriteToSnoMask('o',"'%s' cannot oper, does not match fingerprint", user->nick);
+ ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but wrong fingerprint.",user->nick,user->ident,user->host);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleOperSSLCert);
+
diff --git a/src/modules/extra/m_sslinfo.cpp b/src/modules/extra/m_sslinfo.cpp
index 83de798c8..dc9274f1e 100644
--- a/src/modules/extra/m_sslinfo.cpp
+++ b/src/modules/extra/m_sslinfo.cpp
@@ -1 +1,94 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "transport.h" #include "wildcard.h" #include "dns.h" /* $ModDesc: Provides /sslinfo command used to test who a mask matches */ /* $ModDep: transport.h */ /** Handle /SSLINFO */ class cmd_sslinfo : public command_t { public: cmd_sslinfo (InspIRCd* Instance) : command_t(Instance,"SSLINFO", 0, 1) { this->source = "m_sslinfo.so"; this->syntax = "<nick>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* target = ServerInstance->FindNick(parameters[0]); ssl_cert* cert; if (target) { if (target->GetExt("ssl_cert", cert)) { if (cert->GetError().length()) { user->WriteServ("NOTICE %s :*** Error: %s", user->nick, cert->GetError().c_str()); } user->WriteServ("NOTICE %s :*** Distinguised Name: %s", user->nick, cert->GetDN().c_str()); user->WriteServ("NOTICE %s :*** Issuer: %s", user->nick, cert->GetIssuer().c_str()); user->WriteServ("NOTICE %s :*** Key Fingerprint: %s", user->nick, cert->GetFingerprint().c_str()); return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :*** No SSL certificate information for this user.", user->nick); return CMD_FAILURE; } } else user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]); return CMD_FAILURE; } }; class ModuleSSLInfo : public Module { cmd_sslinfo* newcommand; public: ModuleSSLInfo(InspIRCd* Me) : Module(Me) { newcommand = new cmd_sslinfo(ServerInstance); ServerInstance->AddCommand(newcommand); } void Implements(char* List) { } virtual ~ModuleSSLInfo() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleSSLInfo); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "transport.h"
+#include "wildcard.h"
+#include "dns.h"
+
+/* $ModDesc: Provides /sslinfo command used to test who a mask matches */
+/* $ModDep: transport.h */
+
+/** Handle /SSLINFO
+ */
+class cmd_sslinfo : public command_t
+{
+ public:
+ cmd_sslinfo (InspIRCd* Instance) : command_t(Instance,"SSLINFO", 0, 1)
+ {
+ this->source = "m_sslinfo.so";
+ this->syntax = "<nick>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* target = ServerInstance->FindNick(parameters[0]);
+ ssl_cert* cert;
+
+ if (target)
+ {
+ if (target->GetExt("ssl_cert", cert))
+ {
+ if (cert->GetError().length())
+ {
+ user->WriteServ("NOTICE %s :*** Error: %s", user->nick, cert->GetError().c_str());
+ }
+ user->WriteServ("NOTICE %s :*** Distinguised Name: %s", user->nick, cert->GetDN().c_str());
+ user->WriteServ("NOTICE %s :*** Issuer: %s", user->nick, cert->GetIssuer().c_str());
+ user->WriteServ("NOTICE %s :*** Key Fingerprint: %s", user->nick, cert->GetFingerprint().c_str());
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** No SSL certificate information for this user.", user->nick);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]);
+
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleSSLInfo : public Module
+{
+ cmd_sslinfo* newcommand;
+ public:
+ ModuleSSLInfo(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ newcommand = new cmd_sslinfo(ServerInstance);
+ ServerInstance->AddCommand(newcommand);
+ }
+
+ void Implements(char* List)
+ {
+ }
+
+ virtual ~ModuleSSLInfo()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSSLInfo);
+
diff --git a/src/modules/extra/m_testclient.cpp b/src/modules/extra/m_testclient.cpp
index a867dad20..f4e58b7b5 100644
--- a/src/modules/extra/m_testclient.cpp
+++ b/src/modules/extra/m_testclient.cpp
@@ -1 +1,110 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" #include "m_sqlv2.h" class ModuleTestClient : public Module { private: public: ModuleTestClient(InspIRCd* Me) : Module::Module(Me) { } void Implements(char* List) { List[I_OnRequest] = List[I_OnBackgroundTimer] = 1; } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } virtual void OnBackgroundTimer(time_t foo) { Module* target = ServerInstance->FindFeature("SQL"); if(target) { SQLrequest foo = SQLreq(this, target, "foo", "UPDATE rawr SET foo = '?' WHERE bar = 42", ConvToStr(time(NULL))); if(foo.Send()) { ServerInstance->Log(DEBUG, "Sent query, got given ID %lu", foo.id); } else { ServerInstance->Log(DEBUG, "SQLrequest failed: %s", foo.error.Str()); } } } virtual char* OnRequest(Request* request) { if(strcmp(SQLRESID, request->GetId()) == 0) { ServerInstance->Log(DEBUG, "Got SQL result (%s)", request->GetId()); SQLresult* res = (SQLresult*)request; if (res->error.Id() == NO_ERROR) { if(res->Cols()) { ServerInstance->Log(DEBUG, "Got result with %d rows and %d columns", res->Rows(), res->Cols()); for (int r = 0; r < res->Rows(); r++) { ServerInstance->Log(DEBUG, "Row %d:", r); for(int i = 0; i < res->Cols(); i++) { ServerInstance->Log(DEBUG, "\t[%s]: %s", res->ColName(i).c_str(), res->GetValue(r, i).d.c_str()); } } } else { ServerInstance->Log(DEBUG, "%d rows affected in query", res->Rows()); } } else { ServerInstance->Log(DEBUG, "SQLrequest failed: %s", res->error.Str()); } return SQLSUCCESS; } ServerInstance->Log(DEBUG, "Got unsupported API version string: %s", request->GetId()); return NULL; } virtual ~ModuleTestClient() { } }; MODULE_INIT(ModuleTestClient); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+#include "m_sqlv2.h"
+
+class ModuleTestClient : public Module
+{
+private:
+
+
+public:
+ ModuleTestClient(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRequest] = List[I_OnBackgroundTimer] = 1;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ virtual void OnBackgroundTimer(time_t foo)
+ {
+ Module* target = ServerInstance->FindFeature("SQL");
+
+ if(target)
+ {
+ SQLrequest foo = SQLreq(this, target, "foo", "UPDATE rawr SET foo = '?' WHERE bar = 42", ConvToStr(time(NULL)));
+
+ if(foo.Send())
+ {
+ ServerInstance->Log(DEBUG, "Sent query, got given ID %lu", foo.id);
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG, "SQLrequest failed: %s", foo.error.Str());
+ }
+ }
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ if(strcmp(SQLRESID, request->GetId()) == 0)
+ {
+ ServerInstance->Log(DEBUG, "Got SQL result (%s)", request->GetId());
+
+ SQLresult* res = (SQLresult*)request;
+
+ if (res->error.Id() == NO_ERROR)
+ {
+ if(res->Cols())
+ {
+ ServerInstance->Log(DEBUG, "Got result with %d rows and %d columns", res->Rows(), res->Cols());
+
+ for (int r = 0; r < res->Rows(); r++)
+ {
+ ServerInstance->Log(DEBUG, "Row %d:", r);
+
+ for(int i = 0; i < res->Cols(); i++)
+ {
+ ServerInstance->Log(DEBUG, "\t[%s]: %s", res->ColName(i).c_str(), res->GetValue(r, i).d.c_str());
+ }
+ }
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG, "%d rows affected in query", res->Rows());
+ }
+ }
+ else
+ {
+ ServerInstance->Log(DEBUG, "SQLrequest failed: %s", res->error.Str());
+
+ }
+
+ return SQLSUCCESS;
+ }
+
+ ServerInstance->Log(DEBUG, "Got unsupported API version string: %s", request->GetId());
+
+ return NULL;
+ }
+
+ virtual ~ModuleTestClient()
+ {
+ }
+};
+
+MODULE_INIT(ModuleTestClient);
+
diff --git a/src/modules/extra/m_ziplink.cpp b/src/modules/extra/m_ziplink.cpp
index 2a127258d..e815d1042 100644
--- a/src/modules/extra/m_ziplink.cpp
+++ b/src/modules/extra/m_ziplink.cpp
@@ -1 +1,452 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <zlib.h> #include "users.h" #include "channels.h" #include "modules.h" #include "socket.h" #include "hashcomp.h" #include "transport.h" /* $ModDesc: Provides zlib link support for servers */ /* $LinkerFlags: -lz */ /* $ModDep: transport.h */ /* * Compressed data is transmitted across the link in the following format: * * 0 1 2 3 4 ... n * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * | n | Z0 -> Zn | * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * * Where: n is the size of a frame, in network byte order, 4 bytes. * Z0 through Zn are Zlib compressed data, n bytes in length. * * If the module fails to read the entire frame, then it will buffer * the portion of the last frame it received, then attempt to read * the next part of the frame next time a write notification arrives. * * ZLIB_BEST_COMPRESSION (9) is used for all sending of data with * a flush after each frame. A frame may contain multiple lines * and should be treated as raw binary data. * */ /* Status of a connection */ enum izip_status { IZIP_OPEN, IZIP_CLOSED }; /* Maximum transfer size per read operation */ const unsigned int CHUNK = 128 * 1024; /* This class manages a compressed chunk of data preceeded by * a length count. * * It can handle having multiple chunks of data in the buffer * at any time. */ class CountedBuffer : public classbase { std::string buffer; /* Current buffer contents */ unsigned int amount_expected; /* Amount of data expected */ public: CountedBuffer() { amount_expected = 0; } /** Adds arbitrary compressed data to the buffer. * - Binsry safe, of course. */ void AddData(unsigned char* data, int data_length) { buffer.append((const char*)data, data_length); this->NextFrameSize(); } /** Works out the size of the next compressed frame */ void NextFrameSize() { if ((!amount_expected) && (buffer.length() >= 4)) { /* We have enough to read an int - * Yes, this is safe, but its ugly. Give me * a nicer way to read 4 bytes from a binary * stream, and push them into a 32 bit int, * and i'll consider replacing this. */ amount_expected = ntohl((buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]); buffer = buffer.substr(4); } } /** Gets the next frame and returns its size, or returns * zero if there isnt one available yet. * A frame can contain multiple plaintext lines. * - Binary safe. */ int GetFrame(unsigned char* frame, int maxsize) { if (amount_expected) { /* We know how much we're expecting... * Do we have enough yet? */ if (buffer.length() >= amount_expected) { int j = 0; for (unsigned int i = 0; i < amount_expected; i++, j++) frame[i] = buffer[i]; buffer = buffer.substr(j); amount_expected = 0; NextFrameSize(); return j; } } /* Not enough for a frame yet, COME AGAIN! */ return 0; } }; /** Represents an zipped connections extra data */ class izip_session : public classbase { public: z_stream c_stream; /* compression stream */ z_stream d_stream; /* decompress stream */ izip_status status; /* Connection status */ int fd; /* File descriptor */ CountedBuffer* inbuf; /* Holds input buffer */ std::string outbuf; /* Holds output buffer */ }; class ModuleZLib : public Module { izip_session sessions[MAX_DESCRIPTORS]; /* Used for stats z extensions */ float total_out_compressed; float total_in_compressed; float total_out_uncompressed; float total_in_uncompressed; public: ModuleZLib(InspIRCd* Me) : Module::Module(Me) { ServerInstance->PublishInterface("InspSocketHook", this); total_out_compressed = total_in_compressed = 0; total_out_uncompressed = total_out_uncompressed = 0; } virtual ~ModuleZLib() { ServerInstance->UnpublishInterface("InspSocketHook", this); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = 1; List[I_OnStats] = List[I_OnRequest] = 1; } /* Handle InspSocketHook API requests */ virtual char* OnRequest(Request* request) { ISHRequest* ISR = (ISHRequest*)request; if (strcmp("IS_NAME", request->GetId()) == 0) { /* Return name */ return "zip"; } else if (strcmp("IS_HOOK", request->GetId()) == 0) { /* Attach to an inspsocket */ char* ret = "OK"; try { ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; } catch (ModuleException& e) { return NULL; } return ret; } else if (strcmp("IS_UNHOOK", request->GetId()) == 0) { /* Detatch from an inspsocket */ return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; } else if (strcmp("IS_HSDONE", request->GetId()) == 0) { /* Check for completion of handshake * (actually, this module doesnt handshake) */ return "OK"; } else if (strcmp("IS_ATTACH", request->GetId()) == 0) { /* Attach certificate data to the inspsocket * (this module doesnt do that, either) */ return NULL; } return NULL; } /* Handle stats z (misc stats) */ virtual int OnStats(char symbol, userrec* user, string_list &results) { if (symbol == 'z') { std::string sn = ServerInstance->Config->ServerName; /* Yeah yeah, i know, floats are ew. * We used them here because we'd be casting to float anyway to do this maths, * and also only floating point numbers can deal with the pretty large numbers * involved in the total throughput of a server over a large period of time. * (we dont count 64 bit ints because not all systems have 64 bit ints, and floats * can still hold more. */ float outbound_r = 100 - ((total_out_compressed / (total_out_uncompressed + 0.001)) * 100); float inbound_r = 100 - ((total_in_compressed / (total_in_uncompressed + 0.001)) * 100); float total_compressed = total_in_compressed + total_out_compressed; float total_uncompressed = total_in_uncompressed + total_out_uncompressed; float total_r = 100 - ((total_compressed / (total_uncompressed + 0.001)) * 100); char outbound_ratio[MAXBUF], inbound_ratio[MAXBUF], combined_ratio[MAXBUF]; sprintf(outbound_ratio, "%3.2f%%", outbound_r); sprintf(inbound_ratio, "%3.2f%%", inbound_r); sprintf(combined_ratio, "%3.2f%%", total_r); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_compressed = "+ConvToStr(total_out_compressed)); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_compressed = "+ConvToStr(total_in_compressed)); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_uncompressed = "+ConvToStr(total_out_uncompressed)); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_uncompressed = "+ConvToStr(total_in_uncompressed)); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_ratio = "+outbound_ratio); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_ratio = "+inbound_ratio); results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS combined_ratio = "+combined_ratio); return 0; } return 0; } virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport) { izip_session* session = &sessions[fd]; /* allocate state and buffers */ session->fd = fd; session->status = IZIP_OPEN; session->inbuf = new CountedBuffer(); session->c_stream.zalloc = (alloc_func)0; session->c_stream.zfree = (free_func)0; session->c_stream.opaque = (voidpf)0; session->d_stream.zalloc = (alloc_func)0; session->d_stream.zfree = (free_func)0; session->d_stream.opaque = (voidpf)0; } virtual void OnRawSocketConnect(int fd) { /* Nothing special needs doing here compared to accept() */ OnRawSocketAccept(fd, "", 0); } virtual void OnRawSocketClose(int fd) { CloseSession(&sessions[fd]); } virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { /* Find the sockets session */ izip_session* session = &sessions[fd]; if (session->status == IZIP_CLOSED) return 0; unsigned char compr[CHUNK + 4]; unsigned int offset = 0; unsigned int total_size = 0; /* Read CHUNK bytes at a time to the buffer (usually 128k) */ readresult = read(fd, compr, CHUNK); /* Did we get anything? */ if (readresult > 0) { /* Add it to the frame queue */ session->inbuf->AddData(compr, readresult); total_in_compressed += readresult; /* Parse all completed frames */ int size = 0; while ((size = session->inbuf->GetFrame(compr, CHUNK)) != 0) { session->d_stream.next_in = (Bytef*)compr; session->d_stream.avail_in = 0; session->d_stream.next_out = (Bytef*)(buffer + offset); /* If we cant call this, well, we're boned. */ if (inflateInit(&session->d_stream) != Z_OK) return 0; while ((session->d_stream.total_out < count) && (session->d_stream.total_in < (unsigned int)size)) { session->d_stream.avail_in = session->d_stream.avail_out = 1; if (inflate(&session->d_stream, Z_NO_FLUSH) == Z_STREAM_END) break; } /* Stick a fork in me, i'm done */ inflateEnd(&session->d_stream); /* Update counters and offsets */ total_size += session->d_stream.total_out; total_in_uncompressed += session->d_stream.total_out; offset += session->d_stream.total_out; } /* Null-terminate the buffer -- this doesnt harm binary data */ buffer[total_size] = 0; /* Set the read size to the correct total size */ readresult = total_size; } return (readresult > 0); } virtual int OnRawSocketWrite(int fd, const char* buffer, int count) { izip_session* session = &sessions[fd]; int ocount = count; if (!count) /* Nothing to do! */ return 0; if(session->status != IZIP_OPEN) { /* Seriously, wtf? */ CloseSession(session); return 0; } unsigned char compr[CHUNK + 4]; /* Gentlemen, start your engines! */ if (deflateInit(&session->c_stream, Z_BEST_COMPRESSION) != Z_OK) { CloseSession(session); return 0; } /* Set buffer sizes (we reserve 4 bytes at the start of the * buffer for the length counters) */ session->c_stream.next_in = (Bytef*)buffer; session->c_stream.next_out = compr + 4; /* Compress the text */ while ((session->c_stream.total_in < (unsigned int)count) && (session->c_stream.total_out < CHUNK)) { session->c_stream.avail_in = session->c_stream.avail_out = 1; if (deflate(&session->c_stream, Z_NO_FLUSH) != Z_OK) { CloseSession(session); return 0; } } /* Finish the stream */ for (session->c_stream.avail_out = 1; deflate(&session->c_stream, Z_FINISH) != Z_STREAM_END; session->c_stream.avail_out = 1); deflateEnd(&session->c_stream); total_out_uncompressed += ocount; total_out_compressed += session->c_stream.total_out; /** Assemble the frame length onto the frame, in network byte order */ compr[0] = (session->c_stream.total_out >> 24); compr[1] = (session->c_stream.total_out >> 16); compr[2] = (session->c_stream.total_out >> 8); compr[3] = (session->c_stream.total_out & 0xFF); /* Add compressed data plus leading length to the output buffer - * Note, we may have incomplete half-sent frames in here. */ session->outbuf.append((const char*)compr, session->c_stream.total_out + 4); /* Lets see how much we can send out */ int ret = write(fd, session->outbuf.data(), session->outbuf.length()); /* Check for errors, and advance the buffer if any was sent */ if (ret > 0) session->outbuf = session->outbuf.substr(ret); else if (ret < 1) { if (ret == -1) { if (errno == EAGAIN) return 0; else { session->outbuf.clear(); return 0; } } else { session->outbuf.clear(); return 0; } } /* ALL LIES the lot of it, we havent really written * this amount, but the layer above doesnt need to know. */ return ocount; } void CloseSession(izip_session* session) { if (session->status == IZIP_OPEN) { session->status = IZIP_CLOSED; session->outbuf.clear(); delete session->inbuf; } } }; MODULE_INIT(ModuleZLib); \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <zlib.h>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "socket.h"
+#include "hashcomp.h"
+#include "transport.h"
+
+/* $ModDesc: Provides zlib link support for servers */
+/* $LinkerFlags: -lz */
+/* $ModDep: transport.h */
+
+/*
+ * Compressed data is transmitted across the link in the following format:
+ *
+ * 0 1 2 3 4 ... n
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | n | Z0 -> Zn |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * Where: n is the size of a frame, in network byte order, 4 bytes.
+ * Z0 through Zn are Zlib compressed data, n bytes in length.
+ *
+ * If the module fails to read the entire frame, then it will buffer
+ * the portion of the last frame it received, then attempt to read
+ * the next part of the frame next time a write notification arrives.
+ *
+ * ZLIB_BEST_COMPRESSION (9) is used for all sending of data with
+ * a flush after each frame. A frame may contain multiple lines
+ * and should be treated as raw binary data.
+ *
+ */
+
+/* Status of a connection */
+enum izip_status { IZIP_OPEN, IZIP_CLOSED };
+
+/* Maximum transfer size per read operation */
+const unsigned int CHUNK = 128 * 1024;
+
+/* This class manages a compressed chunk of data preceeded by
+ * a length count.
+ *
+ * It can handle having multiple chunks of data in the buffer
+ * at any time.
+ */
+class CountedBuffer : public classbase
+{
+ std::string buffer; /* Current buffer contents */
+ unsigned int amount_expected; /* Amount of data expected */
+ public:
+ CountedBuffer()
+ {
+ amount_expected = 0;
+ }
+
+ /** Adds arbitrary compressed data to the buffer.
+ * - Binsry safe, of course.
+ */
+ void AddData(unsigned char* data, int data_length)
+ {
+ buffer.append((const char*)data, data_length);
+ this->NextFrameSize();
+ }
+
+ /** Works out the size of the next compressed frame
+ */
+ void NextFrameSize()
+ {
+ if ((!amount_expected) && (buffer.length() >= 4))
+ {
+ /* We have enough to read an int -
+ * Yes, this is safe, but its ugly. Give me
+ * a nicer way to read 4 bytes from a binary
+ * stream, and push them into a 32 bit int,
+ * and i'll consider replacing this.
+ */
+ amount_expected = ntohl((buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]);
+ buffer = buffer.substr(4);
+ }
+ }
+
+ /** Gets the next frame and returns its size, or returns
+ * zero if there isnt one available yet.
+ * A frame can contain multiple plaintext lines.
+ * - Binary safe.
+ */
+ int GetFrame(unsigned char* frame, int maxsize)
+ {
+ if (amount_expected)
+ {
+ /* We know how much we're expecting...
+ * Do we have enough yet?
+ */
+ if (buffer.length() >= amount_expected)
+ {
+ int j = 0;
+ for (unsigned int i = 0; i < amount_expected; i++, j++)
+ frame[i] = buffer[i];
+
+ buffer = buffer.substr(j);
+ amount_expected = 0;
+ NextFrameSize();
+ return j;
+ }
+ }
+ /* Not enough for a frame yet, COME AGAIN! */
+ return 0;
+ }
+};
+
+/** Represents an zipped connections extra data
+ */
+class izip_session : public classbase
+{
+ public:
+ z_stream c_stream; /* compression stream */
+ z_stream d_stream; /* decompress stream */
+ izip_status status; /* Connection status */
+ int fd; /* File descriptor */
+ CountedBuffer* inbuf; /* Holds input buffer */
+ std::string outbuf; /* Holds output buffer */
+};
+
+class ModuleZLib : public Module
+{
+ izip_session sessions[MAX_DESCRIPTORS];
+
+ /* Used for stats z extensions */
+ float total_out_compressed;
+ float total_in_compressed;
+ float total_out_uncompressed;
+ float total_in_uncompressed;
+
+ public:
+
+ ModuleZLib(InspIRCd* Me)
+ : Module::Module(Me)
+ {
+ ServerInstance->PublishInterface("InspSocketHook", this);
+
+ total_out_compressed = total_in_compressed = 0;
+ total_out_uncompressed = total_out_uncompressed = 0;
+ }
+
+ virtual ~ModuleZLib()
+ {
+ ServerInstance->UnpublishInterface("InspSocketHook", this);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = 1;
+ List[I_OnStats] = List[I_OnRequest] = 1;
+ }
+
+ /* Handle InspSocketHook API requests */
+ virtual char* OnRequest(Request* request)
+ {
+ ISHRequest* ISR = (ISHRequest*)request;
+ if (strcmp("IS_NAME", request->GetId()) == 0)
+ {
+ /* Return name */
+ return "zip";
+ }
+ else if (strcmp("IS_HOOK", request->GetId()) == 0)
+ {
+ /* Attach to an inspsocket */
+ char* ret = "OK";
+ try
+ {
+ ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
+ }
+ catch (ModuleException& e)
+ {
+ return NULL;
+ }
+ return ret;
+ }
+ else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
+ {
+ /* Detatch from an inspsocket */
+ return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
+ }
+ else if (strcmp("IS_HSDONE", request->GetId()) == 0)
+ {
+ /* Check for completion of handshake
+ * (actually, this module doesnt handshake)
+ */
+ return "OK";
+ }
+ else if (strcmp("IS_ATTACH", request->GetId()) == 0)
+ {
+ /* Attach certificate data to the inspsocket
+ * (this module doesnt do that, either)
+ */
+ return NULL;
+ }
+ return NULL;
+ }
+
+ /* Handle stats z (misc stats) */
+ virtual int OnStats(char symbol, userrec* user, string_list &results)
+ {
+ if (symbol == 'z')
+ {
+ std::string sn = ServerInstance->Config->ServerName;
+
+ /* Yeah yeah, i know, floats are ew.
+ * We used them here because we'd be casting to float anyway to do this maths,
+ * and also only floating point numbers can deal with the pretty large numbers
+ * involved in the total throughput of a server over a large period of time.
+ * (we dont count 64 bit ints because not all systems have 64 bit ints, and floats
+ * can still hold more.
+ */
+ float outbound_r = 100 - ((total_out_compressed / (total_out_uncompressed + 0.001)) * 100);
+ float inbound_r = 100 - ((total_in_compressed / (total_in_uncompressed + 0.001)) * 100);
+
+ float total_compressed = total_in_compressed + total_out_compressed;
+ float total_uncompressed = total_in_uncompressed + total_out_uncompressed;
+
+ float total_r = 100 - ((total_compressed / (total_uncompressed + 0.001)) * 100);
+
+ char outbound_ratio[MAXBUF], inbound_ratio[MAXBUF], combined_ratio[MAXBUF];
+
+ sprintf(outbound_ratio, "%3.2f%%", outbound_r);
+ sprintf(inbound_ratio, "%3.2f%%", inbound_r);
+ sprintf(combined_ratio, "%3.2f%%", total_r);
+
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_compressed = "+ConvToStr(total_out_compressed));
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_compressed = "+ConvToStr(total_in_compressed));
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_uncompressed = "+ConvToStr(total_out_uncompressed));
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_uncompressed = "+ConvToStr(total_in_uncompressed));
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_ratio = "+outbound_ratio);
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_ratio = "+inbound_ratio);
+ results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS combined_ratio = "+combined_ratio);
+ return 0;
+ }
+
+ return 0;
+ }
+
+ virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
+ {
+ izip_session* session = &sessions[fd];
+
+ /* allocate state and buffers */
+ session->fd = fd;
+ session->status = IZIP_OPEN;
+ session->inbuf = new CountedBuffer();
+
+ session->c_stream.zalloc = (alloc_func)0;
+ session->c_stream.zfree = (free_func)0;
+ session->c_stream.opaque = (voidpf)0;
+
+ session->d_stream.zalloc = (alloc_func)0;
+ session->d_stream.zfree = (free_func)0;
+ session->d_stream.opaque = (voidpf)0;
+ }
+
+ virtual void OnRawSocketConnect(int fd)
+ {
+ /* Nothing special needs doing here compared to accept() */
+ OnRawSocketAccept(fd, "", 0);
+ }
+
+ virtual void OnRawSocketClose(int fd)
+ {
+ CloseSession(&sessions[fd]);
+ }
+
+ virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
+ {
+ /* Find the sockets session */
+ izip_session* session = &sessions[fd];
+
+ if (session->status == IZIP_CLOSED)
+ return 0;
+
+ unsigned char compr[CHUNK + 4];
+ unsigned int offset = 0;
+ unsigned int total_size = 0;
+
+ /* Read CHUNK bytes at a time to the buffer (usually 128k) */
+ readresult = read(fd, compr, CHUNK);
+
+ /* Did we get anything? */
+ if (readresult > 0)
+ {
+ /* Add it to the frame queue */
+ session->inbuf->AddData(compr, readresult);
+ total_in_compressed += readresult;
+
+ /* Parse all completed frames */
+ int size = 0;
+ while ((size = session->inbuf->GetFrame(compr, CHUNK)) != 0)
+ {
+ session->d_stream.next_in = (Bytef*)compr;
+ session->d_stream.avail_in = 0;
+ session->d_stream.next_out = (Bytef*)(buffer + offset);
+
+ /* If we cant call this, well, we're boned. */
+ if (inflateInit(&session->d_stream) != Z_OK)
+ return 0;
+
+ while ((session->d_stream.total_out < count) && (session->d_stream.total_in < (unsigned int)size))
+ {
+ session->d_stream.avail_in = session->d_stream.avail_out = 1;
+ if (inflate(&session->d_stream, Z_NO_FLUSH) == Z_STREAM_END)
+ break;
+ }
+
+ /* Stick a fork in me, i'm done */
+ inflateEnd(&session->d_stream);
+
+ /* Update counters and offsets */
+ total_size += session->d_stream.total_out;
+ total_in_uncompressed += session->d_stream.total_out;
+ offset += session->d_stream.total_out;
+ }
+
+ /* Null-terminate the buffer -- this doesnt harm binary data */
+ buffer[total_size] = 0;
+
+ /* Set the read size to the correct total size */
+ readresult = total_size;
+
+ }
+ return (readresult > 0);
+ }
+
+ virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
+ {
+ izip_session* session = &sessions[fd];
+ int ocount = count;
+
+ if (!count) /* Nothing to do! */
+ return 0;
+
+ if(session->status != IZIP_OPEN)
+ {
+ /* Seriously, wtf? */
+ CloseSession(session);
+ return 0;
+ }
+
+ unsigned char compr[CHUNK + 4];
+
+ /* Gentlemen, start your engines! */
+ if (deflateInit(&session->c_stream, Z_BEST_COMPRESSION) != Z_OK)
+ {
+ CloseSession(session);
+ return 0;
+ }
+
+ /* Set buffer sizes (we reserve 4 bytes at the start of the
+ * buffer for the length counters)
+ */
+ session->c_stream.next_in = (Bytef*)buffer;
+ session->c_stream.next_out = compr + 4;
+
+ /* Compress the text */
+ while ((session->c_stream.total_in < (unsigned int)count) && (session->c_stream.total_out < CHUNK))
+ {
+ session->c_stream.avail_in = session->c_stream.avail_out = 1;
+ if (deflate(&session->c_stream, Z_NO_FLUSH) != Z_OK)
+ {
+ CloseSession(session);
+ return 0;
+ }
+ }
+ /* Finish the stream */
+ for (session->c_stream.avail_out = 1; deflate(&session->c_stream, Z_FINISH) != Z_STREAM_END; session->c_stream.avail_out = 1);
+ deflateEnd(&session->c_stream);
+
+ total_out_uncompressed += ocount;
+ total_out_compressed += session->c_stream.total_out;
+
+ /** Assemble the frame length onto the frame, in network byte order */
+ compr[0] = (session->c_stream.total_out >> 24);
+ compr[1] = (session->c_stream.total_out >> 16);
+ compr[2] = (session->c_stream.total_out >> 8);
+ compr[3] = (session->c_stream.total_out & 0xFF);
+
+ /* Add compressed data plus leading length to the output buffer -
+ * Note, we may have incomplete half-sent frames in here.
+ */
+ session->outbuf.append((const char*)compr, session->c_stream.total_out + 4);
+
+ /* Lets see how much we can send out */
+ int ret = write(fd, session->outbuf.data(), session->outbuf.length());
+
+ /* Check for errors, and advance the buffer if any was sent */
+ if (ret > 0)
+ session->outbuf = session->outbuf.substr(ret);
+ else if (ret < 1)
+ {
+ if (ret == -1)
+ {
+ if (errno == EAGAIN)
+ return 0;
+ else
+ {
+ session->outbuf.clear();
+ return 0;
+ }
+ }
+ else
+ {
+ session->outbuf.clear();
+ return 0;
+ }
+ }
+
+ /* ALL LIES the lot of it, we havent really written
+ * this amount, but the layer above doesnt need to know.
+ */
+ return ocount;
+ }
+
+ void CloseSession(izip_session* session)
+ {
+ if (session->status == IZIP_OPEN)
+ {
+ session->status = IZIP_CLOSED;
+ session->outbuf.clear();
+ delete session->inbuf;
+ }
+ }
+
+};
+
+MODULE_INIT(ModuleZLib);
+
diff --git a/src/modules/httpclient.h b/src/modules/httpclient.h
index 109bfa666..c5e84261f 100644
--- a/src/modules/httpclient.h
+++ b/src/modules/httpclient.h
@@ -1 +1,127 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "base.h" #ifndef HTTPCLIENT_H__ #define HTTPCLIENT_H__ #include <string> #include <map> typedef std::map<std::string,std::string> HeaderMap; const char* HTTP_CLIENT_RESPONSE = "HTTPCLIENT_RESPONSE"; const char* HTTP_CLIENT_REQUEST = "HTTPCLIENT_REQUEST"; /** This class represents an outgoing HTTP request */ class HTTPClientRequest : public Request { protected: std::string url; InspIRCd *Instance; Module *src; HeaderMap Headers; public: HTTPClientRequest(InspIRCd *Instance, Module *src, Module* target, const std::string &url) : Request(src, target, HTTP_CLIENT_REQUEST), url(url), Instance(Instance), src(src) { Headers["User-Agent"] = "InspIRCd (m_http_client.so)"; Headers["Connection"] = "Close"; Headers["Accept"] = "*/*"; } HTTPClientRequest() : Request(NULL, NULL, HTTP_CLIENT_REQUEST) { } const std::string &GetURL() { return url; } void AddHeader(std::string &header, std::string &data) { Headers[header] = data; } void DeleteHeader(std::string &header) { Headers.erase(header); } HeaderMap GetHeaders() { return Headers; } }; class HTTPClientResponse : public Request { protected: friend class HTTPSocket; std::string url; std::string data; int response; std::string responsestr; HeaderMap Headers; public: HTTPClientResponse(Module* src, Module* target, std::string &url, int response, std::string responsestr) : Request(src, target, HTTP_CLIENT_RESPONSE), url(url), response(response), responsestr(responsestr) { } HTTPClientResponse() : Request(NULL, NULL, HTTP_CLIENT_RESPONSE) { } void SetData(const std::string &ndata) { data = ndata; } void AddHeader(const std::string &header, const std::string &data) { Headers[header] = data; } const std::string &GetURL() { return url; } const std::string &GetData() { return data; } int GetResponse(std::string &str) { str = responsestr; return response; } std::string GetHeader(const std::string &header) { HeaderMap::iterator i = Headers.find(header); if (i != Headers.end()) return i->second; else return ""; } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "base.h"
+
+#ifndef HTTPCLIENT_H__
+#define HTTPCLIENT_H__
+
+#include <string>
+#include <map>
+
+typedef std::map<std::string,std::string> HeaderMap;
+
+const char* HTTP_CLIENT_RESPONSE = "HTTPCLIENT_RESPONSE";
+const char* HTTP_CLIENT_REQUEST = "HTTPCLIENT_REQUEST";
+
+/** This class represents an outgoing HTTP request
+ */
+class HTTPClientRequest : public Request
+{
+ protected:
+ std::string url;
+ InspIRCd *Instance;
+ Module *src;
+ HeaderMap Headers;
+ public:
+ HTTPClientRequest(InspIRCd *Instance, Module *src, Module* target, const std::string &url)
+ : Request(src, target, HTTP_CLIENT_REQUEST), url(url), Instance(Instance), src(src)
+ {
+ Headers["User-Agent"] = "InspIRCd (m_http_client.so)";
+ Headers["Connection"] = "Close";
+ Headers["Accept"] = "*/*";
+ }
+
+ HTTPClientRequest() : Request(NULL, NULL, HTTP_CLIENT_REQUEST)
+ {
+ }
+
+ const std::string &GetURL()
+ {
+ return url;
+ }
+
+ void AddHeader(std::string &header, std::string &data)
+ {
+ Headers[header] = data;
+ }
+
+ void DeleteHeader(std::string &header)
+ {
+ Headers.erase(header);
+ }
+
+ HeaderMap GetHeaders()
+ {
+ return Headers;
+ }
+};
+
+class HTTPClientResponse : public Request
+{
+ protected:
+ friend class HTTPSocket;
+
+ std::string url;
+ std::string data;
+ int response;
+ std::string responsestr;
+ HeaderMap Headers;
+ public:
+ HTTPClientResponse(Module* src, Module* target, std::string &url, int response, std::string responsestr)
+ : Request(src, target, HTTP_CLIENT_RESPONSE), url(url), response(response), responsestr(responsestr)
+ {
+ }
+
+ HTTPClientResponse() : Request(NULL, NULL, HTTP_CLIENT_RESPONSE)
+ {
+ }
+
+ void SetData(const std::string &ndata)
+ {
+ data = ndata;
+ }
+
+ void AddHeader(const std::string &header, const std::string &data)
+ {
+ Headers[header] = data;
+ }
+
+ const std::string &GetURL()
+ {
+ return url;
+ }
+
+ const std::string &GetData()
+ {
+ return data;
+ }
+
+ int GetResponse(std::string &str)
+ {
+ str = responsestr;
+ return response;
+ }
+
+ std::string GetHeader(const std::string &header)
+ {
+ HeaderMap::iterator i = Headers.find(header);
+
+ if (i != Headers.end())
+ return i->second;
+ else
+ return "";
+ }
+};
+
+#endif
diff --git a/src/modules/httpd.h b/src/modules/httpd.h
index 32bac757f..a8b0bafcd 100644
--- a/src/modules/httpd.h
+++ b/src/modules/httpd.h
@@ -1 +1,166 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "base.h" #ifndef __HTTPD_H__ #define __HTTPD_H__ #include <string> #include <sstream> /** This class represents a HTTP request. * It will be sent to all modules as the data section of * an Event. */ class HTTPRequest : public classbase { protected: std::string type; std::string document; std::string ipaddr; std::string postdata; std::stringstream* headers; public: /** A socket pointer, which you must return in your HTTPDocument class * if you reply to this request. */ void* 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, std::stringstream* hdr, void* opaque, const std::string &ip, const std::string &pdata) : type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(opaque) { } /** Get headers. * All the headers for the web request are returned, as a pointer to a stringstream. * @return The header information */ std::stringstream* GetHeaders() { return headers; } /** 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 HTTPDocument : public classbase { protected: std::stringstream* document; int responsecode; std::string extraheaders; public: /** The socket pointer from an earlier HTTPRequest */ void* sock; /** 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. */ HTTPDocument(void* opaque, std::stringstream* doc, int response, const std::string &extra) : document(doc), responsecode(response), extraheaders(extra), sock(opaque) { } /** Get the document text. * @return The document text */ std::stringstream* GetDocument() { return this->document; } /** Get the document size. * @return the size of the document text in bytes */ unsigned long GetDocumentSize() { return this->document->str().length(); } /** Get the response code. * @return The response code */ int GetResponseCode() { return this->responsecode; } /** Get the headers. * @return The header text, headers seperated by carriage return and linefeed. */ std::string& GetExtraHeaders() { return this->extraheaders; } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "base.h"
+
+#ifndef __HTTPD_H__
+#define __HTTPD_H__
+
+#include <string>
+#include <sstream>
+
+/** This class represents a HTTP request.
+ * It will be sent to all modules as the data section of
+ * an Event.
+ */
+class HTTPRequest : public classbase
+{
+ protected:
+
+ std::string type;
+ std::string document;
+ std::string ipaddr;
+ std::string postdata;
+ std::stringstream* headers;
+
+ public:
+
+ /** A socket pointer, which you must return in your HTTPDocument class
+ * if you reply to this request.
+ */
+ void* 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, std::stringstream* hdr, void* opaque, const std::string &ip, const std::string &pdata)
+ : type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(opaque)
+ {
+ }
+
+ /** Get headers.
+ * All the headers for the web request are returned, as a pointer to a stringstream.
+ * @return The header information
+ */
+ std::stringstream* GetHeaders()
+ {
+ return headers;
+ }
+
+ /** 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 HTTPDocument : public classbase
+{
+ protected:
+
+ std::stringstream* document;
+ int responsecode;
+ std::string extraheaders;
+
+ public:
+
+ /** The socket pointer from an earlier HTTPRequest
+ */
+ void* sock;
+
+ /** 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.
+ */
+ HTTPDocument(void* opaque, std::stringstream* doc, int response, const std::string &extra) : document(doc), responsecode(response), extraheaders(extra), sock(opaque)
+ {
+ }
+
+ /** Get the document text.
+ * @return The document text
+ */
+ std::stringstream* GetDocument()
+ {
+ return this->document;
+ }
+
+ /** Get the document size.
+ * @return the size of the document text in bytes
+ */
+ unsigned long GetDocumentSize()
+ {
+ return this->document->str().length();
+ }
+
+ /** Get the response code.
+ * @return The response code
+ */
+ int GetResponseCode()
+ {
+ return this->responsecode;
+ }
+
+ /** Get the headers.
+ * @return The header text, headers seperated by carriage return and linefeed.
+ */
+ std::string& GetExtraHeaders()
+ {
+ return this->extraheaders;
+ }
+};
+
+#endif
+
diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp
index 039aeed92..94c64b405 100644
--- a/src/modules/m_alias.cpp
+++ b/src/modules/m_alias.cpp
@@ -1 +1,272 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" /* $ModDesc: Provides aliases of commands. */ /** An alias definition */ class Alias : public classbase { public: /** The text of the alias command */ irc::string text; /** Text to replace with */ std::string replace_with; /** Nickname required to perform alias */ std::string requires; /** Alias requires ulined server */ bool uline; /** Requires oper? */ bool operonly; /* is case sensitive params */ bool case_sensitive; /** Format that must be matched for use */ std::string format; }; class ModuleAlias : public Module { private: /** We cant use a map, there may be multiple aliases with the same name */ std::vector<Alias> Aliases; std::map<std::string, int> AliasMap; std::vector<std::string> pars; virtual void ReadAliases() { ConfigReader MyConf(ServerInstance); Aliases.clear(); AliasMap.clear(); for (int i = 0; i < MyConf.Enumerate("alias"); i++) { Alias a; std::string txt; txt = MyConf.ReadValue("alias", "text", i); a.text = txt.c_str(); a.replace_with = MyConf.ReadValue("alias", "replace", i, true); a.requires = MyConf.ReadValue("alias", "requires", i); a.uline = MyConf.ReadFlag("alias", "uline", i); a.operonly = MyConf.ReadFlag("alias", "operonly", i); a.format = MyConf.ReadValue("alias", "format", i); a.case_sensitive = MyConf.ReadFlag("alias", "matchcase", i); Aliases.push_back(a); AliasMap[txt] = 1; } } public: ModuleAlias(InspIRCd* Me) : Module(Me) { ReadAliases(); pars.resize(127); } void Implements(char* List) { List[I_OnPreCommand] = List[I_OnRehash] = 1; } virtual ~ModuleAlias() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } std::string GetVar(std::string varname, const std::string &original_line) { irc::spacesepstream ss(original_line); varname.erase(varname.begin()); int index = *(varname.begin()) - 48; varname.erase(varname.begin()); bool everything_after = (varname == "-"); std::string word; for (int j = 0; j < index; j++) word = ss.GetToken(); if (everything_after) { std::string more = "*"; while ((more = ss.GetToken()) != "") { word.append(" "); word.append(more); } } return word; } void SearchAndReplace(std::string& newline, const std::string &find, const std::string &replace) { std::string::size_type x = newline.find(find); while (x != std::string::npos) { newline.erase(x, find.length()); newline.insert(x, replace); x = newline.find(find); } } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { userrec *u = NULL; /* If theyre not registered yet, we dont want * to know. */ if (user->registered != REG_ALL) return 0; /* We dont have any commands looking like this, dont bother with the loop */ if (AliasMap.find(command) == AliasMap.end()) return 0; 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()); while (*(compare.c_str()) == ' ') compare.erase(compare.begin()); std::string safe(original_line); /* Escape out any $ symbols in the user provided text */ SearchAndReplace(safe, "$", "\r"); for (unsigned int i = 0; i < Aliases.size(); i++) { if (Aliases[i].text == c) { /* Does it match the pattern? */ if (!Aliases[i].format.empty()) { if (!match(Aliases[i].case_sensitive, compare.c_str(), Aliases[i].format.c_str())) continue; } if ((Aliases[i].operonly) && (!IS_OPER(user))) return 0; if (!Aliases[i].requires.empty()) { u = ServerInstance->FindNick(Aliases[i].requires); if (!u) { user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is currently unavailable. Please try again later."); return 1; } } if ((u != NULL) && (!Aliases[i].requires.empty()) && (Aliases[i].uline)) { if (!ServerInstance->ULine(u->server)) { ServerInstance->WriteOpers("*** NOTICE -- Service "+Aliases[i].requires+" required by alias "+std::string(Aliases[i].text.c_str())+" is not on a u-lined server, possibly underhanded antics detected!"); user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is an imposter! Please inform an IRC operator as soon as possible."); return 1; } } /* Now, search and replace in a copy of the original_line, replacing $1 through $9 and $1- etc */ std::string::size_type crlf = Aliases[i].replace_with.find('\n'); if (crlf == std::string::npos) { DoCommand(Aliases[i].replace_with, user, safe); return 1; } else { irc::sepstream commands(Aliases[i].replace_with, '\n'); std::string command = "*"; while ((command = commands.GetToken()) != "") { DoCommand(command, user, safe); } return 1; } } } return 0; } void DoCommand(std::string newline, userrec* user, const std::string &original_line) { for (int v = 1; v < 10; v++) { std::string var = "$"; var.append(ConvToStr(v)); var.append("-"); std::string::size_type x = newline.find(var); while (x != std::string::npos) { newline.erase(x, var.length()); newline.insert(x, GetVar(var, original_line)); x = newline.find(var); } var = "$"; var.append(ConvToStr(v)); x = newline.find(var); while (x != std::string::npos) { newline.erase(x, var.length()); newline.insert(x, GetVar(var, original_line)); x = newline.find(var); } } /* Special variables */ SearchAndReplace(newline, "$nick", user->nick); SearchAndReplace(newline, "$ident", user->ident); SearchAndReplace(newline, "$host", user->host); SearchAndReplace(newline, "$vhost", user->dhost); /* Unescape any variable names in the user text before sending */ SearchAndReplace(newline, "\r", "$"); irc::tokenstream ss(newline); const char* parv[127]; int x = 0; while (ss.GetToken(pars[x])) { parv[x] = pars[x].c_str(); x++; } ServerInstance->Parser->CallHandler(parv[0], &parv[1], x-1, user); } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadAliases(); } }; MODULE_INIT(ModuleAlias) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides aliases of commands. */
+
+/** An alias definition
+ */
+class Alias : public classbase
+{
+ public:
+ /** The text of the alias command */
+ irc::string text;
+ /** Text to replace with */
+ std::string replace_with;
+ /** Nickname required to perform alias */
+ std::string requires;
+ /** Alias requires ulined server */
+ bool uline;
+ /** Requires oper? */
+ bool operonly;
+ /* is case sensitive params */
+ bool case_sensitive;
+ /** Format that must be matched for use */
+ std::string format;
+};
+
+class ModuleAlias : public Module
+{
+ private:
+ /** We cant use a map, there may be multiple aliases with the same name */
+ std::vector<Alias> Aliases;
+ std::map<std::string, int> AliasMap;
+ std::vector<std::string> pars;
+
+ virtual void ReadAliases()
+ {
+ ConfigReader MyConf(ServerInstance);
+
+ Aliases.clear();
+ AliasMap.clear();
+ for (int i = 0; i < MyConf.Enumerate("alias"); i++)
+ {
+ Alias a;
+ std::string txt;
+ txt = MyConf.ReadValue("alias", "text", i);
+ a.text = txt.c_str();
+ a.replace_with = MyConf.ReadValue("alias", "replace", i, true);
+ a.requires = MyConf.ReadValue("alias", "requires", i);
+ a.uline = MyConf.ReadFlag("alias", "uline", i);
+ a.operonly = MyConf.ReadFlag("alias", "operonly", i);
+ a.format = MyConf.ReadValue("alias", "format", i);
+ a.case_sensitive = MyConf.ReadFlag("alias", "matchcase", i);
+ Aliases.push_back(a);
+ AliasMap[txt] = 1;
+ }
+ }
+
+ public:
+
+ ModuleAlias(InspIRCd* Me)
+ : Module(Me)
+ {
+ ReadAliases();
+ pars.resize(127);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPreCommand] = List[I_OnRehash] = 1;
+ }
+
+ virtual ~ModuleAlias()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ std::string GetVar(std::string varname, const std::string &original_line)
+ {
+ irc::spacesepstream ss(original_line);
+ varname.erase(varname.begin());
+ int index = *(varname.begin()) - 48;
+ varname.erase(varname.begin());
+ bool everything_after = (varname == "-");
+ std::string word;
+
+ for (int j = 0; j < index; j++)
+ word = ss.GetToken();
+
+ if (everything_after)
+ {
+ std::string more = "*";
+ while ((more = ss.GetToken()) != "")
+ {
+ word.append(" ");
+ word.append(more);
+ }
+ }
+
+ return word;
+ }
+
+ void SearchAndReplace(std::string& newline, const std::string &find, const std::string &replace)
+ {
+ std::string::size_type x = newline.find(find);
+ while (x != std::string::npos)
+ {
+ newline.erase(x, find.length());
+ newline.insert(x, replace);
+ x = newline.find(find);
+ }
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ userrec *u = NULL;
+
+ /* If theyre not registered yet, we dont want
+ * to know.
+ */
+ if (user->registered != REG_ALL)
+ return 0;
+
+ /* We dont have any commands looking like this, dont bother with the loop */
+ if (AliasMap.find(command) == AliasMap.end())
+ return 0;
+
+ 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());
+ while (*(compare.c_str()) == ' ')
+ compare.erase(compare.begin());
+
+ std::string safe(original_line);
+
+ /* Escape out any $ symbols in the user provided text */
+
+ SearchAndReplace(safe, "$", "\r");
+
+ for (unsigned int i = 0; i < Aliases.size(); i++)
+ {
+ if (Aliases[i].text == c)
+ {
+ /* Does it match the pattern? */
+ if (!Aliases[i].format.empty())
+ {
+ if (!match(Aliases[i].case_sensitive, compare.c_str(), Aliases[i].format.c_str()))
+ continue;
+ }
+
+ if ((Aliases[i].operonly) && (!IS_OPER(user)))
+ return 0;
+
+ if (!Aliases[i].requires.empty())
+ {
+ u = ServerInstance->FindNick(Aliases[i].requires);
+ if (!u)
+ {
+ user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is currently unavailable. Please try again later.");
+ return 1;
+ }
+ }
+ if ((u != NULL) && (!Aliases[i].requires.empty()) && (Aliases[i].uline))
+ {
+ if (!ServerInstance->ULine(u->server))
+ {
+ ServerInstance->WriteOpers("*** NOTICE -- Service "+Aliases[i].requires+" required by alias "+std::string(Aliases[i].text.c_str())+" is not on a u-lined server, possibly underhanded antics detected!");
+ user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is an imposter! Please inform an IRC operator as soon as possible.");
+ return 1;
+ }
+ }
+
+ /* Now, search and replace in a copy of the original_line, replacing $1 through $9 and $1- etc */
+
+ std::string::size_type crlf = Aliases[i].replace_with.find('\n');
+
+ if (crlf == std::string::npos)
+ {
+ DoCommand(Aliases[i].replace_with, user, safe);
+ return 1;
+ }
+ else
+ {
+ irc::sepstream commands(Aliases[i].replace_with, '\n');
+ std::string command = "*";
+ while ((command = commands.GetToken()) != "")
+ {
+ DoCommand(command, user, safe);
+ }
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ void DoCommand(std::string newline, userrec* user, const std::string &original_line)
+ {
+ for (int v = 1; v < 10; v++)
+ {
+ std::string var = "$";
+ var.append(ConvToStr(v));
+ var.append("-");
+ std::string::size_type x = newline.find(var);
+
+ while (x != std::string::npos)
+ {
+ newline.erase(x, var.length());
+ newline.insert(x, GetVar(var, original_line));
+ x = newline.find(var);
+ }
+
+ var = "$";
+ var.append(ConvToStr(v));
+ x = newline.find(var);
+
+ while (x != std::string::npos)
+ {
+ newline.erase(x, var.length());
+ newline.insert(x, GetVar(var, original_line));
+ x = newline.find(var);
+ }
+ }
+
+ /* Special variables */
+ SearchAndReplace(newline, "$nick", user->nick);
+ SearchAndReplace(newline, "$ident", user->ident);
+ SearchAndReplace(newline, "$host", user->host);
+ SearchAndReplace(newline, "$vhost", user->dhost);
+
+ /* Unescape any variable names in the user text before sending */
+ SearchAndReplace(newline, "\r", "$");
+
+ irc::tokenstream ss(newline);
+ const char* parv[127];
+ int x = 0;
+
+ while (ss.GetToken(pars[x]))
+ {
+ parv[x] = pars[x].c_str();
+ x++;
+ }
+
+ ServerInstance->Parser->CallHandler(parv[0], &parv[1], x-1, user);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadAliases();
+ }
+};
+
+MODULE_INIT(ModuleAlias)
diff --git a/src/modules/m_alltime.cpp b/src/modules/m_alltime.cpp
index 6a3f27ba8..97ab6a3fe 100644
--- a/src/modules/m_alltime.cpp
+++ b/src/modules/m_alltime.cpp
@@ -1 +1,83 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "modules.h" /* $ModDesc: Display timestamps from all servers connected to the network */ class cmd_alltime : public command_t { public: cmd_alltime(InspIRCd *Instance) : command_t(Instance, "ALLTIME", 'o', 0) { this->source = "m_alltime.so"; syntax.clear(); } CmdResult Handle(const char **parameters, int pcnt, userrec *user) { char fmtdate[64]; char fmtdate2[64]; time_t now = ServerInstance->Time(false); strftime(fmtdate, sizeof(fmtdate), "%F %T", gmtime(&now)); now = ServerInstance->Time(true); strftime(fmtdate2, sizeof(fmtdate2), "%F %T", gmtime(&now)); int delta = ServerInstance->GetTimeDelta(); string msg = ":" + string(ServerInstance->Config->ServerName) + " NOTICE " + user->nick + " :System time for " + ServerInstance->Config->ServerName + " is: " + fmtdate + " (delta " + ConvToStr(delta) + " seconds): Time with delta: "+ fmtdate2; if (IS_LOCAL(user)) { user->Write(msg); } else { deque<string> params; params.push_back(user->nick); params.push_back(msg); Event ev((char *) &params, NULL, "send_push"); ev.Send(ServerInstance); } /* we want this routed out! */ return CMD_SUCCESS; } }; class Modulealltime : public Module { cmd_alltime *mycommand; public: Modulealltime(InspIRCd *Me) : Module(Me) { mycommand = new cmd_alltime(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~Modulealltime() { } virtual Version GetVersion() { return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; MODULE_INIT(Modulealltime) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "modules.h"
+
+/* $ModDesc: Display timestamps from all servers connected to the network */
+
+class cmd_alltime : public command_t
+{
+ public:
+ cmd_alltime(InspIRCd *Instance) : command_t(Instance, "ALLTIME", 'o', 0)
+ {
+ this->source = "m_alltime.so";
+ syntax.clear();
+ }
+
+ CmdResult Handle(const char **parameters, int pcnt, userrec *user)
+ {
+ char fmtdate[64];
+ char fmtdate2[64];
+ time_t now = ServerInstance->Time(false);
+ strftime(fmtdate, sizeof(fmtdate), "%F %T", gmtime(&now));
+ now = ServerInstance->Time(true);
+ strftime(fmtdate2, sizeof(fmtdate2), "%F %T", gmtime(&now));
+
+ int delta = ServerInstance->GetTimeDelta();
+
+ string msg = ":" + string(ServerInstance->Config->ServerName) + " NOTICE " + user->nick + " :System time for " +
+ ServerInstance->Config->ServerName + " is: " + fmtdate + " (delta " + ConvToStr(delta) + " seconds): Time with delta: "+ fmtdate2;
+
+ if (IS_LOCAL(user))
+ {
+ user->Write(msg);
+ }
+ else
+ {
+ deque<string> params;
+ params.push_back(user->nick);
+ params.push_back(msg);
+ Event ev((char *) &params, NULL, "send_push");
+ ev.Send(ServerInstance);
+ }
+
+ /* we want this routed out! */
+ return CMD_SUCCESS;
+ }
+};
+
+
+class Modulealltime : public Module
+{
+ cmd_alltime *mycommand;
+ public:
+ Modulealltime(InspIRCd *Me)
+ : Module(Me)
+ {
+ mycommand = new cmd_alltime(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~Modulealltime()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+};
+
+MODULE_INIT(Modulealltime)
diff --git a/src/modules/m_antibear.cpp b/src/modules/m_antibear.cpp
index d95c70282..2718cbb4c 100644
--- a/src/modules/m_antibear.cpp
+++ b/src/modules/m_antibear.cpp
@@ -1 +1,78 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "xline.h" /* $ModDesc: Sends a numeric on connect which cripples a common type of trojan/spambot */ class ModuleAntiBear : public Module { private: public: ModuleAntiBear(InspIRCd* Me) : Module(Me) { } virtual ~ModuleAntiBear() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnUserRegister] = List[I_OnPreCommand] = 1; } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { if (command == "NOTICE" && !validated && pcnt > 1 && user->GetExt("antibear_timewait")) { if (!strncmp(parameters[1], "\1TIME Mon May 01 18:54:20 2006", 30)) { if (ServerInstance->XLines->add_zline(86400, ServerInstance->Config->ServerName, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP())) { ServerInstance->XLines->apply_lines(APPLY_ZLINES); FOREACH_MOD(I_OnAddGLine,OnAddZLine(86400, NULL, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP())); return 1; } } user->Shrink("antibear_timewait"); // Block the command, so the user doesn't receive a no such nick notice return 1; } return 0; } virtual int OnUserRegister(userrec* user) { user->WriteServ("439 %s :This server has anti-spambot mechanisms enabled.", user->nick); user->WriteServ("931 %s :Malicious bots, spammers, and other automated systems of dubious origin are NOT welcome here.", user->nick); user->WriteServ("PRIVMSG %s :\1TIME\1", user->nick); user->Extend("antibear_timewait"); return 0; } }; MODULE_INIT(ModuleAntiBear) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "xline.h"
+
+/* $ModDesc: Sends a numeric on connect which cripples a common type of trojan/spambot */
+
+class ModuleAntiBear : public Module
+{
+ private:
+
+ public:
+ ModuleAntiBear(InspIRCd* Me) : Module(Me)
+ {
+
+ }
+
+ virtual ~ModuleAntiBear()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserRegister] = List[I_OnPreCommand] = 1;
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ if (command == "NOTICE" && !validated && pcnt > 1 && user->GetExt("antibear_timewait"))
+ {
+ if (!strncmp(parameters[1], "\1TIME Mon May 01 18:54:20 2006", 30))
+ {
+ if (ServerInstance->XLines->add_zline(86400, ServerInstance->Config->ServerName, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP()))
+ {
+ ServerInstance->XLines->apply_lines(APPLY_ZLINES);
+ FOREACH_MOD(I_OnAddGLine,OnAddZLine(86400, NULL, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP()));
+ return 1;
+ }
+ }
+
+ user->Shrink("antibear_timewait");
+ // Block the command, so the user doesn't receive a no such nick notice
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ user->WriteServ("439 %s :This server has anti-spambot mechanisms enabled.", user->nick);
+ user->WriteServ("931 %s :Malicious bots, spammers, and other automated systems of dubious origin are NOT welcome here.", user->nick);
+ user->WriteServ("PRIVMSG %s :\1TIME\1", user->nick);
+ user->Extend("antibear_timewait");
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleAntiBear)
diff --git a/src/modules/m_antibottler.cpp b/src/modules/m_antibottler.cpp
index 907cfb39d..3aa5592cc 100644
--- a/src/modules/m_antibottler.cpp
+++ b/src/modules/m_antibottler.cpp
@@ -1 +1,99 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Changes the ident of connecting bottler clients to 'bottler' */ class ModuleAntiBottler : public Module { public: ModuleAntiBottler(InspIRCd* Me) : Module(Me) { } void Implements(char* List) { List[I_OnPreCommand] = 1; } virtual ~ModuleAntiBottler() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { char data[MAXBUF]; strlcpy(data,original_line.c_str(),MAXBUF); bool not_bottler = false; if (!strncmp(data,"user ",5)) { for (char* j = data; *j; j++) { if (*j == ':') break; if (*j == '"') { not_bottler = true; } } // Bug Fix (#14) -- FCS if (!(data) || !(*data)) return 0; strtok(data," "); char *ident = strtok(NULL," "); char *local = strtok(NULL," "); char *remote = strtok(NULL," :"); char *gecos = strtok(NULL,"\r\n"); if (!ident || !local || !remote || !gecos) return 0; for (char* j = remote; *j; j++) { if (((*j < '0') || (*j > '9')) && (*j != '.')) { not_bottler = true; } } if (!not_bottler) { std::string strgecos = std::string(gecos) + "[Possible bottler, ident: " + std::string(ident) + "]"; const char* modified[4]; modified[0] = "bottler"; modified[1] = local; modified[2] = remote; modified[3] = strgecos.c_str(); ServerInstance->Parser->CallHandler("USER", modified, 4, user); return 1; } } return 0; } }; MODULE_INIT(ModuleAntiBottler) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Changes the ident of connecting bottler clients to 'bottler' */
+
+class ModuleAntiBottler : public Module
+{
+ public:
+ ModuleAntiBottler(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPreCommand] = 1;
+ }
+
+
+ virtual ~ModuleAntiBottler()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ char data[MAXBUF];
+ strlcpy(data,original_line.c_str(),MAXBUF);
+ bool not_bottler = false;
+ if (!strncmp(data,"user ",5))
+ {
+ for (char* j = data; *j; j++)
+ {
+ if (*j == ':')
+ break;
+
+ if (*j == '"')
+ {
+ not_bottler = true;
+ }
+ }
+ // Bug Fix (#14) -- FCS
+ if (!(data) || !(*data))
+ return 0;
+
+ strtok(data," ");
+ char *ident = strtok(NULL," ");
+ char *local = strtok(NULL," ");
+ char *remote = strtok(NULL," :");
+ char *gecos = strtok(NULL,"\r\n");
+
+ if (!ident || !local || !remote || !gecos)
+ return 0;
+
+ for (char* j = remote; *j; j++)
+ {
+ if (((*j < '0') || (*j > '9')) && (*j != '.'))
+ {
+ not_bottler = true;
+ }
+ }
+
+ if (!not_bottler)
+ {
+ std::string strgecos = std::string(gecos) + "[Possible bottler, ident: " + std::string(ident) + "]";
+ const char* modified[4];
+ modified[0] = "bottler";
+ modified[1] = local;
+ modified[2] = remote;
+ modified[3] = strgecos.c_str();
+ ServerInstance->Parser->CallHandler("USER", modified, 4, user);
+ return 1;
+ }
+ }
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleAntiBottler)
diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp
index 419738ea7..6d709e431 100644
--- a/src/modules/m_auditorium.cpp
+++ b/src/modules/m_auditorium.cpp
@@ -1 +1,191 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */ class AuditoriumMode : public ModeHandler { public: AuditoriumMode(InspIRCd* Instance) : ModeHandler(Instance, 'u', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (channel->IsModeSet('u') != adding) { if (IS_LOCAL(source) && (channel->GetStatus(source) < STATUS_OP)) { source->WriteServ("482 %s %s :Only channel operators may %sset channel mode +u", source->nick, channel->name, adding ? "" : "un"); return MODEACTION_DENY; } else { channel->SetMode('u', adding); return MODEACTION_ALLOW; } } else { return MODEACTION_DENY; } } }; class ModuleAuditorium : public Module { private: AuditoriumMode* aum; bool ShowOps; CUList nl; CUList except_list; public: ModuleAuditorium(InspIRCd* Me) : Module(Me) { aum = new AuditoriumMode(ServerInstance); if (!ServerInstance->AddMode(aum, 'u')) throw ModuleException("Could not add new modes!"); OnRehash(NULL, ""); } virtual ~ModuleAuditorium() { ServerInstance->Modes->DelMode(aum); DELETE(aum); } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader conf(ServerInstance); ShowOps = conf.ReadFlag("auditorium", "showops", 0); } Priority Prioritize() { /* To ensure that we get priority over namesx for names list generation on +u channels */ return (Priority)ServerInstance->PriorityBefore("m_namesx.so"); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserKick] = List[I_OnUserQuit] = List[I_OnUserList] = List[I_OnRehash] = 1; } virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &nameslist) { if (Ptr->IsModeSet('u')) { if (ShowOps) { /* Leave the names list alone, theyre an op * doing /names on the channel after joining it */ if (Ptr->GetStatus(user) >= STATUS_OP) { nameslist = Ptr->GetUsers(); return 0; } /* Show all the opped users */ nl = *(Ptr->GetOppedUsers()); nl[user] = user->nick; nameslist = &nl; return 0; } else { /* HELLOOO, IS ANYBODY THERE? -- nope, just us. */ user->WriteServ("353 %s = %s :%s", user->nick, Ptr->name, user->nick); user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name); return 1; } } return 0; } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { if (channel->IsModeSet('u')) { silent = true; /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */ user->WriteFrom(user, "JOIN %s", channel->name); if (ShowOps) channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "JOIN %s", channel->name); } } void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { if (channel->IsModeSet('u')) { silent = true; /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */ user->WriteFrom(user, "PART %s%s%s", channel->name, partmessage.empty() ? "" : " :", partmessage.empty() ? "" : partmessage.c_str()); if (ShowOps) { channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "PART %s%s%s", channel->name, partmessage.empty() ? "" : " :", partmessage.empty() ? "" : partmessage.c_str()); } } } void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { if (chan->IsModeSet('u')) { silent = true; /* Send silenced event only to the user being kicked and the user doing the kick */ source->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str()); if (ShowOps) chan->WriteAllExcept(source, false, chan->GetStatus(source) >= STATUS_OP ? 0 : '@', except_list, "KICK %s %s %s", chan->name, user->nick, reason.c_str()); else user->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str()); } } void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { command_t* parthandler = ServerInstance->Parser->GetHandler("PART"); std::vector<std::string> to_leave; const char* parameters[2]; if (parthandler) { for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++) { if (f->first->IsModeSet('u')) to_leave.push_back(f->first->name); } /* We cant do this neatly in one loop, as we are modifying the map we are iterating */ for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++) { parameters[0] = n->c_str(); /* This triggers our OnUserPart, above, making the PART silent */ parthandler->Handle(parameters, 1, user); } } } }; MODULE_INIT(ModuleAuditorium) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */
+
+class AuditoriumMode : public ModeHandler
+{
+ public:
+ AuditoriumMode(InspIRCd* Instance) : ModeHandler(Instance, 'u', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (channel->IsModeSet('u') != adding)
+ {
+ if (IS_LOCAL(source) && (channel->GetStatus(source) < STATUS_OP))
+ {
+ source->WriteServ("482 %s %s :Only channel operators may %sset channel mode +u", source->nick, channel->name, adding ? "" : "un");
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ channel->SetMode('u', adding);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+ }
+};
+
+class ModuleAuditorium : public Module
+{
+ private:
+ AuditoriumMode* aum;
+ bool ShowOps;
+ CUList nl;
+ CUList except_list;
+ public:
+ ModuleAuditorium(InspIRCd* Me)
+ : Module(Me)
+ {
+ aum = new AuditoriumMode(ServerInstance);
+ if (!ServerInstance->AddMode(aum, 'u'))
+ throw ModuleException("Could not add new modes!");
+ OnRehash(NULL, "");
+ }
+
+ virtual ~ModuleAuditorium()
+ {
+ ServerInstance->Modes->DelMode(aum);
+ DELETE(aum);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader conf(ServerInstance);
+ ShowOps = conf.ReadFlag("auditorium", "showops", 0);
+ }
+
+ Priority Prioritize()
+ {
+ /* To ensure that we get priority over namesx for names list generation on +u channels */
+ return (Priority)ServerInstance->PriorityBefore("m_namesx.so");
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserKick] = List[I_OnUserQuit] = List[I_OnUserList] = List[I_OnRehash] = 1;
+ }
+
+ virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &nameslist)
+ {
+ if (Ptr->IsModeSet('u'))
+ {
+ if (ShowOps)
+ {
+ /* Leave the names list alone, theyre an op
+ * doing /names on the channel after joining it
+ */
+ if (Ptr->GetStatus(user) >= STATUS_OP)
+ {
+ nameslist = Ptr->GetUsers();
+ return 0;
+ }
+
+ /* Show all the opped users */
+ nl = *(Ptr->GetOppedUsers());
+ nl[user] = user->nick;
+ nameslist = &nl;
+ return 0;
+ }
+ else
+ {
+ /* HELLOOO, IS ANYBODY THERE? -- nope, just us. */
+ user->WriteServ("353 %s = %s :%s", user->nick, Ptr->name, user->nick);
+ user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ if (channel->IsModeSet('u'))
+ {
+ silent = true;
+ /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */
+ user->WriteFrom(user, "JOIN %s", channel->name);
+ if (ShowOps)
+ channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "JOIN %s", channel->name);
+ }
+ }
+
+ void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
+ {
+ if (channel->IsModeSet('u'))
+ {
+ silent = true;
+ /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */
+ user->WriteFrom(user, "PART %s%s%s", channel->name,
+ partmessage.empty() ? "" : " :",
+ partmessage.empty() ? "" : partmessage.c_str());
+ if (ShowOps)
+ {
+ channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "PART %s%s%s", channel->name, partmessage.empty() ? "" : " :",
+ partmessage.empty() ? "" : partmessage.c_str());
+ }
+ }
+ }
+
+ void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
+ {
+ if (chan->IsModeSet('u'))
+ {
+ silent = true;
+ /* Send silenced event only to the user being kicked and the user doing the kick */
+ source->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str());
+ if (ShowOps)
+ chan->WriteAllExcept(source, false, chan->GetStatus(source) >= STATUS_OP ? 0 : '@', except_list, "KICK %s %s %s", chan->name, user->nick, reason.c_str());
+ else
+ user->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str());
+ }
+ }
+
+ void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ command_t* parthandler = ServerInstance->Parser->GetHandler("PART");
+ std::vector<std::string> to_leave;
+ const char* parameters[2];
+ if (parthandler)
+ {
+ for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++)
+ {
+ if (f->first->IsModeSet('u'))
+ to_leave.push_back(f->first->name);
+ }
+ /* We cant do this neatly in one loop, as we are modifying the map we are iterating */
+ for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++)
+ {
+ parameters[0] = n->c_str();
+ /* This triggers our OnUserPart, above, making the PART silent */
+ parthandler->Handle(parameters, 1, user);
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleAuditorium)
diff --git a/src/modules/m_banexception.cpp b/src/modules/m_banexception.cpp
index 8a73e6070..0cd03a08b 100644
--- a/src/modules/m_banexception.cpp
+++ b/src/modules/m_banexception.cpp
@@ -1 +1,153 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "mode.h" #include "u_listmode.h" #include "wildcard.h" /* $ModDesc: Provides support for the +e channel mode */ /* $ModDep: ../../include/u_listmode.h */ /* Written by Om<om@inspircd.org>, April 2005. */ /* Rewritten to use the listmode utility by Om, December 2005 */ /* Adapted from m_exception, which was originally based on m_chanprotect and m_silence */ // The +e channel mode takes a nick!ident@host, glob patterns allowed, // and if a user matches an entry on the +e list then they can join the channel, overriding any (+b) bans set on them // Now supports CIDR and IP addresses -- Brain /** Handles +e channel mode */ class BanException : public ListModeBase { public: BanException(InspIRCd* Instance) : ListModeBase(Instance, 'e', "End of Channel Exception List", "348", "349", true) { } }; class ModuleBanException : public Module { BanException* be; public: ModuleBanException(InspIRCd* Me) : Module(Me) { be = new BanException(ServerInstance); if (!ServerInstance->AddMode(be, 'e')) throw ModuleException("Could not add new modes!"); ServerInstance->PublishInterface("ChannelBanList", this); } virtual void Implements(char* List) { be->DoImplements(List); List[I_OnRehash] = List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckBan] = 1; } virtual void On005Numeric(std::string &output) { output.append(" EXCEPTS=e"); } virtual int OnCheckBan(userrec* user, chanrec* chan) { if (chan != NULL) { modelist* list; chan->GetExt(be->GetInfoKey(), list); if (list) { char mask[MAXBUF]; snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString()); for (modelist::iterator it = list->begin(); it != list->end(); it++) { if (match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) { // They match an entry on the list, so let them in. return 1; } } return 0; } // or if there wasn't a list, there can't be anyone on it, so we don't need to do anything. } return 0; } virtual void OnCleanup(int target_type, void* item) { be->DoCleanup(target_type, item); } virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { be->DoSyncChannel(chan, proto, opaque); } virtual void OnChannelDelete(chanrec* chan) { be->DoChannelDelete(chan); } virtual void OnRehash(userrec* user, const std::string &param) { be->DoRehash(); } virtual char* OnRequest(Request* request) { ListModeRequest* LM = (ListModeRequest*)request; if (strcmp("LM_CHECKLIST", request->GetId()) == 0) { modelist* list; LM->chan->GetExt(be->GetInfoKey(), list); if (list) { char mask[MAXBUF]; snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString()); for (modelist::iterator it = list->begin(); it != list->end(); it++) { if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) { // They match an entry return (char*)it->mask.c_str(); } } return NULL; } } return NULL; } virtual Version GetVersion() { return Version(1, 1, 0, 3, VF_COMMON | VF_VENDOR, API_VERSION); } virtual ~ModuleBanException() { ServerInstance->Modes->DelMode(be); DELETE(be); ServerInstance->UnpublishInterface("ChannelBanList", this); } }; MODULE_INIT(ModuleBanException) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "mode.h"
+#include "u_listmode.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides support for the +e channel mode */
+/* $ModDep: ../../include/u_listmode.h */
+
+/* Written by Om<om@inspircd.org>, April 2005. */
+/* Rewritten to use the listmode utility by Om, December 2005 */
+/* Adapted from m_exception, which was originally based on m_chanprotect and m_silence */
+
+// The +e channel mode takes a nick!ident@host, glob patterns allowed,
+// and if a user matches an entry on the +e list then they can join the channel, overriding any (+b) bans set on them
+// Now supports CIDR and IP addresses -- Brain
+
+
+/** Handles +e channel mode
+ */
+class BanException : public ListModeBase
+{
+ public:
+ BanException(InspIRCd* Instance) : ListModeBase(Instance, 'e', "End of Channel Exception List", "348", "349", true) { }
+};
+
+
+class ModuleBanException : public Module
+{
+ BanException* be;
+
+
+public:
+ ModuleBanException(InspIRCd* Me)
+ : Module(Me)
+ {
+ be = new BanException(ServerInstance);
+ if (!ServerInstance->AddMode(be, 'e'))
+ throw ModuleException("Could not add new modes!");
+ ServerInstance->PublishInterface("ChannelBanList", this);
+ }
+
+ virtual void Implements(char* List)
+ {
+ be->DoImplements(List);
+ List[I_OnRehash] = List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckBan] = 1;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" EXCEPTS=e");
+ }
+
+ virtual int OnCheckBan(userrec* user, chanrec* chan)
+ {
+ if (chan != NULL)
+ {
+ modelist* list;
+ chan->GetExt(be->GetInfoKey(), list);
+
+ if (list)
+ {
+ char mask[MAXBUF];
+ snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString());
+ for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ {
+ if (match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
+ {
+ // They match an entry on the list, so let them in.
+ return 1;
+ }
+ }
+ return 0;
+ }
+ // or if there wasn't a list, there can't be anyone on it, so we don't need to do anything.
+ }
+ return 0;
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ be->DoCleanup(target_type, item);
+ }
+
+ virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
+ {
+ be->DoSyncChannel(chan, proto, opaque);
+ }
+
+ virtual void OnChannelDelete(chanrec* chan)
+ {
+ be->DoChannelDelete(chan);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ be->DoRehash();
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ ListModeRequest* LM = (ListModeRequest*)request;
+ if (strcmp("LM_CHECKLIST", request->GetId()) == 0)
+ {
+ modelist* list;
+ LM->chan->GetExt(be->GetInfoKey(), list);
+ if (list)
+ {
+ char mask[MAXBUF];
+ snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString());
+ for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ {
+ if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
+ {
+ // They match an entry
+ return (char*)it->mask.c_str();
+ }
+ }
+ return NULL;
+ }
+ }
+ return NULL;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 3, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ virtual ~ModuleBanException()
+ {
+ ServerInstance->Modes->DelMode(be);
+ DELETE(be);
+ ServerInstance->UnpublishInterface("ChannelBanList", this);
+ }
+};
+
+MODULE_INIT(ModuleBanException)
diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp
index 2a0ed2ded..1764e6f56 100644
--- a/src/modules/m_banredirect.cpp
+++ b/src/modules/m_banredirect.cpp
@@ -1 +1,343 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "mode.h" #include "users.h" #include "channels.h" #include "modules.h" #include "u_listmode.h" /* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */ /* Originally written by Om, January 2007 */ class BanRedirectEntry { public: std::string targetchan; std::string banmask; BanRedirectEntry(const std::string &target = "", const std::string &mask = "") : targetchan(target), banmask(mask) { } }; typedef std::vector<BanRedirectEntry> BanRedirectList; typedef std::deque<std::string> StringDeque; class BanRedirect : public ModeWatcher { private: InspIRCd* Srv; public: BanRedirect(InspIRCd* Instance) : ModeWatcher(Instance, 'b', MODETYPE_CHANNEL), Srv(Instance) { } bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &param, bool adding, ModeType type) { /* nick!ident@host -> nick!ident@host * nick!ident@host#chan -> nick!ident@host#chan * nick@host#chan -> nick!*@host#chan * nick!ident#chan -> nick!ident@*#chan * nick#chan -> nick!*@*#chan */ if(channel && (type == MODETYPE_CHANNEL) && param.length()) { 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(channel->bans.size() > static_cast<unsigned>(maxbans)) { source->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)", source->nick, channel->name, channel->name, maxbans); return false; } for(std::string::iterator curr = start_pos; curr != param.end(); curr++) { switch(*curr) { case '!': mask[current].assign(start_pos, curr); current = IDENT; start_pos = curr+1; break; case '@': mask[current].assign(start_pos, curr); current = HOST; start_pos = curr+1; break; case '#': mask[current].assign(start_pos, curr); current = CHAN; start_pos = curr; break; } } if(mask[current].empty()) { mask[current].assign(start_pos, param.end()); } /* nick@host wants to be changed to *!nick@host rather than nick!*@host... */ if(mask[NICK].length() && mask[HOST].length() && mask[IDENT].empty()) { /* std::string::swap() is fast - it runs in constant time */ mask[NICK].swap(mask[IDENT]); } for(int i = 0; i < 3; i++) { if(mask[i].empty()) { mask[i].assign("*"); } } param.assign(mask[NICK]).append(1, '!').append(mask[IDENT]).append(1, '@').append(mask[HOST]); if(mask[CHAN].length()) { if(Srv->IsChannel(mask[CHAN].c_str())) { if(irc::string(channel->name) == irc::string(mask[CHAN].c_str())) { source->WriteServ("690 %s %s :You cannot set a ban redirection to the channel the ban is on", source->nick, channel->name); return false; } else { if(adding) { /* It's a properly valid redirecting ban, and we're adding it */ if(!channel->GetExt("banredirects", redirects)) { redirects = new BanRedirectList; channel->Extend("banredirects", redirects); } /* Here 'param' doesn't have the channel on it yet */ redirects->push_back(BanRedirectEntry(mask[CHAN].c_str(), param.c_str())); /* Now it does */ param.append(mask[CHAN]); } else { /* Removing a ban, if there's no extensible there are no redirecting bans and we're fine. */ if(channel->GetExt("banredirects", redirects)) { /* But there were, so we need to remove the matching one if there is one */ for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++) { /* Ugly as fuck */ if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str()))) { redirects->erase(redir); if(redirects->empty()) { DELETE(redirects); channel->Shrink("banredirects"); } break; } } } /* Append the channel so the default +b handler can remove the entry too */ param.append(mask[CHAN]); } } } else { source->WriteServ("403 %s %s :Invalid channel name in redirection (%s)", source->nick, channel->name, mask[CHAN].c_str()); return false; } } } return true; } }; class ModuleBanRedirect : public Module { BanRedirect* re; bool nofollow; Module* ExceptionModule; public: ModuleBanRedirect(InspIRCd* Me) : Module(Me) { re = new BanRedirect(Me); nofollow = false; if(!ServerInstance->AddModeWatcher(re)) throw ModuleException("Could not add mode watcher"); OnRehash(NULL, ""); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserPreJoin] = List[I_OnChannelDelete] = List[I_OnCleanup] = 1; } virtual void OnChannelDelete(chanrec* chan) { OnCleanup(TYPE_CHANNEL, chan); } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_CHANNEL) { chanrec* chan = static_cast<chanrec*>(item); BanRedirectList* redirects; if(chan->GetExt("banredirects", redirects)) { irc::modestacker modestack(false); StringDeque stackresult; const char* mode_junk[MAXMODES+2]; userrec* myhorriblefakeuser = new userrec(ServerInstance); myhorriblefakeuser->SetFd(FD_MAGIC_NUMBER); mode_junk[0] = chan->name; for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++) { modestack.Push('b', i->targetchan.insert(0, i->banmask)); } for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++) { modestack.PushPlus(); modestack.Push('b', i->banmask); } while(modestack.GetStackedLine(stackresult)) { for(StringDeque::size_type i = 0; i < stackresult.size(); i++) { mode_junk[i+1] = stackresult[i].c_str(); } ServerInstance->SendMode(mode_junk, stackresult.size() + 1, myhorriblefakeuser); } DELETE(myhorriblefakeuser); DELETE(redirects); chan->Shrink("banredirects"); } } } virtual void OnRehash(userrec* user, const std::string &param) { ExceptionModule = ServerInstance->FindModule("m_banexception.so"); } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { /* This prevents recursion when a user sets multiple ban redirects in a chain * (thanks Potter) */ if (nofollow) return 0; /* Return 1 to prevent the join, 0 to allow it */ if (chan) { BanRedirectList* redirects; if(chan->GetExt("banredirects", redirects)) { /* We actually had some ban redirects to check */ /* This was replaced with user->MakeHostIP() when I had a snprintf(), but MakeHostIP() doesn't seem to add the nick. * Maybe we should have a GetFullIPHost() or something to match GetFullHost() and GetFullRealHost? */ if (ExceptionModule) { ListModeRequest n(this, ExceptionModule, user, chan); /* Users with ban exceptions are allowed to join without being redirected */ if (n.Send()) return 0; } std::string ipmask(user->nick); ipmask.append(1, '!').append(user->MakeHostIP()); for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++) { if(ServerInstance->MatchText(user->GetFullRealHost(), redir->banmask) || ServerInstance->MatchText(user->GetFullHost(), redir->banmask) || ServerInstance->MatchText(ipmask, redir->banmask)) { /* tell them they're banned and are being transferred */ chanrec* destchan = ServerInstance->FindChan(redir->targetchan); if(destchan && ServerInstance->FindModule("m_redirect.so") && destchan->IsModeSet('L') && destchan->limit && (destchan->GetUserCounter() >= destchan->limit)) { user->WriteServ("474 %s %s :Cannot join channel (You are banned)", user->nick, chan->name); return 1; } else { user->WriteServ("470 %s :You are banned from %s. You are being automatically redirected to %s", user->nick, chan->name, redir->targetchan.c_str()); nofollow = true; chanrec::JoinUser(ServerInstance, user, redir->targetchan.c_str(), false, "", ServerInstance->Time(true)); nofollow = false; return 1; } } } } } return 0; } virtual ~ModuleBanRedirect() { ServerInstance->Modes->DelModeWatcher(re); DELETE(re); } virtual Version GetVersion() { return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } Priority Prioritize() { return (Priority)ServerInstance->PriorityBefore("m_banexception.so"); } }; MODULE_INIT(ModuleBanRedirect) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "mode.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "u_listmode.h"
+
+/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */
+
+/* Originally written by Om, January 2007
+ */
+
+class BanRedirectEntry
+{
+ public:
+ std::string targetchan;
+ std::string banmask;
+
+ BanRedirectEntry(const std::string &target = "", const std::string &mask = "")
+ : targetchan(target), banmask(mask)
+ {
+ }
+};
+
+typedef std::vector<BanRedirectEntry> BanRedirectList;
+typedef std::deque<std::string> StringDeque;
+
+class BanRedirect : public ModeWatcher
+{
+ private:
+ InspIRCd* Srv;
+ public:
+ BanRedirect(InspIRCd* Instance)
+ : ModeWatcher(Instance, 'b', MODETYPE_CHANNEL), Srv(Instance)
+ {
+ }
+
+ bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &param, bool adding, ModeType type)
+ {
+ /* nick!ident@host -> nick!ident@host
+ * nick!ident@host#chan -> nick!ident@host#chan
+ * nick@host#chan -> nick!*@host#chan
+ * nick!ident#chan -> nick!ident@*#chan
+ * nick#chan -> nick!*@*#chan
+ */
+
+ if(channel && (type == MODETYPE_CHANNEL) && param.length())
+ {
+ 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(channel->bans.size() > static_cast<unsigned>(maxbans))
+ {
+ source->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)", source->nick, channel->name, channel->name, maxbans);
+ return false;
+ }
+
+ for(std::string::iterator curr = start_pos; curr != param.end(); curr++)
+ {
+ switch(*curr)
+ {
+ case '!':
+ mask[current].assign(start_pos, curr);
+ current = IDENT;
+ start_pos = curr+1;
+ break;
+ case '@':
+ mask[current].assign(start_pos, curr);
+ current = HOST;
+ start_pos = curr+1;
+ break;
+ case '#':
+ mask[current].assign(start_pos, curr);
+ current = CHAN;
+ start_pos = curr;
+ break;
+ }
+ }
+
+ if(mask[current].empty())
+ {
+ mask[current].assign(start_pos, param.end());
+ }
+
+ /* nick@host wants to be changed to *!nick@host rather than nick!*@host... */
+ if(mask[NICK].length() && mask[HOST].length() && mask[IDENT].empty())
+ {
+ /* std::string::swap() is fast - it runs in constant time */
+ mask[NICK].swap(mask[IDENT]);
+ }
+
+ for(int i = 0; i < 3; i++)
+ {
+ if(mask[i].empty())
+ {
+ mask[i].assign("*");
+ }
+ }
+
+ param.assign(mask[NICK]).append(1, '!').append(mask[IDENT]).append(1, '@').append(mask[HOST]);
+
+ if(mask[CHAN].length())
+ {
+ if(Srv->IsChannel(mask[CHAN].c_str()))
+ {
+ if(irc::string(channel->name) == irc::string(mask[CHAN].c_str()))
+ {
+ source->WriteServ("690 %s %s :You cannot set a ban redirection to the channel the ban is on", source->nick, channel->name);
+ return false;
+ }
+ else
+ {
+ if(adding)
+ {
+ /* It's a properly valid redirecting ban, and we're adding it */
+ if(!channel->GetExt("banredirects", redirects))
+ {
+ redirects = new BanRedirectList;
+ channel->Extend("banredirects", redirects);
+ }
+
+ /* Here 'param' doesn't have the channel on it yet */
+ redirects->push_back(BanRedirectEntry(mask[CHAN].c_str(), param.c_str()));
+
+ /* Now it does */
+ param.append(mask[CHAN]);
+ }
+ else
+ {
+ /* Removing a ban, if there's no extensible there are no redirecting bans and we're fine. */
+ if(channel->GetExt("banredirects", redirects))
+ {
+ /* But there were, so we need to remove the matching one if there is one */
+
+ for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
+ {
+ /* Ugly as fuck */
+ if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str())))
+ {
+ redirects->erase(redir);
+
+ if(redirects->empty())
+ {
+ DELETE(redirects);
+ channel->Shrink("banredirects");
+ }
+
+ break;
+ }
+ }
+ }
+
+ /* Append the channel so the default +b handler can remove the entry too */
+ param.append(mask[CHAN]);
+ }
+ }
+ }
+ else
+ {
+ source->WriteServ("403 %s %s :Invalid channel name in redirection (%s)", source->nick, channel->name, mask[CHAN].c_str());
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class ModuleBanRedirect : public Module
+{
+ BanRedirect* re;
+ bool nofollow;
+ Module* ExceptionModule;
+
+ public:
+ ModuleBanRedirect(InspIRCd* Me)
+ : Module(Me)
+ {
+ re = new BanRedirect(Me);
+ nofollow = false;
+
+ if(!ServerInstance->AddModeWatcher(re))
+ throw ModuleException("Could not add mode watcher");
+
+ OnRehash(NULL, "");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserPreJoin] = List[I_OnChannelDelete] = List[I_OnCleanup] = 1;
+ }
+
+ virtual void OnChannelDelete(chanrec* chan)
+ {
+ OnCleanup(TYPE_CHANNEL, chan);
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_CHANNEL)
+ {
+ chanrec* chan = static_cast<chanrec*>(item);
+ BanRedirectList* redirects;
+
+ if(chan->GetExt("banredirects", redirects))
+ {
+ irc::modestacker modestack(false);
+ StringDeque stackresult;
+ const char* mode_junk[MAXMODES+2];
+ userrec* myhorriblefakeuser = new userrec(ServerInstance);
+ myhorriblefakeuser->SetFd(FD_MAGIC_NUMBER);
+
+ mode_junk[0] = chan->name;
+
+ for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
+ {
+ modestack.Push('b', i->targetchan.insert(0, i->banmask));
+ }
+
+ for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
+ {
+ modestack.PushPlus();
+ modestack.Push('b', i->banmask);
+ }
+
+ while(modestack.GetStackedLine(stackresult))
+ {
+ for(StringDeque::size_type i = 0; i < stackresult.size(); i++)
+ {
+ mode_junk[i+1] = stackresult[i].c_str();
+ }
+
+ ServerInstance->SendMode(mode_junk, stackresult.size() + 1, myhorriblefakeuser);
+ }
+
+ DELETE(myhorriblefakeuser);
+ DELETE(redirects);
+ chan->Shrink("banredirects");
+ }
+ }
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ ExceptionModule = ServerInstance->FindModule("m_banexception.so");
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ /* This prevents recursion when a user sets multiple ban redirects in a chain
+ * (thanks Potter)
+ */
+ if (nofollow)
+ return 0;
+
+ /* Return 1 to prevent the join, 0 to allow it */
+ if (chan)
+ {
+ BanRedirectList* redirects;
+
+ if(chan->GetExt("banredirects", redirects))
+ {
+ /* We actually had some ban redirects to check */
+
+ /* This was replaced with user->MakeHostIP() when I had a snprintf(), but MakeHostIP() doesn't seem to add the nick.
+ * Maybe we should have a GetFullIPHost() or something to match GetFullHost() and GetFullRealHost?
+ */
+
+ if (ExceptionModule)
+ {
+ ListModeRequest n(this, ExceptionModule, user, chan);
+ /* Users with ban exceptions are allowed to join without being redirected */
+ if (n.Send())
+ return 0;
+ }
+
+ std::string ipmask(user->nick);
+ ipmask.append(1, '!').append(user->MakeHostIP());
+
+ for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
+ {
+ if(ServerInstance->MatchText(user->GetFullRealHost(), redir->banmask) || ServerInstance->MatchText(user->GetFullHost(), redir->banmask) || ServerInstance->MatchText(ipmask, redir->banmask))
+ {
+ /* tell them they're banned and are being transferred */
+ chanrec* destchan = ServerInstance->FindChan(redir->targetchan);
+
+ if(destchan && ServerInstance->FindModule("m_redirect.so") && destchan->IsModeSet('L') && destchan->limit && (destchan->GetUserCounter() >= destchan->limit))
+ {
+ user->WriteServ("474 %s %s :Cannot join channel (You are banned)", user->nick, chan->name);
+ return 1;
+ }
+ else
+ {
+ user->WriteServ("470 %s :You are banned from %s. You are being automatically redirected to %s", user->nick, chan->name, redir->targetchan.c_str());
+ nofollow = true;
+ chanrec::JoinUser(ServerInstance, user, redir->targetchan.c_str(), false, "", ServerInstance->Time(true));
+ nofollow = false;
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleBanRedirect()
+ {
+ ServerInstance->Modes->DelModeWatcher(re);
+ DELETE(re);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ Priority Prioritize()
+ {
+ return (Priority)ServerInstance->PriorityBefore("m_banexception.so");
+ }
+};
+
+
+MODULE_INIT(ModuleBanRedirect)
diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp
index 5ff0c1100..69e99d0bb 100644
--- a/src/modules/m_blockamsg.cpp
+++ b/src/modules/m_blockamsg.cpp
@@ -1 +1,191 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.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. * IBLOCK_SILENT - Generate no output, silently drop messages. * IBLOCK_KILL - Kill the user with the reason "Global message (/amsg or /ame) detected". * IBLOCK_KILLOPERS - As above, but send an oper notice as well. This is the default. */ /** Holds a blocked message's details */ class BlockedMessage : public classbase { 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) { } }; class ModuleBlockAmsg : public Module { int ForgetDelay; BlockAction action; public: ModuleBlockAmsg(InspIRCd* Me) : Module(Me) { this->OnRehash(NULL,""); } void Implements(char* List) { List[I_OnRehash] = List[I_OnPreCommand] = List[I_OnCleanup] = 1; } virtual ~ModuleBlockAmsg() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); ForgetDelay = Conf.ReadInteger("blockamsg", "delay", 0, false); if(Conf.GetError() == CONF_VALUE_NOT_FOUND) ForgetDelay = -1; std::string act = Conf.ReadValue("blockamsg", "action", 0); if(act == "notice") action = IBLOCK_NOTICE; else if(act == "noticeopers") action = IBLOCK_NOTICEOPERS; else if(act == "silent") action = IBLOCK_SILENT; else if(act == "kill") action = IBLOCK_KILL; else action = IBLOCK_KILLOPERS; } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { // Don't do anything with unregistered users, or remote ones. if(!user || (user->registered != REG_ALL) || !IS_LOCAL(user)) return 0; // We want case insensitive command comparison. // Add std::string contructor for irc::string :x irc::string cmd = command.c_str(); if(validated && (cmd == "PRIVMSG" || cmd == "NOTICE") && (pcnt >= 2)) { // 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... int targets = 1; int userchans = 0; if(*parameters[0] != '#') { // Decrement if the first target wasn't a channel. targets--; } for(const char* c = parameters[0]; *c; c++) if((*c == ',') && *(c+1) && (*(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) { return 0; } userchans = user->chans.size(); // Check that this message wasn't already sent within a few seconds. BlockedMessage* m; user->GetExt("amsgblock", m); // If the message is identical and within the time. // We check the target is *not* identical, that'd straying into the realms of flood control. Which isn't what we're doing... // 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))) { // Block it... if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS) ServerInstance->WriteOpers("*** %s had an /amsg or /ame denied", user->nick); if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS) userrec::QuitUser(ServerInstance, user, "Global message (/amsg or /ame) detected"); else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS) user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) detected", user->nick); return 1; } if(m) { // If there's already a BlockedMessage allocated, use it. m->message = parameters[1]; m->target = parameters[0]; m->sent = ServerInstance->Time(); } else { m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time()); user->Extend("amsgblock", (char*)m); } } return 0; } void OnCleanup(int target_type, void* item) { if(target_type == TYPE_USER) { userrec* user = (userrec*)item; BlockedMessage* m; user->GetExt("amsgblock", m); if(m) { DELETE(m); user->Shrink("amsgblock"); } } } }; MODULE_INIT(ModuleBlockAmsg) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.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.
+ * IBLOCK_SILENT - Generate no output, silently drop messages.
+ * IBLOCK_KILL - Kill the user with the reason "Global message (/amsg or /ame) detected".
+ * IBLOCK_KILLOPERS - As above, but send an oper notice as well. This is the default.
+ */
+
+/** Holds a blocked message's details
+ */
+class BlockedMessage : public classbase
+{
+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)
+ {
+ }
+};
+
+class ModuleBlockAmsg : public Module
+{
+ int ForgetDelay;
+ BlockAction action;
+
+ public:
+ ModuleBlockAmsg(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ this->OnRehash(NULL,"");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnPreCommand] = List[I_OnCleanup] = 1;
+ }
+
+ virtual ~ModuleBlockAmsg()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+
+ ForgetDelay = Conf.ReadInteger("blockamsg", "delay", 0, false);
+
+ if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
+ ForgetDelay = -1;
+
+ std::string act = Conf.ReadValue("blockamsg", "action", 0);
+
+ if(act == "notice")
+ action = IBLOCK_NOTICE;
+ else if(act == "noticeopers")
+ action = IBLOCK_NOTICEOPERS;
+ else if(act == "silent")
+ action = IBLOCK_SILENT;
+ else if(act == "kill")
+ action = IBLOCK_KILL;
+ else
+ action = IBLOCK_KILLOPERS;
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ // Don't do anything with unregistered users, or remote ones.
+ if(!user || (user->registered != REG_ALL) || !IS_LOCAL(user))
+ return 0;
+
+ // We want case insensitive command comparison.
+ // Add std::string contructor for irc::string :x
+ irc::string cmd = command.c_str();
+
+ if(validated && (cmd == "PRIVMSG" || cmd == "NOTICE") && (pcnt >= 2))
+ {
+ // 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...
+
+ int targets = 1;
+ int userchans = 0;
+
+ if(*parameters[0] != '#')
+ {
+ // Decrement if the first target wasn't a channel.
+ targets--;
+ }
+
+ for(const char* c = parameters[0]; *c; c++)
+ if((*c == ',') && *(c+1) && (*(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)
+ {
+ return 0;
+ }
+
+ userchans = user->chans.size();
+
+ // Check that this message wasn't already sent within a few seconds.
+ BlockedMessage* m;
+ user->GetExt("amsgblock", m);
+
+ // If the message is identical and within the time.
+ // We check the target is *not* identical, that'd straying into the realms of flood control. Which isn't what we're doing...
+ // 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)))
+ {
+ // Block it...
+ if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
+ ServerInstance->WriteOpers("*** %s had an /amsg or /ame denied", user->nick);
+
+ if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
+ userrec::QuitUser(ServerInstance, user, "Global message (/amsg or /ame) detected");
+ else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
+ user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) detected", user->nick);
+
+ return 1;
+ }
+
+ if(m)
+ {
+ // If there's already a BlockedMessage allocated, use it.
+ m->message = parameters[1];
+ m->target = parameters[0];
+ m->sent = ServerInstance->Time();
+ }
+ else
+ {
+ m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time());
+ user->Extend("amsgblock", (char*)m);
+ }
+ }
+ return 0;
+ }
+
+ void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ BlockedMessage* m;
+ user->GetExt("amsgblock", m);
+ if(m)
+ {
+ DELETE(m);
+ user->Shrink("amsgblock");
+ }
+ }
+ }
+};
+
+
+MODULE_INIT(ModuleBlockAmsg)
diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp
index 8713f8c0d..9197a8f11 100644
--- a/src/modules/m_blockcaps.cpp
+++ b/src/modules/m_blockcaps.cpp
@@ -1 +1,143 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "mode.h" /* $ModDesc: Provides support for channel mode +P to block all-CAPS channel messages and notices */ /** Handles the +P channel mode */ class BlockCaps : public ModeHandler { public: BlockCaps(InspIRCd* Instance) : ModeHandler(Instance, 'P', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* 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('P',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleBlockCAPS : public Module { BlockCaps* bc; int percent; unsigned int minlen; char capsmap[256]; public: ModuleBlockCAPS(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); bc = new BlockCaps(ServerInstance); if (!ServerInstance->AddMode(bc, 'P')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &param) { ReadConf(); } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (target_type == TYPE_CHANNEL) { if ((!IS_LOCAL(user)) || (text.length() < minlen)) return 0; chanrec* c = (chanrec*)dest; if (c->IsModeSet('P')) { int caps = 0; for (std::string::iterator i = text.begin(); i != text.end(); i++) caps += capsmap[(unsigned char)*i]; if ( ((caps*100)/(int)text.length()) >= percent ) { user->WriteServ( "404 %s %s :Your line cannot be more than %d%% capital letters if it is %d or more letters long", user->nick, c->name, percent, minlen); return 1; } } } return 0; } virtual int OnUserPreNotice(userrec* 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() { ConfigReader Conf(ServerInstance); percent = Conf.ReadInteger("blockcaps", "percent", "100", 0, true); minlen = Conf.ReadInteger("blockcaps", "minlen", "0", 0, true); std::string hmap = Conf.ReadValue("blockcaps", "capsmap", 0); if (hmap.empty()) hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; memset(&capsmap, 0, 255); for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++) capsmap[(unsigned char)*n] = 1; if (percent < 0 || percent > 100) { ServerInstance->Log(DEFAULT, "<blockcaps:percent> out of range, setting to default of 100."); percent = 100; } if (minlen < 0 || minlen > MAXBUF-1) { ServerInstance->Log(DEFAULT, "<blockcaps:minlen> out of range, setting to default of 0."); minlen = 0; } } virtual ~ModuleBlockCAPS() { ServerInstance->Modes->DelMode(bc); DELETE(bc); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleBlockCAPS) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "mode.h"
+
+/* $ModDesc: Provides support for channel mode +P to block all-CAPS channel messages and notices */
+
+
+/** Handles the +P channel mode
+ */
+class BlockCaps : public ModeHandler
+{
+ public:
+ BlockCaps(InspIRCd* Instance) : ModeHandler(Instance, 'P', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* 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('P',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleBlockCAPS : public Module
+{
+ BlockCaps* bc;
+ int percent;
+ unsigned int minlen;
+ char capsmap[256];
+public:
+
+ ModuleBlockCAPS(InspIRCd* Me) : Module(Me)
+ {
+ OnRehash(NULL,"");
+ bc = new BlockCaps(ServerInstance);
+ if (!ServerInstance->AddMode(bc, 'P'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ ReadConf();
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (target_type == TYPE_CHANNEL)
+ {
+ if ((!IS_LOCAL(user)) || (text.length() < minlen))
+ return 0;
+
+ chanrec* c = (chanrec*)dest;
+
+ if (c->IsModeSet('P'))
+ {
+ int caps = 0;
+ for (std::string::iterator i = text.begin(); i != text.end(); i++)
+ caps += capsmap[(unsigned char)*i];
+ if ( ((caps*100)/(int)text.length()) >= percent )
+ {
+ user->WriteServ( "404 %s %s :Your line cannot be more than %d%% capital letters if it is %d or more letters long", user->nick, c->name, percent, minlen);
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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()
+ {
+ ConfigReader Conf(ServerInstance);
+ percent = Conf.ReadInteger("blockcaps", "percent", "100", 0, true);
+ minlen = Conf.ReadInteger("blockcaps", "minlen", "0", 0, true);
+ std::string hmap = Conf.ReadValue("blockcaps", "capsmap", 0);
+ if (hmap.empty())
+ hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ memset(&capsmap, 0, 255);
+ for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
+ capsmap[(unsigned char)*n] = 1;
+ if (percent < 0 || percent > 100)
+ {
+ ServerInstance->Log(DEFAULT, "<blockcaps:percent> out of range, setting to default of 100.");
+ percent = 100;
+ }
+ if (minlen < 0 || minlen > MAXBUF-1)
+ {
+ ServerInstance->Log(DEFAULT, "<blockcaps:minlen> out of range, setting to default of 0.");
+ minlen = 0;
+ }
+ }
+
+ virtual ~ModuleBlockCAPS()
+ {
+ ServerInstance->Modes->DelMode(bc);
+ DELETE(bc);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleBlockCAPS)
diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp
index 0646caa0b..69b0e4686 100644
--- a/src/modules/m_blockcolor.cpp
+++ b/src/modules/m_blockcolor.cpp
@@ -1 +1,118 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style channel mode +c */ /** Handles the +c channel mode */ class BlockColor : public ModeHandler { public: BlockColor(InspIRCd* Instance) : ModeHandler(Instance, 'c', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('c')) { channel->SetMode('c',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('c')) { channel->SetMode('c',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleBlockColour : public Module { bool AllowChanOps; BlockColor *bc; public: ModuleBlockColour(InspIRCd* Me) : Module(Me) { bc = new BlockColor(ServerInstance); if (!ServerInstance->AddMode(bc, 'c')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user))) { chanrec* c = (chanrec*)dest; if(c->IsModeSet('c')) { if (!CHANOPS_EXEMPT(ServerInstance, 'c') || CHANOPS_EXEMPT(ServerInstance, 'c') && c->GetStatus(user) != STATUS_OP) { for (std::string::iterator i = text.begin(); i != text.end(); i++) { switch (*i) { case 2: case 3: case 15: case 21: case 22: case 31: user->WriteServ("404 %s %s :Can't send colours to channel (+c set)",user->nick, c->name); return 1; break; } } } } } return 0; } virtual int OnUserPreNotice(userrec* 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 ~ModuleBlockColour() { ServerInstance->Modes->DelMode(bc); DELETE(bc); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleBlockColour) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style channel mode +c */
+
+/** Handles the +c channel mode
+ */
+class BlockColor : public ModeHandler
+{
+ public:
+ BlockColor(InspIRCd* Instance) : ModeHandler(Instance, 'c', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('c'))
+ {
+ channel->SetMode('c',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('c'))
+ {
+ channel->SetMode('c',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleBlockColour : public Module
+{
+ bool AllowChanOps;
+ BlockColor *bc;
+ public:
+
+ ModuleBlockColour(InspIRCd* Me) : Module(Me)
+ {
+ bc = new BlockColor(ServerInstance);
+ if (!ServerInstance->AddMode(bc, 'c'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
+ }
+
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ {
+ chanrec* c = (chanrec*)dest;
+
+ if(c->IsModeSet('c'))
+ {
+ if (!CHANOPS_EXEMPT(ServerInstance, 'c') || CHANOPS_EXEMPT(ServerInstance, 'c') && c->GetStatus(user) != STATUS_OP)
+ {
+ for (std::string::iterator i = text.begin(); i != text.end(); i++)
+ {
+ switch (*i)
+ {
+ case 2:
+ case 3:
+ case 15:
+ case 21:
+ case 22:
+ case 31:
+ user->WriteServ("404 %s %s :Can't send colours to channel (+c set)",user->nick, c->name);
+ return 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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 ~ModuleBlockColour()
+ {
+ ServerInstance->Modes->DelMode(bc);
+ DELETE(bc);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleBlockColour)
diff --git a/src/modules/m_botmode.cpp b/src/modules/m_botmode.cpp
index 8cc999f12..a6cad9577 100644
--- a/src/modules/m_botmode.cpp
+++ b/src/modules/m_botmode.cpp
@@ -1 +1,95 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <stdio.h> #include <string> #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" /* $ModDesc: Provides support for unreal-style umode +B */ /** Handles user mode +B */ class BotMode : public ModeHandler { public: BotMode(InspIRCd* Instance) : ModeHandler(Instance, 'B', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!dest->IsModeSet('B')) { dest->SetMode('B',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('B')) { dest->SetMode('B',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleBotMode : public Module { BotMode* bm; public: ModuleBotMode(InspIRCd* Me) : Module(Me) { bm = new BotMode(ServerInstance); if (!ServerInstance->AddMode(bm, 'B')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnWhois] = 1; } virtual ~ModuleBotMode() { ServerInstance->Modes->DelMode(bm); DELETE(bm); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } virtual void OnWhois(userrec* src, userrec* dst) { if (dst->IsModeSet('B')) { ServerInstance->SendWhoisLine(src, dst, 335, std::string(src->nick)+" "+std::string(dst->nick)+" :is a bot on "+ServerInstance->Config->Network); } } }; MODULE_INIT(ModuleBotMode) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <stdio.h>
+#include <string>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+
+/* $ModDesc: Provides support for unreal-style umode +B */
+
+/** Handles user mode +B
+ */
+class BotMode : public ModeHandler
+{
+ public:
+ BotMode(InspIRCd* Instance) : ModeHandler(Instance, 'B', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!dest->IsModeSet('B'))
+ {
+ dest->SetMode('B',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('B'))
+ {
+ dest->SetMode('B',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleBotMode : public Module
+{
+
+ BotMode* bm;
+ public:
+ ModuleBotMode(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ bm = new BotMode(ServerInstance);
+ if (!ServerInstance->AddMode(bm, 'B'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnWhois] = 1;
+ }
+
+ virtual ~ModuleBotMode()
+ {
+ ServerInstance->Modes->DelMode(bm);
+ DELETE(bm);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnWhois(userrec* src, userrec* dst)
+ {
+ if (dst->IsModeSet('B'))
+ {
+ ServerInstance->SendWhoisLine(src, dst, 335, std::string(src->nick)+" "+std::string(dst->nick)+" :is a bot on "+ServerInstance->Config->Network);
+ }
+ }
+
+};
+
+
+MODULE_INIT(ModuleBotMode)
diff --git a/src/modules/m_cban.cpp b/src/modules/m_cban.cpp
index 65be0d9bb..c8e6a86b9 100644
--- a/src/modules/m_cban.cpp
+++ b/src/modules/m_cban.cpp
@@ -1 +1,251 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <algorithm> #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" /* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */ /** Holds a CBAN item */ class CBan : public classbase { public: irc::string chname; std::string set_by; time_t set_on; long length; std::string reason; CBan() { } CBan(irc::string cn, std::string sb, time_t so, long ln, std::string rs) : chname(cn), set_by(sb), set_on(so), length(ln), reason(rs) { } }; bool CBanComp(const CBan &ban1, const CBan &ban2); typedef std::vector<CBan> cbanlist; /* cbans is declared here, as our type is right above. Don't try move it. */ cbanlist cbans; /** Handle /CBAN */ class cmd_cban : public command_t { public: cmd_cban(InspIRCd* Me) : command_t(Me, "CBAN", 'o', 1) { this->source = "m_cban.so"; this->syntax = "<channel> [<duration> :<reason>]"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { /* syntax: CBAN #channel time :reason goes here */ /* 'time' is a human-readable timestring, like 2d3h2s. */ if(pcnt == 1) { /* form: CBAN #channel removes a CBAN */ for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) { if (parameters[0] == iter->chname) { long remaining = iter->length + ServerInstance->Time(); user->WriteServ("386 %s %s :Removed CBAN due to expire at %s (%s)", user->nick, iter->chname.c_str(), ServerInstance->TimeString(remaining).c_str(), iter->reason.c_str()); cbans.erase(iter); break; } } } else if (pcnt >= 2) { /* full form to add a CBAN */ if (ServerInstance->IsChannel(parameters[0])) { // parameters[0] = #channel // parameters[1] = 1h3m2s // parameters[2] = Tortoise abuser long length = ServerInstance->Duration(parameters[1]); std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied"; cbans.push_back(CBan(parameters[0], user->nick, ServerInstance->Time(), length, reason)); std::sort(cbans.begin(), cbans.end(), CBanComp); if(length > 0) { user->WriteServ("385 %s %s :Added %lu second channel ban (%s)", user->nick, parameters[0], length, reason.c_str()); ServerInstance->WriteOpers("*** %s added %lu second channel ban on %s (%s)", user->nick, length, parameters[0], reason.c_str()); } else { user->WriteServ("385 %s %s :Added permanent channel ban (%s)", user->nick, parameters[0], reason.c_str()); ServerInstance->WriteOpers("*** %s added permanent channel ban on %s (%s)", user->nick, parameters[0], reason.c_str()); } } else { user->WriteServ("403 %s %s :Invalid channel name", user->nick, parameters[0]); return CMD_FAILURE; } } /* we want this routed! */ return CMD_SUCCESS; } }; bool CBanComp(const CBan &ban1, const CBan &ban2) { return ((ban1.set_on + ban1.length) < (ban2.set_on + ban2.length)); } class ModuleCBan : public Module { cmd_cban* mycommand; public: ModuleCBan(InspIRCd* Me) : Module(Me) { mycommand = new cmd_cban(Me); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_OnUserPreJoin] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1; } virtual int OnStats(char symbol, userrec* user, string_list &results) { ExpireBans(); if(symbol == 'C') { for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) { unsigned long remaining = (iter->set_on + iter->length) - ServerInstance->Time(); results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+iter->chname.c_str()+" "+iter->set_by+" "+ConvToStr(iter->set_on)+" "+ConvToStr(iter->length)+" "+ConvToStr(remaining)+" :"+iter->reason); } } return 0; } virtual int OnUserPreJoin(userrec *user, chanrec *chan, const char *cname, std::string &privs) { ExpireBans(); /* check cbans in here, and apply as necessary. */ for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) { if(iter->chname == cname && !user->modes[UM_OPERATOR]) { // Channel is banned. user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick, cname, iter->reason.c_str()); ServerInstance->WriteOpers("*** %s tried to join %s which is CBANed (%s)", user->nick, cname, iter->reason.c_str()); return 1; } } return 0; } virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) { proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "cban", EncodeCBan(*iter)); } } virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { if((target_type == TYPE_OTHER) && (extname == "cban")) { cbans.push_back(DecodeCBan(extdata)); std::sort(cbans.begin(), cbans.end(), CBanComp); } } virtual ~ModuleCBan() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } std::string EncodeCBan(const CBan &ban) { std::ostringstream stream; stream << ban.chname << " " << ban.set_by << " " << ban.set_on << " " << ban.length << " :" << ban.reason; return stream.str(); } CBan DecodeCBan(const std::string &data) { CBan res; int set_on; irc::tokenstream tokens(data); tokens.GetToken(res.chname); tokens.GetToken(res.set_by); tokens.GetToken(set_on); res.set_on = set_on; tokens.GetToken(res.length); tokens.GetToken(res.reason); return res; } void ExpireBans() { bool go_again = true; while (go_again) { go_again = false; for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) { /* 0 == permanent, don't mess with them! -- w00t */ if (iter->length != 0) { if (iter->set_on + iter->length <= ServerInstance->Time()) { ServerInstance->WriteOpers("*** %li second CBAN on %s (%s) set on %s expired", iter->length, iter->chname.c_str(), iter->reason.c_str(), ServerInstance->TimeString(iter->set_on).c_str()); cbans.erase(iter); go_again = true; } } if (go_again == true) break; } } } }; MODULE_INIT(ModuleCBan) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <algorithm>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+
+/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */
+
+/** Holds a CBAN item
+ */
+class CBan : public classbase
+{
+public:
+ irc::string chname;
+ std::string set_by;
+ time_t set_on;
+ long length;
+ std::string reason;
+
+ CBan()
+ {
+ }
+
+ CBan(irc::string cn, std::string sb, time_t so, long ln, std::string rs) : chname(cn), set_by(sb), set_on(so), length(ln), reason(rs)
+ {
+ }
+};
+
+bool CBanComp(const CBan &ban1, const CBan &ban2);
+
+typedef std::vector<CBan> cbanlist;
+
+/* cbans is declared here, as our type is right above. Don't try move it. */
+cbanlist cbans;
+
+/** Handle /CBAN
+ */
+class cmd_cban : public command_t
+{
+ public:
+ cmd_cban(InspIRCd* Me) : command_t(Me, "CBAN", 'o', 1)
+ {
+ this->source = "m_cban.so";
+ this->syntax = "<channel> [<duration> :<reason>]";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ /* syntax: CBAN #channel time :reason goes here */
+ /* 'time' is a human-readable timestring, like 2d3h2s. */
+
+ if(pcnt == 1)
+ {
+ /* form: CBAN #channel removes a CBAN */
+ for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
+ {
+ if (parameters[0] == iter->chname)
+ {
+ long remaining = iter->length + ServerInstance->Time();
+ user->WriteServ("386 %s %s :Removed CBAN due to expire at %s (%s)", user->nick, iter->chname.c_str(), ServerInstance->TimeString(remaining).c_str(), iter->reason.c_str());
+ cbans.erase(iter);
+ break;
+ }
+ }
+ }
+ else if (pcnt >= 2)
+ {
+ /* full form to add a CBAN */
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ // parameters[0] = #channel
+ // parameters[1] = 1h3m2s
+ // parameters[2] = Tortoise abuser
+ long length = ServerInstance->Duration(parameters[1]);
+ std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied";
+
+ cbans.push_back(CBan(parameters[0], user->nick, ServerInstance->Time(), length, reason));
+
+ std::sort(cbans.begin(), cbans.end(), CBanComp);
+
+ if(length > 0)
+ {
+ user->WriteServ("385 %s %s :Added %lu second channel ban (%s)", user->nick, parameters[0], length, reason.c_str());
+ ServerInstance->WriteOpers("*** %s added %lu second channel ban on %s (%s)", user->nick, length, parameters[0], reason.c_str());
+ }
+ else
+ {
+ user->WriteServ("385 %s %s :Added permanent channel ban (%s)", user->nick, parameters[0], reason.c_str());
+ ServerInstance->WriteOpers("*** %s added permanent channel ban on %s (%s)", user->nick, parameters[0], reason.c_str());
+ }
+ }
+ else
+ {
+ user->WriteServ("403 %s %s :Invalid channel name", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+
+ /* we want this routed! */
+ return CMD_SUCCESS;
+ }
+};
+
+bool CBanComp(const CBan &ban1, const CBan &ban2)
+{
+ return ((ban1.set_on + ban1.length) < (ban2.set_on + ban2.length));
+}
+
+class ModuleCBan : public Module
+{
+ cmd_cban* mycommand;
+
+
+ public:
+ ModuleCBan(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_cban(Me);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1;
+ }
+
+ virtual int OnStats(char symbol, userrec* user, string_list &results)
+ {
+ ExpireBans();
+
+ if(symbol == 'C')
+ {
+ for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
+ {
+ unsigned long remaining = (iter->set_on + iter->length) - ServerInstance->Time();
+ results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+iter->chname.c_str()+" "+iter->set_by+" "+ConvToStr(iter->set_on)+" "+ConvToStr(iter->length)+" "+ConvToStr(remaining)+" :"+iter->reason);
+ }
+ }
+
+ return 0;
+ }
+
+ virtual int OnUserPreJoin(userrec *user, chanrec *chan, const char *cname, std::string &privs)
+ {
+ ExpireBans();
+
+ /* check cbans in here, and apply as necessary. */
+ for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
+ {
+ if(iter->chname == cname && !user->modes[UM_OPERATOR])
+ {
+ // Channel is banned.
+ user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick, cname, iter->reason.c_str());
+ ServerInstance->WriteOpers("*** %s tried to join %s which is CBANed (%s)", user->nick, cname, iter->reason.c_str());
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
+ {
+ for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
+ {
+ proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "cban", EncodeCBan(*iter));
+ }
+ }
+
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ if((target_type == TYPE_OTHER) && (extname == "cban"))
+ {
+ cbans.push_back(DecodeCBan(extdata));
+ std::sort(cbans.begin(), cbans.end(), CBanComp);
+ }
+ }
+
+ virtual ~ModuleCBan()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ std::string EncodeCBan(const CBan &ban)
+ {
+ std::ostringstream stream;
+ stream << ban.chname << " " << ban.set_by << " " << ban.set_on << " " << ban.length << " :" << ban.reason;
+ return stream.str();
+ }
+
+ CBan DecodeCBan(const std::string &data)
+ {
+ CBan res;
+ int set_on;
+ irc::tokenstream tokens(data);
+ tokens.GetToken(res.chname);
+ tokens.GetToken(res.set_by);
+ tokens.GetToken(set_on);
+ res.set_on = set_on;
+ tokens.GetToken(res.length);
+ tokens.GetToken(res.reason);
+ return res;
+ }
+
+ void ExpireBans()
+ {
+ bool go_again = true;
+
+ while (go_again)
+ {
+ go_again = false;
+
+ for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
+ {
+ /* 0 == permanent, don't mess with them! -- w00t */
+ if (iter->length != 0)
+ {
+ if (iter->set_on + iter->length <= ServerInstance->Time())
+ {
+ ServerInstance->WriteOpers("*** %li second CBAN on %s (%s) set on %s expired", iter->length, iter->chname.c_str(), iter->reason.c_str(), ServerInstance->TimeString(iter->set_on).c_str());
+ cbans.erase(iter);
+ go_again = true;
+ }
+ }
+
+ if (go_again == true)
+ break;
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleCBan)
+
diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp
index a7aa2f8b1..f4a5bd620 100644
--- a/src/modules/m_censor.cpp
+++ b/src/modules/m_censor.cpp
@@ -1 +1,196 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #define _CRT_SECURE_NO_DEPRECATE #define _SCL_SECURE_NO_DEPRECATE #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" typedef std::map<irc::string,irc::string> censor_t; /* $ModDesc: Provides user and channel +G mode */ /** Handles usermode +G */ class CensorUser : public ModeHandler { public: CensorUser(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!dest->IsModeSet('G')) { dest->SetMode('G',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('G')) { dest->SetMode('G',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Handles channel mode +G */ class CensorChannel : public ModeHandler { public: CensorChannel(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('G')) { channel->SetMode('G',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('G')) { channel->SetMode('G',false); return MODEACTION_ALLOW; } } return MODEACTION_ALLOW; } }; class ModuleCensor : public Module { censor_t censors; CensorUser *cu; CensorChannel *cc; public: ModuleCensor(InspIRCd* Me) : Module(Me) { /* Read the configuration file on startup. */ OnRehash(NULL,""); cu = new CensorUser(ServerInstance); cc = new CensorChannel(ServerInstance); if (!ServerInstance->AddMode(cu, 'G') || !ServerInstance->AddMode(cc, 'G')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; } virtual ~ModuleCensor() { ServerInstance->Modes->DelMode(cu); ServerInstance->Modes->DelMode(cc); DELETE(cu); DELETE(cc); } virtual void ReplaceLine(irc::string &text, irc::string pattern, irc::string replace) { if ((!pattern.empty()) && (!text.empty())) { std::string::size_type pos; while ((pos = text.find(pattern)) != irc::string::npos) { text.erase(pos,pattern.length()); text.insert(pos,replace); } } } // format of a config entry is <badword text="shit" replace="poo"> virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (!IS_LOCAL(user)) return 0; bool active = false; if (target_type == TYPE_USER) active = ((userrec*)dest)->IsModeSet('G'); else if (target_type == TYPE_CHANNEL) active = ((chanrec*)dest)->IsModeSet('G'); if (!active) return 0; irc::string text2 = text.c_str(); for (censor_t::iterator index = censors.begin(); index != censors.end(); index++) { if (text2.find(index->first) != irc::string::npos) { if (index->second.empty()) { user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked", user->nick, ((chanrec*)dest)->name, index->first.c_str()); return 1; } this->ReplaceLine(text2,index->first,index->second); } } text = text2.c_str(); return 0; } virtual int OnUserPreNotice(userrec* 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(userrec* user, const std::string &parameter) { /* * reload our config file on rehash - we must destroy and re-allocate the classes * to call the constructor again and re-read our data. */ ConfigReader* MyConf = new ConfigReader(ServerInstance); censors.clear(); for (int index = 0; index < MyConf->Enumerate("badword"); index++) { irc::string pattern = (MyConf->ReadValue("badword","text",index)).c_str(); irc::string replace = (MyConf->ReadValue("badword","replace",index)).c_str(); censors[pattern] = replace; } DELETE(MyConf); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleCensor) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE
+#define _SCL_SECURE_NO_DEPRECATE
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+typedef std::map<irc::string,irc::string> censor_t;
+
+/* $ModDesc: Provides user and channel +G mode */
+
+/** Handles usermode +G
+ */
+class CensorUser : public ModeHandler
+{
+ public:
+ CensorUser(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!dest->IsModeSet('G'))
+ {
+ dest->SetMode('G',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('G'))
+ {
+ dest->SetMode('G',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** Handles channel mode +G
+ */
+class CensorChannel : public ModeHandler
+{
+ public:
+ CensorChannel(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('G'))
+ {
+ channel->SetMode('G',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('G'))
+ {
+ channel->SetMode('G',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_ALLOW;
+ }
+};
+
+class ModuleCensor : public Module
+{
+
+
+ censor_t censors;
+ CensorUser *cu;
+ CensorChannel *cc;
+
+ public:
+ ModuleCensor(InspIRCd* Me)
+ : Module(Me)
+ {
+ /* Read the configuration file on startup.
+ */
+ OnRehash(NULL,"");
+ cu = new CensorUser(ServerInstance);
+ cc = new CensorChannel(ServerInstance);
+ if (!ServerInstance->AddMode(cu, 'G') || !ServerInstance->AddMode(cc, 'G'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
+ }
+
+ virtual ~ModuleCensor()
+ {
+ ServerInstance->Modes->DelMode(cu);
+ ServerInstance->Modes->DelMode(cc);
+ DELETE(cu);
+ DELETE(cc);
+ }
+
+ virtual void ReplaceLine(irc::string &text, irc::string pattern, irc::string replace)
+ {
+ if ((!pattern.empty()) && (!text.empty()))
+ {
+ std::string::size_type pos;
+ while ((pos = text.find(pattern)) != irc::string::npos)
+ {
+ text.erase(pos,pattern.length());
+ text.insert(pos,replace);
+ }
+ }
+ }
+
+ // format of a config entry is <badword text="shit" replace="poo">
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (!IS_LOCAL(user))
+ return 0;
+
+ bool active = false;
+
+ if (target_type == TYPE_USER)
+ active = ((userrec*)dest)->IsModeSet('G');
+ else if (target_type == TYPE_CHANNEL)
+ active = ((chanrec*)dest)->IsModeSet('G');
+
+ if (!active)
+ return 0;
+
+ irc::string text2 = text.c_str();
+ for (censor_t::iterator index = censors.begin(); index != censors.end(); index++)
+ {
+ if (text2.find(index->first) != irc::string::npos)
+ {
+ if (index->second.empty())
+ {
+ user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked", user->nick, ((chanrec*)dest)->name, index->first.c_str());
+ return 1;
+ }
+
+ this->ReplaceLine(text2,index->first,index->second);
+ }
+ }
+ text = text2.c_str();
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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(userrec* user, const std::string &parameter)
+ {
+ /*
+ * reload our config file on rehash - we must destroy and re-allocate the classes
+ * to call the constructor again and re-read our data.
+ */
+ ConfigReader* MyConf = new ConfigReader(ServerInstance);
+ censors.clear();
+ for (int index = 0; index < MyConf->Enumerate("badword"); index++)
+ {
+ irc::string pattern = (MyConf->ReadValue("badword","text",index)).c_str();
+ irc::string replace = (MyConf->ReadValue("badword","replace",index)).c_str();
+ censors[pattern] = replace;
+ }
+ DELETE(MyConf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleCensor)
diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp
index 64fd6c69f..290f55d22 100644
--- a/src/modules/m_cgiirc.cpp
+++ b/src/modules/m_cgiirc.cpp
@@ -1 +1,511 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" #include "dns.h" #ifndef WINDOWS #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #endif /* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */ enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC }; /** Holds a CGI site's details */ class CGIhost : public classbase { public: std::string hostmask; CGItype type; std::string password; CGIhost(const std::string &mask = "", CGItype t = IDENTFIRST, const std::string &password ="") : hostmask(mask), type(t), password(password) { } }; typedef std::vector<CGIhost> CGIHostlist; class cmd_webirc : public command_t { InspIRCd* Me; CGIHostlist Hosts; bool notify; public: cmd_webirc(InspIRCd* Me, CGIHostlist &Hosts, bool notify) : command_t(Me, "WEBIRC", 0, 4, true), Hosts(Hosts), notify(notify) { this->source = "m_cgiirc.so"; this->syntax = "password client hostname ip"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { if(user->registered == REG_ALL) return CMD_FAILURE; for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++) { if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask)) { if(iter->type == WEBIRC && parameters[0] == iter->password) { user->Extend("cgiirc_realhost", new std::string(user->host)); user->Extend("cgiirc_realip", new std::string(user->GetIPString())); if (notify) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick, user->host, parameters[2], user->host); user->Extend("cgiirc_webirc_hostname", new std::string(parameters[2])); user->Extend("cgiirc_webirc_ip", new std::string(parameters[3])); return CMD_LOCALONLY; } } } return CMD_FAILURE; } }; /** Resolver for CGI:IRC hostnames encoded in ident/GECOS */ class CGIResolver : public Resolver { std::string typ; int theirfd; userrec* them; bool notify; public: CGIResolver(Module* me, InspIRCd* ServerInstance, bool NotifyOpers, const std::string &source, bool forward, userrec* u, int userfd, const std::string &type, bool &cached) : Resolver(ServerInstance, source, forward ? DNS_QUERY_A : DNS_QUERY_PTR4, cached, me), typ(type), theirfd(userfd), them(u), notify(NotifyOpers) { } virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) { /* Check the user still exists */ if ((them) && (them == ServerInstance->SE->GetRef(theirfd))) { if (notify) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick, them->host, result.c_str(), typ.c_str()); strlcpy(them->host, result.c_str(), 63); strlcpy(them->dhost, result.c_str(), 63); strlcpy(them->ident, "~cgiirc", 8); them->InvalidateCache(); } } virtual void OnError(ResolverError e, const std::string &errormessage) { if ((them) && (them == ServerInstance->SE->GetRef(theirfd))) { if (notify) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick, them->host,typ.c_str()); } } virtual ~CGIResolver() { } }; class ModuleCgiIRC : public Module { cmd_webirc* mycommand; bool NotifyOpers; CGIHostlist Hosts; public: ModuleCgiIRC(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); mycommand=new cmd_webirc(Me, Hosts, NotifyOpers); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCleanup] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserQuit] = List[I_OnUserConnect] = 1; } virtual Priority Prioritize() { // We want to get here before m_cloaking and m_hostchange etc return PRIORITY_FIRST; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); NotifyOpers = Conf.ReadFlag("cgiirc", "opernotice", 0); // If we send an oper notice when a CGI:IRC has their host changed. if(Conf.GetError() == CONF_VALUE_NOT_FOUND) NotifyOpers = true; for(int i = 0; i < Conf.Enumerate("cgihost"); i++) { std::string hostmask = Conf.ReadValue("cgihost", "mask", i); // An allowed CGI:IRC host std::string type = Conf.ReadValue("cgihost", "type", i); // What type of user-munging we do on this host. std::string password = Conf.ReadValue("cgihost", "password", i); if(hostmask.length()) { if(type == "webirc" && !password.length()) { ServerInstance->Log(DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str()); } else { CGItype cgitype; if(type == "pass") cgitype = PASS; else if(type == "ident") cgitype = IDENT; else if(type == "passfirst") cgitype = PASSFIRST; else if(type == "webirc") { cgitype = WEBIRC; } Hosts.push_back(CGIhost(hostmask,cgitype, password.length() ? password : "" )); } } else { ServerInstance->Log(DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str()); continue; } } } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_USER) { userrec* user = (userrec*)item; std::string* realhost; std::string* realip; if(user->GetExt("cgiirc_realhost", realhost)) { delete realhost; user->Shrink("cgiirc_realhost"); } if(user->GetExt("cgiirc_realip", realip)) { delete realip; user->Shrink("cgiirc_realip"); } } } virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) { if((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) { std::string* data; if(user->GetExt(extname, data)) { proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *data); } } } virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { if(target_type == TYPE_USER) { userrec* dest = (userrec*)target; std::string* bleh; if(((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) && (!dest->GetExt(extname, bleh))) { dest->Extend(extname, new std::string(extdata)); } } } virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) { OnCleanup(TYPE_USER, user); } virtual int OnUserRegister(userrec* user) { for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++) { if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask)) { // Deal with it... if(iter->type == PASS) { CheckPass(user); // We do nothing if it fails so... } else if(iter->type == PASSFIRST && !CheckPass(user)) { // If the password lookup failed, try the ident CheckIdent(user); // If this fails too, do nothing } else if(iter->type == IDENT) { CheckIdent(user); // Nothing on failure. } else if(iter->type == IDENTFIRST && !CheckIdent(user)) { // If the ident lookup fails, try the password. CheckPass(user); } else if(iter->type == WEBIRC) { // We don't need to do anything here } return 0; } } return 0; } virtual void OnUserConnect(userrec* user) { std::string *webirc_hostname, *webirc_ip; if(user->GetExt("cgiirc_webirc_hostname", webirc_hostname)) { strlcpy(user->host,webirc_hostname->c_str(),63); strlcpy(user->dhost,webirc_hostname->c_str(),63); delete webirc_hostname; user->InvalidateCache(); user->Shrink("cgiirc_webirc_hostname"); } if(user->GetExt("cgiirc_webirc_ip", webirc_ip)) { bool valid=false; user->RemoveCloneCounts(); #ifdef IPV6 valid = (inet_pton(AF_INET6, webirc_ip->c_str(), &((sockaddr_in6*)user->ip)->sin6_addr) > 0); if(!valid) valid = (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr)); #else if (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr)) valid = true; #endif delete webirc_ip; user->InvalidateCache(); user->Shrink("cgiirc_webirc_ip"); ServerInstance->AddLocalClone(user); ServerInstance->AddGlobalClone(user); user->CheckClass(); } } bool CheckPass(userrec* user) { if(IsValidHost(user->password)) { user->Extend("cgiirc_realhost", new std::string(user->host)); user->Extend("cgiirc_realip", new std::string(user->GetIPString())); strlcpy(user->host, user->password, 64); strlcpy(user->dhost, user->password, 64); user->InvalidateCache(); bool valid = false; user->RemoveCloneCounts(); #ifdef IPV6 if (user->GetProtocolFamily() == AF_INET6) valid = (inet_pton(AF_INET6, user->password, &((sockaddr_in6*)user->ip)->sin6_addr) > 0); else valid = (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr)); #else if (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr)) valid = true; #endif ServerInstance->AddLocalClone(user); ServerInstance->AddGlobalClone(user); user->CheckClass(); if (valid) { /* We were given a IP in the password, we don't do DNS so they get this is as their host as well. */ if(NotifyOpers) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password); } else { /* We got as resolved hostname in the password. */ try { bool cached; CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, user->password, false, user, user->GetFd(), "PASS", cached); ServerInstance->AddResolver(r, cached); } catch (...) { if (NotifyOpers) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host); } } *user->password = 0; /*if(NotifyOpers) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);*/ return true; } return false; } bool CheckIdent(userrec* user) { int ip[4]; char* ident; char newip[16]; int len = strlen(user->ident); if(len == 8) ident = user->ident; else if(len == 9 && *user->ident == '~') ident = user->ident+1; else return false; for(int i = 0; i < 4; i++) if(!HexToInt(ip[i], ident + i*2)) return false; snprintf(newip, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); user->Extend("cgiirc_realhost", new std::string(user->host)); user->Extend("cgiirc_realip", new std::string(user->GetIPString())); user->RemoveCloneCounts(); #ifdef IPV6 if (user->GetProtocolFamily() == AF_INET6) inet_pton(AF_INET6, newip, &((sockaddr_in6*)user->ip)->sin6_addr); else #endif inet_aton(newip, &((sockaddr_in*)user->ip)->sin_addr); ServerInstance->AddLocalClone(user); ServerInstance->AddGlobalClone(user); user->CheckClass(); try { strlcpy(user->host, newip, 16); strlcpy(user->dhost, newip, 16); strlcpy(user->ident, "~cgiirc", 8); bool cached; CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, newip, false, user, user->GetFd(), "IDENT", cached); ServerInstance->AddResolver(r, cached); } catch (...) { strlcpy(user->host, newip, 16); strlcpy(user->dhost, newip, 16); strlcpy(user->ident, "~cgiirc", 8); user->InvalidateCache(); if(NotifyOpers) ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host); } /*strlcpy(user->host, newip, 16); strlcpy(user->dhost, newip, 16); strlcpy(user->ident, "~cgiirc", 8);*/ return true; } bool IsValidHost(const std::string &host) { if(!host.size()) return false; for(unsigned int i = 0; i < host.size(); i++) { if( ((host[i] >= '0') && (host[i] <= '9')) || ((host[i] >= 'A') && (host[i] <= 'Z')) || ((host[i] >= 'a') && (host[i] <= 'z')) || ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) || ((host[i] == '.') && (i > 0) && (i+1 < host.size())) ) continue; else return false; } return true; } bool IsValidIP(const std::string &ip) { if(ip.size() < 7 || ip.size() > 15) return false; short sincedot = 0; short dots = 0; for(unsigned int i = 0; i < ip.size(); i++) { if((dots <= 3) && (sincedot <= 3)) { if((ip[i] >= '0') && (ip[i] <= '9')) { sincedot++; } else if(ip[i] == '.') { sincedot = 0; dots++; } } else { return false; } } if(dots != 3) return false; return true; } bool HexToInt(int &out, const char* in) { char ip[3]; ip[0] = in[0]; ip[1] = in[1]; ip[2] = 0; out = strtol(ip, NULL, 16); if(out > 255 || out < 0) return false; return true; } virtual ~ModuleCgiIRC() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleCgiIRC) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+#include "dns.h"
+#ifndef WINDOWS
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
+
+enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
+
+
+/** Holds a CGI site's details
+ */
+class CGIhost : public classbase
+{
+public:
+ std::string hostmask;
+ CGItype type;
+ std::string password;
+
+ CGIhost(const std::string &mask = "", CGItype t = IDENTFIRST, const std::string &password ="")
+ : hostmask(mask), type(t), password(password)
+ {
+ }
+};
+typedef std::vector<CGIhost> CGIHostlist;
+
+class cmd_webirc : public command_t
+{
+ InspIRCd* Me;
+ CGIHostlist Hosts;
+ bool notify;
+ public:
+ cmd_webirc(InspIRCd* Me, CGIHostlist &Hosts, bool notify) : command_t(Me, "WEBIRC", 0, 4, true), Hosts(Hosts), notify(notify)
+ {
+ this->source = "m_cgiirc.so";
+ this->syntax = "password client hostname ip";
+ }
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ if(user->registered == REG_ALL)
+ return CMD_FAILURE;
+
+ for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
+ {
+ if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask))
+ {
+ if(iter->type == WEBIRC && parameters[0] == iter->password)
+ {
+ user->Extend("cgiirc_realhost", new std::string(user->host));
+ user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
+ if (notify)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick, user->host, parameters[2], user->host);
+ user->Extend("cgiirc_webirc_hostname", new std::string(parameters[2]));
+ user->Extend("cgiirc_webirc_ip", new std::string(parameters[3]));
+ return CMD_LOCALONLY;
+ }
+ }
+ }
+ return CMD_FAILURE;
+ }
+};
+
+
+/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
+ */
+class CGIResolver : public Resolver
+{
+ std::string typ;
+ int theirfd;
+ userrec* them;
+ bool notify;
+ public:
+ CGIResolver(Module* me, InspIRCd* ServerInstance, bool NotifyOpers, const std::string &source, bool forward, userrec* u, int userfd, const std::string &type, bool &cached)
+ : Resolver(ServerInstance, source, forward ? DNS_QUERY_A : DNS_QUERY_PTR4, cached, me), typ(type), theirfd(userfd), them(u), notify(NotifyOpers) { }
+
+ virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ {
+ /* Check the user still exists */
+ if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
+ {
+ if (notify)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick, them->host, result.c_str(), typ.c_str());
+
+ strlcpy(them->host, result.c_str(), 63);
+ strlcpy(them->dhost, result.c_str(), 63);
+ strlcpy(them->ident, "~cgiirc", 8);
+ them->InvalidateCache();
+ }
+ }
+
+ virtual void OnError(ResolverError e, const std::string &errormessage)
+ {
+ if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
+ {
+ if (notify)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick, them->host,typ.c_str());
+ }
+ }
+
+ virtual ~CGIResolver()
+ {
+ }
+};
+
+class ModuleCgiIRC : public Module
+{
+ cmd_webirc* mycommand;
+ bool NotifyOpers;
+ CGIHostlist Hosts;
+public:
+ ModuleCgiIRC(InspIRCd* Me) : Module(Me)
+ {
+
+ OnRehash(NULL,"");
+ mycommand=new cmd_webirc(Me, Hosts, NotifyOpers);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCleanup] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserQuit] = List[I_OnUserConnect] = 1;
+ }
+
+ virtual Priority Prioritize()
+ {
+ // We want to get here before m_cloaking and m_hostchange etc
+ return PRIORITY_FIRST;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+
+ NotifyOpers = Conf.ReadFlag("cgiirc", "opernotice", 0); // If we send an oper notice when a CGI:IRC has their host changed.
+
+ if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
+ NotifyOpers = true;
+
+ for(int i = 0; i < Conf.Enumerate("cgihost"); i++)
+ {
+ std::string hostmask = Conf.ReadValue("cgihost", "mask", i); // An allowed CGI:IRC host
+ std::string type = Conf.ReadValue("cgihost", "type", i); // What type of user-munging we do on this host.
+ std::string password = Conf.ReadValue("cgihost", "password", i);
+
+ if(hostmask.length())
+ {
+ if(type == "webirc" && !password.length()) {
+ ServerInstance->Log(DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
+ } else {
+ CGItype cgitype;
+ if(type == "pass")
+ cgitype = PASS;
+ else if(type == "ident")
+ cgitype = IDENT;
+ else if(type == "passfirst")
+ cgitype = PASSFIRST;
+ else if(type == "webirc") {
+ cgitype = WEBIRC;
+ }
+ Hosts.push_back(CGIhost(hostmask,cgitype, password.length() ? password : "" ));
+ }
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
+ continue;
+ }
+ }
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ std::string* realhost;
+ std::string* realip;
+
+ if(user->GetExt("cgiirc_realhost", realhost))
+ {
+ delete realhost;
+ user->Shrink("cgiirc_realhost");
+ }
+
+ if(user->GetExt("cgiirc_realip", realip))
+ {
+ delete realip;
+ user->Shrink("cgiirc_realip");
+ }
+ }
+ }
+
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
+ {
+ if((extname == "cgiirc_realhost") || (extname == "cgiirc_realip"))
+ {
+ std::string* data;
+
+ if(user->GetExt(extname, data))
+ {
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *data);
+ }
+ }
+ }
+
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* dest = (userrec*)target;
+ std::string* bleh;
+ if(((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) && (!dest->GetExt(extname, bleh)))
+ {
+ dest->Extend(extname, new std::string(extdata));
+ }
+ }
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
+ {
+ OnCleanup(TYPE_USER, user);
+ }
+
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
+ {
+ if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask))
+ {
+ // Deal with it...
+ if(iter->type == PASS)
+ {
+ CheckPass(user); // We do nothing if it fails so...
+ }
+ else if(iter->type == PASSFIRST && !CheckPass(user))
+ {
+ // If the password lookup failed, try the ident
+ CheckIdent(user); // If this fails too, do nothing
+ }
+ else if(iter->type == IDENT)
+ {
+ CheckIdent(user); // Nothing on failure.
+ }
+ else if(iter->type == IDENTFIRST && !CheckIdent(user))
+ {
+ // If the ident lookup fails, try the password.
+ CheckPass(user);
+ }
+ else if(iter->type == WEBIRC)
+ {
+ // We don't need to do anything here
+ }
+ return 0;
+ }
+ }
+ return 0;
+ }
+
+ virtual void OnUserConnect(userrec* user)
+ {
+ std::string *webirc_hostname, *webirc_ip;
+ if(user->GetExt("cgiirc_webirc_hostname", webirc_hostname))
+ {
+ strlcpy(user->host,webirc_hostname->c_str(),63);
+ strlcpy(user->dhost,webirc_hostname->c_str(),63);
+ delete webirc_hostname;
+ user->InvalidateCache();
+ user->Shrink("cgiirc_webirc_hostname");
+ }
+ if(user->GetExt("cgiirc_webirc_ip", webirc_ip))
+ {
+ bool valid=false;
+ user->RemoveCloneCounts();
+#ifdef IPV6
+ valid = (inet_pton(AF_INET6, webirc_ip->c_str(), &((sockaddr_in6*)user->ip)->sin6_addr) > 0);
+
+ if(!valid)
+ valid = (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr));
+#else
+ if (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr))
+ valid = true;
+#endif
+
+ delete webirc_ip;
+ user->InvalidateCache();
+ user->Shrink("cgiirc_webirc_ip");
+ ServerInstance->AddLocalClone(user);
+ ServerInstance->AddGlobalClone(user);
+ user->CheckClass();
+ }
+ }
+
+ bool CheckPass(userrec* user)
+ {
+ if(IsValidHost(user->password))
+ {
+ user->Extend("cgiirc_realhost", new std::string(user->host));
+ user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
+ strlcpy(user->host, user->password, 64);
+ strlcpy(user->dhost, user->password, 64);
+ user->InvalidateCache();
+
+ bool valid = false;
+ user->RemoveCloneCounts();
+#ifdef IPV6
+ if (user->GetProtocolFamily() == AF_INET6)
+ valid = (inet_pton(AF_INET6, user->password, &((sockaddr_in6*)user->ip)->sin6_addr) > 0);
+ else
+ valid = (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr));
+#else
+ if (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr))
+ valid = true;
+#endif
+ ServerInstance->AddLocalClone(user);
+ ServerInstance->AddGlobalClone(user);
+ user->CheckClass();
+
+ if (valid)
+ {
+ /* We were given a IP in the password, we don't do DNS so they get this is as their host as well. */
+ if(NotifyOpers)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);
+ }
+ else
+ {
+ /* We got as resolved hostname in the password. */
+ try
+ {
+
+ bool cached;
+ CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, user->password, false, user, user->GetFd(), "PASS", cached);
+ ServerInstance->AddResolver(r, cached);
+ }
+ catch (...)
+ {
+ if (NotifyOpers)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host);
+ }
+ }
+
+ *user->password = 0;
+
+ /*if(NotifyOpers)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);*/
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool CheckIdent(userrec* user)
+ {
+ int ip[4];
+ char* ident;
+ char newip[16];
+ int len = strlen(user->ident);
+
+ if(len == 8)
+ ident = user->ident;
+ else if(len == 9 && *user->ident == '~')
+ ident = user->ident+1;
+ else
+ return false;
+
+ for(int i = 0; i < 4; i++)
+ if(!HexToInt(ip[i], ident + i*2))
+ return false;
+
+ snprintf(newip, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+ user->Extend("cgiirc_realhost", new std::string(user->host));
+ user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
+ user->RemoveCloneCounts();
+#ifdef IPV6
+ if (user->GetProtocolFamily() == AF_INET6)
+ inet_pton(AF_INET6, newip, &((sockaddr_in6*)user->ip)->sin6_addr);
+ else
+#endif
+ inet_aton(newip, &((sockaddr_in*)user->ip)->sin_addr);
+ ServerInstance->AddLocalClone(user);
+ ServerInstance->AddGlobalClone(user);
+ user->CheckClass();
+ try
+ {
+ strlcpy(user->host, newip, 16);
+ strlcpy(user->dhost, newip, 16);
+ strlcpy(user->ident, "~cgiirc", 8);
+
+ bool cached;
+ CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, newip, false, user, user->GetFd(), "IDENT", cached);
+ ServerInstance->AddResolver(r, cached);
+ }
+ catch (...)
+ {
+ strlcpy(user->host, newip, 16);
+ strlcpy(user->dhost, newip, 16);
+ strlcpy(user->ident, "~cgiirc", 8);
+ user->InvalidateCache();
+
+ if(NotifyOpers)
+ ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host);
+ }
+ /*strlcpy(user->host, newip, 16);
+ strlcpy(user->dhost, newip, 16);
+ strlcpy(user->ident, "~cgiirc", 8);*/
+
+ return true;
+ }
+
+ bool IsValidHost(const std::string &host)
+ {
+ if(!host.size())
+ return false;
+
+ for(unsigned int i = 0; i < host.size(); i++)
+ {
+ if( ((host[i] >= '0') && (host[i] <= '9')) ||
+ ((host[i] >= 'A') && (host[i] <= 'Z')) ||
+ ((host[i] >= 'a') && (host[i] <= 'z')) ||
+ ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) ||
+ ((host[i] == '.') && (i > 0) && (i+1 < host.size())) )
+
+ continue;
+ else
+ return false;
+ }
+
+ return true;
+ }
+
+ bool IsValidIP(const std::string &ip)
+ {
+ if(ip.size() < 7 || ip.size() > 15)
+ return false;
+
+ short sincedot = 0;
+ short dots = 0;
+
+ for(unsigned int i = 0; i < ip.size(); i++)
+ {
+ if((dots <= 3) && (sincedot <= 3))
+ {
+ if((ip[i] >= '0') && (ip[i] <= '9'))
+ {
+ sincedot++;
+ }
+ else if(ip[i] == '.')
+ {
+ sincedot = 0;
+ dots++;
+ }
+ }
+ else
+ {
+ return false;
+
+ }
+ }
+
+ if(dots != 3)
+ return false;
+
+ return true;
+ }
+
+ bool HexToInt(int &out, const char* in)
+ {
+ char ip[3];
+ ip[0] = in[0];
+ ip[1] = in[1];
+ ip[2] = 0;
+ out = strtol(ip, NULL, 16);
+
+ if(out > 255 || out < 0)
+ return false;
+
+ return true;
+ }
+
+ virtual ~ModuleCgiIRC()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleCgiIRC)
diff --git a/src/modules/m_chancreate.cpp b/src/modules/m_chancreate.cpp
index 8837db9c5..915e7c8cb 100644
--- a/src/modules/m_chancreate.cpp
+++ b/src/modules/m_chancreate.cpp
@@ -1 +1,55 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Creates a snomask with notices whenever a new channel is created */ class ModuleChanCreate : public Module { private: public: ModuleChanCreate(InspIRCd* Me) : Module(Me) { ServerInstance->SNO->EnableSnomask('j', "CHANCREATE"); } virtual ~ModuleChanCreate() { ServerInstance->SNO->DisableSnomask('j'); } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnUserJoin] = 1; } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { if (channel->GetUserCounter() == 1) { ServerInstance->SNO->WriteToSnoMask('j', "Channel %s created by %s!%s@%s", channel->name, user->nick, user->ident, user->host); } } }; MODULE_INIT(ModuleChanCreate) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Creates a snomask with notices whenever a new channel is created */
+
+class ModuleChanCreate : public Module
+{
+ private:
+ public:
+ ModuleChanCreate(InspIRCd* Me)
+ : Module(Me)
+ {
+ ServerInstance->SNO->EnableSnomask('j', "CHANCREATE");
+ }
+
+ virtual ~ModuleChanCreate()
+ {
+ ServerInstance->SNO->DisableSnomask('j');
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserJoin] = 1;
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ if (channel->GetUserCounter() == 1)
+ {
+ ServerInstance->SNO->WriteToSnoMask('j', "Channel %s created by %s!%s@%s", channel->name, user->nick, user->ident, user->host);
+ }
+ }
+};
+
+MODULE_INIT(ModuleChanCreate)
diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp
index 44aac9dae..375fbce9c 100644
--- a/src/modules/m_chanfilter.cpp
+++ b/src/modules/m_chanfilter.cpp
@@ -1 +1,155 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #define _CRT_SECURE_NO_DEPRECATE #define _SCL_SECURE_NO_DEPRECATE #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "u_listmode.h" /* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */ /* $ModDep: ../../include/u_listmode.h */ /** Handles channel mode +g */ class ChanFilter : public ListModeBase { public: ChanFilter(InspIRCd* Instance) : ListModeBase(Instance, 'g', "End of channel spamfilter list", "941", "940", false, "chanfilter") { } virtual bool ValidateParam(userrec* user, chanrec* chan, std::string &word) { if ((word.length() > 35) || (word.empty())) { user->WriteServ("935 %s %s %s :word is too %s for censor list",user->nick, chan->name,word.c_str(), (word.empty() ? "short" : "long")); return false; } return true; } virtual bool TellListTooLong(userrec* user, chanrec* chan, std::string &word) { user->WriteServ("939 %s %s %s :Channel spamfilter list is full",user->nick, chan->name, word.c_str()); return true; } virtual void TellAlreadyOnList(userrec* user, chanrec* chan, std::string &word) { user->WriteServ("937 %s %s :The word %s is already on the spamfilter list",user->nick, chan->name,word.c_str()); } virtual void TellNotSet(userrec* user, chanrec* chan, std::string &word) { user->WriteServ("938 %s %s :No such spamfilter word is set",user->nick, chan->name); } }; class ModuleChanFilter : public Module { ChanFilter* cf; public: ModuleChanFilter(InspIRCd* Me) : Module(Me) { cf = new ChanFilter(ServerInstance); if (!ServerInstance->AddMode(cf, 'g')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { cf->DoImplements(List); List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnSyncChannel] = 1; } virtual void OnChannelDelete(chanrec* chan) { cf->DoChannelDelete(chan); } virtual void OnRehash(userrec* user, const std::string &parameter) { cf->DoRehash(); } virtual int ProcessMessages(userrec* user,chanrec* chan,std::string &text) { if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'g') && chan->GetStatus(user) == STATUS_OP) return 0; // Create a copy of the string in irc::string irc::string line = text.c_str(); modelist* list; chan->GetExt(cf->GetInfoKey(), list); if (list) { for (modelist::iterator i = list->begin(); i != list->end(); i++) { if (line.find(i->mask.c_str()) != std::string::npos) { user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked",user->nick, chan->name, i->mask.c_str()); return 1; } } } return 0; } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (target_type == TYPE_CHANNEL) { return ProcessMessages(user,(chanrec*)dest,text); } else return 0; } virtual void OnCleanup(int target_type, void* item) { cf->DoCleanup(target_type, item); } virtual int OnUserPreNotice(userrec* 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(chanrec* chan, Module* proto, void* opaque) { cf->DoSyncChannel(chan, proto, opaque); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } virtual ~ModuleChanFilter() { ServerInstance->Modes->DelMode(cf); DELETE(cf); } }; MODULE_INIT(ModuleChanFilter) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE
+#define _SCL_SECURE_NO_DEPRECATE
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "u_listmode.h"
+
+/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */
+/* $ModDep: ../../include/u_listmode.h */
+
+/** Handles channel mode +g
+ */
+class ChanFilter : public ListModeBase
+{
+ public:
+ ChanFilter(InspIRCd* Instance) : ListModeBase(Instance, 'g', "End of channel spamfilter list", "941", "940", false, "chanfilter") { }
+
+ virtual bool ValidateParam(userrec* user, chanrec* chan, std::string &word)
+ {
+ if ((word.length() > 35) || (word.empty()))
+ {
+ user->WriteServ("935 %s %s %s :word is too %s for censor list",user->nick, chan->name,word.c_str(), (word.empty() ? "short" : "long"));
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual bool TellListTooLong(userrec* user, chanrec* chan, std::string &word)
+ {
+ user->WriteServ("939 %s %s %s :Channel spamfilter list is full",user->nick, chan->name, word.c_str());
+ return true;
+ }
+
+ virtual void TellAlreadyOnList(userrec* user, chanrec* chan, std::string &word)
+ {
+ user->WriteServ("937 %s %s :The word %s is already on the spamfilter list",user->nick, chan->name,word.c_str());
+ }
+
+ virtual void TellNotSet(userrec* user, chanrec* chan, std::string &word)
+ {
+ user->WriteServ("938 %s %s :No such spamfilter word is set",user->nick, chan->name);
+ }
+};
+
+class ModuleChanFilter : public Module
+{
+
+ ChanFilter* cf;
+
+ public:
+
+ ModuleChanFilter(InspIRCd* Me)
+ : Module(Me)
+ {
+ cf = new ChanFilter(ServerInstance);
+ if (!ServerInstance->AddMode(cf, 'g'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ cf->DoImplements(List);
+ List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnSyncChannel] = 1;
+ }
+
+ virtual void OnChannelDelete(chanrec* chan)
+ {
+ cf->DoChannelDelete(chan);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ cf->DoRehash();
+ }
+
+ virtual int ProcessMessages(userrec* user,chanrec* chan,std::string &text)
+ {
+ if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'g') && chan->GetStatus(user) == STATUS_OP)
+ return 0;
+
+ // Create a copy of the string in irc::string
+ irc::string line = text.c_str();
+
+ modelist* list;
+ chan->GetExt(cf->GetInfoKey(), list);
+
+ if (list)
+ {
+ for (modelist::iterator i = list->begin(); i != list->end(); i++)
+ {
+ if (line.find(i->mask.c_str()) != std::string::npos)
+ {
+ user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked",user->nick, chan->name, i->mask.c_str());
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (target_type == TYPE_CHANNEL)
+ {
+ return ProcessMessages(user,(chanrec*)dest,text);
+ }
+ else return 0;
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ cf->DoCleanup(target_type, item);
+ }
+
+ virtual int OnUserPreNotice(userrec* 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(chanrec* chan, Module* proto, void* opaque)
+ {
+ cf->DoSyncChannel(chan, proto, opaque);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ virtual ~ModuleChanFilter()
+ {
+ ServerInstance->Modes->DelMode(cf);
+ DELETE(cf);
+ }
+};
+
+MODULE_INIT(ModuleChanFilter)
diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp
index 74640fe52..87bc1ca4c 100644
--- a/src/modules/m_chanprotect.cpp
+++ b/src/modules/m_chanprotect.cpp
@@ -1 +1,531 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides channel modes +a and +q */ /* $ModDep: ../../include/u_listmode.h */ #define PROTECT_VALUE 40000 #define FOUNDER_VALUE 50000 const char* fakevalue = "on"; /* When this is set to true, no restrictions apply to setting or * removal of +qa. This is used while unloading so that the server * can freely clear all of its users of the modes. */ bool unload_kludge = false; /** Handles basic operation of +qa channel modes */ class FounderProtectBase { private: InspIRCd* MyInstance; std::string extend; std::string type; int list; int end; char* dummyptr; protected: bool& remove_own_privs; bool& remove_other_privs; public: FounderProtectBase(InspIRCd* Instance, const std::string &ext, const std::string &mtype, int l, int e, bool &remove_own, bool &remove_others) : MyInstance(Instance), extend(ext), type(mtype), list(l), end(e), remove_own_privs(remove_own), remove_other_privs(remove_others) { } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { userrec* x = MyInstance->FindNick(parameter); if (x) { if (!channel->HasUser(x)) { return std::make_pair(false, parameter); } else { std::string item = extend+std::string(channel->name); if (x->GetExt(item,dummyptr)) { return std::make_pair(true, x->nick); } else { return std::make_pair(false, parameter); } } } return std::make_pair(false, parameter); } void RemoveMode(chanrec* channel, char mc) { unload_kludge = true; CUList* cl = channel->GetUsers(); std::string item = extend + std::string(channel->name); const char* mode_junk[MAXMODES+2]; userrec* n = new userrec(MyInstance); n->SetFd(FD_MAGIC_NUMBER); mode_junk[0] = channel->name; irc::modestacker modestack(false); std::deque<std::string> stackresult; for (CUList::iterator i = cl->begin(); i != cl->end(); i++) { if (i->first->GetExt(item, dummyptr)) { modestack.Push(mc, i->first->nick); } } while (modestack.GetStackedLine(stackresult)) { for (size_t j = 0; j < stackresult.size(); j++) { mode_junk[j+1] = stackresult[j].c_str(); } MyInstance->SendMode(mode_junk, stackresult.size() + 1, n); } delete n; unload_kludge = false; } void DisplayList(userrec* user, chanrec* channel) { CUList* cl = channel->GetUsers(); std::string item = extend+std::string(channel->name); for (CUList::reverse_iterator i = cl->rbegin(); i != cl->rend(); ++i) { if (i->first->GetExt(item, dummyptr)) { user->WriteServ("%d %s %s %s", list, user->nick, channel->name,i->first->nick); } } user->WriteServ("%d %s %s :End of channel %s list", end, user->nick, channel->name, type.c_str()); } userrec* FindAndVerify(std::string &parameter, chanrec* channel) { userrec* theuser = MyInstance->FindNick(parameter); if ((!theuser) || (!channel->HasUser(theuser))) { parameter.clear(); return NULL; } return theuser; } bool CanRemoveOthers(userrec* u1, userrec* u2, chanrec* c) { std::string item = extend+std::string(c->name); return (u1->GetExt(item, dummyptr) && u2->GetExt(item, dummyptr)); } ModeAction HandleChange(userrec* source, userrec* theuser, bool adding, chanrec* channel, std::string &parameter) { std::string item = extend+std::string(channel->name); if (adding) { if (!theuser->GetExt(item, dummyptr)) { theuser->Extend(item, fakevalue); parameter = theuser->nick; return MODEACTION_ALLOW; } } else { if (theuser->GetExt(item, dummyptr)) { theuser->Shrink(item); parameter = theuser->nick; return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Abstraction of FounderProtectBase for channel mode +q */ class ChanFounder : public ModeHandler, public FounderProtectBase { char* dummyptr; public: ChanFounder(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others) : ModeHandler(Instance, 'q', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '~' : 0), FounderProtectBase(Instance, "cm_founder_", "founder", 386, 387, depriv_self, depriv_others) { } unsigned int GetPrefixRank() { return FOUNDER_VALUE; } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { return FounderProtectBase::ModeSet(source, dest, channel, parameter); } void RemoveMode(chanrec* channel) { FounderProtectBase::RemoveMode(channel, this->GetModeChar()); } void RemoveMode(userrec* user) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel); if (!theuser) { return MODEACTION_DENY; } if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel)) { return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); } // source is a server, or ulined, we'll let them +-q the user. if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (!IS_LOCAL(source))) { return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); } else { // whoops, someones being naughty! source->WriteServ("468 %s %s :Only servers may set channel mode +q",source->nick, channel->name); parameter.clear(); return MODEACTION_DENY; } } void DisplayList(userrec* user, chanrec* channel) { FounderProtectBase::DisplayList(user,channel); } }; /** Abstraction of FounderProtectBase for channel mode +a */ class ChanProtect : public ModeHandler, public FounderProtectBase { char* dummyptr; public: ChanProtect(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others) : ModeHandler(Instance, 'a', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '&' : 0), FounderProtectBase(Instance,"cm_protect_","protected user", 388, 389, depriv_self, depriv_others) { } unsigned int GetPrefixRank() { return PROTECT_VALUE; } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { return FounderProtectBase::ModeSet(source, dest, channel, parameter); } void RemoveMode(chanrec* channel) { FounderProtectBase::RemoveMode(channel, this->GetModeChar()); } void RemoveMode(userrec* user) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel); if (!theuser) return MODEACTION_DENY; std::string founder = "cm_founder_"+std::string(channel->name); if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel)) { return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); } // source has +q, is a server, or ulined, we'll let them +-a the user. if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (source->GetExt(founder,dummyptr)) || (!IS_LOCAL(source))) { return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); } else { // bzzzt, wrong answer! source->WriteServ("482 %s %s :You are not a channel founder",source->nick, channel->name); return MODEACTION_DENY; } } virtual void DisplayList(userrec* user, chanrec* channel) { FounderProtectBase::DisplayList(user, channel); } }; class ModuleChanProtect : public Module { bool FirstInGetsFounder; bool QAPrefixes; bool DeprivSelf; bool DeprivOthers; bool booting; ChanProtect* cp; ChanFounder* cf; char* dummyptr; public: ModuleChanProtect(InspIRCd* Me) : Module(Me), FirstInGetsFounder(false), QAPrefixes(false), DeprivSelf(false), DeprivOthers(false), booting(true) { /* Load config stuff */ OnRehash(NULL,""); booting = false; /* Initialise module variables */ cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); if (!ServerInstance->AddMode(cp, 'a') || !ServerInstance->AddMode(cf, 'q')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserKick] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserJoin] = List[I_OnAccessCheck] = List[I_OnSyncChannel] = 1; } virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { // FIX: when someone gets kicked from a channel we must remove their Extensibles! user->Shrink("cm_founder_"+std::string(chan->name)); user->Shrink("cm_protect_"+std::string(chan->name)); } virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent) { // FIX: when someone parts a channel we must remove their Extensibles! user->Shrink("cm_founder_"+std::string(channel->name)); user->Shrink("cm_protect_"+std::string(channel->name)); } virtual void OnRehash(userrec* user, const std::string &parameter) { /* Create a configreader class and read our flag, * in old versions this was heap-allocated and the * object was kept between rehashes...now we just * stack-allocate it locally. */ ConfigReader Conf(ServerInstance); bool old_qa = QAPrefixes; FirstInGetsFounder = Conf.ReadFlag("options","noservices",0); QAPrefixes = Conf.ReadFlag("options","qaprefixes",0); DeprivSelf = Conf.ReadFlag("options","deprotectself",0); DeprivOthers = Conf.ReadFlag("options","deprotectothers",0); /* Did the user change the QA prefixes on the fly? * If so, remove all instances of the mode, and reinit * the module with prefixes enabled. */ if ((old_qa != QAPrefixes) && (!booting)) { ServerInstance->Modes->DelMode(cp); ServerInstance->Modes->DelMode(cf); DELETE(cp); DELETE(cf); cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); /* These wont fail, we already owned the mode characters before */ ServerInstance->AddMode(cp, 'a'); ServerInstance->AddMode(cf, 'q'); ServerInstance->WriteOpers("*** WARNING: +qa prefixes were enabled or disabled via a REHASH. Clients will probably need to reconnect to pick up this change."); } } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { // 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 (FirstInGetsFounder) { if (channel->GetUserCounter() == 1) { // we're using Extensible::Extend to add data into user objects. // this way is best as it adds data thats accessible to other modules // (so long as you document your code properly) without breaking anything // because its encapsulated neatly in a map. // Change requested by katsklaw... when the first in is set to get founder, // to make it clearer that +q has been given, send that one user the +q notice // so that their client's syncronization and their sanity are left intact. user->WriteServ("MODE %s +q %s",channel->name,user->nick); user->Extend("cm_founder_"+std::string(channel->name),fakevalue); } } } virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { // here we perform access checks, this is the important bit that actually stops kicking/deopping // etc of protected users. There are many types of access check, we're going to handle // a relatively small number of them relevent to our module using a switch statement. // don't allow action if: // (A) Theyre founder (no matter what) // (B) Theyre protected, and you're not // always allow the action if: // (A) The source is ulined // firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode // without any access checks, we're not worthy :p if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server)) return ACR_ALLOW; std::string founder = "cm_founder_"+std::string(channel->name); std::string protect = "cm_protect_"+std::string(channel->name); switch (access_type) { // a user has been deopped. Do we let them? hmmm... case AC_DEOP: if (dest->GetExt(founder,dummyptr)) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're a channel founder"); return ACR_DENY; } if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're protected (+a)"); return ACR_DENY; } break; // a user is being kicked. do we chop off the end of the army boot? case AC_KICK: if (dest->GetExt(founder,dummyptr)) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're a channel founder"); return ACR_DENY; } if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're protected (+a)"); return ACR_DENY; } break; // a user is being dehalfopped. Yes, we do disallow -h of a +ha user case AC_DEHALFOP: if (dest->GetExt(founder,dummyptr)) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're a channel founder"); return ACR_DENY; } if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're protected (+a)"); return ACR_DENY; } break; // same with devoice. case AC_DEVOICE: if (dest->GetExt(founder,dummyptr)) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're a channel founder"); return ACR_DENY; } if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) { source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're protected (+a)"); return ACR_DENY; } break; } // we dont know what this access check is, or dont care. just carry on, nothing to see here. return ACR_DEFAULT; } virtual ~ModuleChanProtect() { ServerInstance->Modes->DelMode(cp); ServerInstance->Modes->DelMode(cf); DELETE(cp); DELETE(cf); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { /* NOTE: If +qa prefix is on, this is propogated by the channel join, * so we dont need to propogate it manually */ if (!QAPrefixes) { // this is called when the server is linking into a net and wants to sync channel data. // we should send our mode changes for the channel here to ensure that other servers // know whos +q/+a on the channel. CUList* cl = chan->GetUsers(); string_list commands; std::string founder = "cm_founder_"+std::string(chan->name); std::string protect = "cm_protect_"+std::string(chan->name); irc::modestacker modestack(true); std::deque<std::string> stackresult; for (CUList::iterator i = cl->begin(); i != cl->end(); i++) { if (i->first->GetExt(founder,dummyptr)) { modestack.Push('q',i->first->nick); } if (i->first->GetExt(protect,dummyptr)) { modestack.Push('a',i->first->nick); } } while (modestack.GetStackedLine(stackresult)) { irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1); std::string line = mode_join.GetJoined(); proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan, line); } } } }; MODULE_INIT(ModuleChanProtect) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides channel modes +a and +q */
+/* $ModDep: ../../include/u_listmode.h */
+
+#define PROTECT_VALUE 40000
+#define FOUNDER_VALUE 50000
+
+const char* fakevalue = "on";
+
+/* When this is set to true, no restrictions apply to setting or
+ * removal of +qa. This is used while unloading so that the server
+ * can freely clear all of its users of the modes.
+ */
+bool unload_kludge = false;
+
+/** Handles basic operation of +qa channel modes
+ */
+class FounderProtectBase
+{
+ private:
+ InspIRCd* MyInstance;
+ std::string extend;
+ std::string type;
+ int list;
+ int end;
+ char* dummyptr;
+ protected:
+ bool& remove_own_privs;
+ bool& remove_other_privs;
+ public:
+ FounderProtectBase(InspIRCd* Instance, const std::string &ext, const std::string &mtype, int l, int e, bool &remove_own, bool &remove_others) :
+ MyInstance(Instance), extend(ext), type(mtype), list(l), end(e), remove_own_privs(remove_own), remove_other_privs(remove_others)
+ {
+ }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ userrec* x = MyInstance->FindNick(parameter);
+ if (x)
+ {
+ if (!channel->HasUser(x))
+ {
+ return std::make_pair(false, parameter);
+ }
+ else
+ {
+ std::string item = extend+std::string(channel->name);
+ if (x->GetExt(item,dummyptr))
+ {
+ return std::make_pair(true, x->nick);
+ }
+ else
+ {
+ return std::make_pair(false, parameter);
+ }
+ }
+ }
+ return std::make_pair(false, parameter);
+ }
+
+ void RemoveMode(chanrec* channel, char mc)
+ {
+ unload_kludge = true;
+ CUList* cl = channel->GetUsers();
+ std::string item = extend + std::string(channel->name);
+ const char* mode_junk[MAXMODES+2];
+ userrec* n = new userrec(MyInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+ mode_junk[0] = channel->name;
+ irc::modestacker modestack(false);
+ std::deque<std::string> stackresult;
+ for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
+ {
+ if (i->first->GetExt(item, dummyptr))
+ {
+ modestack.Push(mc, i->first->nick);
+ }
+ }
+
+ while (modestack.GetStackedLine(stackresult))
+ {
+ for (size_t j = 0; j < stackresult.size(); j++)
+ {
+ mode_junk[j+1] = stackresult[j].c_str();
+ }
+ MyInstance->SendMode(mode_junk, stackresult.size() + 1, n);
+ }
+
+ delete n;
+ unload_kludge = false;
+ }
+
+ void DisplayList(userrec* user, chanrec* channel)
+ {
+ CUList* cl = channel->GetUsers();
+ std::string item = extend+std::string(channel->name);
+ for (CUList::reverse_iterator i = cl->rbegin(); i != cl->rend(); ++i)
+ {
+ if (i->first->GetExt(item, dummyptr))
+ {
+ user->WriteServ("%d %s %s %s", list, user->nick, channel->name,i->first->nick);
+ }
+ }
+ user->WriteServ("%d %s %s :End of channel %s list", end, user->nick, channel->name, type.c_str());
+ }
+
+ userrec* FindAndVerify(std::string &parameter, chanrec* channel)
+ {
+ userrec* theuser = MyInstance->FindNick(parameter);
+ if ((!theuser) || (!channel->HasUser(theuser)))
+ {
+ parameter.clear();
+ return NULL;
+ }
+ return theuser;
+ }
+
+ bool CanRemoveOthers(userrec* u1, userrec* u2, chanrec* c)
+ {
+ std::string item = extend+std::string(c->name);
+ return (u1->GetExt(item, dummyptr) && u2->GetExt(item, dummyptr));
+ }
+
+ ModeAction HandleChange(userrec* source, userrec* theuser, bool adding, chanrec* channel, std::string &parameter)
+ {
+ std::string item = extend+std::string(channel->name);
+
+ if (adding)
+ {
+ if (!theuser->GetExt(item, dummyptr))
+ {
+ theuser->Extend(item, fakevalue);
+ parameter = theuser->nick;
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (theuser->GetExt(item, dummyptr))
+ {
+ theuser->Shrink(item);
+ parameter = theuser->nick;
+ return MODEACTION_ALLOW;
+ }
+ }
+ return MODEACTION_DENY;
+ }
+};
+
+/** Abstraction of FounderProtectBase for channel mode +q
+ */
+class ChanFounder : public ModeHandler, public FounderProtectBase
+{
+ char* dummyptr;
+ public:
+ ChanFounder(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others)
+ : ModeHandler(Instance, 'q', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '~' : 0),
+ FounderProtectBase(Instance, "cm_founder_", "founder", 386, 387, depriv_self, depriv_others) { }
+
+ unsigned int GetPrefixRank()
+ {
+ return FOUNDER_VALUE;
+ }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ return FounderProtectBase::ModeSet(source, dest, channel, parameter);
+ }
+
+ void RemoveMode(chanrec* channel)
+ {
+ FounderProtectBase::RemoveMode(channel, this->GetModeChar());
+ }
+
+ void RemoveMode(userrec* user)
+ {
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel);
+
+ if (!theuser)
+ {
+ return MODEACTION_DENY;
+ }
+
+ if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel))
+ {
+ return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
+ }
+ // source is a server, or ulined, we'll let them +-q the user.
+ if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (!IS_LOCAL(source)))
+ {
+ return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
+ }
+ else
+ {
+ // whoops, someones being naughty!
+ source->WriteServ("468 %s %s :Only servers may set channel mode +q",source->nick, channel->name);
+ parameter.clear();
+ return MODEACTION_DENY;
+ }
+ }
+
+ void DisplayList(userrec* user, chanrec* channel)
+ {
+ FounderProtectBase::DisplayList(user,channel);
+ }
+};
+
+/** Abstraction of FounderProtectBase for channel mode +a
+ */
+class ChanProtect : public ModeHandler, public FounderProtectBase
+{
+ char* dummyptr;
+ public:
+ ChanProtect(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others)
+ : ModeHandler(Instance, 'a', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '&' : 0),
+ FounderProtectBase(Instance,"cm_protect_","protected user", 388, 389, depriv_self, depriv_others) { }
+
+ unsigned int GetPrefixRank()
+ {
+ return PROTECT_VALUE;
+ }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ return FounderProtectBase::ModeSet(source, dest, channel, parameter);
+ }
+
+ void RemoveMode(chanrec* channel)
+ {
+ FounderProtectBase::RemoveMode(channel, this->GetModeChar());
+ }
+
+ void RemoveMode(userrec* user)
+ {
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel);
+
+ if (!theuser)
+ return MODEACTION_DENY;
+
+ std::string founder = "cm_founder_"+std::string(channel->name);
+
+ if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel))
+ {
+ return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
+ }
+ // source has +q, is a server, or ulined, we'll let them +-a the user.
+ if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (source->GetExt(founder,dummyptr)) || (!IS_LOCAL(source)))
+ {
+ return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
+ }
+ else
+ {
+ // bzzzt, wrong answer!
+ source->WriteServ("482 %s %s :You are not a channel founder",source->nick, channel->name);
+ return MODEACTION_DENY;
+ }
+ }
+
+ virtual void DisplayList(userrec* user, chanrec* channel)
+ {
+ FounderProtectBase::DisplayList(user, channel);
+ }
+
+};
+
+class ModuleChanProtect : public Module
+{
+
+ bool FirstInGetsFounder;
+ bool QAPrefixes;
+ bool DeprivSelf;
+ bool DeprivOthers;
+ bool booting;
+ ChanProtect* cp;
+ ChanFounder* cf;
+ char* dummyptr;
+
+ public:
+
+ ModuleChanProtect(InspIRCd* Me)
+ : Module(Me), FirstInGetsFounder(false), QAPrefixes(false), DeprivSelf(false), DeprivOthers(false), booting(true)
+ {
+ /* Load config stuff */
+ OnRehash(NULL,"");
+ booting = false;
+
+ /* Initialise module variables */
+
+ cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
+ cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
+
+ if (!ServerInstance->AddMode(cp, 'a') || !ServerInstance->AddMode(cf, 'q'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserKick] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserJoin] = List[I_OnAccessCheck] = List[I_OnSyncChannel] = 1;
+ }
+
+ virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
+ {
+ // FIX: when someone gets kicked from a channel we must remove their Extensibles!
+ user->Shrink("cm_founder_"+std::string(chan->name));
+ user->Shrink("cm_protect_"+std::string(chan->name));
+ }
+
+ virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent)
+ {
+ // FIX: when someone parts a channel we must remove their Extensibles!
+ user->Shrink("cm_founder_"+std::string(channel->name));
+ user->Shrink("cm_protect_"+std::string(channel->name));
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ /* Create a configreader class and read our flag,
+ * in old versions this was heap-allocated and the
+ * object was kept between rehashes...now we just
+ * stack-allocate it locally.
+ */
+ ConfigReader Conf(ServerInstance);
+
+ bool old_qa = QAPrefixes;
+
+ FirstInGetsFounder = Conf.ReadFlag("options","noservices",0);
+ QAPrefixes = Conf.ReadFlag("options","qaprefixes",0);
+ DeprivSelf = Conf.ReadFlag("options","deprotectself",0);
+ DeprivOthers = Conf.ReadFlag("options","deprotectothers",0);
+
+ /* Did the user change the QA prefixes on the fly?
+ * If so, remove all instances of the mode, and reinit
+ * the module with prefixes enabled.
+ */
+ if ((old_qa != QAPrefixes) && (!booting))
+ {
+ ServerInstance->Modes->DelMode(cp);
+ ServerInstance->Modes->DelMode(cf);
+ DELETE(cp);
+ DELETE(cf);
+ cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
+ cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
+ /* These wont fail, we already owned the mode characters before */
+ ServerInstance->AddMode(cp, 'a');
+ ServerInstance->AddMode(cf, 'q');
+ ServerInstance->WriteOpers("*** WARNING: +qa prefixes were enabled or disabled via a REHASH. Clients will probably need to reconnect to pick up this change.");
+ }
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ // 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 (FirstInGetsFounder)
+ {
+ if (channel->GetUserCounter() == 1)
+ {
+ // we're using Extensible::Extend to add data into user objects.
+ // this way is best as it adds data thats accessible to other modules
+ // (so long as you document your code properly) without breaking anything
+ // because its encapsulated neatly in a map.
+
+ // Change requested by katsklaw... when the first in is set to get founder,
+ // to make it clearer that +q has been given, send that one user the +q notice
+ // so that their client's syncronization and their sanity are left intact.
+ user->WriteServ("MODE %s +q %s",channel->name,user->nick);
+ user->Extend("cm_founder_"+std::string(channel->name),fakevalue);
+ }
+ }
+ }
+
+ virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
+ {
+ // here we perform access checks, this is the important bit that actually stops kicking/deopping
+ // etc of protected users. There are many types of access check, we're going to handle
+ // a relatively small number of them relevent to our module using a switch statement.
+ // don't allow action if:
+ // (A) Theyre founder (no matter what)
+ // (B) Theyre protected, and you're not
+ // always allow the action if:
+ // (A) The source is ulined
+
+
+ // firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode
+ // without any access checks, we're not worthy :p
+ if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server))
+ return ACR_ALLOW;
+
+ std::string founder = "cm_founder_"+std::string(channel->name);
+ std::string protect = "cm_protect_"+std::string(channel->name);
+
+ switch (access_type)
+ {
+ // a user has been deopped. Do we let them? hmmm...
+ case AC_DEOP:
+ if (dest->GetExt(founder,dummyptr))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're a channel founder");
+ return ACR_DENY;
+ }
+ if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're protected (+a)");
+ return ACR_DENY;
+ }
+ break;
+
+ // a user is being kicked. do we chop off the end of the army boot?
+ case AC_KICK:
+ if (dest->GetExt(founder,dummyptr))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're a channel founder");
+ return ACR_DENY;
+ }
+ if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're protected (+a)");
+ return ACR_DENY;
+ }
+ break;
+
+ // a user is being dehalfopped. Yes, we do disallow -h of a +ha user
+ case AC_DEHALFOP:
+ if (dest->GetExt(founder,dummyptr))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're a channel founder");
+ return ACR_DENY;
+ }
+ if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're protected (+a)");
+ return ACR_DENY;
+ }
+ break;
+
+ // same with devoice.
+ case AC_DEVOICE:
+ if (dest->GetExt(founder,dummyptr))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're a channel founder");
+ return ACR_DENY;
+ }
+ if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
+ {
+ source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're protected (+a)");
+ return ACR_DENY;
+ }
+ break;
+ }
+
+ // we dont know what this access check is, or dont care. just carry on, nothing to see here.
+ return ACR_DEFAULT;
+ }
+
+ virtual ~ModuleChanProtect()
+ {
+ ServerInstance->Modes->DelMode(cp);
+ ServerInstance->Modes->DelMode(cf);
+ DELETE(cp);
+ DELETE(cf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
+ {
+ /* NOTE: If +qa prefix is on, this is propogated by the channel join,
+ * so we dont need to propogate it manually
+ */
+ if (!QAPrefixes)
+ {
+ // this is called when the server is linking into a net and wants to sync channel data.
+ // we should send our mode changes for the channel here to ensure that other servers
+ // know whos +q/+a on the channel.
+ CUList* cl = chan->GetUsers();
+ string_list commands;
+ std::string founder = "cm_founder_"+std::string(chan->name);
+ std::string protect = "cm_protect_"+std::string(chan->name);
+ irc::modestacker modestack(true);
+ std::deque<std::string> stackresult;
+ for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
+ {
+ if (i->first->GetExt(founder,dummyptr))
+ {
+ modestack.Push('q',i->first->nick);
+ }
+ if (i->first->GetExt(protect,dummyptr))
+ {
+ modestack.Push('a',i->first->nick);
+ }
+ }
+ while (modestack.GetStackedLine(stackresult))
+ {
+ irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
+ std::string line = mode_join.GetJoined();
+ proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan, line);
+ }
+ }
+ }
+
+};
+
+MODULE_INIT(ModuleChanProtect)
diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp
index c99e985cc..643af8e15 100644
--- a/src/modules/m_check.cpp
+++ b/src/modules/m_check.cpp
@@ -1 +1,188 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" /* $ModDesc: Provides the /check command to retrieve information on a user, channel, or IP address */ /** Handle /CHECK */ class cmd_check : public command_t { public: cmd_check (InspIRCd* Instance) : command_t(Instance,"CHECK", 'o', 1) { this->source = "m_check.so"; syntax = "<nickname>|<ip>|<hostmask>|<channel>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec *targuser; chanrec *targchan; std::string checkstr; std::string chliststr; char timebuf[60]; struct tm *mytime; checkstr = "304 " + std::string(user->nick) + " :CHECK"; targuser = ServerInstance->FindNick(parameters[0]); targchan = ServerInstance->FindChan(parameters[0]); /* * Syntax of a /check reply: * :server.name 304 target :CHECK START <target> * :server.name 304 target :CHECK <field> <value> * :server.name 304 target :CHECK END */ user->WriteServ(checkstr + " START " + parameters[0]); if (targuser) { /* /check on a user */ user->WriteServ(checkstr + " nuh " + targuser->GetFullHost()); user->WriteServ(checkstr + " realnuh " + targuser->GetFullRealHost()); user->WriteServ(checkstr + " realname " + targuser->fullname); user->WriteServ(checkstr + " modes +" + targuser->FormatModes()); user->WriteServ(checkstr + " snomasks +" + targuser->FormatNoticeMasks()); user->WriteServ(checkstr + " server " + targuser->server); if (IS_AWAY(targuser)) { /* user is away */ user->WriteServ(checkstr + " awaymsg " + targuser->awaymsg); } if (IS_OPER(targuser)) { /* user is an oper of type ____ */ user->WriteServ(checkstr + " opertype " + irc::Spacify(targuser->oper)); } if (IS_LOCAL(targuser)) { /* port information is only held for a local user! */ user->WriteServ(checkstr + " onport " + ConvToStr(targuser->GetPort())); } chliststr = targuser->ChannelList(targuser); std::stringstream dump(chliststr); ServerInstance->DumpText(user,checkstr + " onchans ", dump); } else if (targchan) { /* /check on a channel */ time_t creation_time = targchan->created; time_t topic_time = targchan->topicset; mytime = gmtime(&creation_time); strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime); user->WriteServ(checkstr + " created " + timebuf); if (targchan->topic[0] != 0) { /* there is a topic, assume topic related information exists */ user->WriteServ(checkstr + " topic " + targchan->topic); user->WriteServ(checkstr + " topic_setby " + targchan->setby); mytime = gmtime(&topic_time); strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime); user->WriteServ(checkstr + " topic_setat " + timebuf); } user->WriteServ(checkstr + " modes " + targchan->ChanModes(true)); user->WriteServ(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter())); /* now the ugly bit, spool current members of a channel. :| */ CUList *ulist= targchan->GetUsers(); /* note that unlike /names, we do NOT check +i vs in the channel */ for (CUList::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, "%lu %s%s (%s@%s) %s ", i->first->GlobalCloneCount(), targchan->GetAllPrefixChars(i->first), i->first->nick, i->first->ident, i->first->dhost, i->first->fullname); user->WriteServ(checkstr + " member " + tmpbuf); } } else { /* /check on an IP address, or something that doesn't exist */ long x = 0; /* hostname or other */ for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++) { if (match(a->second->host, parameters[0]) || match(a->second->dhost, parameters[0])) { /* host or vhost matches mask */ user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost()); } /* IP address */ else if (match(a->second->GetIPString(), parameters[0], true)) { /* same IP. */ user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost()); } } user->WriteServ(checkstr + " matches " + ConvToStr(x)); } user->WriteServ(checkstr + " END " + std::string(parameters[0])); return CMD_LOCALONLY; } }; class ModuleCheck : public Module { private: cmd_check *mycommand; public: ModuleCheck(InspIRCd* Me) : Module(Me) { mycommand = new cmd_check(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleCheck() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { /* we don't hook anything, nothing required */ } }; MODULE_INIT(ModuleCheck) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides the /check command to retrieve information on a user, channel, or IP address */
+
+/** Handle /CHECK
+ */
+class cmd_check : public command_t
+{
+ public:
+ cmd_check (InspIRCd* Instance) : command_t(Instance,"CHECK", 'o', 1)
+ {
+ this->source = "m_check.so";
+ syntax = "<nickname>|<ip>|<hostmask>|<channel>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec *targuser;
+ chanrec *targchan;
+ std::string checkstr;
+ std::string chliststr;
+
+ char timebuf[60];
+ struct tm *mytime;
+
+
+ checkstr = "304 " + std::string(user->nick) + " :CHECK";
+
+ targuser = ServerInstance->FindNick(parameters[0]);
+ targchan = ServerInstance->FindChan(parameters[0]);
+
+ /*
+ * Syntax of a /check reply:
+ * :server.name 304 target :CHECK START <target>
+ * :server.name 304 target :CHECK <field> <value>
+ * :server.name 304 target :CHECK END
+ */
+
+ user->WriteServ(checkstr + " START " + parameters[0]);
+
+ if (targuser)
+ {
+ /* /check on a user */
+ user->WriteServ(checkstr + " nuh " + targuser->GetFullHost());
+ user->WriteServ(checkstr + " realnuh " + targuser->GetFullRealHost());
+ user->WriteServ(checkstr + " realname " + targuser->fullname);
+ user->WriteServ(checkstr + " modes +" + targuser->FormatModes());
+ user->WriteServ(checkstr + " snomasks +" + targuser->FormatNoticeMasks());
+ user->WriteServ(checkstr + " server " + targuser->server);
+
+ if (IS_AWAY(targuser))
+ {
+ /* user is away */
+ user->WriteServ(checkstr + " awaymsg " + targuser->awaymsg);
+ }
+
+ if (IS_OPER(targuser))
+ {
+ /* user is an oper of type ____ */
+ user->WriteServ(checkstr + " opertype " + irc::Spacify(targuser->oper));
+ }
+
+ if (IS_LOCAL(targuser))
+ {
+ /* port information is only held for a local user! */
+ user->WriteServ(checkstr + " onport " + ConvToStr(targuser->GetPort()));
+ }
+
+ chliststr = targuser->ChannelList(targuser);
+ std::stringstream dump(chliststr);
+
+ ServerInstance->DumpText(user,checkstr + " onchans ", dump);
+ }
+ else if (targchan)
+ {
+ /* /check on a channel */
+ time_t creation_time = targchan->created;
+ time_t topic_time = targchan->topicset;
+
+ mytime = gmtime(&creation_time);
+ strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime);
+ user->WriteServ(checkstr + " created " + timebuf);
+
+ if (targchan->topic[0] != 0)
+ {
+ /* there is a topic, assume topic related information exists */
+ user->WriteServ(checkstr + " topic " + targchan->topic);
+ user->WriteServ(checkstr + " topic_setby " + targchan->setby);
+ mytime = gmtime(&topic_time);
+ strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime);
+ user->WriteServ(checkstr + " topic_setat " + timebuf);
+ }
+
+ user->WriteServ(checkstr + " modes " + targchan->ChanModes(true));
+ user->WriteServ(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter()));
+
+ /* now the ugly bit, spool current members of a channel. :| */
+
+ CUList *ulist= targchan->GetUsers();
+
+ /* note that unlike /names, we do NOT check +i vs in the channel */
+ for (CUList::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, "%lu %s%s (%s@%s) %s ", i->first->GlobalCloneCount(), targchan->GetAllPrefixChars(i->first), i->first->nick, i->first->ident, i->first->dhost, i->first->fullname);
+ user->WriteServ(checkstr + " member " + tmpbuf);
+ }
+ }
+ else
+ {
+ /* /check on an IP address, or something that doesn't exist */
+ long x = 0;
+
+ /* hostname or other */
+ for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++)
+ {
+ if (match(a->second->host, parameters[0]) || match(a->second->dhost, parameters[0]))
+ {
+ /* host or vhost matches mask */
+ user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost());
+ }
+ /* IP address */
+ else if (match(a->second->GetIPString(), parameters[0], true))
+ {
+ /* same IP. */
+ user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost());
+ }
+ }
+
+ user->WriteServ(checkstr + " matches " + ConvToStr(x));
+ }
+
+ user->WriteServ(checkstr + " END " + std::string(parameters[0]));
+
+ return CMD_LOCALONLY;
+ }
+};
+
+
+class ModuleCheck : public Module
+{
+ private:
+ cmd_check *mycommand;
+ public:
+ ModuleCheck(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_check(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleCheck()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ /* we don't hook anything, nothing required */
+ }
+
+};
+
+MODULE_INIT(ModuleCheck)
diff --git a/src/modules/m_chghost.cpp b/src/modules/m_chghost.cpp
index 9fb751b8e..0ec88d7e1 100644
--- a/src/modules/m_chghost.cpp
+++ b/src/modules/m_chghost.cpp
@@ -1 +1,120 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for the CHGHOST command */ /** Handle /CHGHOST */ class cmd_chghost : public command_t { private: char* hostmap; public: cmd_chghost (InspIRCd* Instance, char* hmap) : command_t(Instance,"CHGHOST",'o',2), hostmap(hmap) { this->source = "m_chghost.so"; syntax = "<nick> <newhost>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { const char * x = parameters[1]; for (; *x; x++) { if (!hostmap[(unsigned char)*x]) { user->WriteServ("NOTICE "+std::string(user->nick)+" :*** CHGHOST: Invalid characters in hostname"); return CMD_FAILURE; } } if (!*parameters[0]) { user->WriteServ("NOTICE %s :*** CHGHOST: Host must be specified", user->nick); return CMD_FAILURE; } if ((parameters[1] - x) > 63) { user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick); return CMD_FAILURE; } userrec* dest = ServerInstance->FindNick(parameters[0]); if (!dest) { user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); return CMD_FAILURE; } if ((dest->ChangeDisplayedHost(parameters[1])) && (!ServerInstance->ULine(user->server))) { // fix by brain - ulines set hosts silently ServerInstance->WriteOpers(std::string(user->nick)+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost); } /* route it! */ return CMD_SUCCESS; } }; class ModuleChgHost : public Module { cmd_chghost* mycommand; char hostmap[256]; public: ModuleChgHost(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); mycommand = new cmd_chghost(ServerInstance, hostmap); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_OnRehash] = 1; } void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); std::string hmap = Conf.ReadValue("hostname", "charmap", 0); if (hmap.empty()) hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789"; memset(&hostmap, 0, 255); for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++) hostmap[(unsigned char)*n] = 1; } ~ModuleChgHost() { } Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleChgHost) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for the CHGHOST command */
+
+/** Handle /CHGHOST
+ */
+class cmd_chghost : public command_t
+{
+ private:
+ char* hostmap;
+ public:
+ cmd_chghost (InspIRCd* Instance, char* hmap) : command_t(Instance,"CHGHOST",'o',2), hostmap(hmap)
+ {
+ this->source = "m_chghost.so";
+ syntax = "<nick> <newhost>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ const char * x = parameters[1];
+
+ for (; *x; x++)
+ {
+ if (!hostmap[(unsigned char)*x])
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** CHGHOST: Invalid characters in hostname");
+ return CMD_FAILURE;
+ }
+ }
+ if (!*parameters[0])
+ {
+ user->WriteServ("NOTICE %s :*** CHGHOST: Host must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if ((parameters[1] - x) > 63)
+ {
+ user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick);
+ return CMD_FAILURE;
+ }
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+
+ if (!dest)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ if ((dest->ChangeDisplayedHost(parameters[1])) && (!ServerInstance->ULine(user->server)))
+ {
+ // fix by brain - ulines set hosts silently
+ ServerInstance->WriteOpers(std::string(user->nick)+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost);
+ }
+
+ /* route it! */
+ return CMD_SUCCESS;
+
+ }
+};
+
+
+class ModuleChgHost : public Module
+{
+ cmd_chghost* mycommand;
+ char hostmap[256];
+ public:
+ ModuleChgHost(InspIRCd* Me)
+ : Module(Me)
+ {
+ OnRehash(NULL,"");
+ mycommand = new cmd_chghost(ServerInstance, hostmap);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = 1;
+ }
+
+ void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+ std::string hmap = Conf.ReadValue("hostname", "charmap", 0);
+
+ if (hmap.empty())
+ hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789";
+
+ memset(&hostmap, 0, 255);
+ for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
+ hostmap[(unsigned char)*n] = 1;
+ }
+
+ ~ModuleChgHost()
+ {
+ }
+
+ Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleChgHost)
diff --git a/src/modules/m_chgident.cpp b/src/modules/m_chgident.cpp
index fb909b7ff..168adcc93 100644
--- a/src/modules/m_chgident.cpp
+++ b/src/modules/m_chgident.cpp
@@ -1 +1,92 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" /* $ModDesc: Provides support for the CHGIDENT command */ /** Handle /CHGIDENT */ class cmd_chgident : public command_t { public: cmd_chgident (InspIRCd* Instance) : command_t(Instance,"CHGIDENT", 'o', 2) { this->source = "m_chgident.so"; syntax = "<nick> <newident>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { userrec* dest = ServerInstance->FindNick(parameters[0]); if (!dest) { user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); return CMD_FAILURE; } if (!*parameters[1]) { user->WriteServ("NOTICE %s :*** CHGIDENT: Ident must be specified", user->nick); return CMD_FAILURE; } if (strlen(parameters[1]) > IDENTMAX) { user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick); return CMD_FAILURE; } if (!ServerInstance->IsIdent(parameters[1])) { user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick); return CMD_FAILURE; } dest->ChangeIdent(parameters[1]); ServerInstance->WriteOpers("%s used CHGIDENT to change %s's ident to '%s'", user->nick, dest->nick, dest->ident); /* route it! */ return CMD_SUCCESS; } }; class ModuleChgIdent : public Module { cmd_chgident* mycommand; public: ModuleChgIdent(InspIRCd* Me) : Module(Me) { mycommand = new cmd_chgident(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleChgIdent() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleChgIdent) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for the CHGIDENT command */
+
+/** Handle /CHGIDENT
+ */
+class cmd_chgident : public command_t
+{
+ public:
+ cmd_chgident (InspIRCd* Instance) : command_t(Instance,"CHGIDENT", 'o', 2)
+ {
+ this->source = "m_chgident.so";
+ syntax = "<nick> <newident>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+
+ if (!dest)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ if (!*parameters[1])
+ {
+ user->WriteServ("NOTICE %s :*** CHGIDENT: Ident must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (strlen(parameters[1]) > IDENTMAX)
+ {
+ user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (!ServerInstance->IsIdent(parameters[1]))
+ {
+ user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick);
+ return CMD_FAILURE;
+ }
+
+ dest->ChangeIdent(parameters[1]);
+ ServerInstance->WriteOpers("%s used CHGIDENT to change %s's ident to '%s'", user->nick, dest->nick, dest->ident);
+
+ /* route it! */
+ return CMD_SUCCESS;
+ }
+};
+
+
+class ModuleChgIdent : public Module
+{
+ cmd_chgident* mycommand;
+
+
+public:
+ ModuleChgIdent(InspIRCd* Me) : Module(Me)
+ {
+ mycommand = new cmd_chgident(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleChgIdent()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleChgIdent)
+
diff --git a/src/modules/m_chgname.cpp b/src/modules/m_chgname.cpp
index 0bf9004dd..a4a31714b 100644
--- a/src/modules/m_chgname.cpp
+++ b/src/modules/m_chgname.cpp
@@ -1 +1,89 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" /* $ModDesc: Provides support for the CHGNAME command */ /** Handle /CHGNAME */ class cmd_chgname : public command_t { public: cmd_chgname (InspIRCd* Instance) : command_t(Instance,"CHGNAME", 'o', 2) { this->source = "m_chgname.so"; syntax = "<nick> <newname>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { userrec* dest = ServerInstance->FindNick(parameters[0]); if (!dest) { user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); return CMD_FAILURE; } if (!*parameters[1]) { user->WriteServ("NOTICE %s :*** GECOS must be specified", user->nick); return CMD_FAILURE; } if (strlen(parameters[1]) > MAXGECOS) { user->WriteServ("NOTICE %s :*** GECOS too long", user->nick); return CMD_FAILURE; } if (IS_LOCAL(dest)) { dest->ChangeName(parameters[1]); ServerInstance->WriteOpers("%s used CHGNAME to change %s's real name to '%s'", user->nick, dest->nick, dest->fullname); return CMD_LOCALONLY; /* name change routed by FNAME in spanningtree now */ } /* route it! */ return CMD_SUCCESS; } }; class ModuleChgName : public Module { cmd_chgname* mycommand; public: ModuleChgName(InspIRCd* Me) : Module(Me) { mycommand = new cmd_chgname(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleChgName() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleChgName) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for the CHGNAME command */
+
+/** Handle /CHGNAME
+ */
+class cmd_chgname : public command_t
+{
+ public:
+ cmd_chgname (InspIRCd* Instance) : command_t(Instance,"CHGNAME", 'o', 2)
+ {
+ this->source = "m_chgname.so";
+ syntax = "<nick> <newname>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+
+ if (!dest)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ if (!*parameters[1])
+ {
+ user->WriteServ("NOTICE %s :*** GECOS must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (strlen(parameters[1]) > MAXGECOS)
+ {
+ user->WriteServ("NOTICE %s :*** GECOS too long", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (IS_LOCAL(dest))
+ {
+ dest->ChangeName(parameters[1]);
+ ServerInstance->WriteOpers("%s used CHGNAME to change %s's real name to '%s'", user->nick, dest->nick, dest->fullname);
+ return CMD_LOCALONLY; /* name change routed by FNAME in spanningtree now */
+ }
+
+ /* route it! */
+ return CMD_SUCCESS;
+ }
+};
+
+
+class ModuleChgName : public Module
+{
+ cmd_chgname* mycommand;
+
+
+public:
+ ModuleChgName(InspIRCd* Me) : Module(Me)
+ {
+ mycommand = new cmd_chgname(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleChgName()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleChgName)
diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp
index d7992301c..dfa5ee4e8 100644
--- a/src/modules/m_cloaking.cpp
+++ b/src/modules/m_cloaking.cpp
@@ -1 +1,315 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "m_hash.h" /* $ModDesc: Provides masking of user hostnames */ /* $ModDep: m_hash.h */ /* Used to vary the output a little more depending on the cloak keys */ static const char* xtab[] = {"F92E45D871BCA630", "A1B9D80C72E653F4", "1ABC078934DEF562", "ABCDEF5678901234"}; /** Handles user mode +x */ class CloakUser : public ModeHandler { std::string prefix; unsigned int key1; unsigned int key2; unsigned int key3; unsigned int key4; Module* Sender; Module* HashProvider; /** 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. * * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org". * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk", * and if it is passed "localhost.localdomain" it will return ".localdomain". * * This is used to ensure a significant part of the host is always cloaked (see Bug #216) */ std::string LastTwoDomainParts(const std::string &host) { int dots = 0; std::string::size_type splitdot = host.length(); for (std::string::size_type x = host.length() - 1; x; --x) { if (host[x] == '.') { splitdot = x; dots++; } if (dots >= 3) break; } if (splitdot == host.length()) return host; else return host.substr(splitdot); } public: CloakUser(InspIRCd* Instance, Module* Source, Module* Hash) : ModeHandler(Instance, 'x', 0, 0, false, MODETYPE_USER, false), Sender(Source), HashProvider(Hash) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (source != dest) return MODEACTION_DENY; /* For remote clients, we dont take any action, we just allow it. * The local server where they are will set their cloak instead. */ if (!IS_LOCAL(dest)) return MODEACTION_ALLOW; if (adding) { if(!dest->IsModeSet('x')) { /* The mode is being turned on - so attempt to * allocate the user a cloaked host using a non-reversible * algorithm (its simple, but its non-reversible so the * simplicity doesnt really matter). This algorithm * will not work if the user has only one level of domain * naming in their hostname (e.g. if they are on a lan or * are connecting via localhost) -- this doesnt matter much. */ char* n1 = strchr(dest->host,'.'); char* n2 = strchr(dest->host,':'); if (n1 || n2) { /* InspIRCd users have two hostnames; A displayed * hostname which can be modified by modules (e.g. * to create vhosts, implement chghost, etc) and a * 'real' hostname which you shouldnt write to. */ unsigned int iv[] = { key1, key2, key3, key4 }; std::string a = LastTwoDomainParts(dest->host); std::string b; /** Reset the Hash module, and send it our IV and hex table */ HashResetRequest(Sender, HashProvider).Send(); HashKeyRequest(Sender, HashProvider, iv).Send(); HashHexRequest(Sender, HashProvider, xtab[(*dest->host) % 4]); /* Generate a cloak using specialized Hash */ std::string hostcloak = prefix + "-" + std::string(HashSumRequest(Sender, HashProvider, dest->host).Send()).substr(0,8) + a; /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes * according to the DNS RFC) then tough titty, they get cloaked as an IP. * Their ISP shouldnt go to town on subdomains, or they shouldnt have a kiddie * vhost. */ #ifdef IPV6 in6_addr testaddr; in_addr testaddr2; if ((dest->GetProtocolFamily() == AF_INET6) && (inet_pton(AF_INET6,dest->host,&testaddr) < 1) && (hostcloak.length() <= 64)) /* Invalid ipv6 address, and ipv6 user (resolved host) */ b = hostcloak; else if ((dest->GetProtocolFamily() == AF_INET) && (inet_aton(dest->host,&testaddr2) < 1) && (hostcloak.length() <= 64)) /* Invalid ipv4 address, and ipv4 user (resolved host) */ b = hostcloak; else /* Valid ipv6 or ipv4 address (not resolved) ipv4 or ipv6 user */ b = ((!strchr(dest->host,':')) ? Cloak4(dest->host) : Cloak6(dest->host)); #else in_addr testaddr; if ((inet_aton(dest->host,&testaddr) < 1) && (hostcloak.length() <= 64)) /* Invalid ipv4 address, and ipv4 user (resolved host) */ b = hostcloak; else /* Valid ipv4 address (not resolved) ipv4 user */ b = Cloak4(dest->host); #endif dest->ChangeDisplayedHost(b.c_str()); } dest->SetMode('x',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('x')) { /* User is removing the mode, so just restore their real host * and make it match the displayed one. */ dest->ChangeDisplayedHost(dest->host); dest->SetMode('x',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } std::string Cloak4(const char* ip) { unsigned int iv[] = { key1, key2, key3, key4 }; irc::sepstream seps(ip, '.'); std::string ra[4];; std::string octet[4]; int i[4]; for (int j = 0; j < 4; j++) { octet[j] = seps.GetToken(); 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 */ HashResetRequest(Sender, HashProvider).Send(); HashKeyRequest(Sender, HashProvider, iv).Send(); /* Send the Hash module a different hex table for each octet group's Hash sum */ for (int k = 0; k < 4; k++) { HashHexRequest(Sender, HashProvider, xtab[(iv[k]+i[k]) % 4]).Send(); ra[k] = std::string(HashSumRequest(Sender, HashProvider, octet[k]).Send()).substr(0,6); } /* Stick them all together */ return std::string().append(ra[0]).append(".").append(ra[1]).append(".").append(ra[2]).append(".").append(ra[3]); } std::string Cloak6(const char* ip) { /* Theyre using 4in6 (YUCK). Translate as ipv4 cloak */ if (!strncmp(ip, "0::ffff:", 8)) return Cloak4(ip + 8); /* If we get here, yes it really is an ipv6 ip */ unsigned int iv[] = { key1, key2, key3, key4 }; std::vector<std::string> hashies; std::string item; int rounds = 0; /* Reset the Hash module and send it our IV */ HashResetRequest(Sender, HashProvider).Send(); HashKeyRequest(Sender, HashProvider, iv).Send(); for (const char* input = ip; *input; input++) { item += *input; if (item.length() > 7) { /* Send the Hash module a different hex table for each octet group's Hash sum */ HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send(); hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8)); item.clear(); } rounds++; } if (!item.empty()) { /* Send the Hash module a different hex table for each octet group's Hash sum */ HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send(); hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8)); item.clear(); } /* Stick them all together */ return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined(); } void DoRehash() { ConfigReader Conf(ServerInstance); key1 = key2 = key3 = key4 = 0; key1 = Conf.ReadInteger("cloak","key1",0,true); key2 = Conf.ReadInteger("cloak","key2",0,true); key3 = Conf.ReadInteger("cloak","key3",0,true); key4 = Conf.ReadInteger("cloak","key4",0,true); prefix = Conf.ReadValue("cloak","prefix",0); if (prefix.empty()) prefix = ServerInstance->Config->Network; if (!key1 && !key2 && !key3 && !key4) throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED!"); } }; class ModuleCloaking : public Module { private: CloakUser* cu; Module* HashModule; public: ModuleCloaking(InspIRCd* Me) : Module(Me) { ServerInstance->UseInterface("HashRequest"); /* Attempt to locate the md5 service provider, bail if we can't find it */ HashModule = ServerInstance->FindModule("m_md5.so"); if (!HashModule) throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_cloaking.so."); /* Create new mode handler object */ cu = new CloakUser(ServerInstance, this, HashModule); /* Register it with the core */ if (!ServerInstance->AddMode(cu, 'x')) throw ModuleException("Could not add new modes!"); OnRehash(NULL,""); } virtual ~ModuleCloaking() { ServerInstance->Modes->DelMode(cu); DELETE(cu); ServerInstance->DoneWithInterface("HashRequest"); } virtual Version GetVersion() { // returns the version number of the module to be // listed in /MODULES return Version(1,1,0,2,VF_COMMON|VF_VENDOR,API_VERSION); } virtual void OnRehash(userrec* user, const std::string &parameter) { cu->DoRehash(); } void Implements(char* List) { List[I_OnRehash] = 1; } }; MODULE_INIT(ModuleCloaking) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_hash.h"
+
+/* $ModDesc: Provides masking of user hostnames */
+/* $ModDep: m_hash.h */
+
+/* Used to vary the output a little more depending on the cloak keys */
+static const char* xtab[] = {"F92E45D871BCA630", "A1B9D80C72E653F4", "1ABC078934DEF562", "ABCDEF5678901234"};
+
+/** Handles user mode +x
+ */
+class CloakUser : public ModeHandler
+{
+
+ std::string prefix;
+ unsigned int key1;
+ unsigned int key2;
+ unsigned int key3;
+ unsigned int key4;
+ Module* Sender;
+ Module* HashProvider;
+
+ /** 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.
+ *
+ * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org".
+ * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk",
+ * and if it is passed "localhost.localdomain" it will return ".localdomain".
+ *
+ * This is used to ensure a significant part of the host is always cloaked (see Bug #216)
+ */
+ std::string LastTwoDomainParts(const std::string &host)
+ {
+ int dots = 0;
+ std::string::size_type splitdot = host.length();
+
+ for (std::string::size_type x = host.length() - 1; x; --x)
+ {
+ if (host[x] == '.')
+ {
+ splitdot = x;
+ dots++;
+ }
+ if (dots >= 3)
+ break;
+ }
+
+ if (splitdot == host.length())
+ return host;
+ else
+ return host.substr(splitdot);
+ }
+
+ public:
+ CloakUser(InspIRCd* Instance, Module* Source, Module* Hash) : ModeHandler(Instance, 'x', 0, 0, false, MODETYPE_USER, false), Sender(Source), HashProvider(Hash)
+ {
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (source != dest)
+ return MODEACTION_DENY;
+
+ /* For remote clients, we dont take any action, we just allow it.
+ * The local server where they are will set their cloak instead.
+ */
+ if (!IS_LOCAL(dest))
+ return MODEACTION_ALLOW;
+
+ if (adding)
+ {
+ if(!dest->IsModeSet('x'))
+ {
+ /* The mode is being turned on - so attempt to
+ * allocate the user a cloaked host using a non-reversible
+ * algorithm (its simple, but its non-reversible so the
+ * simplicity doesnt really matter). This algorithm
+ * will not work if the user has only one level of domain
+ * naming in their hostname (e.g. if they are on a lan or
+ * are connecting via localhost) -- this doesnt matter much.
+ */
+
+ char* n1 = strchr(dest->host,'.');
+ char* n2 = strchr(dest->host,':');
+
+ if (n1 || n2)
+ {
+ /* InspIRCd users have two hostnames; A displayed
+ * hostname which can be modified by modules (e.g.
+ * to create vhosts, implement chghost, etc) and a
+ * 'real' hostname which you shouldnt write to.
+ */
+
+ unsigned int iv[] = { key1, key2, key3, key4 };
+ std::string a = LastTwoDomainParts(dest->host);
+ std::string b;
+
+ /** Reset the Hash module, and send it our IV and hex table */
+ HashResetRequest(Sender, HashProvider).Send();
+ HashKeyRequest(Sender, HashProvider, iv).Send();
+ HashHexRequest(Sender, HashProvider, xtab[(*dest->host) % 4]);
+
+ /* Generate a cloak using specialized Hash */
+ std::string hostcloak = prefix + "-" + std::string(HashSumRequest(Sender, HashProvider, dest->host).Send()).substr(0,8) + a;
+
+ /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
+ * according to the DNS RFC) then tough titty, they get cloaked as an IP.
+ * Their ISP shouldnt go to town on subdomains, or they shouldnt have a kiddie
+ * vhost.
+ */
+#ifdef IPV6
+ in6_addr testaddr;
+ in_addr testaddr2;
+ if ((dest->GetProtocolFamily() == AF_INET6) && (inet_pton(AF_INET6,dest->host,&testaddr) < 1) && (hostcloak.length() <= 64))
+ /* Invalid ipv6 address, and ipv6 user (resolved host) */
+ b = hostcloak;
+ else if ((dest->GetProtocolFamily() == AF_INET) && (inet_aton(dest->host,&testaddr2) < 1) && (hostcloak.length() <= 64))
+ /* Invalid ipv4 address, and ipv4 user (resolved host) */
+ b = hostcloak;
+ else
+ /* Valid ipv6 or ipv4 address (not resolved) ipv4 or ipv6 user */
+ b = ((!strchr(dest->host,':')) ? Cloak4(dest->host) : Cloak6(dest->host));
+#else
+ in_addr testaddr;
+ if ((inet_aton(dest->host,&testaddr) < 1) && (hostcloak.length() <= 64))
+ /* Invalid ipv4 address, and ipv4 user (resolved host) */
+ b = hostcloak;
+ else
+ /* Valid ipv4 address (not resolved) ipv4 user */
+ b = Cloak4(dest->host);
+#endif
+
+ dest->ChangeDisplayedHost(b.c_str());
+ }
+
+ dest->SetMode('x',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('x'))
+ {
+ /* User is removing the mode, so just restore their real host
+ * and make it match the displayed one.
+ */
+ dest->ChangeDisplayedHost(dest->host);
+ dest->SetMode('x',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+
+ std::string Cloak4(const char* ip)
+ {
+ unsigned int iv[] = { key1, key2, key3, key4 };
+ irc::sepstream seps(ip, '.');
+ std::string ra[4];;
+ std::string octet[4];
+ int i[4];
+
+ for (int j = 0; j < 4; j++)
+ {
+ octet[j] = seps.GetToken();
+ 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 */
+ HashResetRequest(Sender, HashProvider).Send();
+ HashKeyRequest(Sender, HashProvider, iv).Send();
+
+ /* Send the Hash module a different hex table for each octet group's Hash sum */
+ for (int k = 0; k < 4; k++)
+ {
+ HashHexRequest(Sender, HashProvider, xtab[(iv[k]+i[k]) % 4]).Send();
+ ra[k] = std::string(HashSumRequest(Sender, HashProvider, octet[k]).Send()).substr(0,6);
+ }
+ /* Stick them all together */
+ return std::string().append(ra[0]).append(".").append(ra[1]).append(".").append(ra[2]).append(".").append(ra[3]);
+ }
+
+ std::string Cloak6(const char* ip)
+ {
+ /* Theyre using 4in6 (YUCK). Translate as ipv4 cloak */
+ if (!strncmp(ip, "0::ffff:", 8))
+ return Cloak4(ip + 8);
+
+ /* If we get here, yes it really is an ipv6 ip */
+ unsigned int iv[] = { key1, key2, key3, key4 };
+ std::vector<std::string> hashies;
+ std::string item;
+ int rounds = 0;
+
+ /* Reset the Hash module and send it our IV */
+ HashResetRequest(Sender, HashProvider).Send();
+ HashKeyRequest(Sender, HashProvider, iv).Send();
+
+ for (const char* input = ip; *input; input++)
+ {
+ item += *input;
+ if (item.length() > 7)
+ {
+ /* Send the Hash module a different hex table for each octet group's Hash sum */
+ HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send();
+ hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8));
+ item.clear();
+ }
+ rounds++;
+ }
+ if (!item.empty())
+ {
+ /* Send the Hash module a different hex table for each octet group's Hash sum */
+ HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send();
+ hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8));
+ item.clear();
+ }
+ /* Stick them all together */
+ return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined();
+ }
+
+ void DoRehash()
+ {
+ ConfigReader Conf(ServerInstance);
+ key1 = key2 = key3 = key4 = 0;
+ key1 = Conf.ReadInteger("cloak","key1",0,true);
+ key2 = Conf.ReadInteger("cloak","key2",0,true);
+ key3 = Conf.ReadInteger("cloak","key3",0,true);
+ key4 = Conf.ReadInteger("cloak","key4",0,true);
+ prefix = Conf.ReadValue("cloak","prefix",0);
+
+ if (prefix.empty())
+ prefix = ServerInstance->Config->Network;
+
+ if (!key1 && !key2 && !key3 && !key4)
+ throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED!");
+ }
+};
+
+
+class ModuleCloaking : public Module
+{
+ private:
+
+ CloakUser* cu;
+ Module* HashModule;
+
+ public:
+ ModuleCloaking(InspIRCd* Me)
+ : Module(Me)
+ {
+ ServerInstance->UseInterface("HashRequest");
+
+ /* Attempt to locate the md5 service provider, bail if we can't find it */
+ HashModule = ServerInstance->FindModule("m_md5.so");
+ if (!HashModule)
+ throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_cloaking.so.");
+
+ /* Create new mode handler object */
+ cu = new CloakUser(ServerInstance, this, HashModule);
+
+ /* Register it with the core */
+ if (!ServerInstance->AddMode(cu, 'x'))
+ throw ModuleException("Could not add new modes!");
+
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleCloaking()
+ {
+ ServerInstance->Modes->DelMode(cu);
+ DELETE(cu);
+ ServerInstance->DoneWithInterface("HashRequest");
+ }
+
+ virtual Version GetVersion()
+ {
+ // returns the version number of the module to be
+ // listed in /MODULES
+ return Version(1,1,0,2,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ cu->DoRehash();
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = 1;
+ }
+};
+
+MODULE_INIT(ModuleCloaking)
diff --git a/src/modules/m_clones.cpp b/src/modules/m_clones.cpp
index 429b9c2b9..72a7ca7e8 100644
--- a/src/modules/m_clones.cpp
+++ b/src/modules/m_clones.cpp
@@ -1 +1,100 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" /* $ModDesc: Provides the /clones command to retrieve information on a user, channel, or IP address */ /** Handle /CHECK */ class cmd_clones : public command_t { public: cmd_clones (InspIRCd* Instance) : command_t(Instance,"CLONES", 'o', 1) { this->source = "m_clones.so"; syntax = "<limit>"; } std::string FindMatchingIP(const irc::string &ipaddr) { std::string n = assign(ipaddr); for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++) if (a->second->GetIPString() == n) return a->second->GetFullRealHost(); return "<?>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { std::string clonesstr = "304 " + std::string(user->nick) + " :CLONES"; unsigned long limit = atoi(parameters[0]); /* * Syntax of a /clones reply: * :server.name 304 target :CLONES START * :server.name 304 target :CLONES <count> <ip> <fullhost> * :server.name 304 target :CHECK END */ user->WriteServ(clonesstr + " START"); /* hostname or other */ for (clonemap::iterator x = ServerInstance->global_clones.begin(); x != ServerInstance->global_clones.end(); x++) { if (x->second >= limit) user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + assign(x->first) + " " + FindMatchingIP(x->first)); } user->WriteServ(clonesstr + " END"); return CMD_LOCALONLY; } }; class ModuleClones : public Module { private: cmd_clones *mycommand; public: ModuleClones(InspIRCd* Me) : Module(Me) { mycommand = new cmd_clones(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleClones() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { /* we don't hook anything, nothing required */ } }; MODULE_INIT(ModuleClones) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides the /clones command to retrieve information on a user, channel, or IP address */
+
+/** Handle /CHECK
+ */
+class cmd_clones : public command_t
+{
+ public:
+ cmd_clones (InspIRCd* Instance) : command_t(Instance,"CLONES", 'o', 1)
+ {
+ this->source = "m_clones.so";
+ syntax = "<limit>";
+ }
+
+ std::string FindMatchingIP(const irc::string &ipaddr)
+ {
+ std::string n = assign(ipaddr);
+ for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++)
+ if (a->second->GetIPString() == n)
+ return a->second->GetFullRealHost();
+ return "<?>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+
+ std::string clonesstr = "304 " + std::string(user->nick) + " :CLONES";
+
+ unsigned long limit = atoi(parameters[0]);
+
+ /*
+ * Syntax of a /clones reply:
+ * :server.name 304 target :CLONES START
+ * :server.name 304 target :CLONES <count> <ip> <fullhost>
+ * :server.name 304 target :CHECK END
+ */
+
+ user->WriteServ(clonesstr + " START");
+
+ /* hostname or other */
+ for (clonemap::iterator x = ServerInstance->global_clones.begin(); x != ServerInstance->global_clones.end(); x++)
+ {
+ if (x->second >= limit)
+ user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + assign(x->first) + " " + FindMatchingIP(x->first));
+ }
+
+ user->WriteServ(clonesstr + " END");
+
+ return CMD_LOCALONLY;
+ }
+};
+
+
+class ModuleClones : public Module
+{
+ private:
+ cmd_clones *mycommand;
+ public:
+ ModuleClones(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_clones(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleClones()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ /* we don't hook anything, nothing required */
+ }
+
+};
+
+MODULE_INIT(ModuleClones)
diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp
index a8e81fcd8..2d639f310 100644
--- a/src/modules/m_conn_join.cpp
+++ b/src/modules/m_conn_join.cpp
@@ -1 +1,96 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Forces users to join the specified channel(s) on connect */ class ModuleConnJoin : public Module { private: std::string JoinChan; std::vector<std::string> Joinchans; int tokenize(const string &str, std::vector<std::string> &tokens) { // skip delimiters at beginning. string::size_type lastPos = str.find_first_not_of(",", 0); // find first "non-delimiter". string::size_type pos = str.find_first_of(",", lastPos); while (string::npos != pos || 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: ModuleConnJoin(InspIRCd* Me) : Module(Me) { OnRehash(NULL, ""); } Priority Prioritize() { return PRIORITY_LAST; } void Implements(char* List) { List[I_OnPostConnect] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader* conf = new ConfigReader(ServerInstance); JoinChan = conf->ReadValue("autojoin", "channel", 0); Joinchans.clear(); if (!JoinChan.empty()) tokenize(JoinChan,Joinchans); DELETE(conf); } virtual ~ModuleConnJoin() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void OnPostConnect(userrec* user) { if (!IS_LOCAL(user)) return; for(std::vector<std::string>::iterator it = Joinchans.begin(); it != Joinchans.end(); it++) if (ServerInstance->IsChannel(it->c_str())) chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true)); } }; MODULE_INIT(ModuleConnJoin) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Forces users to join the specified channel(s) on connect */
+
+class ModuleConnJoin : public Module
+{
+ private:
+ std::string JoinChan;
+ std::vector<std::string> Joinchans;
+
+
+ int tokenize(const string &str, std::vector<std::string> &tokens)
+ {
+ // skip delimiters at beginning.
+ string::size_type lastPos = str.find_first_not_of(",", 0);
+ // find first "non-delimiter".
+ string::size_type pos = str.find_first_of(",", lastPos);
+
+ while (string::npos != pos || 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:
+ ModuleConnJoin(InspIRCd* Me)
+ : Module(Me)
+ {
+ OnRehash(NULL, "");
+ }
+
+ Priority Prioritize()
+ {
+ return PRIORITY_LAST;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPostConnect] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader* conf = new ConfigReader(ServerInstance);
+ JoinChan = conf->ReadValue("autojoin", "channel", 0);
+ Joinchans.clear();
+ if (!JoinChan.empty())
+ tokenize(JoinChan,Joinchans);
+ DELETE(conf);
+ }
+
+ virtual ~ModuleConnJoin()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnPostConnect(userrec* user)
+ {
+ if (!IS_LOCAL(user))
+ return;
+
+ for(std::vector<std::string>::iterator it = Joinchans.begin(); it != Joinchans.end(); it++)
+ if (ServerInstance->IsChannel(it->c_str()))
+ chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true));
+ }
+
+};
+
+
+MODULE_INIT(ModuleConnJoin)
diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp
index f9118a384..3f27eeff5 100644
--- a/src/modules/m_conn_umodes.cpp
+++ b/src/modules/m_conn_umodes.cpp
@@ -1 +1,104 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" /* $ModDesc: Sets (and unsets) modes on users when they connect */ class ModuleModesOnConnect : public Module { private: ConfigReader *Conf; public: ModuleModesOnConnect(InspIRCd* Me) : Module(Me) { Conf = new ConfigReader(ServerInstance); } void Implements(char* List) { List[I_OnPostConnect] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { DELETE(Conf); Conf = new ConfigReader(ServerInstance); } virtual ~ModuleModesOnConnect() { DELETE(Conf); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void OnPostConnect(userrec* user) { if (!IS_LOCAL(user)) return; for (int j = 0; j < Conf->Enumerate("connect"); j++) { std::string hostn = Conf->ReadValue("connect","allow",j); if ((match(user->GetIPString(),hostn.c_str(),true)) || (match(user->host,hostn.c_str()))) { std::string ThisModes = Conf->ReadValue("connect","modes",j); if (!ThisModes.empty()) { std::string buf; stringstream ss(ThisModes); vector<string> tokens; // split ThisUserModes into modes and mode params while (ss >> buf) tokens.push_back(buf); int size = tokens.size() + 1; const char** modes = new const char*[size]; modes[0] = user->nick; modes[1] = tokens[0].c_str(); if (tokens.size() > 1) { // process mode params int i = 2; for (unsigned int k = 1; k < tokens.size(); k++) { modes[i] = tokens[k].c_str(); i++; } } ServerInstance->Parser->CallHandler("MODE", modes, size, user); delete [] modes; } break; } } } }; MODULE_INIT(ModuleModesOnConnect) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+/* $ModDesc: Sets (and unsets) modes on users when they connect */
+
+class ModuleModesOnConnect : public Module
+{
+ private:
+
+ ConfigReader *Conf;
+
+ public:
+ ModuleModesOnConnect(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPostConnect] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ DELETE(Conf);
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual ~ModuleModesOnConnect()
+ {
+ DELETE(Conf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnPostConnect(userrec* user)
+ {
+ if (!IS_LOCAL(user))
+ return;
+
+ for (int j = 0; j < Conf->Enumerate("connect"); j++)
+ {
+ std::string hostn = Conf->ReadValue("connect","allow",j);
+ if ((match(user->GetIPString(),hostn.c_str(),true)) || (match(user->host,hostn.c_str())))
+ {
+ std::string ThisModes = Conf->ReadValue("connect","modes",j);
+ if (!ThisModes.empty())
+ {
+ std::string buf;
+ stringstream ss(ThisModes);
+
+ vector<string> tokens;
+
+ // split ThisUserModes into modes and mode params
+ while (ss >> buf)
+ tokens.push_back(buf);
+
+ int size = tokens.size() + 1;
+ const char** modes = new const char*[size];
+ modes[0] = user->nick;
+ modes[1] = tokens[0].c_str();
+
+ if (tokens.size() > 1)
+ {
+ // process mode params
+ int i = 2;
+ for (unsigned int k = 1; k < tokens.size(); k++)
+ {
+ modes[i] = tokens[k].c_str();
+ i++;
+ }
+ }
+
+ ServerInstance->Parser->CallHandler("MODE", modes, size, user);
+ delete [] modes;
+ }
+ break;
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleModesOnConnect)
diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp
index b84533f88..0dd27ddbd 100644
--- a/src/modules/m_conn_waitpong.cpp
+++ b/src/modules/m_conn_waitpong.cpp
@@ -1 +1,148 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.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; bool killonbadreply; const char* extenstr; public: ModuleWaitPong(InspIRCd* Me) : Module(Me), extenstr("waitpong_pingstr") { OnRehash(NULL,""); } virtual void OnRehash(userrec* user, const std::string &param) { ConfigReader Conf(ServerInstance); sendsnotice = Conf.ReadFlag("waitpong", "sendsnotice", 0); if(Conf.GetError() == CONF_VALUE_NOT_FOUND) sendsnotice = true; killonbadreply = Conf.ReadFlag("waitpong", "killonbadreply", 0); if(Conf.GetError() == CONF_VALUE_NOT_FOUND) killonbadreply = true; } void Implements(char* List) { List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnPreCommand] = List[I_OnRehash] = List[I_OnUserDisconnect] = List[I_OnCleanup] = 1; } char* RandString(unsigned int length) { unsigned char* out = new unsigned char[length+1]; for(unsigned int i = 0; i < length; i++) out[i] = ((rand() % 26) + 65); out[length] = '\0'; return (char*)out; } virtual int OnUserRegister(userrec* user) { char* pingrpl = RandString(10); user->Write("PING :%s", pingrpl); 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, pingrpl, pingrpl); user->Extend(extenstr, pingrpl); return 0; } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec* user, bool validated, const std::string &original_line) { if(command == "PONG") { char* pingrpl; user->GetExt(extenstr, pingrpl); if(pingrpl) { if(strcmp(pingrpl, parameters[0]) == 0) { DELETE(pingrpl); user->Shrink(extenstr); return 1; } else { if(killonbadreply) userrec::QuitUser(ServerInstance, user, "Incorrect ping reply for registration"); return 1; } } } return 0; } virtual bool OnCheckReady(userrec* user) { char* pingrpl; return (!user->GetExt(extenstr, pingrpl)); } virtual void OnUserDisconnect(userrec* user) { char* pingrpl; user->GetExt(extenstr, pingrpl); if(pingrpl) { DELETE(pingrpl); user->Shrink(extenstr); } } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_USER) { userrec* user = (userrec*)item; char* pingrpl; user->GetExt(extenstr, pingrpl); if(pingrpl) { DELETE(pingrpl); user->Shrink(extenstr); } } } virtual ~ModuleWaitPong() { } virtual Version GetVersion() { return Version(1, 1, 0, 1, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleWaitPong) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.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;
+ bool killonbadreply;
+ const char* extenstr;
+
+ public:
+ ModuleWaitPong(InspIRCd* Me)
+ : Module(Me), extenstr("waitpong_pingstr")
+ {
+ OnRehash(NULL,"");
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ ConfigReader Conf(ServerInstance);
+
+ sendsnotice = Conf.ReadFlag("waitpong", "sendsnotice", 0);
+
+ if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
+ sendsnotice = true;
+
+ killonbadreply = Conf.ReadFlag("waitpong", "killonbadreply", 0);
+
+ if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
+ killonbadreply = true;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnPreCommand] = List[I_OnRehash] = List[I_OnUserDisconnect] = List[I_OnCleanup] = 1;
+ }
+
+ char* RandString(unsigned int length)
+ {
+ unsigned char* out = new unsigned char[length+1];
+ for(unsigned int i = 0; i < length; i++)
+ out[i] = ((rand() % 26) + 65);
+ out[length] = '\0';
+
+ return (char*)out;
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ char* pingrpl = RandString(10);
+
+ user->Write("PING :%s", pingrpl);
+
+ 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, pingrpl, pingrpl);
+
+ user->Extend(extenstr, pingrpl);
+ return 0;
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec* user, bool validated, const std::string &original_line)
+ {
+ if(command == "PONG")
+ {
+ char* pingrpl;
+ user->GetExt(extenstr, pingrpl);
+
+ if(pingrpl)
+ {
+ if(strcmp(pingrpl, parameters[0]) == 0)
+ {
+ DELETE(pingrpl);
+ user->Shrink(extenstr);
+ return 1;
+ }
+ else
+ {
+ if(killonbadreply)
+ userrec::QuitUser(ServerInstance, user, "Incorrect ping reply for registration");
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual bool OnCheckReady(userrec* user)
+ {
+ char* pingrpl;
+ return (!user->GetExt(extenstr, pingrpl));
+ }
+
+ virtual void OnUserDisconnect(userrec* user)
+ {
+ char* pingrpl;
+ user->GetExt(extenstr, pingrpl);
+
+ if(pingrpl)
+ {
+ DELETE(pingrpl);
+ user->Shrink(extenstr);
+ }
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ char* pingrpl;
+ user->GetExt(extenstr, pingrpl);
+
+ if(pingrpl)
+ {
+ DELETE(pingrpl);
+ user->Shrink(extenstr);
+ }
+ }
+ }
+
+ virtual ~ModuleWaitPong()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 1, VF_VENDOR, API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleWaitPong)
diff --git a/src/modules/m_connflood.cpp b/src/modules/m_connflood.cpp
index 71e52fd01..47b19fdf4 100644
--- a/src/modules/m_connflood.cpp
+++ b/src/modules/m_connflood.cpp
@@ -1 +1,120 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" /* $ModDesc: Connection throttle */ int conns = 0, throttled = 0; class ModuleConnFlood : public Module { private: int seconds, maxconns, timeout, boot_wait; time_t first; std::string quitmsg; ConfigReader* conf; public: ModuleConnFlood(InspIRCd* Me) : Module(Me) { InitConf(); } virtual ~ModuleConnFlood() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserRegister] = 1; } void InitConf() { /* read configuration variables */ conf = new ConfigReader(ServerInstance); /* throttle configuration */ seconds = conf->ReadInteger("connflood", "seconds", 0, true); maxconns = conf->ReadInteger("connflood", "maxconns", 0, true); timeout = conf->ReadInteger("connflood", "timeout", 0, true); quitmsg = conf->ReadValue("connflood", "quitmsg", 0); /* seconds to wait when the server just booted */ boot_wait = conf->ReadInteger("connflood", "bootwait", 0, true); first = ServerInstance->Time(); } virtual int OnUserRegister(userrec* user) { time_t next = ServerInstance->Time(); if ((ServerInstance->startup_time + boot_wait) > next) return 0; /* time difference between first and latest connection */ time_t tdiff = next - first; /* increase connection count */ conns++; if (throttled == 1) { if (tdiff > seconds + timeout) { /* expire throttle */ throttled = 0; ServerInstance->WriteOpers("*** Connection throttle deactivated"); return 0; } userrec::QuitUser(ServerInstance, user, quitmsg); return 1; } if (tdiff <= seconds) { if (conns >= maxconns) { throttled = 1; ServerInstance->WriteOpers("*** Connection throttle activated"); userrec::QuitUser(ServerInstance, user, quitmsg); return 1; } } else { conns = 1; first = next; } return 0; } virtual void OnRehash(userrec* user, const std::string &parameter) { InitConf(); } }; MODULE_INIT(ModuleConnFlood) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+
+/* $ModDesc: Connection throttle */
+
+int conns = 0, throttled = 0;
+
+class ModuleConnFlood : public Module
+{
+private:
+ int seconds, maxconns, timeout, boot_wait;
+ time_t first;
+ std::string quitmsg;
+
+ ConfigReader* conf;
+
+
+public:
+ ModuleConnFlood(InspIRCd* Me) : Module(Me)
+ {
+
+ InitConf();
+ }
+
+ virtual ~ModuleConnFlood()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserRegister] = 1;
+ }
+
+ void InitConf()
+ {
+ /* read configuration variables */
+ conf = new ConfigReader(ServerInstance);
+ /* throttle configuration */
+ seconds = conf->ReadInteger("connflood", "seconds", 0, true);
+ maxconns = conf->ReadInteger("connflood", "maxconns", 0, true);
+ timeout = conf->ReadInteger("connflood", "timeout", 0, true);
+ quitmsg = conf->ReadValue("connflood", "quitmsg", 0);
+
+ /* seconds to wait when the server just booted */
+ boot_wait = conf->ReadInteger("connflood", "bootwait", 0, true);
+
+ first = ServerInstance->Time();
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ time_t next = ServerInstance->Time();
+
+ if ((ServerInstance->startup_time + boot_wait) > next)
+ return 0;
+
+ /* time difference between first and latest connection */
+ time_t tdiff = next - first;
+
+ /* increase connection count */
+ conns++;
+
+ if (throttled == 1)
+ {
+ if (tdiff > seconds + timeout)
+ {
+ /* expire throttle */
+ throttled = 0;
+ ServerInstance->WriteOpers("*** Connection throttle deactivated");
+ return 0;
+ }
+ userrec::QuitUser(ServerInstance, user, quitmsg);
+ return 1;
+ }
+
+ if (tdiff <= seconds)
+ {
+ if (conns >= maxconns)
+ {
+ throttled = 1;
+ ServerInstance->WriteOpers("*** Connection throttle activated");
+ userrec::QuitUser(ServerInstance, user, quitmsg);
+ return 1;
+ }
+ }
+ else
+ {
+ conns = 1;
+ first = next;
+ }
+ return 0;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ InitConf();
+ }
+
+};
+
+MODULE_INIT(ModuleConnFlood)
diff --git a/src/modules/m_cycle.cpp b/src/modules/m_cycle.cpp
index b1a22941d..eb20f4f99 100644
--- a/src/modules/m_cycle.cpp
+++ b/src/modules/m_cycle.cpp
@@ -1 +1,99 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style CYCLE command. */ /** Handle /CYCLE */ class cmd_cycle : public command_t { public: cmd_cycle (InspIRCd* Instance) : command_t(Instance,"CYCLE", 0, 1) { this->source = "m_cycle.so"; syntax = "<channel> :[reason]"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { chanrec* channel = ServerInstance->FindChan(parameters[0]); std::string reason = ConvToStr("Cycling"); if (pcnt > 1) { /* reason provided, use it */ reason = reason + ": " + parameters[1]; } if (channel) { /* * technically, this is only ever sent locally, but pays to be safe ;p */ if (IS_LOCAL(user)) { if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user)) { /* banned, boned. drop the message. */ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not cycle, as you are banned on channel " + channel->name); return CMD_FAILURE; } /* XXX in the future, this may move to a static chanrec method (the delete.) -- w00t */ if (!channel->PartUser(user, reason.c_str())) delete channel; chanrec::JoinUser(ServerInstance, user, parameters[0], true, "", ServerInstance->Time(true)); } return CMD_LOCALONLY; } else { user->WriteServ("NOTICE %s :*** You are not on this channel", user->nick); } return CMD_FAILURE; } }; class ModuleCycle : public Module { cmd_cycle* mycommand; public: ModuleCycle(InspIRCd* Me) : Module(Me) { mycommand = new cmd_cycle(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleCycle() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleCycle) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style CYCLE command. */
+
+/** Handle /CYCLE
+ */
+class cmd_cycle : public command_t
+{
+ public:
+ cmd_cycle (InspIRCd* Instance) : command_t(Instance,"CYCLE", 0, 1)
+ {
+ this->source = "m_cycle.so";
+ syntax = "<channel> :[reason]";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ chanrec* channel = ServerInstance->FindChan(parameters[0]);
+ std::string reason = ConvToStr("Cycling");
+
+ if (pcnt > 1)
+ {
+ /* reason provided, use it */
+ reason = reason + ": " + parameters[1];
+ }
+
+ if (channel)
+ {
+ /*
+ * technically, this is only ever sent locally, but pays to be safe ;p
+ */
+ if (IS_LOCAL(user))
+ {
+ if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user))
+ {
+ /* banned, boned. drop the message. */
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not cycle, as you are banned on channel " + channel->name);
+ return CMD_FAILURE;
+ }
+
+ /* XXX in the future, this may move to a static chanrec method (the delete.) -- w00t */
+ if (!channel->PartUser(user, reason.c_str()))
+ delete channel;
+
+ chanrec::JoinUser(ServerInstance, user, parameters[0], true, "", ServerInstance->Time(true));
+ }
+
+ return CMD_LOCALONLY;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** You are not on this channel", user->nick);
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+
+class ModuleCycle : public Module
+{
+ cmd_cycle* mycommand;
+ public:
+ ModuleCycle(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_cycle(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleCycle()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleCycle)
diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp
index 61ef90d89..bfec3c5e1 100644
--- a/src/modules/m_dccallow.cpp
+++ b/src/modules/m_dccallow.cpp
@@ -1 +1,489 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Povides support for the /DCCALLOW command */ static ConfigReader *Conf; class BannedFileList { public: std::string filemask; std::string action; }; class DCCAllow { public: std::string nickname; std::string hostmask; time_t set_on; long length; DCCAllow() { } DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { } }; typedef std::vector<userrec *> userlist; userlist ul; typedef std::vector<DCCAllow> dccallowlist; dccallowlist* dl; typedef std::vector<BannedFileList> bannedfilelist; bannedfilelist bfl; class cmd_dccallow : public command_t { public: cmd_dccallow(InspIRCd* Me) : command_t(Me, "DCCALLOW", 0, 0) { this->source = "m_dccallow.so"; syntax = "{[+|-]<nick> <time>|HELP|LIST}"; } CmdResult Handle(const char **parameters, int pcnt, userrec *user) { /* syntax: DCCALLOW [+|-]<nick> (<time>) */ if (!pcnt) { // display current DCCALLOW list DisplayDCCAllowList(user); return CMD_FAILURE; } else if (pcnt > 0) { char action = *parameters[0]; // if they didn't specify an action, this is probably a command if (action != '+' && action != '-') { if (!strcasecmp(parameters[0], "LIST")) { // list current DCCALLOW list DisplayDCCAllowList(user); return CMD_FAILURE; } else if (!strcasecmp(parameters[0], "HELP")) { // display help DisplayHelp(user); return CMD_FAILURE; } } std::string nick = parameters[0] + 1; userrec *target = ServerInstance->FindNick(nick); if (target) { if (action == '-') { user->GetExt("dccallow_list", dl); // check if it contains any entries if (dl) { if (dl->size()) { for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i) { // search through list if (i->nickname == target->nick) { dl->erase(i); user->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", user->nick, user->nick, target->nick); break; } } } } else { DELETE(dl); user->Shrink("dccallow_list"); // remove from userlist for (userlist::iterator j = ul.begin(); j != ul.end(); ++j) { userrec* u = (userrec*)(*j); if (u == user) { ul.erase(j); break; } } } } else if (action == '+') { // fetch current DCCALLOW list user->GetExt("dccallow_list", dl); // they don't have one, create it if (!dl) { dl = new dccallowlist; user->Extend("dccallow_list", dl); // add this user to the userlist ul.push_back(user); } for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k) { if (k->nickname == target->nick) { user->WriteServ("996 %s %s :%s is already on your DCCALLOW list", user->nick, user->nick, target->nick); return CMD_FAILURE; } else if (ServerInstance->MatchText(user->GetFullHost(), k->hostmask)) { user->WriteServ("996 %s %s :You cannot add yourself to your own DCCALLOW list!", user->nick, user->nick); return CMD_FAILURE; } } std::string mask = std::string(target->nick)+"!"+std::string(target->ident)+"@"+std::string(target->dhost); std::string default_length = Conf->ReadValue("dccallow", "length", 0); long length; if (pcnt < 2) { length = ServerInstance->Duration(default_length); } else if (!atoi(parameters[1])) { length = 0; } else { length = ServerInstance->Duration(parameters[1]); } if (!ServerInstance->IsValidMask(mask.c_str())) { return CMD_FAILURE; } dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length)); if (length > 0) { user->WriteServ("993 %s %s :Added %s to DCCALLOW list for %d seconds", user->nick, user->nick, target->nick, length); } else { user->WriteServ("994 %s %s :Added %s to DCCALLOW list for this session", user->nick, user->nick, target->nick); } /* route it. */ return CMD_SUCCESS; } } else { // nick doesn't exist user->WriteServ("401 %s %s :No such nick/channel", user->nick, nick.c_str()); return CMD_FAILURE; } } return CMD_FAILURE; } void DisplayHelp(userrec* user) { user->WriteServ("998 %s :DCCALLOW [<+|->nick [time]] [list] [help]", user->nick); user->WriteServ("998 %s :You may allow DCCs from specific users by specifying a", user->nick); user->WriteServ("998 %s :DCC allow for the user you want to receive DCCs from.", user->nick); user->WriteServ("998 %s :For example, to allow the user Brain to send you inspircd.exe", user->nick); user->WriteServ("998 %s :you would type:", user->nick); user->WriteServ("998 %s :/DCCALLOW +Brain", user->nick); user->WriteServ("998 %s :Brain would then be able to send you files. They would have to", user->nick); user->WriteServ("998 %s :resend the file again if the server gave them an error message", user->nick); user->WriteServ("998 %s :before you added them to your DCCALLOW list.", user->nick); user->WriteServ("998 %s :DCCALLOW entries will be temporary by default, if you want to add", user->nick); user->WriteServ("998 %s :them to your DCCALLOW list until you leave IRC, type:", user->nick); user->WriteServ("998 %s :/DCCALLOW +Brain 0", user->nick); user->WriteServ("998 %s :To remove the user from your DCCALLOW list, type:", user->nick); user->WriteServ("998 %s :/DCCALLOW -Brain", user->nick); user->WriteServ("998 %s :To see the users in your DCCALLOW list, type:", user->nick); user->WriteServ("998 %s :/DCCALLOW LIST", user->nick); user->WriteServ("998 %s :NOTE: If the user leaves IRC or changes their nickname", user->nick); user->WriteServ("998 %s : they will be removed from your DCCALLOW list.", user->nick); user->WriteServ("998 %s : your DCCALLOW list will be deleted when you leave IRC.", user->nick); user->WriteServ("999 %s :End of DCCALLOW HELP", user->nick); } void DisplayDCCAllowList(userrec* user) { // display current DCCALLOW list user->WriteServ("990 %s :Users on your DCCALLOW list:", user->nick); user->GetExt("dccallow_list", dl); if (dl) { for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c) { user->WriteServ("991 %s %s :%s (%s)", user->nick, user->nick, c->nickname.c_str(), c->hostmask.c_str()); } } user->WriteServ("992 %s :End of DCCALLOW list", user->nick); } }; class ModuleDCCAllow : public Module { cmd_dccallow* mycommand; public: ModuleDCCAllow(InspIRCd* Me) : Module(Me) { Conf = new ConfigReader(ServerInstance); mycommand = new cmd_dccallow(ServerInstance); ServerInstance->AddCommand(mycommand); ReadFileConf(); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserQuit] = List[I_OnUserPreNick] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { delete Conf; Conf = new ConfigReader(ServerInstance); } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { dccallowlist* dl; // remove their DCCALLOW list if they have one user->GetExt("dccallow_list", dl); if (dl) { DELETE(dl); user->Shrink("dccallow_list"); RemoveFromUserlist(user); } // remove them from any DCCALLOW lists // they are currently on RemoveNick(user); } virtual int OnUserPreNick(userrec* user, const std::string &newnick) { RemoveNick(user); return 0; } virtual int OnUserPreMessage(userrec* 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 int OnUserPreNotice(userrec* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list) { if (!IS_LOCAL(user)) return 0; if (target_type == TYPE_USER) { userrec* u = (userrec*)dest; /* Always allow a user to dcc themselves (although... why?) */ if (user == u) return 0; if ((text.length()) && (text[0] == '\1')) { Expire(); // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION if (strncmp(text.c_str(), "\1DCC ", 5) == 0) { u->GetExt("dccallow_list", dl); if (dl && dl->size()) { for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter) if (ServerInstance->MatchText(user->GetFullHost(), iter->hostmask)) return 0; } // tokenize std::stringstream ss(text); std::string buf; std::vector<std::string> tokens; while (ss >> buf) tokens.push_back(buf); irc::string type = tokens[1].c_str(); bool blockchat = Conf->ReadFlag("dccallow", "blockchat", 0); if (type == "SEND") { std::string defaultaction = Conf->ReadValue("dccallow", "action", 0); std::string filename = tokens[2]; if (defaultaction == "allow") return 0; for (unsigned int i = 0; i < bfl.size(); i++) { if (ServerInstance->MatchText(filename, bfl[i].filemask)) { if (bfl[i].action == "allow") return 0; } else { if (defaultaction == "allow") return 0; } user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick, u->nick, filename.c_str()); u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick, user->nick, user->ident, user->dhost, 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, user->nick); return 1; } } else if ((type == "CHAT") && (blockchat)) { user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick, u->nick); u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick, user->nick, user->ident, user->dhost); 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, user->nick); return 1; } } } } return 0; } void Expire() { for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter) { userrec* u = (userrec*)(*iter); u->GetExt("dccallow_list", dl); if (dl) { if (dl->size()) { dccallowlist::iterator iter = dl->begin(); while (iter != dl->end()) { if ((iter->set_on + iter->length) <= ServerInstance->Time()) { u->WriteServ("997 %s %s :DCCALLOW entry for %s has expired", u->nick, u->nick, iter->nickname.c_str()); iter = dl->erase(iter); } else { ++iter; } } } } else { RemoveFromUserlist(u); } } } void RemoveNick(userrec* user) { /* Iterate through all DCCALLOW lists and remove user */ for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter) { userrec *u = (userrec*)(*iter); u->GetExt("dccallow_list", dl); if (dl) { if (dl->size()) { for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i) { 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, i->nickname.c_str()); u->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", u->nick, u->nick, i->nickname.c_str()); dl->erase(i); break; } } } } else { RemoveFromUserlist(u); } } } void RemoveFromUserlist(userrec *user) { // remove user from userlist for (userlist::iterator j = ul.begin(); j != ul.end(); ++j) { userrec* u = (userrec*)(*j); if (u == user) { ul.erase(j); break; } } } void ReadFileConf() { bfl.clear(); for (int i = 0; i < Conf->Enumerate("banfile"); i++) { BannedFileList bf; std::string fileglob = Conf->ReadValue("banfile", "pattern", i); std::string action = Conf->ReadValue("banfile", "action", i); bf.filemask = fileglob; bf.action = action; bfl.push_back(bf); } } virtual ~ModuleDCCAllow() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleDCCAllow) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Povides support for the /DCCALLOW command */
+
+static ConfigReader *Conf;
+
+class BannedFileList
+{
+ public:
+ std::string filemask;
+ std::string action;
+};
+
+class DCCAllow
+{
+ public:
+ std::string nickname;
+ std::string hostmask;
+ time_t set_on;
+ long length;
+
+ DCCAllow() { }
+
+ DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { }
+};
+
+typedef std::vector<userrec *> userlist;
+userlist ul;
+typedef std::vector<DCCAllow> dccallowlist;
+dccallowlist* dl;
+typedef std::vector<BannedFileList> bannedfilelist;
+bannedfilelist bfl;
+
+class cmd_dccallow : public command_t
+{
+ public:
+ cmd_dccallow(InspIRCd* Me) : command_t(Me, "DCCALLOW", 0, 0)
+ {
+ this->source = "m_dccallow.so";
+ syntax = "{[+|-]<nick> <time>|HELP|LIST}";
+ }
+
+ CmdResult Handle(const char **parameters, int pcnt, userrec *user)
+ {
+ /* syntax: DCCALLOW [+|-]<nick> (<time>) */
+ if (!pcnt)
+ {
+ // display current DCCALLOW list
+ DisplayDCCAllowList(user);
+ return CMD_FAILURE;
+ }
+ else if (pcnt > 0)
+ {
+ char action = *parameters[0];
+
+ // if they didn't specify an action, this is probably a command
+ if (action != '+' && action != '-')
+ {
+ if (!strcasecmp(parameters[0], "LIST"))
+ {
+ // list current DCCALLOW list
+ DisplayDCCAllowList(user);
+ return CMD_FAILURE;
+ }
+ else if (!strcasecmp(parameters[0], "HELP"))
+ {
+ // display help
+ DisplayHelp(user);
+ return CMD_FAILURE;
+ }
+ }
+
+ std::string nick = parameters[0] + 1;
+ userrec *target = ServerInstance->FindNick(nick);
+
+ if (target)
+ {
+
+ if (action == '-')
+ {
+ user->GetExt("dccallow_list", dl);
+ // check if it contains any entries
+ if (dl)
+ {
+ if (dl->size())
+ {
+ for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
+ {
+ // search through list
+ if (i->nickname == target->nick)
+ {
+ dl->erase(i);
+ user->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", user->nick, user->nick, target->nick);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ DELETE(dl);
+ user->Shrink("dccallow_list");
+
+ // remove from userlist
+ for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
+ {
+ userrec* u = (userrec*)(*j);
+ if (u == user)
+ {
+ ul.erase(j);
+ break;
+ }
+ }
+ }
+ }
+ else if (action == '+')
+ {
+ // fetch current DCCALLOW list
+ user->GetExt("dccallow_list", dl);
+ // they don't have one, create it
+ if (!dl)
+ {
+ dl = new dccallowlist;
+ user->Extend("dccallow_list", dl);
+ // add this user to the userlist
+ ul.push_back(user);
+ }
+ for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
+ {
+ if (k->nickname == target->nick)
+ {
+ user->WriteServ("996 %s %s :%s is already on your DCCALLOW list", user->nick, user->nick, target->nick);
+ return CMD_FAILURE;
+ }
+ else if (ServerInstance->MatchText(user->GetFullHost(), k->hostmask))
+ {
+ user->WriteServ("996 %s %s :You cannot add yourself to your own DCCALLOW list!", user->nick, user->nick);
+ return CMD_FAILURE;
+ }
+ }
+
+ std::string mask = std::string(target->nick)+"!"+std::string(target->ident)+"@"+std::string(target->dhost);
+ std::string default_length = Conf->ReadValue("dccallow", "length", 0);
+
+ long length;
+ if (pcnt < 2)
+ {
+ length = ServerInstance->Duration(default_length);
+ }
+ else if (!atoi(parameters[1]))
+ {
+ length = 0;
+ }
+ else
+ {
+ length = ServerInstance->Duration(parameters[1]);
+ }
+
+ if (!ServerInstance->IsValidMask(mask.c_str()))
+ {
+ return CMD_FAILURE;
+ }
+
+ dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
+
+ if (length > 0)
+ {
+ user->WriteServ("993 %s %s :Added %s to DCCALLOW list for %d seconds", user->nick, user->nick, target->nick, length);
+ }
+ else
+ {
+ user->WriteServ("994 %s %s :Added %s to DCCALLOW list for this session", user->nick, user->nick, target->nick);
+ }
+
+ /* route it. */
+ return CMD_SUCCESS;
+ }
+ }
+ else
+ {
+ // nick doesn't exist
+ user->WriteServ("401 %s %s :No such nick/channel", user->nick, nick.c_str());
+ return CMD_FAILURE;
+ }
+ }
+ return CMD_FAILURE;
+ }
+
+ void DisplayHelp(userrec* user)
+ {
+ user->WriteServ("998 %s :DCCALLOW [<+|->nick [time]] [list] [help]", user->nick);
+ user->WriteServ("998 %s :You may allow DCCs from specific users by specifying a", user->nick);
+ user->WriteServ("998 %s :DCC allow for the user you want to receive DCCs from.", user->nick);
+ user->WriteServ("998 %s :For example, to allow the user Brain to send you inspircd.exe", user->nick);
+ user->WriteServ("998 %s :you would type:", user->nick);
+ user->WriteServ("998 %s :/DCCALLOW +Brain", user->nick);
+ user->WriteServ("998 %s :Brain would then be able to send you files. They would have to", user->nick);
+ user->WriteServ("998 %s :resend the file again if the server gave them an error message", user->nick);
+ user->WriteServ("998 %s :before you added them to your DCCALLOW list.", user->nick);
+ user->WriteServ("998 %s :DCCALLOW entries will be temporary by default, if you want to add", user->nick);
+ user->WriteServ("998 %s :them to your DCCALLOW list until you leave IRC, type:", user->nick);
+ user->WriteServ("998 %s :/DCCALLOW +Brain 0", user->nick);
+ user->WriteServ("998 %s :To remove the user from your DCCALLOW list, type:", user->nick);
+ user->WriteServ("998 %s :/DCCALLOW -Brain", user->nick);
+ user->WriteServ("998 %s :To see the users in your DCCALLOW list, type:", user->nick);
+ user->WriteServ("998 %s :/DCCALLOW LIST", user->nick);
+ user->WriteServ("998 %s :NOTE: If the user leaves IRC or changes their nickname", user->nick);
+ user->WriteServ("998 %s : they will be removed from your DCCALLOW list.", user->nick);
+ user->WriteServ("998 %s : your DCCALLOW list will be deleted when you leave IRC.", user->nick);
+ user->WriteServ("999 %s :End of DCCALLOW HELP", user->nick);
+ }
+
+ void DisplayDCCAllowList(userrec* user)
+ {
+ // display current DCCALLOW list
+ user->WriteServ("990 %s :Users on your DCCALLOW list:", user->nick);
+ user->GetExt("dccallow_list", dl);
+
+ if (dl)
+ {
+ for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
+ {
+ user->WriteServ("991 %s %s :%s (%s)", user->nick, user->nick, c->nickname.c_str(), c->hostmask.c_str());
+ }
+ }
+
+ user->WriteServ("992 %s :End of DCCALLOW list", user->nick);
+ }
+
+};
+
+class ModuleDCCAllow : public Module
+{
+ cmd_dccallow* mycommand;
+ public:
+
+ ModuleDCCAllow(InspIRCd* Me)
+ : Module(Me)
+ {
+ Conf = new ConfigReader(ServerInstance);
+ mycommand = new cmd_dccallow(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ ReadFileConf();
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserQuit] = List[I_OnUserPreNick] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ delete Conf;
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ dccallowlist* dl;
+
+ // remove their DCCALLOW list if they have one
+ user->GetExt("dccallow_list", dl);
+ if (dl)
+ {
+ DELETE(dl);
+ user->Shrink("dccallow_list");
+ RemoveFromUserlist(user);
+ }
+
+ // remove them from any DCCALLOW lists
+ // they are currently on
+ RemoveNick(user);
+ }
+
+
+ virtual int OnUserPreNick(userrec* user, const std::string &newnick)
+ {
+ RemoveNick(user);
+ return 0;
+ }
+
+ virtual int OnUserPreMessage(userrec* 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 int OnUserPreNotice(userrec* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (!IS_LOCAL(user))
+ return 0;
+
+ if (target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)dest;
+
+ /* Always allow a user to dcc themselves (although... why?) */
+ if (user == u)
+ return 0;
+
+ if ((text.length()) && (text[0] == '\1'))
+ {
+ Expire();
+
+ // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
+ // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
+
+ if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
+ {
+ u->GetExt("dccallow_list", dl);
+
+ if (dl && dl->size())
+ {
+ for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
+ if (ServerInstance->MatchText(user->GetFullHost(), iter->hostmask))
+ return 0;
+ }
+
+ // tokenize
+ std::stringstream ss(text);
+ std::string buf;
+ std::vector<std::string> tokens;
+
+ while (ss >> buf)
+ tokens.push_back(buf);
+
+ irc::string type = tokens[1].c_str();
+
+ bool blockchat = Conf->ReadFlag("dccallow", "blockchat", 0);
+
+ if (type == "SEND")
+ {
+ std::string defaultaction = Conf->ReadValue("dccallow", "action", 0);
+ std::string filename = tokens[2];
+
+ if (defaultaction == "allow")
+ return 0;
+
+ for (unsigned int i = 0; i < bfl.size(); i++)
+ {
+ if (ServerInstance->MatchText(filename, bfl[i].filemask))
+ {
+ if (bfl[i].action == "allow")
+ return 0;
+ }
+ else
+ {
+ if (defaultaction == "allow")
+ return 0;
+ }
+ user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick, u->nick, filename.c_str());
+ u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick, user->nick, user->ident, user->dhost, 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, user->nick);
+ return 1;
+ }
+ }
+ else if ((type == "CHAT") && (blockchat))
+ {
+ user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick, u->nick);
+ u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick, user->nick, user->ident, user->dhost);
+ 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, user->nick);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ void Expire()
+ {
+ for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
+ {
+ userrec* u = (userrec*)(*iter);
+ u->GetExt("dccallow_list", dl);
+
+ if (dl)
+ {
+ if (dl->size())
+ {
+ dccallowlist::iterator iter = dl->begin();
+ while (iter != dl->end())
+ {
+ if ((iter->set_on + iter->length) <= ServerInstance->Time())
+ {
+ u->WriteServ("997 %s %s :DCCALLOW entry for %s has expired", u->nick, u->nick, iter->nickname.c_str());
+ iter = dl->erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ }
+ }
+ else
+ {
+ RemoveFromUserlist(u);
+ }
+ }
+ }
+
+ void RemoveNick(userrec* user)
+ {
+ /* Iterate through all DCCALLOW lists and remove user */
+ for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
+ {
+ userrec *u = (userrec*)(*iter);
+ u->GetExt("dccallow_list", dl);
+
+ if (dl)
+ {
+ if (dl->size())
+ {
+ for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
+ {
+ 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, i->nickname.c_str());
+ u->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", u->nick, u->nick, i->nickname.c_str());
+ dl->erase(i);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ RemoveFromUserlist(u);
+ }
+ }
+ }
+
+ void RemoveFromUserlist(userrec *user)
+ {
+ // remove user from userlist
+ for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
+ {
+ userrec* u = (userrec*)(*j);
+ if (u == user)
+ {
+ ul.erase(j);
+ break;
+ }
+ }
+ }
+
+ void ReadFileConf()
+ {
+ bfl.clear();
+ for (int i = 0; i < Conf->Enumerate("banfile"); i++)
+ {
+ BannedFileList bf;
+ std::string fileglob = Conf->ReadValue("banfile", "pattern", i);
+ std::string action = Conf->ReadValue("banfile", "action", i);
+ bf.filemask = fileglob;
+ bf.action = action;
+ bfl.push_back(bf);
+ }
+
+ }
+
+ virtual ~ModuleDCCAllow()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleDCCAllow)
diff --git a/src/modules/m_deaf.cpp b/src/modules/m_deaf.cpp
index d9681010d..ad8b31714 100644
--- a/src/modules/m_deaf.cpp
+++ b/src/modules/m_deaf.cpp
@@ -1 +1,135 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for ircu style usermode +d (deaf to channel messages and channel notices) */ /** User mode +d - filter out channel messages and channel notices */ class User_d : public ModeHandler { public: User_d(InspIRCd* Instance) : ModeHandler(Instance, 'd', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { 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, dest->nick); dest->SetMode('d',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('d')) { dest->SetMode('d',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleDeaf : public Module { User_d* m1; public: ModuleDeaf(InspIRCd* Me) : Module(Me) { m1 = new User_d(ServerInstance); if (!ServerInstance->AddMode(m1, 'd')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnBuildExemptList] = 1; } virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { return PreText(user, dest, target_type, text, status, exempt_list); } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { return PreText(user, dest, target_type, text, status, exempt_list); } virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) { CUList *ulist; switch (status) { case '@': ulist = chan->GetOppedUsers(); break; case '%': ulist = chan->GetHalfoppedUsers(); break; case '+': ulist = chan->GetVoicedUsers(); break; default: ulist = chan->GetUsers(); break; } for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (IS_LOCAL(i->first)) { if (i->first->IsModeSet('d')) { exempt_list[i->first] = i->first->nick; } } } } virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if ((target_type == TYPE_CHANNEL) & (IS_LOCAL(user))) { chanrec* chan = (chanrec*)dest; if (chan) { this->OnBuildExemptList(MSG_PRIVMSG, chan, user, status, exempt_list); } } return 0; } virtual ~ModuleDeaf() { ServerInstance->Modes->DelMode(m1); DELETE(m1); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleDeaf) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for ircu style usermode +d (deaf to channel messages and channel notices) */
+
+/** User mode +d - filter out channel messages and channel notices
+ */
+class User_d : public ModeHandler
+{
+ public:
+ User_d(InspIRCd* Instance) : ModeHandler(Instance, 'd', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ 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, dest->nick);
+ dest->SetMode('d',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('d'))
+ {
+ dest->SetMode('d',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleDeaf : public Module
+{
+ User_d* m1;
+ public:
+ ModuleDeaf(InspIRCd* Me)
+ : Module(Me)
+ {
+ m1 = new User_d(ServerInstance);
+ if (!ServerInstance->AddMode(m1, 'd'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnBuildExemptList] = 1;
+ }
+
+ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ return PreText(user, dest, target_type, text, status, exempt_list);
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ return PreText(user, dest, target_type, text, status, exempt_list);
+ }
+
+ virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list)
+ {
+ CUList *ulist;
+ switch (status)
+ {
+ case '@':
+ ulist = chan->GetOppedUsers();
+ break;
+ case '%':
+ ulist = chan->GetHalfoppedUsers();
+ break;
+ case '+':
+ ulist = chan->GetVoicedUsers();
+ break;
+ default:
+ ulist = chan->GetUsers();
+ break;
+ }
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (IS_LOCAL(i->first))
+ {
+ if (i->first->IsModeSet('d'))
+ {
+ exempt_list[i->first] = i->first->nick;
+ }
+ }
+ }
+ }
+
+ virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if ((target_type == TYPE_CHANNEL) & (IS_LOCAL(user)))
+ {
+ chanrec* chan = (chanrec*)dest;
+ if (chan)
+ {
+ this->OnBuildExemptList(MSG_PRIVMSG, chan, user, status, exempt_list);
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleDeaf()
+ {
+ ServerInstance->Modes->DelMode(m1);
+ DELETE(m1);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleDeaf)
diff --git a/src/modules/m_denychans.cpp b/src/modules/m_denychans.cpp
index d09e04766..4a01faa7c 100644
--- a/src/modules/m_denychans.cpp
+++ b/src/modules/m_denychans.cpp
@@ -1 +1,80 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "wildcard.h" /* $ModDesc: Implements config tags which allow blocking of joins to channels */ class ModuleDenyChannels : public Module { private: ConfigReader *Conf; public: ModuleDenyChannels(InspIRCd* Me) : Module(Me) { Conf = new ConfigReader(ServerInstance); } virtual void OnRehash(userrec* user, const std::string &param) { DELETE(Conf); Conf = new ConfigReader(ServerInstance); } virtual ~ModuleDenyChannels() { DELETE(Conf); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnUserPreJoin] = List[I_OnRehash] = 1; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { for (int j =0; j < Conf->Enumerate("badchan"); j++) { if (match(cname, Conf->ReadValue("badchan","name",j).c_str())) { if (IS_OPER(user) && Conf->ReadFlag("badchan","allowopers",j)) { return 0; } else { std::string reason = Conf->ReadValue("badchan","reason",j); user->WriteServ("926 %s %s :Channel %s is forbidden: %s",user->nick,cname,cname,reason.c_str()); return 1; } } } return 0; } }; MODULE_INIT(ModuleDenyChannels) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "wildcard.h"
+
+/* $ModDesc: Implements config tags which allow blocking of joins to channels */
+
+class ModuleDenyChannels : public Module
+{
+ private:
+
+
+ ConfigReader *Conf;
+
+ public:
+ ModuleDenyChannels(InspIRCd* Me) : Module(Me)
+ {
+
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ DELETE(Conf);
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual ~ModuleDenyChannels()
+ {
+ DELETE(Conf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = List[I_OnRehash] = 1;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ for (int j =0; j < Conf->Enumerate("badchan"); j++)
+ {
+ if (match(cname, Conf->ReadValue("badchan","name",j).c_str()))
+ {
+ if (IS_OPER(user) && Conf->ReadFlag("badchan","allowopers",j))
+ {
+ return 0;
+ }
+ else
+ {
+ std::string reason = Conf->ReadValue("badchan","reason",j);
+ user->WriteServ("926 %s %s :Channel %s is forbidden: %s",user->nick,cname,cname,reason.c_str());
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleDenyChannels)
diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp
index e760db859..e2ada413b 100644
--- a/src/modules/m_devoice.cpp
+++ b/src/modules/m_devoice.cpp
@@ -1 +1,81 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* * DEVOICE module for InspIRCd * Syntax: /DEVOICE <#chan> */ /* $ModDesc: Provides voiced users with the ability to devoice themselves. */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /** Handle /DEVOICE */ class cmd_devoice : public command_t { public: cmd_devoice (InspIRCd* Instance) : command_t(Instance,"DEVOICE", 0, 1) { this->source = "m_devoice.so"; syntax = "<channel>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { chanrec* c = ServerInstance->FindChan(parameters[0]); if (c && c->HasUser(user)) { const char* modes[3]; modes[0] = parameters[0]; modes[1] = "-v"; modes[2] = user->nick; userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); ServerInstance->SendMode(modes,3,n); delete n; /* route it */ return CMD_SUCCESS; } return CMD_FAILURE; } }; class ModuleDeVoice : public Module { cmd_devoice *mycommand; public: ModuleDeVoice(InspIRCd* Me) : Module(Me) { mycommand = new cmd_devoice(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleDeVoice() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleDeVoice) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/*
+ * DEVOICE module for InspIRCd
+ * Syntax: /DEVOICE <#chan>
+ */
+
+/* $ModDesc: Provides voiced users with the ability to devoice themselves. */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/** Handle /DEVOICE
+ */
+class cmd_devoice : public command_t
+{
+ public:
+ cmd_devoice (InspIRCd* Instance) : command_t(Instance,"DEVOICE", 0, 1)
+ {
+ this->source = "m_devoice.so";
+ syntax = "<channel>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ chanrec* c = ServerInstance->FindChan(parameters[0]);
+ if (c && c->HasUser(user))
+ {
+ const char* modes[3];
+ modes[0] = parameters[0];
+ modes[1] = "-v";
+ modes[2] = user->nick;
+
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+ ServerInstance->SendMode(modes,3,n);
+ delete n;
+
+ /* route it */
+ return CMD_SUCCESS;
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleDeVoice : public Module
+{
+ cmd_devoice *mycommand;
+ public:
+ ModuleDeVoice(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_devoice(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleDeVoice()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleDeVoice)
diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp
index 87e9a2cba..d07b268f7 100644
--- a/src/modules/m_dnsbl.cpp
+++ b/src/modules/m_dnsbl.cpp
@@ -1 +1,353 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "xline.h" #include "dns.h" #include "users.h" #include "channels.h" #include "modules.h" #ifndef WINDOWS #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #endif /* $ModDesc: Provides handling of DNS blacklists */ /* Class holding data for a single entry */ class DNSBLConfEntry { public: enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE }; std::string name, domain, reason; EnumBanaction banaction; long duration; int bitmask; unsigned long stats_hits, stats_misses; DNSBLConfEntry(): duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {} ~DNSBLConfEntry() { } }; /** Resolver for CGI:IRC hostnames encoded in ident/GECOS */ class DNSBLResolver : public Resolver { int theirfd; userrec* them; DNSBLConfEntry *ConfEntry; public: DNSBLResolver(Module *me, InspIRCd *ServerInstance, const std::string &hostname, userrec* u, int userfd, DNSBLConfEntry *conf, bool &cached) : Resolver(ServerInstance, hostname, DNS_QUERY_A, cached, me) { theirfd = userfd; them = u; ConfEntry = conf; } virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) { /* Check the user still exists */ if ((them) && (them == ServerInstance->SE->GetRef(theirfd))) { // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d if(result.length()) { unsigned int bitmask = 0; bool show = false; in_addr resultip; /* Convert the result to an in_addr (we can gaurantee we got ipv4) * Whoever did the loop that was here before, I AM CONFISCATING * YOUR CRACKPIPE. you know who you are. -- Brain */ inet_aton(result.c_str(), &resultip); bitmask = resultip.s_addr >> 24; /* Last octet (network byte order */ bitmask &= ConfEntry->bitmask; if (bitmask != 0) { std::string reason = ConfEntry->reason; std::string::size_type x = reason.find("%ip%"); while (x != std::string::npos) { reason.erase(x, 4); reason.insert(x, them->GetIPString()); x = reason.find("%ip%"); } ConfEntry->stats_hits++; switch (ConfEntry->banaction) { case DNSBLConfEntry::I_KILL: { userrec::QuitUser(ServerInstance, them, std::string("Killed (") + reason + ")"); break; } case DNSBLConfEntry::I_KLINE: { std::string ban = std::string("*@") + them->GetIPString(); if (show) ServerInstance->XLines->apply_lines(APPLY_KLINES); show = ServerInstance->XLines->add_kline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str()); FOREACH_MOD(I_OnAddKLine,OnAddKLine(ConfEntry->duration, NULL, reason, ban)); break; } case DNSBLConfEntry::I_GLINE: { std::string ban = std::string("*@") + them->GetIPString(); show = ServerInstance->XLines->add_gline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str()); if (show) ServerInstance->XLines->apply_lines(APPLY_GLINES); FOREACH_MOD(I_OnAddGLine,OnAddGLine(ConfEntry->duration, NULL, reason, ban)); break; } case DNSBLConfEntry::I_ZLINE: { show = ServerInstance->XLines->add_zline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), them->GetIPString()); if (show) ServerInstance->XLines->apply_lines(APPLY_ZLINES); FOREACH_MOD(I_OnAddZLine,OnAddZLine(ConfEntry->duration, NULL, reason, them->GetIPString())); break; } case DNSBLConfEntry::I_UNKNOWN: { break; } break; } if (show) { ServerInstance->WriteOpers("*** Connecting user %s detected as being on a DNS blacklist (%s) with result %d", them->GetFullRealHost(), ConfEntry->name.c_str(), bitmask); } } else ConfEntry->stats_misses++; } else ConfEntry->stats_misses++; } } virtual void OnError(ResolverError e, const std::string &errormessage) { } virtual ~DNSBLResolver() { } }; class ModuleDNSBL : public Module { private: std::vector<DNSBLConfEntry *> DNSBLConfEntries; /* * Convert a string to EnumBanaction */ DNSBLConfEntry::EnumBanaction str2banaction(const std::string &action) { if(action.compare("KILL")==0) return DNSBLConfEntry::I_KILL; if(action.compare("KLINE")==0) return DNSBLConfEntry::I_KLINE; if(action.compare("ZLINE")==0) return DNSBLConfEntry::I_ZLINE; if(action.compare("GLINE")==0) return DNSBLConfEntry::I_GLINE; return DNSBLConfEntry::I_UNKNOWN; } public: ModuleDNSBL(InspIRCd *Me) : Module(Me) { ReadConf(); } virtual ~ModuleDNSBL() { ClearEntries(); } virtual Version GetVersion() { return Version(2, 0, 0, 1, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnStats] = 1; } /** Clear entries and free the mem it was using */ void ClearEntries() { std::vector<DNSBLConfEntry *>::iterator i; for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++) delete *i; DNSBLConfEntries.clear(); } /** Fill our conf vector with data */ virtual void ReadConf() { ConfigReader *MyConf = new ConfigReader(ServerInstance); ClearEntries(); for (int i=0; i< MyConf->Enumerate("dnsbl"); i++) { DNSBLConfEntry *e = new DNSBLConfEntry(); e->name = MyConf->ReadValue("dnsbl", "name", i); e->reason = MyConf->ReadValue("dnsbl", "reason", i); e->domain = MyConf->ReadValue("dnsbl", "domain", i); e->banaction = str2banaction(MyConf->ReadValue("dnsbl", "action", i)); e->duration = ServerInstance->Duration(MyConf->ReadValue("dnsbl", "duration", i)); e->bitmask = MyConf->ReadInteger("dnsbl", "bitmask", i, false); /* yeah, logic here is a little messy */ if (e->bitmask <= 0) { ServerInstance->WriteOpers("*** DNSBL(#%d): invalid bitmask",i); } else if (e->name.empty()) { ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid name",i); } else if (e->domain.empty()) { ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid domain",i); } else if (e->banaction == DNSBLConfEntry::I_UNKNOWN) { ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid banaction", i); } else { if (e->reason.empty()) { ServerInstance->WriteOpers("*** DNSBL(#%d): empty reason, using defaults",i); e->reason = "Your IP has been blacklisted."; } /* add it, all is ok */ DNSBLConfEntries.push_back(e); continue; } /* delete and drop it, error somewhere */ delete e; } delete MyConf; } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadConf(); } virtual int OnUserRegister(userrec* user) { /* only do lookups on local users */ if (IS_LOCAL(user)) { /* following code taken from bopm, reverses an IP address. */ struct in_addr in; unsigned char a, b, c, d; char reversedipbuf[128]; std::string reversedip; bool success = false; if (!inet_aton(user->GetIPString(), &in)) { #ifdef IPV6 /* We could have an ipv6 address here */ std::string x = user->GetIPString(); /* Is it a 4in6 address? (Compensate for this kernel kludge that people love) */ if (x.find("0::ffff:") == 0) { x.erase(x.begin(), x.begin() + 8); if (inet_aton(x.c_str(), &in)) success = true; } #endif } else { success = true; } if (!success) return 0; d = (unsigned char) (in.s_addr >> 24) & 0xFF; c = (unsigned char) (in.s_addr >> 16) & 0xFF; b = (unsigned char) (in.s_addr >> 8) & 0xFF; a = (unsigned char) in.s_addr & 0xFF; snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a); reversedip = std::string(reversedipbuf); // For each DNSBL, we will run through this lookup for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++) { // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld) std::string hostname = reversedip + "." + (*i)->domain; /* now we'd need to fire off lookups for `hostname'. */ bool cached; DNSBLResolver *r = new DNSBLResolver(this, ServerInstance, hostname, user, user->GetFd(), *i, cached); ServerInstance->AddResolver(r, cached); } } /* don't do anything with this hot potato */ return 0; } virtual int OnStats(char symbol, userrec* user, string_list &results) { if (symbol != 'd') return 0; unsigned long total_hits = 0, total_misses = 0; for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++) { total_hits += (*i)->stats_hits; total_misses += (*i)->stats_misses; results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " + ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses"); } results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits)); results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses)); return 0; } }; MODULE_INIT(ModuleDNSBL) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "xline.h"
+#include "dns.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+#ifndef WINDOWS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+/* $ModDesc: Provides handling of DNS blacklists */
+
+/* Class holding data for a single entry */
+class DNSBLConfEntry
+{
+ public:
+ enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE };
+ std::string name, domain, reason;
+ EnumBanaction banaction;
+ long duration;
+ int bitmask;
+ unsigned long stats_hits, stats_misses;
+ DNSBLConfEntry(): duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
+ ~DNSBLConfEntry() { }
+};
+
+
+/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
+ */
+class DNSBLResolver : public Resolver
+{
+ int theirfd;
+ userrec* them;
+ DNSBLConfEntry *ConfEntry;
+
+ public:
+
+ DNSBLResolver(Module *me, InspIRCd *ServerInstance, const std::string &hostname, userrec* u, int userfd, DNSBLConfEntry *conf, bool &cached)
+ : Resolver(ServerInstance, hostname, DNS_QUERY_A, cached, me)
+ {
+ theirfd = userfd;
+ them = u;
+ ConfEntry = conf;
+ }
+
+ virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ {
+ /* Check the user still exists */
+ if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
+ {
+ // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
+ if(result.length())
+ {
+ unsigned int bitmask = 0;
+ bool show = false;
+ in_addr resultip;
+
+ /* Convert the result to an in_addr (we can gaurantee we got ipv4)
+ * Whoever did the loop that was here before, I AM CONFISCATING
+ * YOUR CRACKPIPE. you know who you are. -- Brain
+ */
+ inet_aton(result.c_str(), &resultip);
+ bitmask = resultip.s_addr >> 24; /* Last octet (network byte order */
+
+ bitmask &= ConfEntry->bitmask;
+
+ if (bitmask != 0)
+ {
+ std::string reason = ConfEntry->reason;
+ std::string::size_type x = reason.find("%ip%");
+ while (x != std::string::npos)
+ {
+ reason.erase(x, 4);
+ reason.insert(x, them->GetIPString());
+ x = reason.find("%ip%");
+ }
+
+ ConfEntry->stats_hits++;
+
+ switch (ConfEntry->banaction)
+ {
+ case DNSBLConfEntry::I_KILL:
+ {
+ userrec::QuitUser(ServerInstance, them, std::string("Killed (") + reason + ")");
+ break;
+ }
+ case DNSBLConfEntry::I_KLINE:
+ {
+ std::string ban = std::string("*@") + them->GetIPString();
+ if (show)
+ ServerInstance->XLines->apply_lines(APPLY_KLINES);
+ show = ServerInstance->XLines->add_kline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str());
+ FOREACH_MOD(I_OnAddKLine,OnAddKLine(ConfEntry->duration, NULL, reason, ban));
+ break;
+ }
+ case DNSBLConfEntry::I_GLINE:
+ {
+ std::string ban = std::string("*@") + them->GetIPString();
+ show = ServerInstance->XLines->add_gline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str());
+ if (show)
+ ServerInstance->XLines->apply_lines(APPLY_GLINES);
+ FOREACH_MOD(I_OnAddGLine,OnAddGLine(ConfEntry->duration, NULL, reason, ban));
+ break;
+ }
+ case DNSBLConfEntry::I_ZLINE:
+ {
+ show = ServerInstance->XLines->add_zline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), them->GetIPString());
+ if (show)
+ ServerInstance->XLines->apply_lines(APPLY_ZLINES);
+ FOREACH_MOD(I_OnAddZLine,OnAddZLine(ConfEntry->duration, NULL, reason, them->GetIPString()));
+ break;
+ }
+ case DNSBLConfEntry::I_UNKNOWN:
+ {
+ break;
+ }
+ break;
+ }
+
+ if (show)
+ {
+ ServerInstance->WriteOpers("*** Connecting user %s detected as being on a DNS blacklist (%s) with result %d", them->GetFullRealHost(), ConfEntry->name.c_str(), bitmask);
+ }
+ }
+ else
+ ConfEntry->stats_misses++;
+ }
+ else
+ ConfEntry->stats_misses++;
+ }
+ }
+
+ virtual void OnError(ResolverError e, const std::string &errormessage)
+ {
+ }
+
+ virtual ~DNSBLResolver()
+ {
+ }
+};
+
+class ModuleDNSBL : public Module
+{
+ private:
+ std::vector<DNSBLConfEntry *> DNSBLConfEntries;
+
+ /*
+ * Convert a string to EnumBanaction
+ */
+ DNSBLConfEntry::EnumBanaction str2banaction(const std::string &action)
+ {
+ if(action.compare("KILL")==0)
+ return DNSBLConfEntry::I_KILL;
+ if(action.compare("KLINE")==0)
+ return DNSBLConfEntry::I_KLINE;
+ if(action.compare("ZLINE")==0)
+ return DNSBLConfEntry::I_ZLINE;
+ if(action.compare("GLINE")==0)
+ return DNSBLConfEntry::I_GLINE;
+
+ return DNSBLConfEntry::I_UNKNOWN;
+ }
+ public:
+ ModuleDNSBL(InspIRCd *Me) : Module(Me)
+ {
+ ReadConf();
+ }
+
+ virtual ~ModuleDNSBL()
+ {
+ ClearEntries();
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(2, 0, 0, 1, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnStats] = 1;
+ }
+
+ /** Clear entries and free the mem it was using
+ */
+ void ClearEntries()
+ {
+ std::vector<DNSBLConfEntry *>::iterator i;
+ for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
+ delete *i;
+ DNSBLConfEntries.clear();
+ }
+
+ /** Fill our conf vector with data
+ */
+ virtual void ReadConf()
+ {
+ ConfigReader *MyConf = new ConfigReader(ServerInstance);
+ ClearEntries();
+
+ for (int i=0; i< MyConf->Enumerate("dnsbl"); i++)
+ {
+ DNSBLConfEntry *e = new DNSBLConfEntry();
+
+ e->name = MyConf->ReadValue("dnsbl", "name", i);
+ e->reason = MyConf->ReadValue("dnsbl", "reason", i);
+ e->domain = MyConf->ReadValue("dnsbl", "domain", i);
+ e->banaction = str2banaction(MyConf->ReadValue("dnsbl", "action", i));
+ e->duration = ServerInstance->Duration(MyConf->ReadValue("dnsbl", "duration", i));
+ e->bitmask = MyConf->ReadInteger("dnsbl", "bitmask", i, false);
+
+ /* yeah, logic here is a little messy */
+ if (e->bitmask <= 0)
+ {
+ ServerInstance->WriteOpers("*** DNSBL(#%d): invalid bitmask",i);
+ }
+ else if (e->name.empty())
+ {
+ ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid name",i);
+ }
+ else if (e->domain.empty())
+ {
+ ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid domain",i);
+ }
+ else if (e->banaction == DNSBLConfEntry::I_UNKNOWN)
+ {
+ ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid banaction", i);
+ }
+ else
+ {
+ if (e->reason.empty())
+ {
+ ServerInstance->WriteOpers("*** DNSBL(#%d): empty reason, using defaults",i);
+ e->reason = "Your IP has been blacklisted.";
+ }
+
+ /* add it, all is ok */
+ DNSBLConfEntries.push_back(e);
+ continue;
+ }
+
+ /* delete and drop it, error somewhere */
+ delete e;
+ }
+
+ delete MyConf;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadConf();
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ /* only do lookups on local users */
+ if (IS_LOCAL(user))
+ {
+ /* following code taken from bopm, reverses an IP address. */
+ struct in_addr in;
+ unsigned char a, b, c, d;
+ char reversedipbuf[128];
+ std::string reversedip;
+ bool success = false;
+
+ if (!inet_aton(user->GetIPString(), &in))
+ {
+#ifdef IPV6
+ /* We could have an ipv6 address here */
+ std::string x = user->GetIPString();
+ /* Is it a 4in6 address? (Compensate for this kernel kludge that people love) */
+ if (x.find("0::ffff:") == 0)
+ {
+ x.erase(x.begin(), x.begin() + 8);
+ if (inet_aton(x.c_str(), &in))
+ success = true;
+ }
+#endif
+ }
+ else
+ {
+ success = true;
+ }
+
+ if (!success)
+ return 0;
+
+ d = (unsigned char) (in.s_addr >> 24) & 0xFF;
+ c = (unsigned char) (in.s_addr >> 16) & 0xFF;
+ b = (unsigned char) (in.s_addr >> 8) & 0xFF;
+ a = (unsigned char) in.s_addr & 0xFF;
+
+ snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
+ reversedip = std::string(reversedipbuf);
+
+ // For each DNSBL, we will run through this lookup
+ for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
+ {
+ // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
+ std::string hostname = reversedip + "." + (*i)->domain;
+
+ /* now we'd need to fire off lookups for `hostname'. */
+ bool cached;
+ DNSBLResolver *r = new DNSBLResolver(this, ServerInstance, hostname, user, user->GetFd(), *i, cached);
+ ServerInstance->AddResolver(r, cached);
+ }
+ }
+
+ /* don't do anything with this hot potato */
+ return 0;
+ }
+
+ virtual int OnStats(char symbol, userrec* user, string_list &results)
+ {
+ if (symbol != 'd')
+ return 0;
+
+ unsigned long total_hits = 0, total_misses = 0;
+
+ for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
+ {
+ total_hits += (*i)->stats_hits;
+ total_misses += (*i)->stats_misses;
+
+ results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
+ ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
+ }
+
+ results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
+ results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
+
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleDNSBL)
diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp
index 9c16c8bf3..70de88e2c 100644
--- a/src/modules/m_filter.cpp
+++ b/src/modules/m_filter.cpp
@@ -1 +1,135 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "m_filter.h" /* $ModDesc: An advanced spam filtering module */ /* $ModDep: m_filter.h */ typedef std::map<std::string,FilterResult*> filter_t; class ModuleFilter : public FilterBase { filter_t filters; public: ModuleFilter(InspIRCd* Me) : FilterBase(Me, "m_filter.so") { OnRehash(NULL,""); } virtual ~ModuleFilter() { } virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) { for (filter_t::iterator index = filters.begin(); index != filters.end(); index++) { /* Skip ones that dont apply to us */ if (!FilterBase::AppliesToMe(user, index->second, flags)) continue; if (ServerInstance->MatchText(text,index->first)) { FilterResult* fr = index->second; if (index != filters.begin()) { std::string pat = index->first; filters.erase(index); filters.insert(filters.begin(), std::make_pair(pat,fr)); } return fr; } } return NULL; } virtual bool DeleteFilter(const std::string &freeform) { if (filters.find(freeform) != filters.end()) { delete (filters.find(freeform))->second; filters.erase(filters.find(freeform)); return true; } return false; } virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) { if (filters.find(freeform) != filters.end()) { return std::make_pair(false, "Filter already exists"); } FilterResult* x = new FilterResult(freeform, reason, type, duration, flags); filters[freeform] = x; return std::make_pair(true, ""); } virtual void SyncFilters(Module* proto, void* opaque) { for (filter_t::iterator n = filters.begin(); n != filters.end(); n++) { this->SendFilter(proto, opaque, n->second); } } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader* MyConf = new ConfigReader(ServerInstance); for (int index = 0; index < MyConf->Enumerate("keyword"); index++) { this->DeleteFilter(MyConf->ReadValue("keyword","pattern",index)); std::string pattern = MyConf->ReadValue("keyword","pattern",index); std::string reason = MyConf->ReadValue("keyword","reason",index); std::string do_action = MyConf->ReadValue("keyword","action",index); std::string flags = MyConf->ReadValue("keyword","flags",index); long gline_time = ServerInstance->Duration(MyConf->ReadValue("keyword","duration",index)); if (do_action.empty()) do_action = "none"; if (flags.empty()) flags = "*"; FilterResult* x = new FilterResult(pattern, reason, do_action, gline_time, flags); filters[pattern] = x; } DELETE(MyConf); } virtual int OnStats(char symbol, userrec* user, string_list &results) { if (symbol == 's') { std::string sn = ServerInstance->Config->ServerName; for (filter_t::iterator n = filters.begin(); n != filters.end(); n++) { results.push_back(sn+" 223 "+user->nick+" :GLOB:"+n->second->freeform+" "+n->second->flags+" "+n->second->action+" "+ConvToStr(n->second->gline_time)+" :"+n->second->reason); } } return 0; } }; MODULE_INIT(ModuleFilter) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_filter.h"
+
+/* $ModDesc: An advanced spam filtering module */
+/* $ModDep: m_filter.h */
+
+typedef std::map<std::string,FilterResult*> filter_t;
+
+class ModuleFilter : public FilterBase
+{
+
+ filter_t filters;
+
+ public:
+ ModuleFilter(InspIRCd* Me)
+ : FilterBase(Me, "m_filter.so")
+ {
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleFilter()
+ {
+ }
+
+ virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags)
+ {
+ for (filter_t::iterator index = filters.begin(); index != filters.end(); index++)
+ {
+
+ /* Skip ones that dont apply to us */
+ if (!FilterBase::AppliesToMe(user, index->second, flags))
+ continue;
+
+ if (ServerInstance->MatchText(text,index->first))
+ {
+ FilterResult* fr = index->second;
+ if (index != filters.begin())
+ {
+ std::string pat = index->first;
+ filters.erase(index);
+ filters.insert(filters.begin(), std::make_pair(pat,fr));
+ }
+ return fr;
+ }
+ }
+ return NULL;
+ }
+
+ virtual bool DeleteFilter(const std::string &freeform)
+ {
+ if (filters.find(freeform) != filters.end())
+ {
+ delete (filters.find(freeform))->second;
+ filters.erase(filters.find(freeform));
+ return true;
+ }
+ return false;
+ }
+
+ virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags)
+ {
+ if (filters.find(freeform) != filters.end())
+ {
+ return std::make_pair(false, "Filter already exists");
+ }
+
+ FilterResult* x = new FilterResult(freeform, reason, type, duration, flags);
+ filters[freeform] = x;
+
+ return std::make_pair(true, "");
+ }
+
+ virtual void SyncFilters(Module* proto, void* opaque)
+ {
+ for (filter_t::iterator n = filters.begin(); n != filters.end(); n++)
+ {
+ this->SendFilter(proto, opaque, n->second);
+ }
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader* MyConf = new ConfigReader(ServerInstance);
+
+ for (int index = 0; index < MyConf->Enumerate("keyword"); index++)
+ {
+ this->DeleteFilter(MyConf->ReadValue("keyword","pattern",index));
+
+ std::string pattern = MyConf->ReadValue("keyword","pattern",index);
+ std::string reason = MyConf->ReadValue("keyword","reason",index);
+ std::string do_action = MyConf->ReadValue("keyword","action",index);
+ std::string flags = MyConf->ReadValue("keyword","flags",index);
+ long gline_time = ServerInstance->Duration(MyConf->ReadValue("keyword","duration",index));
+ if (do_action.empty())
+ do_action = "none";
+ if (flags.empty())
+ flags = "*";
+ FilterResult* x = new FilterResult(pattern, reason, do_action, gline_time, flags);
+ filters[pattern] = x;
+ }
+ DELETE(MyConf);
+ }
+
+ virtual int OnStats(char symbol, userrec* user, string_list &results)
+ {
+ if (symbol == 's')
+ {
+ std::string sn = ServerInstance->Config->ServerName;
+ for (filter_t::iterator n = filters.begin(); n != filters.end(); n++)
+ {
+ results.push_back(sn+" 223 "+user->nick+" :GLOB:"+n->second->freeform+" "+n->second->flags+" "+n->second->action+" "+ConvToStr(n->second->gline_time)+" :"+n->second->reason);
+ }
+ }
+ return 0;
+ }
+};
+
+
+MODULE_INIT(ModuleFilter)
diff --git a/src/modules/m_filter.h b/src/modules/m_filter.h
index ddf448e2e..f2986804c 100644
--- a/src/modules/m_filter.h
+++ b/src/modules/m_filter.h
@@ -1 +1,453 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "xline.h" enum FilterFlags { FLAG_NOOPERS = 1, FLAG_PART = 2, FLAG_QUIT = 4, FLAG_PRIVMSG = 8, FLAG_NOTICE = 16 }; class FilterResult : public classbase { public: std::string freeform; std::string reason; std::string action; long gline_time; std::string flags; bool flag_no_opers; bool flag_part_message; bool flag_quit_message; bool flag_privmsg; bool flag_notice; FilterResult(const std::string free, const std::string &rea, const std::string &act, long gt, const std::string &fla) : freeform(free), reason(rea), action(act), gline_time(gt), flags(fla) { this->FillFlags(flags); } int FillFlags(const std::string &fl) { flags = fl; flag_no_opers = flag_part_message = flag_quit_message = flag_privmsg = flag_notice = false; size_t x = 0; for (std::string::const_iterator n = flags.begin(); n != flags.end(); ++n, ++x) { switch (*n) { case 'o': flag_no_opers = true; break; case 'P': flag_part_message = true; break; case 'q': flag_quit_message = true; break; case 'p': flag_privmsg = true; break; case 'n': flag_notice = true; break; case '*': flag_no_opers = flag_part_message = flag_quit_message = flag_privmsg = flag_notice = true; break; default: return x; break; } } return 0; } FilterResult() { } virtual ~FilterResult() { } }; class cmd_filter; class FilterBase : public Module { cmd_filter* filtcommand; int flags; public: FilterBase(InspIRCd* Me, const std::string &source); virtual ~FilterBase(); virtual void Implements(char* List); virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list); virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) = 0; virtual bool DeleteFilter(const std::string &freeform) = 0; virtual void SyncFilters(Module* proto, void* opaque) = 0; virtual void SendFilter(Module* proto, void* opaque, FilterResult* iter); virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) = 0; virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list); virtual void OnRehash(userrec* user, const std::string &parameter); virtual Version GetVersion(); std::string EncodeFilter(FilterResult* filter); FilterResult DecodeFilter(const std::string &data); virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false); virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata); virtual int OnStats(char symbol, userrec* user, string_list &results) = 0; virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line); bool AppliesToMe(userrec* user, FilterResult* filter, int flags); }; class cmd_filter : public command_t { FilterBase* Base; public: cmd_filter(FilterBase* f, InspIRCd* Me, const std::string &source) : command_t(Me, "FILTER", 'o', 1), Base(f) { this->source = source; this->syntax = "<filter-definition> <type> <flags> [<gline-duration>] :<reason>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { if (pcnt == 1) { /* Deleting a filter */ if (Base->DeleteFilter(parameters[0])) { user->WriteServ("NOTICE %s :*** Deleted filter '%s'", user->nick, parameters[0]); return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :*** Filter '%s' not found on list.", user->nick, parameters[0]); return CMD_FAILURE; } } else { /* Adding a filter */ if (pcnt >= 4) { std::string freeform = parameters[0]; std::string type = parameters[1]; std::string flags = parameters[2]; std::string reason; long duration = 0; if ((type != "gline") && (type != "none") && (type != "block") && (type != "kill") && (type != "silent")) { user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick, freeform.c_str()); return CMD_FAILURE; } if (type == "gline") { if (pcnt >= 5) { duration = ServerInstance->Duration(parameters[3]); reason = parameters[4]; } else { this->TooFewParams(user, " When setting a gline type filter, a gline duration must be specified as the third parameter."); return CMD_FAILURE; } } else { reason = parameters[3]; } std::pair<bool, std::string> result = Base->AddFilter(freeform, type, reason, duration, flags); if (result.first) { user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick, freeform.c_str(), type.c_str(), (duration ? " duration: " : ""), (duration ? parameters[3] : ""), flags.c_str(), reason.c_str()); return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick, freeform.c_str(), result.second.c_str()); return CMD_FAILURE; } } else { this->TooFewParams(user, "."); return CMD_FAILURE; } } } void TooFewParams(userrec* user, const std::string &extra_text) { user->WriteServ("NOTICE %s :*** Not enough parameters%s", user->nick, extra_text.c_str()); } }; bool FilterBase::AppliesToMe(userrec* user, FilterResult* filter, int flags) { if ((flags & FLAG_NOOPERS) && (filter->flag_no_opers) && IS_OPER(user)) return false; if ((flags & FLAG_PRIVMSG) && (!filter->flag_privmsg)) return false; if ((flags & FLAG_NOTICE) && (!filter->flag_notice)) return false; if ((flags & FLAG_QUIT) && (!filter->flag_quit_message)) return false; if ((flags & FLAG_PART) && (!filter->flag_part_message)) return false; return true; } FilterBase::FilterBase(InspIRCd* Me, const std::string &source) : Module(Me) { filtcommand = new cmd_filter(this, Me, source); ServerInstance->AddCommand(filtcommand); } FilterBase::~FilterBase() { } void FilterBase::Implements(char* List) { List[I_OnPreCommand] = List[I_OnStats] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1; } int FilterBase::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { flags = FLAG_PRIVMSG; return OnUserPreNotice(user,dest,target_type,text,status,exempt_list); } int FilterBase::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (!flags) flags = FLAG_NOTICE; /* Leave ulines alone */ if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user))) return 0; FilterResult* f = this->FilterMatch(user, text, flags); if (f) { std::string target = ""; if (target_type == TYPE_USER) { userrec* t = (userrec*)dest; target = std::string(t->nick); } else if (target_type == TYPE_CHANNEL) { chanrec* t = (chanrec*)dest; target = std::string(t->name); } if (f->action == "block") { ServerInstance->WriteOpers(std::string("FILTER: ")+user->nick+" had their message filtered, target was "+target+": "+f->reason); user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered and opers notified: "+f->reason); } if (f->action == "silent") { user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered: "+f->reason); } if (f->action == "kill") { userrec::QuitUser(ServerInstance,user,"Filtered: "+f->reason); } if (f->action == "gline") { if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), user->MakeHostIP())) { ServerInstance->XLines->apply_lines(APPLY_GLINES); FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP())); } } ServerInstance->Log(DEFAULT,"FILTER: "+std::string(user->nick)+std::string(" had their message filtered, target was ")+target+": "+f->reason+" Action: "+f->action); return 1; } return 0; } int FilterBase::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { flags = 0; if ((validated == 1) && (IS_LOCAL(user))) { std::string checkline; int replacepoint = 0; bool parting = false; if (command == "QUIT") { /* QUIT with no reason: nothing to do */ if (pcnt < 1) return 0; checkline = parameters[0]; replacepoint = 0; parting = false; flags = FLAG_QUIT; } else if (command == "PART") { /* PART with no reason: nothing to do */ if (pcnt < 2) return 0; checkline = parameters[1]; replacepoint = 1; parting = true; flags = FLAG_PART; } else /* We're only messing with PART and QUIT */ return 0; FilterResult* f = NULL; if (flags) f = this->FilterMatch(user, checkline, flags); if (!f) /* PART or QUIT reason doesnt match a filter */ return 0; /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */ command_t* c = ServerInstance->Parser->GetHandler(command); if (c) { const char* params[127]; for (int item = 0; item < pcnt; item++) params[item] = parameters[item]; params[replacepoint] = "Reason filtered"; /* We're blocking, OR theyre quitting and its a KILL action * (we cant kill someone whos already quitting, so filter them anyway) */ if ((f->action == "block") || (((!parting) && (f->action == "kill"))) || (f->action == "silent")) { c->Handle(params, pcnt, user); return 1; } else { /* Are they parting, if so, kill is applicable */ if ((parting) && (f->action == "kill")) { user->SetWriteError("Filtered: "+f->reason); /* This WriteServ causes the write error to be applied. * Its not safe to kill here with QuitUser in a PreCommand handler, * so we do it this way, which is safe just about anywhere. */ user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick, f->reason.c_str()); } if (f->action == "gline") { /* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */ std::string wild = "*@"; wild.append(user->GetIPString()); if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), wild.c_str())) { ServerInstance->XLines->apply_lines(APPLY_GLINES); FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP())); } } return 1; } } return 0; } return 0; } void FilterBase::OnRehash(userrec* user, const std::string &parameter) { } Version FilterBase::GetVersion() { return Version(1,1,0,2,VF_VENDOR|VF_COMMON,API_VERSION); } std::string FilterBase::EncodeFilter(FilterResult* filter) { std::ostringstream stream; std::string x = filter->freeform; /* Hax to allow spaces in the freeform without changing the design of the irc protocol */ for (std::string::iterator n = x.begin(); n != x.end(); n++) if (*n == ' ') *n = '\7'; stream << x << " " << filter->action << " " << (filter->flags.empty() ? "-" : filter->flags) << " " << filter->gline_time << " :" << filter->reason; return stream.str(); } FilterResult FilterBase::DecodeFilter(const std::string &data) { FilterResult res; irc::tokenstream tokens(data); tokens.GetToken(res.freeform); tokens.GetToken(res.action); tokens.GetToken(res.flags); if (res.flags == "-") res.flags = ""; res.FillFlags(res.flags); tokens.GetToken(res.gline_time); tokens.GetToken(res.reason); /* Hax to allow spaces in the freeform without changing the design of the irc protocol */ for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++) if (*n == '\7') *n = ' '; return res; } void FilterBase::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { this->SyncFilters(proto, opaque); } void FilterBase::SendFilter(Module* proto, void* opaque, FilterResult* iter) { proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "filter", EncodeFilter(iter)); } void FilterBase::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { if ((target_type == TYPE_OTHER) && (extname == "filter")) { FilterResult data = DecodeFilter(extdata); this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.flags); } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "xline.h"
+
+enum FilterFlags
+{
+ FLAG_NOOPERS = 1,
+ FLAG_PART = 2,
+ FLAG_QUIT = 4,
+ FLAG_PRIVMSG = 8,
+ FLAG_NOTICE = 16
+};
+
+class FilterResult : public classbase
+{
+ public:
+ std::string freeform;
+ std::string reason;
+ std::string action;
+ long gline_time;
+ std::string flags;
+
+ bool flag_no_opers;
+ bool flag_part_message;
+ bool flag_quit_message;
+ bool flag_privmsg;
+ bool flag_notice;
+
+ FilterResult(const std::string free, const std::string &rea, const std::string &act, long gt, const std::string &fla) : freeform(free), reason(rea),
+ action(act), gline_time(gt), flags(fla)
+ {
+ this->FillFlags(flags);
+ }
+
+ int FillFlags(const std::string &fl)
+ {
+ flags = fl;
+ flag_no_opers = flag_part_message = flag_quit_message = flag_privmsg = flag_notice = false;
+ size_t x = 0;
+
+ for (std::string::const_iterator n = flags.begin(); n != flags.end(); ++n, ++x)
+ {
+ switch (*n)
+ {
+ case 'o':
+ flag_no_opers = true;
+ break;
+ case 'P':
+ flag_part_message = true;
+ break;
+ case 'q':
+ flag_quit_message = true;
+ break;
+ case 'p':
+ flag_privmsg = true;
+ break;
+ case 'n':
+ flag_notice = true;
+ break;
+ case '*':
+ flag_no_opers = flag_part_message = flag_quit_message =
+ flag_privmsg = flag_notice = true;
+ break;
+ default:
+ return x;
+ break;
+ }
+ }
+ return 0;
+ }
+
+ FilterResult()
+ {
+ }
+
+ virtual ~FilterResult()
+ {
+ }
+};
+
+class cmd_filter;
+
+class FilterBase : public Module
+{
+ cmd_filter* filtcommand;
+ int flags;
+ public:
+ FilterBase(InspIRCd* Me, const std::string &source);
+ virtual ~FilterBase();
+ virtual void Implements(char* List);
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
+ virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) = 0;
+ virtual bool DeleteFilter(const std::string &freeform) = 0;
+ virtual void SyncFilters(Module* proto, void* opaque) = 0;
+ virtual void SendFilter(Module* proto, void* opaque, FilterResult* iter);
+ virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) = 0;
+ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
+ virtual void OnRehash(userrec* user, const std::string &parameter);
+ virtual Version GetVersion();
+ std::string EncodeFilter(FilterResult* filter);
+ FilterResult DecodeFilter(const std::string &data);
+ virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false);
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata);
+ virtual int OnStats(char symbol, userrec* user, string_list &results) = 0;
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line);
+ bool AppliesToMe(userrec* user, FilterResult* filter, int flags);
+};
+
+class cmd_filter : public command_t
+{
+ FilterBase* Base;
+ public:
+ cmd_filter(FilterBase* f, InspIRCd* Me, const std::string &source) : command_t(Me, "FILTER", 'o', 1), Base(f)
+ {
+ this->source = source;
+ this->syntax = "<filter-definition> <type> <flags> [<gline-duration>] :<reason>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ if (pcnt == 1)
+ {
+ /* Deleting a filter */
+ if (Base->DeleteFilter(parameters[0]))
+ {
+ user->WriteServ("NOTICE %s :*** Deleted filter '%s'", user->nick, parameters[0]);
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Filter '%s' not found on list.", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ /* Adding a filter */
+ if (pcnt >= 4)
+ {
+ std::string freeform = parameters[0];
+ std::string type = parameters[1];
+ std::string flags = parameters[2];
+ std::string reason;
+ long duration = 0;
+
+
+ if ((type != "gline") && (type != "none") && (type != "block") && (type != "kill") && (type != "silent"))
+ {
+ user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick, freeform.c_str());
+ return CMD_FAILURE;
+ }
+
+ if (type == "gline")
+ {
+ if (pcnt >= 5)
+ {
+ duration = ServerInstance->Duration(parameters[3]);
+ reason = parameters[4];
+ }
+ else
+ {
+ this->TooFewParams(user, " When setting a gline type filter, a gline duration must be specified as the third parameter.");
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ reason = parameters[3];
+ }
+ std::pair<bool, std::string> result = Base->AddFilter(freeform, type, reason, duration, flags);
+ if (result.first)
+ {
+ user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick, freeform.c_str(),
+ type.c_str(), (duration ? " duration: " : ""), (duration ? parameters[3] : ""),
+ flags.c_str(), reason.c_str());
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick, freeform.c_str(), result.second.c_str());
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ this->TooFewParams(user, ".");
+ return CMD_FAILURE;
+ }
+
+ }
+ }
+
+ void TooFewParams(userrec* user, const std::string &extra_text)
+ {
+ user->WriteServ("NOTICE %s :*** Not enough parameters%s", user->nick, extra_text.c_str());
+ }
+};
+
+bool FilterBase::AppliesToMe(userrec* user, FilterResult* filter, int flags)
+{
+ if ((flags & FLAG_NOOPERS) && (filter->flag_no_opers) && IS_OPER(user))
+ return false;
+ if ((flags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
+ return false;
+ if ((flags & FLAG_NOTICE) && (!filter->flag_notice))
+ return false;
+ if ((flags & FLAG_QUIT) && (!filter->flag_quit_message))
+ return false;
+ if ((flags & FLAG_PART) && (!filter->flag_part_message))
+ return false;
+ return true;
+}
+
+FilterBase::FilterBase(InspIRCd* Me, const std::string &source) : Module(Me)
+{
+ filtcommand = new cmd_filter(this, Me, source);
+ ServerInstance->AddCommand(filtcommand);
+}
+
+FilterBase::~FilterBase()
+{
+}
+
+void FilterBase::Implements(char* List)
+{
+ List[I_OnPreCommand] = List[I_OnStats] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1;
+}
+
+int FilterBase::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+{
+ flags = FLAG_PRIVMSG;
+ return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
+}
+
+int FilterBase::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+{
+ if (!flags)
+ flags = FLAG_NOTICE;
+
+ /* Leave ulines alone */
+ if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
+ return 0;
+
+ FilterResult* f = this->FilterMatch(user, text, flags);
+ if (f)
+ {
+ std::string target = "";
+ if (target_type == TYPE_USER)
+ {
+ userrec* t = (userrec*)dest;
+ target = std::string(t->nick);
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ chanrec* t = (chanrec*)dest;
+ target = std::string(t->name);
+ }
+ if (f->action == "block")
+ {
+ ServerInstance->WriteOpers(std::string("FILTER: ")+user->nick+" had their message filtered, target was "+target+": "+f->reason);
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered and opers notified: "+f->reason);
+ }
+ if (f->action == "silent")
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered: "+f->reason);
+ }
+ if (f->action == "kill")
+ {
+ userrec::QuitUser(ServerInstance,user,"Filtered: "+f->reason);
+ }
+ if (f->action == "gline")
+ {
+ if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), user->MakeHostIP()))
+ {
+ ServerInstance->XLines->apply_lines(APPLY_GLINES);
+ FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP()));
+ }
+ }
+
+ ServerInstance->Log(DEFAULT,"FILTER: "+std::string(user->nick)+std::string(" had their message filtered, target was ")+target+": "+f->reason+" Action: "+f->action);
+ return 1;
+ }
+ return 0;
+}
+
+int FilterBase::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+{
+ flags = 0;
+ if ((validated == 1) && (IS_LOCAL(user)))
+ {
+ std::string checkline;
+ int replacepoint = 0;
+ bool parting = false;
+
+ if (command == "QUIT")
+ {
+ /* QUIT with no reason: nothing to do */
+ if (pcnt < 1)
+ return 0;
+
+ checkline = parameters[0];
+ replacepoint = 0;
+ parting = false;
+ flags = FLAG_QUIT;
+ }
+ else if (command == "PART")
+ {
+ /* PART with no reason: nothing to do */
+ if (pcnt < 2)
+ return 0;
+
+ checkline = parameters[1];
+ replacepoint = 1;
+ parting = true;
+ flags = FLAG_PART;
+ }
+ else
+ /* We're only messing with PART and QUIT */
+ return 0;
+
+ FilterResult* f = NULL;
+
+ if (flags)
+ f = this->FilterMatch(user, checkline, flags);
+
+ if (!f)
+ /* PART or QUIT reason doesnt match a filter */
+ return 0;
+
+ /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */
+ command_t* c = ServerInstance->Parser->GetHandler(command);
+ if (c)
+ {
+ const char* params[127];
+ for (int item = 0; item < pcnt; item++)
+ params[item] = parameters[item];
+ params[replacepoint] = "Reason filtered";
+
+ /* We're blocking, OR theyre quitting and its a KILL action
+ * (we cant kill someone whos already quitting, so filter them anyway)
+ */
+ if ((f->action == "block") || (((!parting) && (f->action == "kill"))) || (f->action == "silent"))
+ {
+ c->Handle(params, pcnt, user);
+ return 1;
+ }
+ else
+ {
+ /* Are they parting, if so, kill is applicable */
+ if ((parting) && (f->action == "kill"))
+ {
+ user->SetWriteError("Filtered: "+f->reason);
+ /* This WriteServ causes the write error to be applied.
+ * Its not safe to kill here with QuitUser in a PreCommand handler,
+ * so we do it this way, which is safe just about anywhere.
+ */
+ user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick, f->reason.c_str());
+ }
+ if (f->action == "gline")
+ {
+ /* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */
+ std::string wild = "*@";
+ wild.append(user->GetIPString());
+
+ if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), wild.c_str()))
+ {
+ ServerInstance->XLines->apply_lines(APPLY_GLINES);
+ FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP()));
+ }
+ }
+ return 1;
+ }
+ }
+ return 0;
+ }
+ return 0;
+}
+
+void FilterBase::OnRehash(userrec* user, const std::string &parameter)
+{
+}
+
+Version FilterBase::GetVersion()
+{
+ return Version(1,1,0,2,VF_VENDOR|VF_COMMON,API_VERSION);
+}
+
+
+std::string FilterBase::EncodeFilter(FilterResult* filter)
+{
+ std::ostringstream stream;
+ std::string x = filter->freeform;
+
+ /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
+ for (std::string::iterator n = x.begin(); n != x.end(); n++)
+ if (*n == ' ')
+ *n = '\7';
+
+ stream << x << " " << filter->action << " " << (filter->flags.empty() ? "-" : filter->flags) << " " << filter->gline_time << " :" << filter->reason;
+ return stream.str();
+}
+
+FilterResult FilterBase::DecodeFilter(const std::string &data)
+{
+ FilterResult res;
+ irc::tokenstream tokens(data);
+ tokens.GetToken(res.freeform);
+ tokens.GetToken(res.action);
+ tokens.GetToken(res.flags);
+ if (res.flags == "-")
+ res.flags = "";
+ res.FillFlags(res.flags);
+ tokens.GetToken(res.gline_time);
+ tokens.GetToken(res.reason);
+
+ /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
+ for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
+ if (*n == '\7')
+ *n = ' ';
+
+ return res;
+}
+
+void FilterBase::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
+{
+ this->SyncFilters(proto, opaque);
+}
+
+void FilterBase::SendFilter(Module* proto, void* opaque, FilterResult* iter)
+{
+ proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "filter", EncodeFilter(iter));
+}
+
+void FilterBase::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+{
+ if ((target_type == TYPE_OTHER) && (extname == "filter"))
+ {
+ FilterResult data = DecodeFilter(extdata);
+ this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.flags);
+ }
+}
+
diff --git a/src/modules/m_foobar.cpp b/src/modules/m_foobar.cpp
index 857f4d16d..7de305923 100644
--- a/src/modules/m_foobar.cpp
+++ b/src/modules/m_foobar.cpp
@@ -1 +1,98 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: A dummy module for testing */ // Class ModuleFoobar inherits from Module // It just outputs simple debug strings to show its methods are working. class ModuleFoobar : public Module { private: // It is recommended that your class makes use of one or more Server // objects. A server object is a class which contains methods which // encapsulate the exports from the core of the ircd. // such methods include Debug, SendChannel, etc. public: ModuleFoobar(InspIRCd* Me) : Module(Me) { // The constructor just makes a copy of the server class } virtual ~ModuleFoobar() { } virtual Version GetVersion() { // this method instantiates a class of type Version, and returns // the modules version information using it. return Version(1,1,0,1,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnUserConnect] = List[I_OnUserQuit] = List[I_OnUserJoin] = List[I_OnUserPart] = 1; } virtual void OnUserConnect(userrec* user) { // method called when a user connects std::string b = user->nick; ServerInstance->Log(DEBUG,"Foobar: User connecting: "+b); } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { // method called when a user disconnects std::string b = user->nick; ServerInstance->Log(DEBUG,"Foobar: User quitting: "+b); } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { // method called when a user joins a channel std::string c = channel->name; std::string b = user->nick; ServerInstance->Log(DEBUG,"Foobar: User "+b+" joined "+c); } virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent) { // method called when a user parts a channel std::string c = channel->name; std::string b = user->nick; ServerInstance->Log(DEBUG,"Foobar: User "+b+" parted "+c); } }; MODULE_INIT(ModuleFoobar) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: A dummy module for testing */
+
+// Class ModuleFoobar inherits from Module
+// It just outputs simple debug strings to show its methods are working.
+
+class ModuleFoobar : public Module
+{
+ private:
+
+ // It is recommended that your class makes use of one or more Server
+ // objects. A server object is a class which contains methods which
+ // encapsulate the exports from the core of the ircd.
+ // such methods include Debug, SendChannel, etc.
+
+
+ public:
+ ModuleFoobar(InspIRCd* Me)
+ : Module(Me)
+ {
+ // The constructor just makes a copy of the server class
+
+
+ }
+
+ virtual ~ModuleFoobar()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ // this method instantiates a class of type Version, and returns
+ // the modules version information using it.
+
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserConnect] = List[I_OnUserQuit] = List[I_OnUserJoin] = List[I_OnUserPart] = 1;
+ }
+
+ virtual void OnUserConnect(userrec* user)
+ {
+ // method called when a user connects
+
+ std::string b = user->nick;
+ ServerInstance->Log(DEBUG,"Foobar: User connecting: "+b);
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ // method called when a user disconnects
+
+ std::string b = user->nick;
+ ServerInstance->Log(DEBUG,"Foobar: User quitting: "+b);
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ // method called when a user joins a channel
+
+ std::string c = channel->name;
+ std::string b = user->nick;
+ ServerInstance->Log(DEBUG,"Foobar: User "+b+" joined "+c);
+ }
+
+ virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent)
+ {
+ // method called when a user parts a channel
+
+ std::string c = channel->name;
+ std::string b = user->nick;
+ ServerInstance->Log(DEBUG,"Foobar: User "+b+" parted "+c);
+ }
+
+};
+
+
+MODULE_INIT(ModuleFoobar)
+
diff --git a/src/modules/m_globalload.cpp b/src/modules/m_globalload.cpp
index 4f3438e05..ae87451ba 100644
--- a/src/modules/m_globalload.cpp
+++ b/src/modules/m_globalload.cpp
@@ -1 +1,141 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Allows global loading of a module. */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /** Handle /GLOADMODULE */ class cmd_gloadmodule : public command_t { public: cmd_gloadmodule (InspIRCd* Instance) : command_t(Instance,"GLOADMODULE", 'o', 1) { this->source = "m_globalload.so"; syntax = "<modulename>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { if (ServerInstance->LoadModule(parameters[0])) { ServerInstance->WriteOpers("*** NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0],user->nick); user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]); /* route it! */ return CMD_SUCCESS; } else { user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); /* XXX - returning CMD_FAILURE here could potentially mean half the net loads it, half doesn't. pass it on anyway? -- w00t * * Returning CMD_SUCCESS would have the same effect, just with less servers. Someone should update this module to properly * pass the success/failure for each server to the caller (or to all opers) -Special */ return CMD_FAILURE; } } }; /** Handle /GUNLOADMODULE */ class cmd_gunloadmodule : public command_t { public: cmd_gunloadmodule (InspIRCd* Instance) : command_t(Instance,"GUNLOADMODULE", 'o', 1) { this->source = "m_globalload.so"; syntax = "<modulename>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { if (ServerInstance->UnloadModule(parameters[0])) { ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY UNLOADED BY '%s'",parameters[0],user->nick); user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]); } else { /* Return CMD_SUCCESS so the module will be unloaded on any servers it is loaded on - this is a seperate case entirely from loading -Special */ user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); } return CMD_SUCCESS; } }; /** Handle /GRELOADMODULE */ class cmd_greloadmodule : public command_t { public: cmd_greloadmodule (InspIRCd* Instance) : command_t(Instance, "GRELOADMODULE", 'o', 1) { this->source = "m_globalload.so"; syntax = "<modulename>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { if (!ServerInstance->UnloadModule(parameters[0])) { user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); return CMD_FAILURE; } if (!ServerInstance->LoadModule(parameters[0])) { user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); return CMD_FAILURE; } ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY RELOADED BY '%s'",parameters[0],user->nick); user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]); return CMD_SUCCESS; } }; class ModuleGlobalLoad : public Module { cmd_gloadmodule *mycommand; cmd_gunloadmodule *mycommand2; cmd_greloadmodule *mycommand3; public: ModuleGlobalLoad(InspIRCd* Me) : Module(Me) { mycommand = new cmd_gloadmodule(ServerInstance); mycommand2 = new cmd_gunloadmodule(ServerInstance); mycommand3 = new cmd_greloadmodule(ServerInstance); ServerInstance->AddCommand(mycommand); ServerInstance->AddCommand(mycommand2); ServerInstance->AddCommand(mycommand3); } virtual ~ModuleGlobalLoad() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleGlobalLoad) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Allows global loading of a module. */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/** Handle /GLOADMODULE
+ */
+class cmd_gloadmodule : public command_t
+{
+ public:
+ cmd_gloadmodule (InspIRCd* Instance) : command_t(Instance,"GLOADMODULE", 'o', 1)
+ {
+ this->source = "m_globalload.so";
+ syntax = "<modulename>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ if (ServerInstance->LoadModule(parameters[0]))
+ {
+ ServerInstance->WriteOpers("*** NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0],user->nick);
+ user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]);
+
+ /* route it! */
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
+ /* XXX - returning CMD_FAILURE here could potentially mean half the net loads it, half doesn't. pass it on anyway? -- w00t
+ *
+ * Returning CMD_SUCCESS would have the same effect, just with less servers. Someone should update this module to properly
+ * pass the success/failure for each server to the caller (or to all opers) -Special */
+ return CMD_FAILURE;
+ }
+ }
+};
+
+/** Handle /GUNLOADMODULE
+ */
+class cmd_gunloadmodule : public command_t
+{
+ public:
+ cmd_gunloadmodule (InspIRCd* Instance) : command_t(Instance,"GUNLOADMODULE", 'o', 1)
+ {
+ this->source = "m_globalload.so";
+ syntax = "<modulename>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ if (ServerInstance->UnloadModule(parameters[0]))
+ {
+ ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY UNLOADED BY '%s'",parameters[0],user->nick);
+ user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]);
+ }
+ else
+ {
+ /* Return CMD_SUCCESS so the module will be unloaded on any servers it is loaded on - this is a seperate case entirely from loading -Special */
+ user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
+ }
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /GRELOADMODULE
+ */
+class cmd_greloadmodule : public command_t
+{
+ public:
+ cmd_greloadmodule (InspIRCd* Instance) : command_t(Instance, "GRELOADMODULE", 'o', 1)
+ {
+ this->source = "m_globalload.so";
+ syntax = "<modulename>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ if (!ServerInstance->UnloadModule(parameters[0]))
+ {
+ user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
+ return CMD_FAILURE;
+ }
+
+ if (!ServerInstance->LoadModule(parameters[0]))
+ {
+ user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
+ return CMD_FAILURE;
+ }
+
+ ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY RELOADED BY '%s'",parameters[0],user->nick);
+ user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]);
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleGlobalLoad : public Module
+{
+ cmd_gloadmodule *mycommand;
+ cmd_gunloadmodule *mycommand2;
+ cmd_greloadmodule *mycommand3;
+
+ public:
+ ModuleGlobalLoad(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_gloadmodule(ServerInstance);
+ mycommand2 = new cmd_gunloadmodule(ServerInstance);
+ mycommand3 = new cmd_greloadmodule(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ ServerInstance->AddCommand(mycommand2);
+ ServerInstance->AddCommand(mycommand3);
+ }
+
+ virtual ~ModuleGlobalLoad()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleGlobalLoad)
diff --git a/src/modules/m_globops.cpp b/src/modules/m_globops.cpp
index 5745cc9c6..1a49858e2 100644
--- a/src/modules/m_globops.cpp
+++ b/src/modules/m_globops.cpp
@@ -1 +1,76 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ // Globops and +g support module by C.J.Edwards #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for GLOBOPS and user mode +g */ /** Handle /GLOBOPS */ class cmd_globops : public command_t { public: cmd_globops (InspIRCd* Instance) : command_t(Instance,"GLOBOPS",'o',1) { this->source = "m_globops.so"; syntax = "<any-text>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { std::string line = "From " + std::string(user->nick) + ": "; for (int i = 0; i < pcnt; i++) { line = line + std::string(parameters[i]) + " "; } ServerInstance->SNO->WriteToSnoMask('g',line); /* route it (ofc :p) */ return CMD_SUCCESS; } }; class ModuleGlobops : public Module { cmd_globops* mycommand; public: ModuleGlobops(InspIRCd* Me) : Module(Me) { mycommand = new cmd_globops(ServerInstance); ServerInstance->AddCommand(mycommand); ServerInstance->SNO->EnableSnomask('g',"GLOBOPS"); } virtual ~ModuleGlobops() { ServerInstance->SNO->DisableSnomask('g'); DELETE(mycommand); } virtual Version GetVersion() { return Version(1, 1, 0, 1, VF_COMMON | VF_VENDOR, API_VERSION); } void Implements(char* List) { } }; MODULE_INIT(ModuleGlobops) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+// Globops and +g support module by C.J.Edwards
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for GLOBOPS and user mode +g */
+
+/** Handle /GLOBOPS
+ */
+class cmd_globops : public command_t
+{
+ public:
+ cmd_globops (InspIRCd* Instance) : command_t(Instance,"GLOBOPS",'o',1)
+ {
+ this->source = "m_globops.so";
+ syntax = "<any-text>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ std::string line = "From " + std::string(user->nick) + ": ";
+ for (int i = 0; i < pcnt; i++)
+ {
+ line = line + std::string(parameters[i]) + " ";
+ }
+ ServerInstance->SNO->WriteToSnoMask('g',line);
+
+ /* route it (ofc :p) */
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleGlobops : public Module
+{
+ cmd_globops* mycommand;
+ public:
+ ModuleGlobops(InspIRCd* Me)
+ : Module(Me)
+ {
+ mycommand = new cmd_globops(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ ServerInstance->SNO->EnableSnomask('g',"GLOBOPS");
+ }
+
+ virtual ~ModuleGlobops()
+ {
+ ServerInstance->SNO->DisableSnomask('g');
+ DELETE(mycommand);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 1, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ }
+};
+
+MODULE_INIT(ModuleGlobops)
diff --git a/src/modules/m_hash.h b/src/modules/m_hash.h
index d82104cdb..ee9ead21c 100644
--- a/src/modules/m_hash.h
+++ b/src/modules/m_hash.h
@@ -1 +1,196 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __HASH_H__ #define __HASH_H__ #include "modules.h" #define SHA256_DIGEST_SIZE (256 / 8) #define SHA256_BLOCK_SIZE (512 / 8) /** HashRequest is the base class used to send Hash requests to hashing.so. * You should not instantiate classes of type HashRequest directly, instead * you should instantiate classes of type HashResetRequest, HashSumRequest, * HashKeyRequest and HashHexRequest, shown below. */ class HashRequest : public Request { /** The keys (IV) to use */ unsigned int* keys; /** The output characters (hex sequence) to use */ const char* outputs; /** The string to hash */ std::string tohash; public: /** Initialize HashRequest as an Hash_RESET message */ HashRequest(const char* req, Module* Me, Module* Target) : Request(Me, Target, req) { } /** Initialize HashRequest as an Hash_SUM message */ HashRequest(Module* Me, Module* Target, const std::string &hashable) : Request(Me, Target, "SUM"), keys(NULL), outputs(NULL), tohash(hashable) { } /** Initialize HashRequest as an Hash_KEY message */ HashRequest(Module* Me, Module* Target, unsigned int* k) : Request(Me, Target, "KEY"), keys(k), outputs(NULL), tohash("") { } /** Initialize HashRequest as an Hash_HEX message */ HashRequest(Module* Me, Module* Target, const char* out) : Request(Me, Target, "HEX"), keys(NULL), outputs(out), tohash("") { } /** Get data to be hashed */ const char* GetHashData() { return tohash.c_str(); } /** Get keys (IVs) to be used */ unsigned int* GetKeyData() { return keys; } /** Get output characters (hex sequence) to be used */ const char* GetOutputs() { return outputs; } }; /** Send this class to the hashing module to query for its name. * * Example: * \code * cout << "Using hash algorithm: " << HashNameRequest(this, HashModule).Send(); * \endcode */ class HashNameRequest : public HashRequest { public: /** Initialize HashNameRequest for sending. * @param Me A pointer to the sending module * @param Target A pointer to the hashing module */ HashNameRequest(Module* Me, Module* Target) : HashRequest("NAME", Me, Target) { } }; /** Send this class to the hashing module to reset the Hash module to a known state. * This will reset the IV to the defaults specified by the Hash spec, * and reset the hex sequence to "0123456789abcdef". It should be sent before * ANY other Request types. * * Example: * \code * // Reset the Hash module. * HashResetRequest(this, HashModule).Send(); * \endcode */ class HashResetRequest : public HashRequest { public: /** Initialize HashResetRequest for sending. * @param Me A pointer to the sending module * @param Target A pointer to the hashing module */ HashResetRequest(Module* Me, Module* Target) : HashRequest("RESET", Me, Target) { } }; /** Send this class to the hashing module to HashSUM a std::string. * You should make sure you know the state of the module before you send this * class, e.g. by first sending an HashResetRequest class. The hash will be * returned when you call Send(). * * Example: * \code * // ALWAYS ALWAYS reset first, or set your own IV and hex chars. * HashResetRequest(this, HashModule).Send(); * // Get the Hash sum of the string 'doodads'. * std::string result = HashSumRequest(this, HashModule, "doodads").Send(); * \endcode */ class HashSumRequest : public HashRequest { public: /** Initialize HashSumRequest for sending. * @param Me A pointer to the sending module * @param Target A pointer to the hashing module * @param data The data to be hashed */ HashSumRequest(Module* Me, Module* Target, const std::string &data) : HashRequest(Me, Target, data) { } }; /** Send this class to hashing module to change the IVs (keys) to use for hashing. * You should make sure you know the state of the module before you send this * class, e.g. by first sending an HashResetRequest class. The default values for * the IV's are those specified in the Hash specification. Only in very special * circumstances should you need to change the IV's (see for example m_cloaking.cpp) * * Example: * \code * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC }; * HashKeyRequest(this, HashModule, iv); * \endcode */ class HashKeyRequest : public HashRequest { public: /** Initialize HashKeyRequest for sending. * @param Me A pointer to the sending module * @param Target A pointer to the hashing module * @param data The new IV's. This should be an array of exactly four 32 bit values. * On 64-bit architectures, the upper 32 bits of the values will be discarded. */ HashKeyRequest(Module* Me, Module* Target, unsigned int* data) : HashRequest(Me, Target, data) { } }; /** Send this class to the hashing module to change the hex sequence to use for generating the returned value. * You should make sure you know the state of the module before you send this * class, e.g. by first sending an HashResetRequest class. The default value for * the hex sequence is "0123456789abcdef". Only in very special circumstances should * you need to change the hex sequence (see for example m_cloaking.cpp). * * Example: * \code * static const char tab[] = "fedcba9876543210"; * HashHexRequest(this, HashModule, tab); * \endcode */ class HashHexRequest : public HashRequest { public: /** Initialize HashHexRequest for sending. * @param Me A pointer to the sending module * @param Target A pointer to the hashing module * @param data The hex sequence to use. This should contain exactly 16 ASCII characters, * terminated by a NULL char. */ HashHexRequest(Module* Me, Module* Target, const char* data) : HashRequest(Me, Target, data) { } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __HASH_H__
+#define __HASH_H__
+
+#include "modules.h"
+
+#define SHA256_DIGEST_SIZE (256 / 8)
+#define SHA256_BLOCK_SIZE (512 / 8)
+
+/** HashRequest is the base class used to send Hash requests to hashing.so.
+ * You should not instantiate classes of type HashRequest directly, instead
+ * you should instantiate classes of type HashResetRequest, HashSumRequest,
+ * HashKeyRequest and HashHexRequest, shown below.
+ */
+class HashRequest : public Request
+{
+ /** The keys (IV) to use */
+ unsigned int* keys;
+ /** The output characters (hex sequence) to use */
+ const char* outputs;
+ /** The string to hash */
+ std::string tohash;
+ public:
+ /** Initialize HashRequest as an Hash_RESET message */
+ HashRequest(const char* req, Module* Me, Module* Target) : Request(Me, Target, req)
+ {
+ }
+
+ /** Initialize HashRequest as an Hash_SUM message */
+ HashRequest(Module* Me, Module* Target, const std::string &hashable) : Request(Me, Target, "SUM"), keys(NULL), outputs(NULL), tohash(hashable)
+ {
+ }
+
+ /** Initialize HashRequest as an Hash_KEY message */
+ HashRequest(Module* Me, Module* Target, unsigned int* k) : Request(Me, Target, "KEY"), keys(k), outputs(NULL), tohash("")
+ {
+ }
+
+ /** Initialize HashRequest as an Hash_HEX message */
+ HashRequest(Module* Me, Module* Target, const char* out) : Request(Me, Target, "HEX"), keys(NULL), outputs(out), tohash("")
+ {
+ }
+
+ /** Get data to be hashed */
+ const char* GetHashData()
+ {
+ return tohash.c_str();
+ }
+
+ /** Get keys (IVs) to be used */
+ unsigned int* GetKeyData()
+ {
+ return keys;
+ }
+
+ /** Get output characters (hex sequence) to be used */
+ const char* GetOutputs()
+ {
+ return outputs;
+ }
+};
+
+/** Send this class to the hashing module to query for its name.
+ *
+ * Example:
+ * \code
+ * cout << "Using hash algorithm: " << HashNameRequest(this, HashModule).Send();
+ * \endcode
+ */
+class HashNameRequest : public HashRequest
+{
+ public:
+ /** Initialize HashNameRequest for sending.
+ * @param Me A pointer to the sending module
+ * @param Target A pointer to the hashing module
+ */
+ HashNameRequest(Module* Me, Module* Target) : HashRequest("NAME", Me, Target)
+ {
+ }
+};
+
+/** Send this class to the hashing module to reset the Hash module to a known state.
+ * This will reset the IV to the defaults specified by the Hash spec,
+ * and reset the hex sequence to "0123456789abcdef". It should be sent before
+ * ANY other Request types.
+ *
+ * Example:
+ * \code
+ * // Reset the Hash module.
+ * HashResetRequest(this, HashModule).Send();
+ * \endcode
+ */
+class HashResetRequest : public HashRequest
+{
+ public:
+ /** Initialize HashResetRequest for sending.
+ * @param Me A pointer to the sending module
+ * @param Target A pointer to the hashing module
+ */
+ HashResetRequest(Module* Me, Module* Target) : HashRequest("RESET", Me, Target)
+ {
+ }
+};
+
+/** Send this class to the hashing module to HashSUM a std::string.
+ * You should make sure you know the state of the module before you send this
+ * class, e.g. by first sending an HashResetRequest class. The hash will be
+ * returned when you call Send().
+ *
+ * Example:
+ * \code
+ * // ALWAYS ALWAYS reset first, or set your own IV and hex chars.
+ * HashResetRequest(this, HashModule).Send();
+ * // Get the Hash sum of the string 'doodads'.
+ * std::string result = HashSumRequest(this, HashModule, "doodads").Send();
+ * \endcode
+ */
+class HashSumRequest : public HashRequest
+{
+ public:
+ /** Initialize HashSumRequest for sending.
+ * @param Me A pointer to the sending module
+ * @param Target A pointer to the hashing module
+ * @param data The data to be hashed
+ */
+ HashSumRequest(Module* Me, Module* Target, const std::string &data) : HashRequest(Me, Target, data)
+ {
+ }
+};
+
+/** Send this class to hashing module to change the IVs (keys) to use for hashing.
+ * You should make sure you know the state of the module before you send this
+ * class, e.g. by first sending an HashResetRequest class. The default values for
+ * the IV's are those specified in the Hash specification. Only in very special
+ * circumstances should you need to change the IV's (see for example m_cloaking.cpp)
+ *
+ * Example:
+ * \code
+ * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC };
+ * HashKeyRequest(this, HashModule, iv);
+ * \endcode
+ */
+class HashKeyRequest : public HashRequest
+{
+ public:
+ /** Initialize HashKeyRequest for sending.
+ * @param Me A pointer to the sending module
+ * @param Target A pointer to the hashing module
+ * @param data The new IV's. This should be an array of exactly four 32 bit values.
+ * On 64-bit architectures, the upper 32 bits of the values will be discarded.
+ */
+ HashKeyRequest(Module* Me, Module* Target, unsigned int* data) : HashRequest(Me, Target, data)
+ {
+ }
+};
+
+/** Send this class to the hashing module to change the hex sequence to use for generating the returned value.
+ * You should make sure you know the state of the module before you send this
+ * class, e.g. by first sending an HashResetRequest class. The default value for
+ * the hex sequence is "0123456789abcdef". Only in very special circumstances should
+ * you need to change the hex sequence (see for example m_cloaking.cpp).
+ *
+ * Example:
+ * \code
+ * static const char tab[] = "fedcba9876543210";
+ * HashHexRequest(this, HashModule, tab);
+ * \endcode
+ */
+class HashHexRequest : public HashRequest
+{
+ public:
+ /** Initialize HashHexRequest for sending.
+ * @param Me A pointer to the sending module
+ * @param Target A pointer to the hashing module
+ * @param data The hex sequence to use. This should contain exactly 16 ASCII characters,
+ * terminated by a NULL char.
+ */
+ HashHexRequest(Module* Me, Module* Target, const char* data) : HashRequest(Me, Target, data)
+ {
+ }
+};
+
+#endif
+
diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp
index 965194a08..341f2b861 100644
--- a/src/modules/m_helpop.cpp
+++ b/src/modules/m_helpop.cpp
@@ -1 +1,191 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: /helpop Command, Works like Unreal helpop */ static std::map<irc::string, std::string> helpop_map; /** Handles user mode +h */ class Helpop : public ModeHandler { public: Helpop(InspIRCd* Instance) : ModeHandler(Instance, 'h', 0, 0, false, MODETYPE_USER, true) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!dest->IsModeSet('h')) { dest->SetMode('h',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('h')) { dest->SetMode('h',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Handles /HELPOP */ class cmd_helpop : public command_t { public: cmd_helpop (InspIRCd* Instance) : command_t(Instance, "HELPOP", 0, 0) { this->source = "m_helpop.so"; syntax = "<any-text>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { irc::string parameter; if (pcnt > 0) parameter = parameters[0]; if (pcnt == 0 || parameter == "index") { /* iterate over all helpop items */ user->WriteServ("NOTICE %s :HELPOP topic index", user->nick); for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++) { user->WriteServ("NOTICE %s : %s", user->nick, iter->first.c_str()); } user->WriteServ("NOTICE %s :*** End of HELPOP topic index", user->nick); } else { user->WriteServ("NOTICE %s :*** HELPOP for %s", user->nick, parameters[0]); std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter); if (iter == helpop_map.end()) { iter = helpop_map.find("nohelp"); } std::string value = iter->second; irc::sepstream stream(value, '\n'); std::string token = "*"; while ((token = stream.GetToken()) != "") { user->WriteServ("NOTICE %s :%s", user->nick, token.c_str()); } user->WriteServ("NOTICE %s :*** End of HELPOP", user->nick); } /* We dont want these going out over the network, return CMD_FAILURE * to make sure the protocol module thinks theyre not worth sending. */ return CMD_FAILURE; } }; class ModuleHelpop : public Module { private: std::string h_file; cmd_helpop* mycommand; Helpop* ho; public: ModuleHelpop(InspIRCd* Me) : Module(Me) { ReadConfig(); ho = new Helpop(ServerInstance); if (!ServerInstance->AddMode(ho, 'h')) throw ModuleException("Could not add new modes!"); mycommand = new cmd_helpop(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual void ReadConfig() { ConfigReader *MyConf = new ConfigReader(ServerInstance); helpop_map.clear(); for (int i = 0; i < MyConf->Enumerate("helpop"); i++) { irc::string key = assign(MyConf->ReadValue("helpop", "key", i)); std::string value = MyConf->ReadValue("helpop", "value", i, true); /* Linefeeds allowed! */ if (key == "index") { throw ModuleException("m_helpop: The key 'index' is reserved for internal purposes. Please remove it."); } helpop_map[key] = value; } if (helpop_map.find("start") == helpop_map.end()) { // error! throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf."); } else if (helpop_map.find("nohelp") == helpop_map.end()) { // error! throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf."); } } void Implements(char* List) { List[I_OnRehash] = List[I_OnWhois] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadConfig(); } virtual void OnWhois(userrec* src, userrec* dst) { if (dst->IsModeSet('h')) { ServerInstance->SendWhoisLine(src, dst, 310, std::string(src->nick)+" "+std::string(dst->nick)+" :is available for help."); } } virtual ~ModuleHelpop() { ServerInstance->Modes->DelMode(ho); DELETE(ho); } virtual Version GetVersion() { return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleHelpop) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: /helpop Command, Works like Unreal helpop */
+static std::map<irc::string, std::string> helpop_map;
+
+
+/** Handles user mode +h
+ */
+class Helpop : public ModeHandler
+{
+ public:
+ Helpop(InspIRCd* Instance) : ModeHandler(Instance, 'h', 0, 0, false, MODETYPE_USER, true) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!dest->IsModeSet('h'))
+ {
+ dest->SetMode('h',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('h'))
+ {
+ dest->SetMode('h',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** Handles /HELPOP
+ */
+class cmd_helpop : public command_t
+{
+ public:
+ cmd_helpop (InspIRCd* Instance) : command_t(Instance, "HELPOP", 0, 0)
+ {
+ this->source = "m_helpop.so";
+ syntax = "<any-text>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ irc::string parameter;
+ if (pcnt > 0)
+ parameter = parameters[0];
+
+ if (pcnt == 0 || parameter == "index")
+ {
+ /* iterate over all helpop items */
+ user->WriteServ("NOTICE %s :HELPOP topic index", user->nick);
+ for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
+ {
+ user->WriteServ("NOTICE %s : %s", user->nick, iter->first.c_str());
+ }
+ user->WriteServ("NOTICE %s :*** End of HELPOP topic index", user->nick);
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** HELPOP for %s", user->nick, parameters[0]);
+
+ std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter);
+
+ if (iter == helpop_map.end())
+ {
+ iter = helpop_map.find("nohelp");
+ }
+
+ std::string value = iter->second;
+ irc::sepstream stream(value, '\n');
+ std::string token = "*";
+
+ while ((token = stream.GetToken()) != "")
+ {
+ user->WriteServ("NOTICE %s :%s", user->nick, token.c_str());
+ }
+
+ user->WriteServ("NOTICE %s :*** End of HELPOP", user->nick);
+ }
+
+ /* We dont want these going out over the network, return CMD_FAILURE
+ * to make sure the protocol module thinks theyre not worth sending.
+ */
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleHelpop : public Module
+{
+ private:
+ std::string h_file;
+ cmd_helpop* mycommand;
+ Helpop* ho;
+
+ public:
+ ModuleHelpop(InspIRCd* Me)
+ : Module(Me)
+ {
+ ReadConfig();
+ ho = new Helpop(ServerInstance);
+ if (!ServerInstance->AddMode(ho, 'h'))
+ throw ModuleException("Could not add new modes!");
+ mycommand = new cmd_helpop(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual void ReadConfig()
+ {
+ ConfigReader *MyConf = new ConfigReader(ServerInstance);
+
+ helpop_map.clear();
+
+ for (int i = 0; i < MyConf->Enumerate("helpop"); i++)
+ {
+ irc::string key = assign(MyConf->ReadValue("helpop", "key", i));
+ std::string value = MyConf->ReadValue("helpop", "value", i, true); /* Linefeeds allowed! */
+
+ if (key == "index")
+ {
+ throw ModuleException("m_helpop: The key 'index' is reserved for internal purposes. Please remove it.");
+ }
+
+ helpop_map[key] = value;
+ }
+
+ if (helpop_map.find("start") == helpop_map.end())
+ {
+ // error!
+ throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf.");
+ }
+ else if (helpop_map.find("nohelp") == helpop_map.end())
+ {
+ // error!
+ throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf.");
+ }
+
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnWhois] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadConfig();
+ }
+
+ virtual void OnWhois(userrec* src, userrec* dst)
+ {
+ if (dst->IsModeSet('h'))
+ {
+ ServerInstance->SendWhoisLine(src, dst, 310, std::string(src->nick)+" "+std::string(dst->nick)+" :is available for help.");
+ }
+ }
+
+ virtual ~ModuleHelpop()
+ {
+ ServerInstance->Modes->DelMode(ho);
+ DELETE(ho);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleHelpop)
diff --git a/src/modules/m_hidechans.cpp b/src/modules/m_hidechans.cpp
index 3924b84b9..2c3769f7a 100644
--- a/src/modules/m_hidechans.cpp
+++ b/src/modules/m_hidechans.cpp
@@ -1 +1,95 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for hiding channels with user mode +I */ /** Handles user mode +I */ class HideChans : public ModeHandler { public: HideChans(InspIRCd* Instance) : ModeHandler(Instance, 'I', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if (source != dest) return MODEACTION_DENY; if (adding) { if (!dest->IsModeSet('I')) { dest->SetMode('I',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('I')) { dest->SetMode('I',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleHideChans : public Module { HideChans* hm; public: ModuleHideChans(InspIRCd* Me) : Module(Me) { hm = new HideChans(ServerInstance); if (!ServerInstance->AddMode(hm, 'I')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnWhoisLine] = 1; } virtual ~ModuleHideChans() { ServerInstance->Modes->DelMode(hm); DELETE(hm); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { /* Dont display channels if they have +I set and the * person doing the WHOIS is not an oper */ return ((user != dest) && (!IS_OPER(user)) && (numeric == 319) && dest->IsModeSet('I')); } }; MODULE_INIT(ModuleHideChans) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for hiding channels with user mode +I */
+
+/** Handles user mode +I
+ */
+class HideChans : public ModeHandler
+{
+ public:
+ HideChans(InspIRCd* Instance) : ModeHandler(Instance, 'I', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ /* Only opers can change other users modes */
+ if (source != dest)
+ return MODEACTION_DENY;
+
+ if (adding)
+ {
+ if (!dest->IsModeSet('I'))
+ {
+ dest->SetMode('I',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('I'))
+ {
+ dest->SetMode('I',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleHideChans : public Module
+{
+
+ HideChans* hm;
+ public:
+ ModuleHideChans(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ hm = new HideChans(ServerInstance);
+ if (!ServerInstance->AddMode(hm, 'I'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnWhoisLine] = 1;
+ }
+
+ virtual ~ModuleHideChans()
+ {
+ ServerInstance->Modes->DelMode(hm);
+ DELETE(hm);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+ int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text)
+ {
+ /* Dont display channels if they have +I set and the
+ * person doing the WHOIS is not an oper
+ */
+ return ((user != dest) && (!IS_OPER(user)) && (numeric == 319) && dest->IsModeSet('I'));
+ }
+};
+
+
+MODULE_INIT(ModuleHideChans)
diff --git a/src/modules/m_hideoper.cpp b/src/modules/m_hideoper.cpp
index c2b472bad..9f547d77d 100644
--- a/src/modules/m_hideoper.cpp
+++ b/src/modules/m_hideoper.cpp
@@ -1 +1,94 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for hiding oper status with user mode +H */ /** Handles user mode +B */ class HideOper : public ModeHandler { public: HideOper(InspIRCd* Instance) : ModeHandler(Instance, 'H', 0, 0, false, MODETYPE_USER, true) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (source != dest) return MODEACTION_DENY; if (adding) { if (!dest->IsModeSet('H')) { dest->SetMode('H',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('H')) { dest->SetMode('H',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleHideOper : public Module { HideOper* hm; public: ModuleHideOper(InspIRCd* Me) : Module(Me) { hm = new HideOper(ServerInstance); if (!ServerInstance->AddMode(hm, 'H')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnWhoisLine] = 1; } virtual ~ModuleHideOper() { ServerInstance->Modes->DelMode(hm); DELETE(hm); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { /* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the * person doing the WHOIS is not an oper */ return ((!IS_OPER(user)) && (numeric == 313) && dest->IsModeSet('H')); } }; MODULE_INIT(ModuleHideOper) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for hiding oper status with user mode +H */
+
+/** Handles user mode +B
+ */
+class HideOper : public ModeHandler
+{
+ public:
+ HideOper(InspIRCd* Instance) : ModeHandler(Instance, 'H', 0, 0, false, MODETYPE_USER, true) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (source != dest)
+ return MODEACTION_DENY;
+
+ if (adding)
+ {
+ if (!dest->IsModeSet('H'))
+ {
+ dest->SetMode('H',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('H'))
+ {
+ dest->SetMode('H',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleHideOper : public Module
+{
+
+ HideOper* hm;
+ public:
+ ModuleHideOper(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ hm = new HideOper(ServerInstance);
+ if (!ServerInstance->AddMode(hm, 'H'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnWhoisLine] = 1;
+ }
+
+ virtual ~ModuleHideOper()
+ {
+ ServerInstance->Modes->DelMode(hm);
+ DELETE(hm);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+ int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text)
+ {
+ /* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
+ * person doing the WHOIS is not an oper
+ */
+ return ((!IS_OPER(user)) && (numeric == 313) && dest->IsModeSet('H'));
+ }
+};
+
+
+MODULE_INIT(ModuleHideOper)
diff --git a/src/modules/m_hostchange.cpp b/src/modules/m_hostchange.cpp
index f7ff58fa1..dc45a43d4 100644
--- a/src/modules/m_hostchange.cpp
+++ b/src/modules/m_hostchange.cpp
@@ -1 +1,148 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.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 : public classbase { public: std::string action; std::string newhost; }; typedef std::map<std::string,Host*> hostchanges_t; class ModuleHostChange : public Module { private: hostchanges_t hostchanges; std::string MySuffix; std::string MyPrefix; std::string MySeparator; public: ModuleHostChange(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); } virtual ~ModuleHostChange() { for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++) { DELETE(i->second); } hostchanges.clear(); } Priority Prioritize() { return (Priority)ServerInstance->PriorityAfter("m_cloaking.so"); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserConnect] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); MySuffix = Conf.ReadValue("host","suffix",0); MyPrefix = Conf.ReadValue("host","prefix","",0); MySeparator = Conf.ReadValue("host","separator",".",0); for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++) { DELETE(i->second); } hostchanges.clear(); for (int index = 0; index < Conf.Enumerate("hostchange"); index++) { std::string mask = Conf.ReadValue("hostchange","mask",index); std::string action = Conf.ReadValue("hostchange","action",index); std::string newhost = Conf.ReadValue("hostchange","value",index); Host* x = new Host; x->action = action; x->newhost = newhost; hostchanges[mask] = x; } } virtual Version GetVersion() { // returns the version number of the module to be // listed in /MODULES return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void OnUserConnect(userrec* user) { for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++) { if (ServerInstance->MatchText(std::string(user->ident)+"@"+std::string(user->host),i->first)) { Host* h = (Host*)i->second; // host of new user matches a hostchange tag's mask std::string newhost; if (h->action == "set") { newhost = h->newhost; } else if (h->action == "suffix") { newhost = MySuffix; } else if (h->action == "addnick") { // first take their nick and strip out non-dns, leaving just [A-Z0-9\-] std::string complete; std::string old = user->nick; for (unsigned int j = 0; j < old.length(); j++) { if (((old[j] >= 'A') && (old[j] <= 'Z')) || ((old[j] >= 'a') && (old[j] <= 'z')) || ((old[j] >= '0') && (old[j] <= '9')) || (old[j] == '-')) { complete = complete + old[j]; } } if (complete.empty()) complete = "i-have-a-lame-nick"; if (!MyPrefix.empty()) newhost = MyPrefix + MySeparator + complete; else newhost = complete + MySeparator + MySuffix; } if (!newhost.empty()) { user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your virtual host: " + newhost); if (!user->ChangeDisplayedHost(newhost.c_str())) user->WriteServ("NOTICE "+std::string(user->nick)+" :Could not set your virtual host: " + newhost); return; } } } } }; MODULE_INIT(ModuleHostChange) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.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 : public classbase
+{
+ public:
+ std::string action;
+ std::string newhost;
+};
+
+typedef std::map<std::string,Host*> hostchanges_t;
+
+class ModuleHostChange : public Module
+{
+ private:
+ hostchanges_t hostchanges;
+ std::string MySuffix;
+ std::string MyPrefix;
+ std::string MySeparator;
+
+ public:
+ ModuleHostChange(InspIRCd* Me)
+ : Module(Me)
+ {
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleHostChange()
+ {
+ for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
+ {
+ DELETE(i->second);
+ }
+ hostchanges.clear();
+ }
+
+ Priority Prioritize()
+ {
+ return (Priority)ServerInstance->PriorityAfter("m_cloaking.so");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserConnect] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+ MySuffix = Conf.ReadValue("host","suffix",0);
+ MyPrefix = Conf.ReadValue("host","prefix","",0);
+ MySeparator = Conf.ReadValue("host","separator",".",0);
+ for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
+ {
+ DELETE(i->second);
+ }
+ hostchanges.clear();
+ for (int index = 0; index < Conf.Enumerate("hostchange"); index++)
+ {
+ std::string mask = Conf.ReadValue("hostchange","mask",index);
+ std::string action = Conf.ReadValue("hostchange","action",index);
+ std::string newhost = Conf.ReadValue("hostchange","value",index);
+ Host* x = new Host;
+ x->action = action;
+ x->newhost = newhost;
+ hostchanges[mask] = x;
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ // returns the version number of the module to be
+ // listed in /MODULES
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnUserConnect(userrec* user)
+ {
+ for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
+ {
+ if (ServerInstance->MatchText(std::string(user->ident)+"@"+std::string(user->host),i->first))
+ {
+ Host* h = (Host*)i->second;
+ // host of new user matches a hostchange tag's mask
+ std::string newhost;
+ if (h->action == "set")
+ {
+ newhost = h->newhost;
+ }
+ else if (h->action == "suffix")
+ {
+ newhost = MySuffix;
+ }
+ else if (h->action == "addnick")
+ {
+ // first take their nick and strip out non-dns, leaving just [A-Z0-9\-]
+ std::string complete;
+ std::string old = user->nick;
+ for (unsigned int j = 0; j < old.length(); j++)
+ {
+ if (((old[j] >= 'A') && (old[j] <= 'Z')) ||
+ ((old[j] >= 'a') && (old[j] <= 'z')) ||
+ ((old[j] >= '0') && (old[j] <= '9')) ||
+ (old[j] == '-'))
+ {
+ complete = complete + old[j];
+ }
+ }
+ if (complete.empty())
+ complete = "i-have-a-lame-nick";
+
+ if (!MyPrefix.empty())
+ newhost = MyPrefix + MySeparator + complete;
+ else
+ newhost = complete + MySeparator + MySuffix;
+ }
+ if (!newhost.empty())
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your virtual host: " + newhost);
+ if (!user->ChangeDisplayedHost(newhost.c_str()))
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Could not set your virtual host: " + newhost);
+ return;
+ }
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleHostChange)
diff --git a/src/modules/m_http_client.cpp b/src/modules/m_http_client.cpp
index 3f9875caf..35b93b581 100644
--- a/src/modules/m_http_client.cpp
+++ b/src/modules/m_http_client.cpp
@@ -1 +1,346 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "httpclient.h" /* $ModDesc: HTTP client service provider */ class URL { public: std::string url; std::string protocol, username, password, domain, request; int port; }; class HTTPSocket : public InspSocket { private: InspIRCd *Server; class ModuleHTTPClient *Mod; HTTPClientRequest req; HTTPClientResponse *response; URL url; enum { HTTP_CLOSED, HTTP_REQSENT, HTTP_HEADERS, HTTP_DATA } status; std::string data; std::string buffer; public: HTTPSocket(InspIRCd *Instance, class ModuleHTTPClient *Mod); virtual ~HTTPSocket(); virtual bool DoRequest(HTTPClientRequest *req); virtual bool ParseURL(const std::string &url); virtual void Connect(const std::string &ip); virtual bool OnConnected(); virtual bool OnDataReady(); virtual void OnClose(); }; class HTTPResolver : public Resolver { private: HTTPSocket *socket; public: HTTPResolver(HTTPSocket *socket, InspIRCd *Instance, const string &hostname, bool &cached, Module* me) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), socket(socket) { } void OnLookupComplete(const string &result, unsigned int ttl, bool cached) { socket->Connect(result); } void OnError(ResolverError e, const string &errmsg) { delete socket; } }; typedef vector<HTTPSocket*> HTTPList; class ModuleHTTPClient : public Module { public: HTTPList sockets; ModuleHTTPClient(InspIRCd *Me) : Module(Me) { } virtual ~ModuleHTTPClient() { for (HTTPList::iterator i = sockets.begin(); i != sockets.end(); i++) delete *i; } virtual Version GetVersion() { return Version(1, 0, 0, 0, VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnRequest] = 1; } char* OnRequest(Request *req) { HTTPClientRequest *httpreq = (HTTPClientRequest *)req; if (!strcmp(httpreq->GetId(), HTTP_CLIENT_REQUEST)) { HTTPSocket *sock = new HTTPSocket(ServerInstance, this); sock->DoRequest(httpreq); // No return value } return NULL; } }; HTTPSocket::HTTPSocket(InspIRCd *Instance, ModuleHTTPClient *Mod) : InspSocket(Instance), Server(Instance), Mod(Mod), status(HTTP_CLOSED) { this->ClosePending = false; this->port = 80; } HTTPSocket::~HTTPSocket() { Close(); for (HTTPList::iterator i = Mod->sockets.begin(); i != Mod->sockets.end(); i++) { if (*i == this) { Mod->sockets.erase(i); break; } } } bool HTTPSocket::DoRequest(HTTPClientRequest *req) { /* Tweak by brain - we take a copy of this, * so that the caller doesnt need to leave * pointers knocking around, less chance of * a memory leak. */ this->req = *req; if (!ParseURL(this->req.GetURL())) return false; this->port = url.port; strlcpy(this->host, url.domain.c_str(), MAXBUF); in_addr addy1; #ifdef IPV6 in6_addr addy2; if ((inet_aton(this->host, &addy1) > 0) || (inet_pton(AF_INET6, this->host, &addy2) > 0)) #else if (inet_aton(this->host, &addy1) > 0) #endif { bool cached; HTTPResolver* r = new HTTPResolver(this, Server, url.domain, cached, (Module*)Mod); Instance->AddResolver(r, cached); return true; } else { this->Connect(url.domain); } return true; } bool HTTPSocket::ParseURL(const std::string &iurl) { url.url = iurl; url.port = 80; url.protocol = "http"; irc::sepstream tokenizer(iurl, '/'); for (int p = 0;; p++) { std::string part = tokenizer.GetToken(); if (part.empty() && tokenizer.StreamEnd()) break; if ((p == 0) && (part[part.length() - 1] == ':')) { // Protocol ('http:') url.protocol = part.substr(0, part.length() - 1); } else if ((p == 1) && (part.empty())) { continue; } else if (url.domain.empty()) { // Domain part: [user[:pass]@]domain[:port] std::string::size_type usrpos = part.find('@'); if (usrpos != std::string::npos) { // Have a user (and possibly password) part std::string::size_type ppos = part.find(':'); if ((ppos != std::string::npos) && (ppos < usrpos)) { // Have password too url.password = part.substr(ppos + 1, usrpos - ppos - 1); url.username = part.substr(0, ppos); } else { url.username = part.substr(0, usrpos); } part = part.substr(usrpos + 1); } std::string::size_type popos = part.rfind(':'); if (popos != std::string::npos) { url.port = atoi(part.substr(popos + 1).c_str()); url.domain = part.substr(0, popos); } else { url.domain = part; } } else { // Request (part of it).. url.request.append("/"); url.request.append(part); } } if (url.request.empty()) url.request = "/"; if ((url.domain.empty()) || (!url.port) || (url.protocol.empty())) { Instance->Log(DEFAULT, "Invalid URL (%s): Missing required value", iurl.c_str()); return false; } if (url.protocol != "http") { Instance->Log(DEFAULT, "Invalid URL (%s): Unsupported protocol '%s'", iurl.c_str(), url.protocol.c_str()); return false; } return true; } void HTTPSocket::Connect(const string &ip) { strlcpy(this->IP, ip.c_str(), MAXBUF); if (!this->DoConnect()) { delete this; } } bool HTTPSocket::OnConnected() { std::string request = "GET " + url.request + " HTTP/1.1\r\n"; // Dump headers into the request HeaderMap headers = req.GetHeaders(); for (HeaderMap::iterator i = headers.begin(); i != headers.end(); i++) request += i->first + ": " + i->second + "\r\n"; // The Host header is required for HTTP 1.1 and isn't known when the request is created; if they didn't overload it // manually, add it here if (headers.find("Host") == headers.end()) request += "Host: " + url.domain + "\r\n"; request += "\r\n"; this->status = HTTP_REQSENT; return this->Write(request); } bool HTTPSocket::OnDataReady() { char *data = this->Read(); if (!data) { this->Close(); return false; } if (this->status < HTTP_DATA) { std::string line; std::string::size_type pos; this->buffer += data; while ((pos = buffer.find("\r\n")) != std::string::npos) { line = buffer.substr(0, pos); buffer = buffer.substr(pos + 2); if (line.empty()) { this->status = HTTP_DATA; this->data += this->buffer; this->buffer.clear(); break; } if (this->status == HTTP_REQSENT) { // HTTP reply (HTTP/1.1 200 msg) char const* data = line.c_str(); data += 9; response = new HTTPClientResponse((Module*)Mod, req.GetSource() , url.url, atoi(data), data + 4); this->status = HTTP_HEADERS; continue; } if ((pos = line.find(':')) != std::string::npos) { response->AddHeader(line.substr(0, pos), line.substr(pos + 1)); } else { continue; } } } else { this->data += data; } return true; } void HTTPSocket::OnClose() { if (data.empty()) return; // notification that request failed? response->data = data; response->Send(); delete response; } MODULE_INIT(ModuleHTTPClient) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "httpclient.h"
+
+/* $ModDesc: HTTP client service provider */
+
+class URL
+{
+ public:
+ std::string url;
+ std::string protocol, username, password, domain, request;
+ int port;
+};
+
+class HTTPSocket : public InspSocket
+{
+ private:
+ InspIRCd *Server;
+ class ModuleHTTPClient *Mod;
+ HTTPClientRequest req;
+ HTTPClientResponse *response;
+ URL url;
+ enum { HTTP_CLOSED, HTTP_REQSENT, HTTP_HEADERS, HTTP_DATA } status;
+ std::string data;
+ std::string buffer;
+
+ public:
+ HTTPSocket(InspIRCd *Instance, class ModuleHTTPClient *Mod);
+ virtual ~HTTPSocket();
+ virtual bool DoRequest(HTTPClientRequest *req);
+ virtual bool ParseURL(const std::string &url);
+ virtual void Connect(const std::string &ip);
+ virtual bool OnConnected();
+ virtual bool OnDataReady();
+ virtual void OnClose();
+};
+
+class HTTPResolver : public Resolver
+{
+ private:
+ HTTPSocket *socket;
+ public:
+ HTTPResolver(HTTPSocket *socket, InspIRCd *Instance, const string &hostname, bool &cached, Module* me) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), socket(socket)
+ {
+ }
+
+ void OnLookupComplete(const string &result, unsigned int ttl, bool cached)
+ {
+ socket->Connect(result);
+ }
+
+ void OnError(ResolverError e, const string &errmsg)
+ {
+ delete socket;
+ }
+};
+
+typedef vector<HTTPSocket*> HTTPList;
+
+class ModuleHTTPClient : public Module
+{
+ public:
+ HTTPList sockets;
+
+ ModuleHTTPClient(InspIRCd *Me)
+ : Module(Me)
+ {
+ }
+
+ virtual ~ModuleHTTPClient()
+ {
+ for (HTTPList::iterator i = sockets.begin(); i != sockets.end(); i++)
+ delete *i;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 0, 0, 0, VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRequest] = 1;
+ }
+
+ char* OnRequest(Request *req)
+ {
+ HTTPClientRequest *httpreq = (HTTPClientRequest *)req;
+ if (!strcmp(httpreq->GetId(), HTTP_CLIENT_REQUEST))
+ {
+ HTTPSocket *sock = new HTTPSocket(ServerInstance, this);
+ sock->DoRequest(httpreq);
+ // No return value
+ }
+ return NULL;
+ }
+};
+
+HTTPSocket::HTTPSocket(InspIRCd *Instance, ModuleHTTPClient *Mod)
+ : InspSocket(Instance), Server(Instance), Mod(Mod), status(HTTP_CLOSED)
+{
+ this->ClosePending = false;
+ this->port = 80;
+}
+
+HTTPSocket::~HTTPSocket()
+{
+ Close();
+ for (HTTPList::iterator i = Mod->sockets.begin(); i != Mod->sockets.end(); i++)
+ {
+ if (*i == this)
+ {
+ Mod->sockets.erase(i);
+ break;
+ }
+ }
+}
+
+bool HTTPSocket::DoRequest(HTTPClientRequest *req)
+{
+ /* Tweak by brain - we take a copy of this,
+ * so that the caller doesnt need to leave
+ * pointers knocking around, less chance of
+ * a memory leak.
+ */
+ this->req = *req;
+
+ if (!ParseURL(this->req.GetURL()))
+ return false;
+
+ this->port = url.port;
+ strlcpy(this->host, url.domain.c_str(), MAXBUF);
+
+ in_addr addy1;
+#ifdef IPV6
+ in6_addr addy2;
+ if ((inet_aton(this->host, &addy1) > 0) || (inet_pton(AF_INET6, this->host, &addy2) > 0))
+#else
+ if (inet_aton(this->host, &addy1) > 0)
+#endif
+ {
+ bool cached;
+ HTTPResolver* r = new HTTPResolver(this, Server, url.domain, cached, (Module*)Mod);
+ Instance->AddResolver(r, cached);
+ return true;
+ }
+ else
+ {
+ this->Connect(url.domain);
+ }
+
+ return true;
+}
+
+bool HTTPSocket::ParseURL(const std::string &iurl)
+{
+ url.url = iurl;
+ url.port = 80;
+ url.protocol = "http";
+
+ irc::sepstream tokenizer(iurl, '/');
+
+ for (int p = 0;; p++)
+ {
+ std::string part = tokenizer.GetToken();
+ if (part.empty() && tokenizer.StreamEnd())
+ break;
+
+ if ((p == 0) && (part[part.length() - 1] == ':'))
+ {
+ // Protocol ('http:')
+ url.protocol = part.substr(0, part.length() - 1);
+ }
+ else if ((p == 1) && (part.empty()))
+ {
+ continue;
+ }
+ else if (url.domain.empty())
+ {
+ // Domain part: [user[:pass]@]domain[:port]
+ std::string::size_type usrpos = part.find('@');
+ if (usrpos != std::string::npos)
+ {
+ // Have a user (and possibly password) part
+ std::string::size_type ppos = part.find(':');
+ if ((ppos != std::string::npos) && (ppos < usrpos))
+ {
+ // Have password too
+ url.password = part.substr(ppos + 1, usrpos - ppos - 1);
+ url.username = part.substr(0, ppos);
+ }
+ else
+ {
+ url.username = part.substr(0, usrpos);
+ }
+
+ part = part.substr(usrpos + 1);
+ }
+
+ std::string::size_type popos = part.rfind(':');
+ if (popos != std::string::npos)
+ {
+ url.port = atoi(part.substr(popos + 1).c_str());
+ url.domain = part.substr(0, popos);
+ }
+ else
+ {
+ url.domain = part;
+ }
+ }
+ else
+ {
+ // Request (part of it)..
+ url.request.append("/");
+ url.request.append(part);
+ }
+ }
+
+ if (url.request.empty())
+ url.request = "/";
+
+ if ((url.domain.empty()) || (!url.port) || (url.protocol.empty()))
+ {
+ Instance->Log(DEFAULT, "Invalid URL (%s): Missing required value", iurl.c_str());
+ return false;
+ }
+
+ if (url.protocol != "http")
+ {
+ Instance->Log(DEFAULT, "Invalid URL (%s): Unsupported protocol '%s'", iurl.c_str(), url.protocol.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+void HTTPSocket::Connect(const string &ip)
+{
+ strlcpy(this->IP, ip.c_str(), MAXBUF);
+
+ if (!this->DoConnect())
+ {
+ delete this;
+ }
+}
+
+bool HTTPSocket::OnConnected()
+{
+ std::string request = "GET " + url.request + " HTTP/1.1\r\n";
+
+ // Dump headers into the request
+ HeaderMap headers = req.GetHeaders();
+
+ for (HeaderMap::iterator i = headers.begin(); i != headers.end(); i++)
+ request += i->first + ": " + i->second + "\r\n";
+
+ // The Host header is required for HTTP 1.1 and isn't known when the request is created; if they didn't overload it
+ // manually, add it here
+ if (headers.find("Host") == headers.end())
+ request += "Host: " + url.domain + "\r\n";
+
+ request += "\r\n";
+
+ this->status = HTTP_REQSENT;
+
+ return this->Write(request);
+}
+
+bool HTTPSocket::OnDataReady()
+{
+ char *data = this->Read();
+
+ if (!data)
+ {
+ this->Close();
+ return false;
+ }
+
+ if (this->status < HTTP_DATA)
+ {
+ std::string line;
+ std::string::size_type pos;
+
+ this->buffer += data;
+ while ((pos = buffer.find("\r\n")) != std::string::npos)
+ {
+ line = buffer.substr(0, pos);
+ buffer = buffer.substr(pos + 2);
+ if (line.empty())
+ {
+ this->status = HTTP_DATA;
+ this->data += this->buffer;
+ this->buffer.clear();
+ break;
+ }
+
+ if (this->status == HTTP_REQSENT)
+ {
+ // HTTP reply (HTTP/1.1 200 msg)
+ char const* data = line.c_str();
+ data += 9;
+ response = new HTTPClientResponse((Module*)Mod, req.GetSource() , url.url, atoi(data), data + 4);
+ this->status = HTTP_HEADERS;
+ continue;
+ }
+
+ if ((pos = line.find(':')) != std::string::npos)
+ {
+ response->AddHeader(line.substr(0, pos), line.substr(pos + 1));
+ }
+ else
+ {
+ continue;
+ }
+ }
+ }
+ else
+ {
+ this->data += data;
+ }
+ return true;
+}
+
+void HTTPSocket::OnClose()
+{
+ if (data.empty())
+ return; // notification that request failed?
+
+ response->data = data;
+ response->Send();
+ delete response;
+}
+
+MODULE_INIT(ModuleHTTPClient)
diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp
index 6ff80ad80..8494863a3 100644
--- a/src/modules/m_httpd.cpp
+++ b/src/modules/m_httpd.cpp
@@ -1 +1,419 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <algorithm> #include "modules.h" #include "httpd.h" /* $ModDesc: Provides HTTP serving facilities to modules */ class ModuleHttpServer; static ModuleHttpServer* HttpModule; static bool claimed; /** HTTP socket states */ enum HttpState { HTTP_LISTEN = 0, HTTP_SERVE_WAIT_REQUEST = 1, HTTP_SERVE_RECV_POSTDATA = 2, HTTP_SERVE_SEND_DATA = 3 }; class HttpServerSocket; /** This class is used to handle HTTP socket timeouts */ class HttpServerTimeout : public InspTimer { private: /** HttpServerSocket we are attached to */ HttpServerSocket* s; /** Socketengine the file descriptor is in */ SocketEngine* SE; public: /** Attach timeout to HttpServerSocket */ HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine); /** Handle timer tick */ void Tick(time_t TIME); }; /** A socket used for HTTP transport */ class HttpServerSocket : public InspSocket { FileReader* index; HttpState InternalState; std::stringstream headers; std::string postdata; std::string request_type; std::string uri; std::string http_version; unsigned int postsize; HttpServerTimeout* Timeout; public: HttpServerSocket(InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, FileReader* index_page) : InspSocket(SI, host, port, listening, maxtime), index(index_page), postsize(0) { InternalState = HTTP_LISTEN; Timeout = NULL; } HttpServerSocket(InspIRCd* SI, int newfd, char* ip, FileReader* ind) : InspSocket(SI, newfd, ip), index(ind), postsize(0) { InternalState = HTTP_SERVE_WAIT_REQUEST; Timeout = new HttpServerTimeout(this, Instance->SE); Instance->Timers->AddTimer(Timeout); } FileReader* GetIndex() { return index; } ~HttpServerSocket() { if (Timeout) { if (Instance->Time() < Timeout->GetTimer()) Instance->Timers->DelTimer(Timeout); Timeout = NULL; } } virtual int OnIncomingConnection(int newsock, char* ip) { if (InternalState == HTTP_LISTEN) { HttpServerSocket* s = new HttpServerSocket(this->Instance, newsock, ip, index); s = s; /* Stop GCC whining */ } return true; } virtual void OnClose() { } std::string Response(int response) { switch (response) { case 100: return "CONTINUE"; case 101: return "SWITCHING PROTOCOLS"; case 200: return "OK"; case 201: return "CREATED"; case 202: return "ACCEPTED"; case 203: return "NON-AUTHORITATIVE INFORMATION"; case 204: return "NO CONTENT"; case 205: return "RESET CONTENT"; case 206: return "PARTIAL CONTENT"; case 300: return "MULTIPLE CHOICES"; case 301: return "MOVED PERMENANTLY"; case 302: return "FOUND"; case 303: return "SEE OTHER"; case 304: return "NOT MODIFIED"; case 305: return "USE PROXY"; case 307: return "TEMPORARY REDIRECT"; case 400: return "BAD REQUEST"; case 401: return "UNAUTHORIZED"; case 402: return "PAYMENT REQUIRED"; case 403: return "FORBIDDEN"; case 404: return "NOT FOUND"; case 405: return "METHOD NOT ALLOWED"; case 406: return "NOT ACCEPTABLE"; case 407: return "PROXY AUTHENTICATION REQUIRED"; case 408: return "REQUEST TIMEOUT"; case 409: return "CONFLICT"; case 410: return "GONE"; case 411: return "LENGTH REQUIRED"; case 412: return "PRECONDITION FAILED"; case 413: return "REQUEST ENTITY TOO LARGE"; case 414: return "REQUEST-URI TOO LONG"; case 415: return "UNSUPPORTED MEDIA TYPE"; case 416: return "REQUESTED RANGE NOT SATISFIABLE"; case 417: return "EXPECTATION FAILED"; case 500: return "INTERNAL SERVER ERROR"; case 501: return "NOT IMPLEMENTED"; case 502: return "BAD GATEWAY"; case 503: return "SERVICE UNAVAILABLE"; case 504: return "GATEWAY TIMEOUT"; case 505: return "HTTP VERSION NOT SUPPORTED"; default: return "WTF"; break; } } void SendHeaders(unsigned long size, int response, const std::string &extraheaders) { time_t local = this->Instance->Time(); struct tm *timeinfo = gmtime(&local); this->Write("HTTP/1.1 "+ConvToStr(response)+" "+Response(response)+"\r\nDate: "); this->Write(asctime(timeinfo)); if (extraheaders.empty()) { this->Write("Content-Type: text/html\r\n"); } else { this->Write(extraheaders); } this->Write("Server: InspIRCd/m_httpd.so/1.1\r\nContent-Length: "+ConvToStr(size)+ "\r\nConnection: close\r\n\r\n"); } virtual bool OnDataReady() { char* data = this->Read(); /* Check that the data read is a valid pointer and it has some content */ if (data && *data) { headers << data; if (headers.str().find("\r\n\r\n") != std::string::npos) { if (request_type.empty()) { headers >> request_type; headers >> uri; headers >> http_version; std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper); std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper); } if ((InternalState == HTTP_SERVE_WAIT_REQUEST) && (request_type == "POST")) { /* Do we need to fetch postdata? */ postdata.clear(); InternalState = HTTP_SERVE_RECV_POSTDATA; std::string header_item; while (headers >> header_item) { if (header_item == "Content-Length:") { headers >> header_item; postsize = atoi(header_item.c_str()); } } if (!postsize) { InternalState = HTTP_SERVE_SEND_DATA; SendHeaders(0, 400, ""); Timeout = new HttpServerTimeout(this, Instance->SE); Instance->Timers->AddTimer(Timeout); } else { std::string::size_type x = headers.str().find("\r\n\r\n"); postdata = headers.str().substr(x+4, headers.str().length()); /* Get content length and store */ if (postdata.length() >= postsize) ServeData(); } } else if (InternalState == HTTP_SERVE_RECV_POSTDATA) { /* Add postdata, once we have it all, send the event */ postdata.append(data); if (postdata.length() >= postsize) ServeData(); } else { ServeData(); } return true; } return true; } else { return false; } } void ServeData() { /* Headers are complete */ InternalState = HTTP_SERVE_SEND_DATA; Instance->Timers->DelTimer(Timeout); Timeout = NULL; if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0")) { SendHeaders(0, 505, ""); } else { if ((request_type == "GET") && (uri == "/")) { SendHeaders(index->ContentSize(), 200, ""); this->Write(index->Contents()); } else { claimed = false; HTTPRequest httpr(request_type,uri,&headers,this,this->GetIP(),postdata); Event e((char*)&httpr, (Module*)HttpModule, "httpd_url"); e.Send(this->Instance); if (!claimed) { SendHeaders(0, 404, ""); } } } Timeout = new HttpServerTimeout(this, Instance->SE); Instance->Timers->AddTimer(Timeout); } void Page(std::stringstream* n, int response, std::string& extraheaders) { SendHeaders(n->str().length(), response, extraheaders); this->Write(n->str()); } }; HttpServerTimeout::HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine) : InspTimer(60, time(NULL)), s(sock), SE(engine) { } void HttpServerTimeout::Tick(time_t TIME) { SE->DelFd(s); s->Close(); } class ModuleHttpServer : public Module { std::vector<HttpServerSocket*> httpsocks; public: void ReadConfig() { int port; std::string host; std::string bindip; std::string indexfile; FileReader* index; HttpServerSocket* http; ConfigReader c(ServerInstance); httpsocks.clear(); for (int i = 0; i < c.Enumerate("http"); i++) { host = c.ReadValue("http", "host", i); bindip = c.ReadValue("http", "ip", i); port = c.ReadInteger("http", "port", i, true); indexfile = c.ReadValue("http", "index", i); index = new FileReader(ServerInstance, indexfile); if (!index->Exists()) throw ModuleException("Can't read index file: "+indexfile); http = new HttpServerSocket(ServerInstance, bindip, port, true, 0, index); httpsocks.push_back(http); } } ModuleHttpServer(InspIRCd* Me) : Module(Me) { ReadConfig(); } void OnEvent(Event* event) { } char* OnRequest(Request* request) { claimed = true; HTTPDocument* doc = (HTTPDocument*)request->GetData(); HttpServerSocket* sock = (HttpServerSocket*)doc->sock; sock->Page(doc->GetDocument(), doc->GetResponseCode(), doc->GetExtraHeaders()); return NULL; } void Implements(char* List) { List[I_OnEvent] = List[I_OnRequest] = 1; } virtual ~ModuleHttpServer() { for (size_t i = 0; i < httpsocks.size(); i++) { ServerInstance->SE->DelFd(httpsocks[i]); delete httpsocks[i]->GetIndex(); delete httpsocks[i]; } } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); } }; MODULE_INIT(ModuleHttpServer) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <algorithm>
+#include "modules.h"
+#include "httpd.h"
+
+/* $ModDesc: Provides HTTP serving facilities to modules */
+
+class ModuleHttpServer;
+
+static ModuleHttpServer* HttpModule;
+static bool claimed;
+
+/** HTTP socket states
+ */
+enum HttpState
+{
+ HTTP_LISTEN = 0,
+ HTTP_SERVE_WAIT_REQUEST = 1,
+ HTTP_SERVE_RECV_POSTDATA = 2,
+ HTTP_SERVE_SEND_DATA = 3
+};
+
+class HttpServerSocket;
+
+/** This class is used to handle HTTP socket timeouts
+ */
+class HttpServerTimeout : public InspTimer
+{
+ private:
+ /** HttpServerSocket we are attached to
+ */
+ HttpServerSocket* s;
+ /** Socketengine the file descriptor is in
+ */
+ SocketEngine* SE;
+ public:
+ /** Attach timeout to HttpServerSocket
+ */
+ HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine);
+ /** Handle timer tick
+ */
+ void Tick(time_t TIME);
+};
+
+/** A socket used for HTTP transport
+ */
+class HttpServerSocket : public InspSocket
+{
+ FileReader* index;
+ HttpState InternalState;
+ std::stringstream headers;
+ std::string postdata;
+ std::string request_type;
+ std::string uri;
+ std::string http_version;
+ unsigned int postsize;
+ HttpServerTimeout* Timeout;
+
+ public:
+
+ HttpServerSocket(InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, FileReader* index_page) : InspSocket(SI, host, port, listening, maxtime), index(index_page), postsize(0)
+ {
+ InternalState = HTTP_LISTEN;
+ Timeout = NULL;
+ }
+
+ HttpServerSocket(InspIRCd* SI, int newfd, char* ip, FileReader* ind) : InspSocket(SI, newfd, ip), index(ind), postsize(0)
+ {
+ InternalState = HTTP_SERVE_WAIT_REQUEST;
+ Timeout = new HttpServerTimeout(this, Instance->SE);
+ Instance->Timers->AddTimer(Timeout);
+ }
+
+ FileReader* GetIndex()
+ {
+ return index;
+ }
+
+ ~HttpServerSocket()
+ {
+ if (Timeout)
+ {
+ if (Instance->Time() < Timeout->GetTimer())
+ Instance->Timers->DelTimer(Timeout);
+ Timeout = NULL;
+ }
+ }
+
+ virtual int OnIncomingConnection(int newsock, char* ip)
+ {
+ if (InternalState == HTTP_LISTEN)
+ {
+ HttpServerSocket* s = new HttpServerSocket(this->Instance, newsock, ip, index);
+ s = s; /* Stop GCC whining */
+ }
+ return true;
+ }
+
+ virtual void OnClose()
+ {
+ }
+
+ std::string Response(int response)
+ {
+ switch (response)
+ {
+ case 100:
+ return "CONTINUE";
+ case 101:
+ return "SWITCHING PROTOCOLS";
+ case 200:
+ return "OK";
+ case 201:
+ return "CREATED";
+ case 202:
+ return "ACCEPTED";
+ case 203:
+ return "NON-AUTHORITATIVE INFORMATION";
+ case 204:
+ return "NO CONTENT";
+ case 205:
+ return "RESET CONTENT";
+ case 206:
+ return "PARTIAL CONTENT";
+ case 300:
+ return "MULTIPLE CHOICES";
+ case 301:
+ return "MOVED PERMENANTLY";
+ case 302:
+ return "FOUND";
+ case 303:
+ return "SEE OTHER";
+ case 304:
+ return "NOT MODIFIED";
+ case 305:
+ return "USE PROXY";
+ case 307:
+ return "TEMPORARY REDIRECT";
+ case 400:
+ return "BAD REQUEST";
+ case 401:
+ return "UNAUTHORIZED";
+ case 402:
+ return "PAYMENT REQUIRED";
+ case 403:
+ return "FORBIDDEN";
+ case 404:
+ return "NOT FOUND";
+ case 405:
+ return "METHOD NOT ALLOWED";
+ case 406:
+ return "NOT ACCEPTABLE";
+ case 407:
+ return "PROXY AUTHENTICATION REQUIRED";
+ case 408:
+ return "REQUEST TIMEOUT";
+ case 409:
+ return "CONFLICT";
+ case 410:
+ return "GONE";
+ case 411:
+ return "LENGTH REQUIRED";
+ case 412:
+ return "PRECONDITION FAILED";
+ case 413:
+ return "REQUEST ENTITY TOO LARGE";
+ case 414:
+ return "REQUEST-URI TOO LONG";
+ case 415:
+ return "UNSUPPORTED MEDIA TYPE";
+ case 416:
+ return "REQUESTED RANGE NOT SATISFIABLE";
+ case 417:
+ return "EXPECTATION FAILED";
+ case 500:
+ return "INTERNAL SERVER ERROR";
+ case 501:
+ return "NOT IMPLEMENTED";
+ case 502:
+ return "BAD GATEWAY";
+ case 503:
+ return "SERVICE UNAVAILABLE";
+ case 504:
+ return "GATEWAY TIMEOUT";
+ case 505:
+ return "HTTP VERSION NOT SUPPORTED";
+ default:
+ return "WTF";
+ break;
+
+ }
+ }
+
+ void SendHeaders(unsigned long size, int response, const std::string &extraheaders)
+ {
+ time_t local = this->Instance->Time();
+ struct tm *timeinfo = gmtime(&local);
+ this->Write("HTTP/1.1 "+ConvToStr(response)+" "+Response(response)+"\r\nDate: ");
+ this->Write(asctime(timeinfo));
+ if (extraheaders.empty())
+ {
+ this->Write("Content-Type: text/html\r\n");
+ }
+ else
+ {
+ this->Write(extraheaders);
+ }
+ this->Write("Server: InspIRCd/m_httpd.so/1.1\r\nContent-Length: "+ConvToStr(size)+
+ "\r\nConnection: close\r\n\r\n");
+ }
+
+ virtual bool OnDataReady()
+ {
+ char* data = this->Read();
+
+ /* Check that the data read is a valid pointer and it has some content */
+ if (data && *data)
+ {
+ headers << data;
+
+ if (headers.str().find("\r\n\r\n") != std::string::npos)
+ {
+ if (request_type.empty())
+ {
+ headers >> request_type;
+ headers >> uri;
+ headers >> http_version;
+
+ std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
+ std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
+ }
+
+ if ((InternalState == HTTP_SERVE_WAIT_REQUEST) && (request_type == "POST"))
+ {
+ /* Do we need to fetch postdata? */
+ postdata.clear();
+ InternalState = HTTP_SERVE_RECV_POSTDATA;
+ std::string header_item;
+ while (headers >> header_item)
+ {
+ if (header_item == "Content-Length:")
+ {
+ headers >> header_item;
+ postsize = atoi(header_item.c_str());
+ }
+ }
+ if (!postsize)
+ {
+ InternalState = HTTP_SERVE_SEND_DATA;
+ SendHeaders(0, 400, "");
+ Timeout = new HttpServerTimeout(this, Instance->SE);
+ Instance->Timers->AddTimer(Timeout);
+ }
+ else
+ {
+ std::string::size_type x = headers.str().find("\r\n\r\n");
+ postdata = headers.str().substr(x+4, headers.str().length());
+ /* Get content length and store */
+ if (postdata.length() >= postsize)
+ ServeData();
+ }
+ }
+ else if (InternalState == HTTP_SERVE_RECV_POSTDATA)
+ {
+ /* Add postdata, once we have it all, send the event */
+ postdata.append(data);
+ if (postdata.length() >= postsize)
+ ServeData();
+ }
+ else
+ {
+ ServeData();
+ }
+ return true;
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ void ServeData()
+ {
+ /* Headers are complete */
+ InternalState = HTTP_SERVE_SEND_DATA;
+
+ Instance->Timers->DelTimer(Timeout);
+ Timeout = NULL;
+
+ if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
+ {
+ SendHeaders(0, 505, "");
+ }
+ else
+ {
+ if ((request_type == "GET") && (uri == "/"))
+ {
+ SendHeaders(index->ContentSize(), 200, "");
+ this->Write(index->Contents());
+ }
+ else
+ {
+ claimed = false;
+ HTTPRequest httpr(request_type,uri,&headers,this,this->GetIP(),postdata);
+ Event e((char*)&httpr, (Module*)HttpModule, "httpd_url");
+ e.Send(this->Instance);
+ if (!claimed)
+ {
+ SendHeaders(0, 404, "");
+ }
+ }
+ }
+ Timeout = new HttpServerTimeout(this, Instance->SE);
+ Instance->Timers->AddTimer(Timeout);
+ }
+
+ void Page(std::stringstream* n, int response, std::string& extraheaders)
+ {
+ SendHeaders(n->str().length(), response, extraheaders);
+ this->Write(n->str());
+ }
+};
+
+HttpServerTimeout::HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine) : InspTimer(60, time(NULL)), s(sock), SE(engine)
+{
+}
+
+void HttpServerTimeout::Tick(time_t TIME)
+{
+ SE->DelFd(s);
+ s->Close();
+}
+
+class ModuleHttpServer : public Module
+{
+ std::vector<HttpServerSocket*> httpsocks;
+ public:
+
+ void ReadConfig()
+ {
+ int port;
+ std::string host;
+ std::string bindip;
+ std::string indexfile;
+ FileReader* index;
+ HttpServerSocket* http;
+ ConfigReader c(ServerInstance);
+
+ httpsocks.clear();
+
+ for (int i = 0; i < c.Enumerate("http"); i++)
+ {
+ host = c.ReadValue("http", "host", i);
+ bindip = c.ReadValue("http", "ip", i);
+ port = c.ReadInteger("http", "port", i, true);
+ indexfile = c.ReadValue("http", "index", i);
+ index = new FileReader(ServerInstance, indexfile);
+ if (!index->Exists())
+ throw ModuleException("Can't read index file: "+indexfile);
+ http = new HttpServerSocket(ServerInstance, bindip, port, true, 0, index);
+ httpsocks.push_back(http);
+ }
+ }
+
+ ModuleHttpServer(InspIRCd* Me) : Module(Me)
+ {
+ ReadConfig();
+ }
+
+ void OnEvent(Event* event)
+ {
+ }
+
+ char* OnRequest(Request* request)
+ {
+ claimed = true;
+ HTTPDocument* doc = (HTTPDocument*)request->GetData();
+ HttpServerSocket* sock = (HttpServerSocket*)doc->sock;
+ sock->Page(doc->GetDocument(), doc->GetResponseCode(), doc->GetExtraHeaders());
+ return NULL;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnEvent] = List[I_OnRequest] = 1;
+ }
+
+ virtual ~ModuleHttpServer()
+ {
+ for (size_t i = 0; i < httpsocks.size(); i++)
+ {
+ ServerInstance->SE->DelFd(httpsocks[i]);
+ delete httpsocks[i]->GetIndex();
+ delete httpsocks[i];
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleHttpServer)
diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp
index 49b5bbab5..5c29123f8 100644
--- a/src/modules/m_httpd_stats.cpp
+++ b/src/modules/m_httpd_stats.cpp
@@ -1 +1,241 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "configreader.h" #include "modules.h" #include "inspsocket.h" #include "httpd.h" /* $ModDesc: Provides statistics over HTTP via m_httpd.so */ typedef std::map<irc::string,int> StatsHash; typedef StatsHash::iterator StatsIter; typedef std::vector<std::pair<int,irc::string> > SortedList; typedef SortedList::iterator SortedIter; static StatsHash* sh = new StatsHash(); static SortedList* so = new SortedList(); class ModuleHttpStats : public Module { std::string stylesheet; bool changed; public: void ReadConfig() { ConfigReader c(ServerInstance); this->stylesheet = c.ReadValue("httpstats", "stylesheet", 0); } ModuleHttpStats(InspIRCd* Me) : Module(Me) { ReadConfig(); this->changed = false; } void InsertOrder(irc::string channel, int count) { /* This function figures out where in the sorted list to put an item from the hash */ SortedIter a; for (a = so->begin(); a != so->end(); a++) { /* Found an item equal to or less than, we insert our item before it */ if (a->first <= count) { so->insert(a,std::pair<int,irc::string>(count,channel)); return; } } /* There are no items in the list yet, insert something at the beginning */ so->insert(so->begin(), std::pair<int,irc::string>(count,channel)); } void SortList() { /* Sorts the hash into the sorted list using an insertion sort */ so->clear(); for (StatsIter a = sh->begin(); a != sh->end(); a++) InsertOrder(a->first, a->second); this->changed = false; } void OnEvent(Event* event) { std::stringstream data(""); if (event->GetEventID() == "httpd_url") { HTTPRequest* http = (HTTPRequest*)event->GetData(); if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/")) { data << "<!DOCTYPE html PUBLIC \ \"-//W3C//DTD XHTML 1.1//EN\" \ \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\ <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">"; data << "<head>"; data << "<link rel='stylesheet' href='" << this->stylesheet << "' type='text/css' />"; data << "<title>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</title>"; data << "</head><body>"; data << "<h1>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</h1>"; data << "<div class='totals'>"; data << "<h2>Totals</h2>"; data << "<table>"; data << "<tr><td>Users</td><td>" << ServerInstance->clientlist->size() << "</td></tr>"; data << "<tr><td>Channels</td><td>" << ServerInstance->chanlist->size() << "</td></tr>"; data << "<tr><td>Opers</td><td>" << ServerInstance->all_opers.size() << "</td></tr>"; data << "<tr><td>Sockets</td><td>" << (ServerInstance->SE->GetMaxFds() - ServerInstance->SE->GetRemainingFds()) << " (Max: " << ServerInstance->SE->GetMaxFds() << " via socket engine '" << ServerInstance->SE->GetName() << "')</td></tr>"; data << "</table>"; data << "</div>"; data << "<div class='modules'>"; data << "<h2>Modules</h2>"; data << "<table>"; for (int i = 0; i <= ServerInstance->GetModuleCount(); i++) { if (!ServerInstance->Config->module_names[i].empty()) data << "<tr><td>" << ServerInstance->Config->module_names[i] << "</td></tr>"; } data << "</table>"; data << "</div>"; data << "<div class='channels'>"; data << "<h2>Channels</h2>"; data << "<table>"; data << "<tr><th>Users</th><th>Name</th><th>@</th><th>%</th><th>+</th><th>Topic</th></tr>"; /* If the list has changed since last time it was displayed, re-sort it * this time only (not every time, as this would be moronic) */ if (this->changed) this->SortList(); int n = 0; for (SortedIter a = so->begin(); ((a != so->end()) && (n < 25)); a++, n++) { chanrec* c = ServerInstance->FindChan(a->second.c_str()); if (c) { data << "<tr><td>" << a->first << "</td><td>" << a->second << "</td>"; data << "<td>" << c->GetOppedUsers()->size() << "</td>"; data << "<td>" << c->GetHalfoppedUsers()->size() << "</td>"; data << "<td>" << c->GetVoicedUsers()->size() << "</td>"; data << "<td>" << c->topic << "</td>"; data << "</tr>"; } } data << "</table>"; data << "</div>"; data << "<div class='validion'>"; data << "<p><a href='http://validator.w3.org/check?uri=referer'><img src='http://www.w3.org/Icons/valid-xhtml11' alt='Valid XHTML 1.1' height='31' width='88' /></a></p>"; data << "</div>"; data << "</body>"; data << "</html>"; /* Send the document back to m_httpd */ HTTPDocument response(http->sock, &data, 200, "X-Powered-By: m_http_stats.so\r\nContent-Type: text/html; charset=iso-8859-1\r\n"); Request req((char*)&response, (Module*)this, event->GetSource()); req.Send(); } } } void OnChannelDelete(chanrec* chan) { StatsIter a = sh->find(chan->name); if (a != sh->end()) { sh->erase(a); } this->changed = true; } void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { StatsIter a = sh->find(channel->name); if (a != sh->end()) { a->second++; } else { irc::string name = channel->name; sh->insert(std::pair<irc::string,int>(name,1)); } this->changed = true; } void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { StatsIter a = sh->find(channel->name); if (a != sh->end()) { a->second--; } this->changed = true; } void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) { for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++) { chanrec* c = v->first; StatsIter a = sh->find(c->name); if (a != sh->end()) { a->second--; } } this->changed = true; } char* OnRequest(Request* request) { return NULL; } void Implements(char* List) { List[I_OnEvent] = List[I_OnRequest] = List[I_OnChannelDelete] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = 1; } virtual ~ModuleHttpStats() { delete sh; delete so; } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleHttpStats) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "configreader.h"
+#include "modules.h"
+#include "inspsocket.h"
+#include "httpd.h"
+
+/* $ModDesc: Provides statistics over HTTP via m_httpd.so */
+
+typedef std::map<irc::string,int> StatsHash;
+typedef StatsHash::iterator StatsIter;
+
+typedef std::vector<std::pair<int,irc::string> > SortedList;
+typedef SortedList::iterator SortedIter;
+
+static StatsHash* sh = new StatsHash();
+static SortedList* so = new SortedList();
+
+class ModuleHttpStats : public Module
+{
+
+ std::string stylesheet;
+ bool changed;
+
+ public:
+
+ void ReadConfig()
+ {
+ ConfigReader c(ServerInstance);
+ this->stylesheet = c.ReadValue("httpstats", "stylesheet", 0);
+ }
+
+ ModuleHttpStats(InspIRCd* Me) : Module(Me)
+ {
+
+ ReadConfig();
+ this->changed = false;
+ }
+
+ void InsertOrder(irc::string channel, int count)
+ {
+ /* This function figures out where in the sorted list to put an item from the hash */
+ SortedIter a;
+ for (a = so->begin(); a != so->end(); a++)
+ {
+ /* Found an item equal to or less than, we insert our item before it */
+ if (a->first <= count)
+ {
+ so->insert(a,std::pair<int,irc::string>(count,channel));
+ return;
+ }
+ }
+ /* There are no items in the list yet, insert something at the beginning */
+ so->insert(so->begin(), std::pair<int,irc::string>(count,channel));
+ }
+
+ void SortList()
+ {
+ /* Sorts the hash into the sorted list using an insertion sort */
+ so->clear();
+ for (StatsIter a = sh->begin(); a != sh->end(); a++)
+ InsertOrder(a->first, a->second);
+ this->changed = false;
+ }
+
+ void OnEvent(Event* event)
+ {
+ std::stringstream data("");
+
+ if (event->GetEventID() == "httpd_url")
+ {
+ HTTPRequest* http = (HTTPRequest*)event->GetData();
+
+ if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
+ {
+ data << "<!DOCTYPE html PUBLIC \
+ \"-//W3C//DTD XHTML 1.1//EN\" \
+ \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\
+ <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">";
+
+ data << "<head>";
+ data << "<link rel='stylesheet' href='" << this->stylesheet << "' type='text/css' />";
+ data << "<title>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</title>";
+ data << "</head><body>";
+ data << "<h1>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</h1>";
+
+ data << "<div class='totals'>";
+ data << "<h2>Totals</h2>";
+ data << "<table>";
+ data << "<tr><td>Users</td><td>" << ServerInstance->clientlist->size() << "</td></tr>";
+ data << "<tr><td>Channels</td><td>" << ServerInstance->chanlist->size() << "</td></tr>";
+ data << "<tr><td>Opers</td><td>" << ServerInstance->all_opers.size() << "</td></tr>";
+ data << "<tr><td>Sockets</td><td>" << (ServerInstance->SE->GetMaxFds() - ServerInstance->SE->GetRemainingFds()) << " (Max: " << ServerInstance->SE->GetMaxFds() << " via socket engine '" << ServerInstance->SE->GetName() << "')</td></tr>";
+ data << "</table>";
+ data << "</div>";
+
+ data << "<div class='modules'>";
+ data << "<h2>Modules</h2>";
+ data << "<table>";
+ for (int i = 0; i <= ServerInstance->GetModuleCount(); i++)
+ {
+ if (!ServerInstance->Config->module_names[i].empty())
+ data << "<tr><td>" << ServerInstance->Config->module_names[i] << "</td></tr>";
+ }
+ data << "</table>";
+ data << "</div>";
+
+ data << "<div class='channels'>";
+ data << "<h2>Channels</h2>";
+ data << "<table>";
+ data << "<tr><th>Users</th><th>Name</th><th>@</th><th>%</th><th>+</th><th>Topic</th></tr>";
+
+ /* If the list has changed since last time it was displayed, re-sort it
+ * this time only (not every time, as this would be moronic)
+ */
+ if (this->changed)
+ this->SortList();
+
+ int n = 0;
+ for (SortedIter a = so->begin(); ((a != so->end()) && (n < 25)); a++, n++)
+ {
+ chanrec* c = ServerInstance->FindChan(a->second.c_str());
+ if (c)
+ {
+ data << "<tr><td>" << a->first << "</td><td>" << a->second << "</td>";
+ data << "<td>" << c->GetOppedUsers()->size() << "</td>";
+ data << "<td>" << c->GetHalfoppedUsers()->size() << "</td>";
+ data << "<td>" << c->GetVoicedUsers()->size() << "</td>";
+ data << "<td>" << c->topic << "</td>";
+ data << "</tr>";
+ }
+ }
+
+ data << "</table>";
+ data << "</div>";
+
+
+
+
+
+ data << "<div class='validion'>";
+ data << "<p><a href='http://validator.w3.org/check?uri=referer'><img src='http://www.w3.org/Icons/valid-xhtml11' alt='Valid XHTML 1.1' height='31' width='88' /></a></p>";
+ data << "</div>";
+
+ data << "</body>";
+ data << "</html>";
+
+ /* Send the document back to m_httpd */
+ HTTPDocument response(http->sock, &data, 200, "X-Powered-By: m_http_stats.so\r\nContent-Type: text/html; charset=iso-8859-1\r\n");
+ Request req((char*)&response, (Module*)this, event->GetSource());
+ req.Send();
+ }
+ }
+ }
+
+ void OnChannelDelete(chanrec* chan)
+ {
+ StatsIter a = sh->find(chan->name);
+ if (a != sh->end())
+ {
+ sh->erase(a);
+ }
+ this->changed = true;
+ }
+
+ void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ StatsIter a = sh->find(channel->name);
+ if (a != sh->end())
+ {
+ a->second++;
+ }
+ else
+ {
+ irc::string name = channel->name;
+ sh->insert(std::pair<irc::string,int>(name,1));
+ }
+ this->changed = true;
+ }
+
+ void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
+ {
+ StatsIter a = sh->find(channel->name);
+ if (a != sh->end())
+ {
+ a->second--;
+ }
+ this->changed = true;
+ }
+
+ void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
+ {
+ for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
+ {
+ chanrec* c = v->first;
+ StatsIter a = sh->find(c->name);
+ if (a != sh->end())
+ {
+ a->second--;
+ }
+ }
+ this->changed = true;
+ }
+
+ char* OnRequest(Request* request)
+ {
+ return NULL;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnEvent] = List[I_OnRequest] = List[I_OnChannelDelete] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = 1;
+ }
+
+ virtual ~ModuleHttpStats()
+ {
+ delete sh;
+ delete so;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleHttpStats)
diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp
index bf71f8189..732c2eaee 100644
--- a/src/modules/m_ident.cpp
+++ b/src/modules/m_ident.cpp
@@ -1 +1,326 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for RFC 1413 ident lookups */ // Version 1.5.0.0 - Updated to use InspSocket, faster and neater. /** Handles RFC1413 ident connections to users */ class RFC1413 : public InspSocket { protected: socklen_t uslen; // length of our port number socklen_t themlen; // length of their port number char ident_request[128]; // buffer used to make up the request string public: userrec* u; // user record that the lookup is associated with int ufd; RFC1413(InspIRCd* SI, userrec* user, int maxtime, const std::string &bindto) : InspSocket(SI, user->GetIPString(), 113, false, maxtime, bindto), u(user) { ufd = user->GetFd(); } virtual void OnTimeout() { // When we timeout, the connection failed within the allowed timeframe, // so we just display a notice, and tidy off the ident_data. if (u && (Instance->SE->GetRef(ufd) == u)) { u->Shrink("ident_data"); Instance->next_call = Instance->Time(); } } virtual bool OnDataReady() { char* ibuf = this->Read(); if (ibuf) { char* savept; char* section = strtok_r(ibuf,":",&savept); while (section) { if (strstr(section,"USERID")) { section = strtok_r(NULL,":",&savept); if (section) { // ID type, usually UNIX or OTHER... we dont want it, so read the next token section = strtok_r(NULL,":",&savept); if (section) { while (*section == ' ') section++; // strip leading spaces for (char* j = section; *j; j++) if ((*j < 33) || (*j > 126)) *j = '\0'; // truncate at invalid chars if (*section) { if (u && (Instance->SE->GetRef(ufd) == u)) { if (this->Instance->IsIdent(section)) { u->Extend("IDENT", new std::string(std::string(section) + "," + std::string(u->ident))); strlcpy(u->ident,section,IDENTMAX); u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Found your ident: "+std::string(u->ident)); } } } return false; } } } section = strtok_r(NULL,":",&savept); } } return false; } virtual void OnClose() { // tidy up after ourselves when the connection is done. // We receive this event straight after a timeout, too. // // // OK, now listen up. The weird looking check here is // REQUIRED. Don't try and optimize it away. // // When a socket is closed, it is not immediately removed // from the socket list, there can be a short delay // before it is culled from the list. This means that // without this check, there is a chance that a user // may not exist when we come to ::Shrink them, which // results in a segfault. The value of "u" may not // always be NULL at this point, so, what we do is // check against the fd_ref_table, to see if (1) the user // exists, and (2) its the SAME user, on the same file // descriptor that they were when the lookup began. // // Fixes issue reported by webs, 7 Jun 2006 if (u && (Instance->SE->GetRef(ufd) == u)) { Instance->next_call = Instance->Time(); u->Shrink("ident_data"); } } virtual void OnError(InspSocketError e) { if (u && (Instance->SE->GetRef(ufd) == u)) { if (*u->ident == '~') u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using "+std::string(u->ident)+" instead."); Instance->next_call = Instance->Time(); u->Shrink("ident_data"); } } virtual bool OnConnected() { if (u && (Instance->SE->GetRef(ufd) == u)) { sockaddr* sock_us = new sockaddr[2]; sockaddr* sock_them = new sockaddr[2]; bool success = false; uslen = sizeof(sockaddr_in); themlen = sizeof(sockaddr_in); #ifdef IPV6 if (this->u->GetProtocolFamily() == AF_INET6) { themlen = sizeof(sockaddr_in6); uslen = sizeof(sockaddr_in6); } #endif success = ((getsockname(this->u->GetFd(),sock_us,&uslen) || getpeername(this->u->GetFd(), sock_them, &themlen))); if (success) { delete[] sock_us; delete[] sock_them; return false; } else { // send the request in the following format: theirsocket,oursocket #ifdef IPV6 if (this->u->GetProtocolFamily() == AF_INET6) snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in6*)sock_them)->sin6_port),ntohs(((sockaddr_in6*)sock_us)->sin6_port)); else #endif snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in*)sock_them)->sin_port),ntohs(((sockaddr_in*)sock_us)->sin_port)); this->Write(ident_request); delete[] sock_us; delete[] sock_them; return true; } } else { Instance->next_call = Instance->Time(); return true; } } }; class ModuleIdent : public Module { ConfigReader* Conf; int IdentTimeout; std::string PortBind; public: void ReadSettings() { Conf = new ConfigReader(ServerInstance); IdentTimeout = Conf->ReadInteger("ident", "timeout", 0, true); PortBind = Conf->ReadValue("ident", "bind", 0); if (!IdentTimeout) IdentTimeout = 1; DELETE(Conf); } ModuleIdent(InspIRCd* Me) : Module(Me) { ReadSettings(); } void Implements(char* List) { List[I_OnCleanup] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1; } void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { if ((displayable) && (extname == "IDENT")) { std::string* ident; if (GetExt("IDENT", ident)) proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *ident); } } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadSettings(); } virtual int OnUserRegister(userrec* user) { /* * when the new user connects, before they authenticate with USER/NICK/PASS, we do * their ident lookup. We do this by instantiating an object of type RFC1413, which * is derived from InspSocket, and inserting it into the socket engine using the * Server::AddSocket() call. */ char newident[MAXBUF]; strcpy(newident,"~"); strlcat(newident,user->ident,IDENTMAX); strlcpy(user->ident,newident,IDENTMAX); user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Looking up your ident..."); RFC1413* ident = new RFC1413(ServerInstance, user, IdentTimeout, PortBind); if ((ident->GetState() == I_CONNECTING) || (ident->GetState() == I_CONNECTED)) { user->Extend("ident_data", (char*)ident); } else { user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not find your ident, using "+std::string(user->ident)+" instead."); ServerInstance->next_call = ServerInstance->Time(); } return 0; } virtual bool OnCheckReady(userrec* user) { /* * The socket engine will clean up their ident request for us when it completes, * either due to timeout or due to closing, so, we just hold them until they dont * have an ident field any more. */ RFC1413* ident; return (!user->GetExt("ident_data", ident)); } virtual void OnCleanup(int target_type, void* item) { if (target_type == TYPE_USER) { userrec* user = (userrec*)item; RFC1413* ident; std::string* identstr; if (user->GetExt("ident_data", ident)) { // FIX: If the user record is deleted, the socket wont be removed // immediately so there is chance of the socket trying to write to // a user which has now vanished! To prevent this, set ident::u // to NULL and check it so that we dont write users who have gone away. ident->u = NULL; ServerInstance->SE->DelFd(ident); //delete ident; } if (user->GetExt("IDENT", identstr)) { delete identstr; } } } virtual void OnUserDisconnect(userrec* user) { /* * when the user quits tidy up any ident lookup they have pending to keep things tidy. * When we call RemoveSocket, the abstractions tied into the system evnetually work their * way to RFC1459::OnClose(), which shrinks off the ident_data for us, so we dont need * to do it here. If we don't tidy this up, there may still be lingering idents for users * who have quit, as class RFC1459 is only loosely bound to userrec* via a pair of pointers * and this would leave at least one of the invalid ;) */ RFC1413* ident; std::string* identstr; if (user->GetExt("ident_data", ident)) { ident->u = NULL; ServerInstance->SE->DelFd(ident); } if (user->GetExt("IDENT", identstr)) { delete identstr; } } virtual ~ModuleIdent() { ServerInstance->next_call = ServerInstance->Time(); } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleIdent) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for RFC 1413 ident lookups */
+
+// Version 1.5.0.0 - Updated to use InspSocket, faster and neater.
+
+/** Handles RFC1413 ident connections to users
+ */
+class RFC1413 : public InspSocket
+{
+ protected:
+ socklen_t uslen; // length of our port number
+ socklen_t themlen; // length of their port number
+ char ident_request[128]; // buffer used to make up the request string
+ public:
+
+ userrec* u; // user record that the lookup is associated with
+ int ufd;
+
+ RFC1413(InspIRCd* SI, userrec* user, int maxtime, const std::string &bindto) : InspSocket(SI, user->GetIPString(), 113, false, maxtime, bindto), u(user)
+ {
+ ufd = user->GetFd();
+ }
+
+ virtual void OnTimeout()
+ {
+ // When we timeout, the connection failed within the allowed timeframe,
+ // so we just display a notice, and tidy off the ident_data.
+ if (u && (Instance->SE->GetRef(ufd) == u))
+ {
+ u->Shrink("ident_data");
+ Instance->next_call = Instance->Time();
+ }
+ }
+
+ virtual bool OnDataReady()
+ {
+ char* ibuf = this->Read();
+ if (ibuf)
+ {
+ char* savept;
+ char* section = strtok_r(ibuf,":",&savept);
+ while (section)
+ {
+ if (strstr(section,"USERID"))
+ {
+ section = strtok_r(NULL,":",&savept);
+ if (section)
+ {
+ // ID type, usually UNIX or OTHER... we dont want it, so read the next token
+ section = strtok_r(NULL,":",&savept);
+ if (section)
+ {
+ while (*section == ' ') section++; // strip leading spaces
+ for (char* j = section; *j; j++)
+ if ((*j < 33) || (*j > 126))
+ *j = '\0'; // truncate at invalid chars
+ if (*section)
+ {
+ if (u && (Instance->SE->GetRef(ufd) == u))
+ {
+ if (this->Instance->IsIdent(section))
+ {
+ u->Extend("IDENT", new std::string(std::string(section) + "," + std::string(u->ident)));
+ strlcpy(u->ident,section,IDENTMAX);
+ u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Found your ident: "+std::string(u->ident));
+ }
+ }
+ }
+ return false;
+ }
+ }
+ }
+ section = strtok_r(NULL,":",&savept);
+ }
+ }
+ return false;
+ }
+
+ virtual void OnClose()
+ {
+ // tidy up after ourselves when the connection is done.
+ // We receive this event straight after a timeout, too.
+ //
+ //
+ // OK, now listen up. The weird looking check here is
+ // REQUIRED. Don't try and optimize it away.
+ //
+ // When a socket is closed, it is not immediately removed
+ // from the socket list, there can be a short delay
+ // before it is culled from the list. This means that
+ // without this check, there is a chance that a user
+ // may not exist when we come to ::Shrink them, which
+ // results in a segfault. The value of "u" may not
+ // always be NULL at this point, so, what we do is
+ // check against the fd_ref_table, to see if (1) the user
+ // exists, and (2) its the SAME user, on the same file
+ // descriptor that they were when the lookup began.
+ //
+ // Fixes issue reported by webs, 7 Jun 2006
+ if (u && (Instance->SE->GetRef(ufd) == u))
+ {
+ Instance->next_call = Instance->Time();
+ u->Shrink("ident_data");
+ }
+ }
+
+ virtual void OnError(InspSocketError e)
+ {
+ if (u && (Instance->SE->GetRef(ufd) == u))
+ {
+ if (*u->ident == '~')
+ u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using "+std::string(u->ident)+" instead.");
+
+ Instance->next_call = Instance->Time();
+ u->Shrink("ident_data");
+ }
+ }
+
+ virtual bool OnConnected()
+ {
+ if (u && (Instance->SE->GetRef(ufd) == u))
+ {
+ sockaddr* sock_us = new sockaddr[2];
+ sockaddr* sock_them = new sockaddr[2];
+ bool success = false;
+ uslen = sizeof(sockaddr_in);
+ themlen = sizeof(sockaddr_in);
+#ifdef IPV6
+ if (this->u->GetProtocolFamily() == AF_INET6)
+ {
+ themlen = sizeof(sockaddr_in6);
+ uslen = sizeof(sockaddr_in6);
+ }
+#endif
+ success = ((getsockname(this->u->GetFd(),sock_us,&uslen) || getpeername(this->u->GetFd(), sock_them, &themlen)));
+ if (success)
+ {
+ delete[] sock_us;
+ delete[] sock_them;
+ return false;
+ }
+ else
+ {
+ // send the request in the following format: theirsocket,oursocket
+#ifdef IPV6
+ if (this->u->GetProtocolFamily() == AF_INET6)
+ snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in6*)sock_them)->sin6_port),ntohs(((sockaddr_in6*)sock_us)->sin6_port));
+ else
+#endif
+ snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in*)sock_them)->sin_port),ntohs(((sockaddr_in*)sock_us)->sin_port));
+ this->Write(ident_request);
+ delete[] sock_us;
+ delete[] sock_them;
+ return true;
+ }
+ }
+ else
+ {
+ Instance->next_call = Instance->Time();
+ return true;
+ }
+ }
+};
+
+class ModuleIdent : public Module
+{
+
+ ConfigReader* Conf;
+ int IdentTimeout;
+ std::string PortBind;
+
+ public:
+ void ReadSettings()
+ {
+ Conf = new ConfigReader(ServerInstance);
+ IdentTimeout = Conf->ReadInteger("ident", "timeout", 0, true);
+ PortBind = Conf->ReadValue("ident", "bind", 0);
+ if (!IdentTimeout)
+ IdentTimeout = 1;
+ DELETE(Conf);
+ }
+
+ ModuleIdent(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ ReadSettings();
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnCleanup] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1;
+ }
+
+ void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable)
+ {
+ if ((displayable) && (extname == "IDENT"))
+ {
+ std::string* ident;
+ if (GetExt("IDENT", ident))
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *ident);
+ }
+ }
+
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadSettings();
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ /*
+ * when the new user connects, before they authenticate with USER/NICK/PASS, we do
+ * their ident lookup. We do this by instantiating an object of type RFC1413, which
+ * is derived from InspSocket, and inserting it into the socket engine using the
+ * Server::AddSocket() call.
+ */
+ char newident[MAXBUF];
+ strcpy(newident,"~");
+ strlcat(newident,user->ident,IDENTMAX);
+ strlcpy(user->ident,newident,IDENTMAX);
+
+
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Looking up your ident...");
+ RFC1413* ident = new RFC1413(ServerInstance, user, IdentTimeout, PortBind);
+ if ((ident->GetState() == I_CONNECTING) || (ident->GetState() == I_CONNECTED))
+ {
+ user->Extend("ident_data", (char*)ident);
+ }
+ else
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not find your ident, using "+std::string(user->ident)+" instead.");
+ ServerInstance->next_call = ServerInstance->Time();
+ }
+ return 0;
+ }
+
+ virtual bool OnCheckReady(userrec* user)
+ {
+ /*
+ * The socket engine will clean up their ident request for us when it completes,
+ * either due to timeout or due to closing, so, we just hold them until they dont
+ * have an ident field any more.
+ */
+ RFC1413* ident;
+ return (!user->GetExt("ident_data", ident));
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ RFC1413* ident;
+ std::string* identstr;
+ if (user->GetExt("ident_data", ident))
+ {
+ // FIX: If the user record is deleted, the socket wont be removed
+ // immediately so there is chance of the socket trying to write to
+ // a user which has now vanished! To prevent this, set ident::u
+ // to NULL and check it so that we dont write users who have gone away.
+ ident->u = NULL;
+ ServerInstance->SE->DelFd(ident);
+ //delete ident;
+ }
+ if (user->GetExt("IDENT", identstr))
+ {
+ delete identstr;
+ }
+ }
+ }
+
+ virtual void OnUserDisconnect(userrec* user)
+ {
+ /*
+ * when the user quits tidy up any ident lookup they have pending to keep things tidy.
+ * When we call RemoveSocket, the abstractions tied into the system evnetually work their
+ * way to RFC1459::OnClose(), which shrinks off the ident_data for us, so we dont need
+ * to do it here. If we don't tidy this up, there may still be lingering idents for users
+ * who have quit, as class RFC1459 is only loosely bound to userrec* via a pair of pointers
+ * and this would leave at least one of the invalid ;)
+ */
+ RFC1413* ident;
+ std::string* identstr;
+ if (user->GetExt("ident_data", ident))
+ {
+ ident->u = NULL;
+ ServerInstance->SE->DelFd(ident);
+ }
+ if (user->GetExt("IDENT", identstr))
+ {
+ delete identstr;
+ }
+ }
+
+ virtual ~ModuleIdent()
+ {
+ ServerInstance->next_call = ServerInstance->Time();
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleIdent)
diff --git a/src/modules/m_invisible.cpp b/src/modules/m_invisible.cpp
index ce2f2062b..e1fb88ca0 100644
--- a/src/modules/m_invisible.cpp
+++ b/src/modules/m_invisible.cpp
@@ -1 +1,277 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include <stdarg.h> /* $ModDesc: Allows for opered clients to join channels without being seen, similar to unreal 3.1 +I mode */ static ConfigReader* conf; class QuietOper : public VisData { public: QuietOper() { } virtual ~QuietOper() { } virtual bool VisibleTo(userrec* user) { return IS_OPER(user); } }; class InvisibleMode : public ModeHandler { QuietOper* qo; public: InvisibleMode(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_USER, true) { qo = new QuietOper(); } ~InvisibleMode() { for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) if (i->second->Visibility == qo) i->second->Visibility = NULL; delete qo; } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (source != dest) return MODEACTION_DENY; if (dest->IsModeSet('Q') != adding) { bool ok = false; for (int j = 0; j < conf->Enumerate("type"); j++) { std::string opertype = conf->ReadValue("type","name",j); if (opertype == source->oper) { ok = conf->ReadFlag("type", "canquiet", j); break; } } if (!ok) { source->WriteServ("481 %s :Permission Denied - You do not have access to become invisible via user mode +Q", source->nick); return MODEACTION_DENY; } dest->SetMode('Q', adding); /* Set visibility handler object */ dest->Visibility = adding ? qo : NULL; /* User appears to vanish or appear from nowhere */ for (UCListIter f = dest->chans.begin(); f != dest->chans.end(); f++) { CUList *ulist = f->first->GetUsers(); char tb[MAXBUF]; snprintf(tb,MAXBUF,":%s %s %s", dest->GetFullHost(), adding ? "PART" : "JOIN", f->first->name); std::string out = tb; std::string n = this->ServerInstance->Modes->ModeString(dest, f->first); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { /* User only appears to vanish for non-opers */ if (IS_LOCAL(i->first) && !IS_OPER(i->first)) { i->first->Write(out); if (!n.empty() && !adding) i->first->WriteServ("MODE %s +%s", f->first->name, n.c_str()); } } ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has become %svisible (%sQ)", dest->GetFullHost(), adding ? "in" : "", adding ? "+" : "-"); } return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } }; class InvisibleDeOper : public ModeWatcher { private: InspIRCd* Srv; public: InvisibleDeOper(InspIRCd* Instance) : ModeWatcher(Instance, 'o', MODETYPE_USER), Srv(Instance) { } bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &param, bool adding, ModeType type) { /* Users who are opers and have +Q get their +Q removed when they deoper */ if ((!adding) && (dest->IsModeSet('Q'))) { const char* newmodes[] = { dest->nick, "-Q" }; ServerInstance->Modes->Process(newmodes, 2, source, true); } return true; } }; class ModuleInvisible : public Module { private: InvisibleMode* qm; InvisibleDeOper* ido; public: ModuleInvisible(InspIRCd* Me) : Module(Me) { conf = new ConfigReader(ServerInstance); qm = new InvisibleMode(ServerInstance); if (!ServerInstance->AddMode(qm, 'Q')) throw ModuleException("Could not add new modes!"); ido = new InvisibleDeOper(ServerInstance); if (!ServerInstance->AddModeWatcher(ido)) throw ModuleException("Could not add new mode watcher on usermode +o!"); } virtual ~ModuleInvisible() { ServerInstance->Modes->DelMode(qm); ServerInstance->Modes->DelModeWatcher(ido); DELETE(qm); DELETE(ido); DELETE(conf); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = List[I_OnRehash] = 1; } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { if (user->IsModeSet('Q')) { silent = true; /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */ this->WriteCommonFrom(user, channel, "JOIN %s", channel->name); ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has joined %s invisibly (+Q)", user->GetFullHost(), channel->name); } } virtual void OnRehash(userrec* user, const std::string &parameter) { DELETE(conf); conf = new ConfigReader(ServerInstance); } void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { if (user->IsModeSet('Q')) { silent = true; /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */ this->WriteCommonFrom(user, channel, "PART %s%s%s", channel->name, partmessage.empty() ? "" : " :", partmessage.empty() ? "" : partmessage.c_str()); } } void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { if (user->IsModeSet('Q')) { command_t* parthandler = ServerInstance->Parser->GetHandler("PART"); std::vector<std::string> to_leave; const char* parameters[2]; if (parthandler) { for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++) to_leave.push_back(f->first->name); /* We cant do this neatly in one loop, as we are modifying the map we are iterating */ for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++) { parameters[0] = n->c_str(); /* This triggers our OnUserPart, above, making the PART silent */ parthandler->Handle(parameters, 1, user); } } } } /* No privmsg response when hiding - submitted by Eric at neowin */ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if ((target_type == TYPE_USER) && (IS_LOCAL(user))) { userrec* target = (userrec*)dest; if(target->IsModeSet('Q') && !*user->oper) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, target->nick); return 1; } } return 0; } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { return OnUserPreNotice(user, dest, target_type, text, status, exempt_list); } /* Fix by Eric @ neowin.net, thanks :) -- Brain */ void WriteCommonFrom(userrec *user, chanrec* channel, const char* text, ...) { va_list argsPtr; char textbuffer[MAXBUF]; char tb[MAXBUF]; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),textbuffer); CUList *ulist = channel->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { /* User only appears to vanish for non-opers */ if (IS_LOCAL(i->first) && IS_OPER(i->first)) { i->first->Write(std::string(tb)); } } } }; MODULE_INIT(ModuleInvisible) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include <stdarg.h>
+
+/* $ModDesc: Allows for opered clients to join channels without being seen, similar to unreal 3.1 +I mode */
+
+static ConfigReader* conf;
+
+class QuietOper : public VisData
+{
+ public:
+ QuietOper()
+ {
+ }
+
+ virtual ~QuietOper()
+ {
+ }
+
+ virtual bool VisibleTo(userrec* user)
+ {
+ return IS_OPER(user);
+ }
+};
+
+
+class InvisibleMode : public ModeHandler
+{
+ QuietOper* qo;
+ public:
+ InvisibleMode(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_USER, true)
+ {
+ qo = new QuietOper();
+ }
+
+ ~InvisibleMode()
+ {
+ for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
+ if (i->second->Visibility == qo)
+ i->second->Visibility = NULL;
+ delete qo;
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (source != dest)
+ return MODEACTION_DENY;
+
+ if (dest->IsModeSet('Q') != adding)
+ {
+ bool ok = false;
+
+ for (int j = 0; j < conf->Enumerate("type"); j++)
+ {
+ std::string opertype = conf->ReadValue("type","name",j);
+ if (opertype == source->oper)
+ {
+ ok = conf->ReadFlag("type", "canquiet", j);
+ break;
+ }
+ }
+
+ if (!ok)
+ {
+ source->WriteServ("481 %s :Permission Denied - You do not have access to become invisible via user mode +Q", source->nick);
+ return MODEACTION_DENY;
+ }
+
+ dest->SetMode('Q', adding);
+
+ /* Set visibility handler object */
+ dest->Visibility = adding ? qo : NULL;
+
+ /* User appears to vanish or appear from nowhere */
+ for (UCListIter f = dest->chans.begin(); f != dest->chans.end(); f++)
+ {
+ CUList *ulist = f->first->GetUsers();
+ char tb[MAXBUF];
+
+ snprintf(tb,MAXBUF,":%s %s %s", dest->GetFullHost(), adding ? "PART" : "JOIN", f->first->name);
+ std::string out = tb;
+ std::string n = this->ServerInstance->Modes->ModeString(dest, f->first);
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ /* User only appears to vanish for non-opers */
+ if (IS_LOCAL(i->first) && !IS_OPER(i->first))
+ {
+ i->first->Write(out);
+ if (!n.empty() && !adding)
+ i->first->WriteServ("MODE %s +%s", f->first->name, n.c_str());
+ }
+ }
+
+ ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has become %svisible (%sQ)", dest->GetFullHost(), adding ? "in" : "", adding ? "+" : "-");
+ }
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+ }
+};
+
+class InvisibleDeOper : public ModeWatcher
+{
+ private:
+ InspIRCd* Srv;
+ public:
+ InvisibleDeOper(InspIRCd* Instance) : ModeWatcher(Instance, 'o', MODETYPE_USER), Srv(Instance)
+ {
+ }
+
+ bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &param, bool adding, ModeType type)
+ {
+ /* Users who are opers and have +Q get their +Q removed when they deoper */
+ if ((!adding) && (dest->IsModeSet('Q')))
+ {
+ const char* newmodes[] = { dest->nick, "-Q" };
+ ServerInstance->Modes->Process(newmodes, 2, source, true);
+ }
+ return true;
+ }
+};
+
+
+class ModuleInvisible : public Module
+{
+ private:
+ InvisibleMode* qm;
+ InvisibleDeOper* ido;
+ public:
+ ModuleInvisible(InspIRCd* Me)
+ : Module(Me)
+ {
+ conf = new ConfigReader(ServerInstance);
+ qm = new InvisibleMode(ServerInstance);
+ if (!ServerInstance->AddMode(qm, 'Q'))
+ throw ModuleException("Could not add new modes!");
+ ido = new InvisibleDeOper(ServerInstance);
+ if (!ServerInstance->AddModeWatcher(ido))
+ throw ModuleException("Could not add new mode watcher on usermode +o!");
+ }
+
+ virtual ~ModuleInvisible()
+ {
+ ServerInstance->Modes->DelMode(qm);
+ ServerInstance->Modes->DelModeWatcher(ido);
+ DELETE(qm);
+ DELETE(ido);
+ DELETE(conf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ if (user->IsModeSet('Q'))
+ {
+ silent = true;
+ /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */
+ this->WriteCommonFrom(user, channel, "JOIN %s", channel->name);
+ ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has joined %s invisibly (+Q)", user->GetFullHost(), channel->name);
+ }
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ DELETE(conf);
+ conf = new ConfigReader(ServerInstance);
+ }
+
+ void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
+ {
+ if (user->IsModeSet('Q'))
+ {
+ silent = true;
+ /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */
+ this->WriteCommonFrom(user, channel, "PART %s%s%s", channel->name,
+ partmessage.empty() ? "" : " :",
+ partmessage.empty() ? "" : partmessage.c_str());
+ }
+ }
+
+ void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ if (user->IsModeSet('Q'))
+ {
+ command_t* parthandler = ServerInstance->Parser->GetHandler("PART");
+ std::vector<std::string> to_leave;
+ const char* parameters[2];
+ if (parthandler)
+ {
+ for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++)
+ to_leave.push_back(f->first->name);
+ /* We cant do this neatly in one loop, as we are modifying the map we are iterating */
+ for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++)
+ {
+ parameters[0] = n->c_str();
+ /* This triggers our OnUserPart, above, making the PART silent */
+ parthandler->Handle(parameters, 1, user);
+ }
+ }
+ }
+ }
+
+ /* No privmsg response when hiding - submitted by Eric at neowin */
+ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
+ {
+ userrec* target = (userrec*)dest;
+ if(target->IsModeSet('Q') && !*user->oper)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, target->nick);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
+ }
+
+ /* Fix by Eric @ neowin.net, thanks :) -- Brain */
+ void WriteCommonFrom(userrec *user, chanrec* channel, const char* text, ...)
+ {
+ va_list argsPtr;
+ char textbuffer[MAXBUF];
+ char tb[MAXBUF];
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+ snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),textbuffer);
+
+ CUList *ulist = channel->GetUsers();
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ /* User only appears to vanish for non-opers */
+ if (IS_LOCAL(i->first) && IS_OPER(i->first))
+ {
+ i->first->Write(std::string(tb));
+ }
+ }
+ }
+
+};
+
+MODULE_INIT(ModuleInvisible)
diff --git a/src/modules/m_inviteexception.cpp b/src/modules/m_inviteexception.cpp
index e51503b26..b7b9920c5 100644
--- a/src/modules/m_inviteexception.cpp
+++ b/src/modules/m_inviteexception.cpp
@@ -1 +1,150 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "mode.h" #include "u_listmode.h" /* $ModDesc: Provides support for the +I channel mode */ /* $ModDep: ../../include/u_listmode.h */ /* * Written by Om <om@inspircd.org>, April 2005. * Based on m_exception, which was originally based on m_chanprotect and m_silence * * The +I channel mode takes a nick!ident@host, glob patterns allowed, * and if a user matches an entry on the +I list then they can join the channel, * ignoring if +i is set on the channel * Now supports CIDR and IP addresses -- Brain */ class InspIRCd* ServerInstance; /** Handles channel mode +I */ class InviteException : public ListModeBase { public: InviteException(InspIRCd* Instance) : ListModeBase(Instance, 'I', "End of Channel Invite Exception List", "346", "347", true) { } }; class ModuleInviteException : public Module { InviteException* ie; public: ModuleInviteException(InspIRCd* Me) : Module(Me) { ie = new InviteException(ServerInstance); if (!ServerInstance->AddMode(ie, 'I')) throw ModuleException("Could not add new modes!"); ServerInstance->PublishInterface("ChannelBanList", this); } virtual void Implements(char* List) { ie->DoImplements(List); List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckInvite] = 1; } virtual void On005Numeric(std::string &output) { output.append(" INVEX=I"); } virtual int OnCheckInvite(userrec* user, chanrec* chan) { if(chan != NULL) { modelist* list; chan->GetExt(ie->GetInfoKey(), list); if (list) { char mask[MAXBUF]; snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString()); for (modelist::iterator it = list->begin(); it != list->end(); it++) { if(match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) { // They match an entry on the list, so let them in. return 1; } } } // or if there wasn't a list, there can't be anyone on it, so we don't need to do anything. } return 0; } virtual char* OnRequest(Request* request) { ListModeRequest* LM = (ListModeRequest*)request; if (strcmp("LM_CHECKLIST", request->GetId()) == 0) { modelist* list; LM->chan->GetExt(ie->GetInfoKey(), list); if (list) { char mask[MAXBUF]; snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString()); for (modelist::iterator it = list->begin(); it != list->end(); it++) { if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) { // They match an entry return (char*)it->mask.c_str(); } } return NULL; } } return NULL; } virtual void OnCleanup(int target_type, void* item) { ie->DoCleanup(target_type, item); } virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { ie->DoSyncChannel(chan, proto, opaque); } virtual void OnChannelDelete(chanrec* chan) { ie->DoChannelDelete(chan); } virtual void OnRehash(userrec* user, const std::string &param) { ie->DoRehash(); } virtual Version GetVersion() { return Version(1, 1, 0, 3, VF_VENDOR | VF_COMMON, API_VERSION); } ~ModuleInviteException() { ServerInstance->Modes->DelMode(ie); DELETE(ie); ServerInstance->UnpublishInterface("ChannelBanList", this); } }; MODULE_INIT(ModuleInviteException) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "mode.h"
+#include "u_listmode.h"
+
+/* $ModDesc: Provides support for the +I channel mode */
+/* $ModDep: ../../include/u_listmode.h */
+
+/*
+ * Written by Om <om@inspircd.org>, April 2005.
+ * Based on m_exception, which was originally based on m_chanprotect and m_silence
+ *
+ * The +I channel mode takes a nick!ident@host, glob patterns allowed,
+ * and if a user matches an entry on the +I list then they can join the channel,
+ * ignoring if +i is set on the channel
+ * Now supports CIDR and IP addresses -- Brain
+ */
+
+class InspIRCd* ServerInstance;
+
+/** Handles channel mode +I
+ */
+class InviteException : public ListModeBase
+{
+ public:
+ InviteException(InspIRCd* Instance) : ListModeBase(Instance, 'I', "End of Channel Invite Exception List", "346", "347", true) { }
+};
+
+class ModuleInviteException : public Module
+{
+ InviteException* ie;
+public:
+ ModuleInviteException(InspIRCd* Me) : Module(Me)
+ {
+ ie = new InviteException(ServerInstance);
+ if (!ServerInstance->AddMode(ie, 'I'))
+ throw ModuleException("Could not add new modes!");
+ ServerInstance->PublishInterface("ChannelBanList", this);
+ }
+
+ virtual void Implements(char* List)
+ {
+ ie->DoImplements(List);
+ List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckInvite] = 1;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" INVEX=I");
+ }
+
+ virtual int OnCheckInvite(userrec* user, chanrec* chan)
+ {
+ if(chan != NULL)
+ {
+ modelist* list;
+ chan->GetExt(ie->GetInfoKey(), list);
+ if (list)
+ {
+ char mask[MAXBUF];
+ snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString());
+ for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ {
+ if(match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
+ {
+ // They match an entry on the list, so let them in.
+ return 1;
+ }
+ }
+ }
+ // or if there wasn't a list, there can't be anyone on it, so we don't need to do anything.
+ }
+
+ return 0;
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ ListModeRequest* LM = (ListModeRequest*)request;
+ if (strcmp("LM_CHECKLIST", request->GetId()) == 0)
+ {
+ modelist* list;
+ LM->chan->GetExt(ie->GetInfoKey(), list);
+ if (list)
+ {
+ char mask[MAXBUF];
+ snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString());
+ for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ {
+ if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
+ {
+ // They match an entry
+ return (char*)it->mask.c_str();
+ }
+ }
+ return NULL;
+ }
+ }
+ return NULL;
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ ie->DoCleanup(target_type, item);
+ }
+
+ virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
+ {
+ ie->DoSyncChannel(chan, proto, opaque);
+ }
+
+ virtual void OnChannelDelete(chanrec* chan)
+ {
+ ie->DoChannelDelete(chan);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+ ie->DoRehash();
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 3, VF_VENDOR | VF_COMMON, API_VERSION);
+ }
+
+ ~ModuleInviteException()
+ {
+ ServerInstance->Modes->DelMode(ie);
+ DELETE(ie);
+ ServerInstance->UnpublishInterface("ChannelBanList", this);
+ }
+};
+
+MODULE_INIT(ModuleInviteException)
diff --git a/src/modules/m_joinflood.cpp b/src/modules/m_joinflood.cpp
index 26339e207..3d342b636 100644
--- a/src/modules/m_joinflood.cpp
+++ b/src/modules/m_joinflood.cpp
@@ -1 +1,285 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides channel mode +j (join flood protection) */ /** Holds settings and state associated with channel mode +j */ class joinfloodsettings : public classbase { public: int secs; int joins; time_t reset; time_t unlocktime; int counter; bool locked; InspIRCd* ServerInstance; joinfloodsettings() : secs(0), joins(0) {}; joinfloodsettings(int b, int c) : secs(b), joins(c) { reset = time(NULL) + secs; counter = 0; locked = false; }; void addjoin() { counter++; if (time(NULL) > reset) { counter = 0; reset = time(NULL) + secs; } } bool shouldlock() { return (counter >= this->joins); } void clear() { counter = 0; } bool islocked() { if (locked) { if (time(NULL) > unlocktime) { locked = false; return false; } else { return true; } } return false; } void lock() { locked = true; unlocktime = time(NULL) + 60; } }; /** Handles channel mode +j */ class JoinFlood : public ModeHandler { public: JoinFlood(InspIRCd* Instance) : ModeHandler(Instance, 'j', 1, 0, false, MODETYPE_CHANNEL, false) { } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { joinfloodsettings* x; if (channel->GetExt("joinflood",x)) return std::make_pair(true, ConvToStr(x->joins)+":"+ConvToStr(x->secs)); else return std::make_pair(false, parameter); } bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { /* When TS is equal, the alphabetically later one wins */ return (their_param < our_param); } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { joinfloodsettings* dummy; if (adding) { 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->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); parameter.clear(); return MODEACTION_DENY; } else { if (!channel->GetExt("joinflood", dummy)) { parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs); joinfloodsettings *f = new joinfloodsettings(nsecs,njoins); channel->Extend("joinflood", f); channel->SetMode('j', true); channel->SetModeParam('j', parameter.c_str(), true); 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 if ((nsecs > 0) && (njoins > 0)) { joinfloodsettings* f; channel->GetExt("joinflood", f); delete f; f = new joinfloodsettings(nsecs,njoins); channel->Shrink("joinflood"); channel->Extend("joinflood", f); channel->SetModeParam('j', cur_param.c_str(), false); channel->SetModeParam('j', parameter.c_str(), true); return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } } } } else { source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); return MODEACTION_DENY; } } else { if (channel->GetExt("joinflood", dummy)) { joinfloodsettings *f; channel->GetExt("joinflood", f); DELETE(f); channel->Shrink("joinflood"); channel->SetMode('j', false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleJoinFlood : public Module { JoinFlood* jf; public: ModuleJoinFlood(InspIRCd* Me) : Module(Me) { jf = new JoinFlood(ServerInstance); if (!ServerInstance->AddMode(jf, 'j')) throw ModuleException("Could not add new modes!"); } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (chan) { joinfloodsettings *f; if (chan->GetExt("joinflood", f)) { if (f->islocked()) { user->WriteServ("609 %s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick,chan->name); return 1; } } } return 0; } virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) { joinfloodsettings *f; if (channel->GetExt("joinflood",f)) { f->addjoin(); if (f->shouldlock()) { f->clear(); f->lock(); channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", channel->name, f->joins, f->secs); } } } void OnChannelDelete(chanrec* chan) { joinfloodsettings *f; if (chan->GetExt("joinflood",f)) { DELETE(f); chan->Shrink("joinflood"); } } void Implements(char* List) { List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserJoin] = 1; } virtual ~ModuleJoinFlood() { ServerInstance->Modes->DelMode(jf); DELETE(jf); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleJoinFlood) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides channel mode +j (join flood protection) */
+
+/** Holds settings and state associated with channel mode +j
+ */
+class joinfloodsettings : public classbase
+{
+ public:
+
+ int secs;
+ int joins;
+ time_t reset;
+ time_t unlocktime;
+ int counter;
+ bool locked;
+ InspIRCd* ServerInstance;
+
+ joinfloodsettings() : secs(0), joins(0) {};
+
+ joinfloodsettings(int b, int c) : secs(b), joins(c)
+ {
+ reset = time(NULL) + secs;
+ counter = 0;
+ locked = false;
+ };
+
+ void addjoin()
+ {
+ counter++;
+ if (time(NULL) > reset)
+ {
+ counter = 0;
+ reset = time(NULL) + secs;
+ }
+ }
+
+ bool shouldlock()
+ {
+ return (counter >= this->joins);
+ }
+
+ void clear()
+ {
+ counter = 0;
+ }
+
+ bool islocked()
+ {
+ if (locked)
+ {
+ if (time(NULL) > unlocktime)
+ {
+ locked = false;
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void lock()
+ {
+ locked = true;
+ unlocktime = time(NULL) + 60;
+ }
+
+};
+
+/** Handles channel mode +j
+ */
+class JoinFlood : public ModeHandler
+{
+ public:
+ JoinFlood(InspIRCd* Instance) : ModeHandler(Instance, 'j', 1, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ joinfloodsettings* x;
+ if (channel->GetExt("joinflood",x))
+ return std::make_pair(true, ConvToStr(x->joins)+":"+ConvToStr(x->secs));
+ else
+ return std::make_pair(false, parameter);
+ }
+
+ bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+ {
+ /* When TS is equal, the alphabetically later one wins */
+ return (their_param < our_param);
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ joinfloodsettings* dummy;
+
+ if (adding)
+ {
+ 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->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
+ parameter.clear();
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ if (!channel->GetExt("joinflood", dummy))
+ {
+ parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
+ joinfloodsettings *f = new joinfloodsettings(nsecs,njoins);
+ channel->Extend("joinflood", f);
+ channel->SetMode('j', true);
+ channel->SetModeParam('j', parameter.c_str(), true);
+ 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
+ if ((nsecs > 0) && (njoins > 0))
+ {
+ joinfloodsettings* f;
+ channel->GetExt("joinflood", f);
+ delete f;
+ f = new joinfloodsettings(nsecs,njoins);
+ channel->Shrink("joinflood");
+ channel->Extend("joinflood", f);
+ channel->SetModeParam('j', cur_param.c_str(), false);
+ channel->SetModeParam('j', parameter.c_str(), true);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
+ return MODEACTION_DENY;
+ }
+ }
+ else
+ {
+ if (channel->GetExt("joinflood", dummy))
+ {
+ joinfloodsettings *f;
+ channel->GetExt("joinflood", f);
+ DELETE(f);
+ channel->Shrink("joinflood");
+ channel->SetMode('j', false);
+ return MODEACTION_ALLOW;
+ }
+ }
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleJoinFlood : public Module
+{
+
+ JoinFlood* jf;
+
+ public:
+
+ ModuleJoinFlood(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ jf = new JoinFlood(ServerInstance);
+ if (!ServerInstance->AddMode(jf, 'j'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (chan)
+ {
+ joinfloodsettings *f;
+ if (chan->GetExt("joinflood", f))
+ {
+ if (f->islocked())
+ {
+ user->WriteServ("609 %s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick,chan->name);
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+ {
+ joinfloodsettings *f;
+ if (channel->GetExt("joinflood",f))
+ {
+ f->addjoin();
+ if (f->shouldlock())
+ {
+ f->clear();
+ f->lock();
+ channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", channel->name, f->joins, f->secs);
+ }
+ }
+ }
+
+ void OnChannelDelete(chanrec* chan)
+ {
+ joinfloodsettings *f;
+ if (chan->GetExt("joinflood",f))
+ {
+ DELETE(f);
+ chan->Shrink("joinflood");
+ }
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserJoin] = 1;
+ }
+
+ virtual ~ModuleJoinFlood()
+ {
+ ServerInstance->Modes->DelMode(jf);
+ DELETE(jf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleJoinFlood)
diff --git a/src/modules/m_jumpserver.cpp b/src/modules/m_jumpserver.cpp
index 5a823b44c..28bbd056e 100644
--- a/src/modules/m_jumpserver.cpp
+++ b/src/modules/m_jumpserver.cpp
@@ -1 +1,164 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style SAPART command */ /** Handle /SAPART */ class cmd_jumpserver : public command_t { public: bool redirect_all_immediately; bool redirect_new_users; bool direction; std::string redirect_to; std::string reason; int port; cmd_jumpserver (InspIRCd* Instance) : command_t(Instance, "JUMPSERVER", 'o', 0) { this->source = "m_jumpserver.so"; syntax = "[<server> <port> <+/-a> :<reason>]"; redirect_to.clear(); reason.clear(); port = 0; redirect_all_immediately = redirect_new_users = false; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { int n_done = 0; reason = (pcnt < 4) ? "Please use this server/port instead" : parameters[3]; redirect_all_immediately = false; redirect_new_users = true; direction = true; std::string n_done_s; /* No parameters: jumpserver disabled */ if (!pcnt) { if (port) user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick, redirect_to.c_str(), port); else user->WriteServ("NOTICE %s :*** jumpserver was not enabled.", user->nick); port = 0; redirect_to.clear(); return CMD_LOCALONLY; } port = 0; redirect_to.clear(); for (const char* n = parameters[2]; *n; n++) { switch (*n) { case '+': direction = true; break; case '-': direction = false; break; case 'a': redirect_all_immediately = direction; break; case 'n': redirect_new_users = direction; break; } } if (redirect_all_immediately) { /* Redirect everyone but the oper sending the command */ for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) { userrec* t = *i; if (!IS_OPER(t)) { t->WriteServ("010 %s %s %s :Please use this Server/Port instead", user->nick, parameters[0], parameters[1]); userrec::QuitUser(ServerInstance, t, reason); n_done++; } } if (n_done) { n_done_s = ConvToStr(n_done); } } if (redirect_new_users) { redirect_to = parameters[0]; port = atoi(parameters[1]); } user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick, parameters[0], parameters[1], redirect_all_immediately ? "a" : "", redirect_new_users ? "n" : "", n_done ? " (" : "", n_done ? n_done_s.c_str() : "", n_done ? " user(s) redirected)" : "", reason.c_str()); return CMD_LOCALONLY; } }; class ModuleJumpServer : public Module { cmd_jumpserver* js; public: ModuleJumpServer(InspIRCd* Me) : Module(Me) { js = new cmd_jumpserver(ServerInstance); ServerInstance->AddCommand(js); } virtual ~ModuleJumpServer() { } virtual int OnUserRegister(userrec* user) { if (js->port && js->redirect_new_users) { user->WriteServ("010 %s %s %d :Please use this Server/Port instead", user->nick, js->redirect_to.c_str(), js->port); userrec::QuitUser(ServerInstance, user, js->reason); return 0; } return 0; } virtual void Implements(char* List) { List[I_OnUserRegister] = 1; } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleJumpServer) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style SAPART command */
+
+/** Handle /SAPART
+ */
+class cmd_jumpserver : public command_t
+{
+ public:
+ bool redirect_all_immediately;
+ bool redirect_new_users;
+ bool direction;
+ std::string redirect_to;
+ std::string reason;
+ int port;
+
+ cmd_jumpserver (InspIRCd* Instance) : command_t(Instance, "JUMPSERVER", 'o', 0)
+ {
+ this->source = "m_jumpserver.so";
+ syntax = "[<server> <port> <+/-a> :<reason>]";
+ redirect_to.clear();
+ reason.clear();
+ port = 0;
+ redirect_all_immediately = redirect_new_users = false;
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ int n_done = 0;
+ reason = (pcnt < 4) ? "Please use this server/port instead" : parameters[3];
+ redirect_all_immediately = false;
+ redirect_new_users = true;
+ direction = true;
+ std::string n_done_s;
+
+ /* No parameters: jumpserver disabled */
+ if (!pcnt)
+ {
+ if (port)
+ user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick, redirect_to.c_str(), port);
+ else
+ user->WriteServ("NOTICE %s :*** jumpserver was not enabled.", user->nick);
+
+ port = 0;
+ redirect_to.clear();
+ return CMD_LOCALONLY;
+ }
+
+ port = 0;
+ redirect_to.clear();
+
+ for (const char* n = parameters[2]; *n; n++)
+ {
+ switch (*n)
+ {
+ case '+':
+ direction = true;
+ break;
+ case '-':
+ direction = false;
+ break;
+ case 'a':
+ redirect_all_immediately = direction;
+ break;
+ case 'n':
+ redirect_new_users = direction;
+ break;
+ }
+ }
+
+ if (redirect_all_immediately)
+ {
+ /* Redirect everyone but the oper sending the command */
+ for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
+ {
+ userrec* t = *i;
+ if (!IS_OPER(t))
+ {
+ t->WriteServ("010 %s %s %s :Please use this Server/Port instead", user->nick, parameters[0], parameters[1]);
+ userrec::QuitUser(ServerInstance, t, reason);
+ n_done++;
+ }
+ }
+ if (n_done)
+ {
+ n_done_s = ConvToStr(n_done);
+ }
+ }
+
+ if (redirect_new_users)
+ {
+ redirect_to = parameters[0];
+ port = atoi(parameters[1]);
+ }
+
+ user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick, parameters[0], parameters[1],
+ redirect_all_immediately ? "a" : "",
+ redirect_new_users ? "n" : "",
+ n_done ? " (" : "",
+ n_done ? n_done_s.c_str() : "",
+ n_done ? " user(s) redirected)" : "",
+ reason.c_str());
+
+ return CMD_LOCALONLY;
+ }
+};
+
+
+class ModuleJumpServer : public Module
+{
+ cmd_jumpserver* js;
+ public:
+ ModuleJumpServer(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ js = new cmd_jumpserver(ServerInstance);
+ ServerInstance->AddCommand(js);
+ }
+
+ virtual ~ModuleJumpServer()
+ {
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ if (js->port && js->redirect_new_users)
+ {
+ user->WriteServ("010 %s %s %d :Please use this Server/Port instead", user->nick, js->redirect_to.c_str(), js->port);
+ userrec::QuitUser(ServerInstance, user, js->reason);
+ return 0;
+ }
+ return 0;
+ }
+
+ virtual void Implements(char* List)
+ {
+ List[I_OnUserRegister] = 1;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleJumpServer)
diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp
index 97d88786f..bdb988ad2 100644
--- a/src/modules/m_kicknorejoin.cpp
+++ b/src/modules/m_kicknorejoin.cpp
@@ -1 +1,224 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <sstream> #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides channel mode +J (delay rejoin after kick) */ inline int strtoint(const std::string &str) { std::istringstream ss(str); int result; ss >> result; return result; } typedef std::map<userrec*, time_t> delaylist; /** Handles channel mode +J */ class KickRejoin : public ModeHandler { public: KickRejoin(InspIRCd* Instance) : ModeHandler(Instance, 'J', 1, 0, false, MODETYPE_CHANNEL, false) { } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { if (channel->IsModeSet('J')) return std::make_pair(true, channel->GetModeParameter('J')); else return std::make_pair(false, parameter); } bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { /* When TS is equal, the alphabetically later one wins */ return (their_param < our_param); } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (!adding) { // Taking the mode off, we need to clean up. delaylist* dl; if (channel->GetExt("norejoinusers", dl)) { DELETE(dl); channel->Shrink("norejoinusers"); } if (!channel->IsModeSet('J')) { return MODEACTION_DENY; } else { channel->SetMode('J', false); return MODEACTION_ALLOW; } } else if (atoi(parameter.c_str()) > 0) { if (!channel->IsModeSet('J')) { parameter = ConvToStr(atoi(parameter.c_str())); channel->SetModeParam('J', parameter.c_str(), adding); channel->SetMode('J', adding); return MODEACTION_ALLOW; } else { std::string cur_param = channel->GetModeParameter('J'); if (cur_param == parameter) { // mode params match, don't change mode return MODEACTION_DENY; } else { // new mode param, replace old with new parameter = ConvToStr(atoi(parameter.c_str())); cur_param = ConvToStr(atoi(cur_param.c_str())); if (parameter != "0") { channel->SetModeParam('J', cur_param.c_str(), false); channel->SetModeParam('J', parameter.c_str(), adding); return MODEACTION_ALLOW; } else { /* Fix to jamie's fix, dont allow +J 0 on the new value! */ return MODEACTION_DENY; } } } } else { return MODEACTION_DENY; } } }; class ModuleKickNoRejoin : public Module { KickRejoin* kr; public: ModuleKickNoRejoin(InspIRCd* Me) : Module(Me) { kr = new KickRejoin(ServerInstance); if (!ServerInstance->AddMode(kr, 'J')) throw ModuleException("Could not add new modes!"); } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (chan) { delaylist* dl; if (chan->GetExt("norejoinusers", dl)) { std::vector<userrec*> itemstoremove; for (delaylist::iterator iter = dl->begin(); iter != dl->end(); iter++) { if (iter->second > time(NULL)) { if (iter->first == user) { user->WriteServ( "495 %s %s :You cannot rejoin this channel yet after being kicked (+J)", user->nick, chan->name); return 1; } } else { // Expired record, remove. itemstoremove.push_back(iter->first); } } for (unsigned int i = 0; i < itemstoremove.size(); i++) dl->erase(itemstoremove[i]); if (!dl->size()) { // Now it's empty.. DELETE(dl); chan->Shrink("norejoinusers"); } } } return 0; } virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { if (chan->IsModeSet('J') && (source != user)) { delaylist* dl; if (!chan->GetExt("norejoinusers", dl)) { dl = new delaylist; chan->Extend("norejoinusers", dl); } (*dl)[user] = time(NULL) + strtoint(chan->GetModeParameter('J')); } } virtual void OnChannelDelete(chanrec* chan) { delaylist* dl; if (chan->GetExt("norejoinusers", dl)) { DELETE(dl); chan->Shrink("norejoinusers"); } } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_CHANNEL) OnChannelDelete((chanrec*)item); } virtual void Implements(char* List) { List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserKick] = 1; } virtual ~ModuleKickNoRejoin() { ServerInstance->Modes->DelMode(kr); DELETE(kr); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleKickNoRejoin) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <sstream>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */
+
+inline int strtoint(const std::string &str)
+{
+ std::istringstream ss(str);
+ int result;
+ ss >> result;
+ return result;
+}
+
+typedef std::map<userrec*, time_t> delaylist;
+
+/** Handles channel mode +J
+ */
+class KickRejoin : public ModeHandler
+{
+ public:
+ KickRejoin(InspIRCd* Instance) : ModeHandler(Instance, 'J', 1, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ if (channel->IsModeSet('J'))
+ return std::make_pair(true, channel->GetModeParameter('J'));
+ else
+ return std::make_pair(false, parameter);
+ }
+
+ bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+ {
+ /* When TS is equal, the alphabetically later one wins */
+ return (their_param < our_param);
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (!adding)
+ {
+ // Taking the mode off, we need to clean up.
+ delaylist* dl;
+
+ if (channel->GetExt("norejoinusers", dl))
+ {
+ DELETE(dl);
+ channel->Shrink("norejoinusers");
+ }
+
+ if (!channel->IsModeSet('J'))
+ {
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ channel->SetMode('J', false);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else if (atoi(parameter.c_str()) > 0)
+ {
+ if (!channel->IsModeSet('J'))
+ {
+ parameter = ConvToStr(atoi(parameter.c_str()));
+ channel->SetModeParam('J', parameter.c_str(), adding);
+ channel->SetMode('J', adding);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ std::string cur_param = channel->GetModeParameter('J');
+ if (cur_param == parameter)
+ {
+ // mode params match, don't change mode
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ // new mode param, replace old with new
+ parameter = ConvToStr(atoi(parameter.c_str()));
+ cur_param = ConvToStr(atoi(cur_param.c_str()));
+ if (parameter != "0")
+ {
+ channel->SetModeParam('J', cur_param.c_str(), false);
+ channel->SetModeParam('J', parameter.c_str(), adding);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ /* Fix to jamie's fix, dont allow +J 0 on the new value! */
+ return MODEACTION_DENY;
+ }
+ }
+ }
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+ }
+};
+
+class ModuleKickNoRejoin : public Module
+{
+
+ KickRejoin* kr;
+
+public:
+
+ ModuleKickNoRejoin(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ kr = new KickRejoin(ServerInstance);
+ if (!ServerInstance->AddMode(kr, 'J'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (chan)
+ {
+ delaylist* dl;
+ if (chan->GetExt("norejoinusers", dl))
+ {
+ std::vector<userrec*> itemstoremove;
+
+ for (delaylist::iterator iter = dl->begin(); iter != dl->end(); iter++)
+ {
+ if (iter->second > time(NULL))
+ {
+ if (iter->first == user)
+ {
+ user->WriteServ( "495 %s %s :You cannot rejoin this channel yet after being kicked (+J)", user->nick, chan->name);
+ return 1;
+ }
+ }
+ else
+ {
+ // Expired record, remove.
+ itemstoremove.push_back(iter->first);
+ }
+ }
+
+ for (unsigned int i = 0; i < itemstoremove.size(); i++)
+ dl->erase(itemstoremove[i]);
+
+ if (!dl->size())
+ {
+ // Now it's empty..
+ DELETE(dl);
+ chan->Shrink("norejoinusers");
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
+ {
+ if (chan->IsModeSet('J') && (source != user))
+ {
+ delaylist* dl;
+ if (!chan->GetExt("norejoinusers", dl))
+ {
+ dl = new delaylist;
+ chan->Extend("norejoinusers", dl);
+ }
+ (*dl)[user] = time(NULL) + strtoint(chan->GetModeParameter('J'));
+ }
+ }
+
+ virtual void OnChannelDelete(chanrec* chan)
+ {
+ delaylist* dl;
+
+ if (chan->GetExt("norejoinusers", dl))
+ {
+ DELETE(dl);
+ chan->Shrink("norejoinusers");
+ }
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_CHANNEL)
+ OnChannelDelete((chanrec*)item);
+ }
+
+ virtual void Implements(char* List)
+ {
+ List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserKick] = 1;
+ }
+
+ virtual ~ModuleKickNoRejoin()
+ {
+ ServerInstance->Modes->DelMode(kr);
+ DELETE(kr);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+};
+
+
+MODULE_INIT(ModuleKickNoRejoin)
diff --git a/src/modules/m_knock.cpp b/src/modules/m_knock.cpp
index 9beaa699e..3bc45cceb 100644
--- a/src/modules/m_knock.cpp
+++ b/src/modules/m_knock.cpp
@@ -1 +1,129 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for /KNOCK and mode +K */ /** Handles the /KNOCK command */ class cmd_knock : public command_t { public: cmd_knock (InspIRCd* Instance) : command_t(Instance,"KNOCK", 0, 2) { this->source = "m_knock.so"; syntax = "<channel> <reason>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { chanrec* c = ServerInstance->FindChan(parameters[0]); if (!c) { user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]); return CMD_FAILURE; } std::string line; if (c->IsModeSet('K')) { user->WriteServ("480 %s :Can't KNOCK on %s, +K is set.",user->nick, c->name); return CMD_FAILURE; } for (int i = 1; i < pcnt - 1; i++) { line = line + std::string(parameters[i]) + " "; } line = line + std::string(parameters[pcnt-1]); if (!c->modes[CM_INVITEONLY]) { user->WriteServ("480 %s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick, c->name); return CMD_FAILURE; } c->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :User %s is KNOCKing on %s (%s)", c->name, user->nick, c->name, line.c_str()); user->WriteServ("NOTICE %s :KNOCKing on %s",user->nick,c->name); return CMD_SUCCESS; } }; /** Handles channel mode +K */ class Knock : public ModeHandler { public: Knock(InspIRCd* Instance) : ModeHandler(Instance, 'K', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('K')) { channel->SetMode('K',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('K')) { channel->SetMode('K',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleKnock : public Module { cmd_knock* mycommand; Knock* kn; public: ModuleKnock(InspIRCd* Me) : Module(Me) { kn = new Knock(ServerInstance); if (!ServerInstance->AddMode(kn, 'K')) throw ModuleException("Could not add new modes!"); mycommand = new cmd_knock(ServerInstance); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { } virtual ~ModuleKnock() { ServerInstance->Modes->DelMode(kn); DELETE(kn); } virtual Version GetVersion() { return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleKnock) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for /KNOCK and mode +K */
+
+/** Handles the /KNOCK command
+ */
+class cmd_knock : public command_t
+{
+ public:
+ cmd_knock (InspIRCd* Instance) : command_t(Instance,"KNOCK", 0, 2)
+ {
+ this->source = "m_knock.so";
+ syntax = "<channel> <reason>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ chanrec* c = ServerInstance->FindChan(parameters[0]);
+
+ if (!c)
+ {
+ user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ std::string line;
+
+ if (c->IsModeSet('K'))
+ {
+ user->WriteServ("480 %s :Can't KNOCK on %s, +K is set.",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+
+ for (int i = 1; i < pcnt - 1; i++)
+ {
+ line = line + std::string(parameters[i]) + " ";
+ }
+ line = line + std::string(parameters[pcnt-1]);
+
+ if (!c->modes[CM_INVITEONLY])
+ {
+ user->WriteServ("480 %s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+
+ c->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :User %s is KNOCKing on %s (%s)", c->name, user->nick, c->name, line.c_str());
+ user->WriteServ("NOTICE %s :KNOCKing on %s",user->nick,c->name);
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handles channel mode +K
+ */
+class Knock : public ModeHandler
+{
+ public:
+ Knock(InspIRCd* Instance) : ModeHandler(Instance, 'K', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('K'))
+ {
+ channel->SetMode('K',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('K'))
+ {
+ channel->SetMode('K',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleKnock : public Module
+{
+ cmd_knock* mycommand;
+ Knock* kn;
+ public:
+ ModuleKnock(InspIRCd* Me) : Module(Me)
+ {
+
+ kn = new Knock(ServerInstance);
+ if (!ServerInstance->AddMode(kn, 'K'))
+ throw ModuleException("Could not add new modes!");
+ mycommand = new cmd_knock(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ }
+
+ virtual ~ModuleKnock()
+ {
+ ServerInstance->Modes->DelMode(kn);
+ DELETE(kn);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleKnock)
diff --git a/src/modules/m_lockserv.cpp b/src/modules/m_lockserv.cpp
index d83961233..2ca2e3f44 100644
--- a/src/modules/m_lockserv.cpp
+++ b/src/modules/m_lockserv.cpp
@@ -1 +1,131 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.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 cmd_lockserv : public command_t { private: bool& locked; public: cmd_lockserv (InspIRCd* Instance, bool &lock) : command_t(Instance, "LOCKSERV", 'o', 0), locked(lock) { this->source = "m_lockserv.so"; syntax.clear(); } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { locked = true; user->WriteServ("988 %s %s :Closed for new connections", user->nick, user->server); ServerInstance->WriteOpers("*** Oper %s used LOCKSERV to temporarily close for new connections", user->nick); /* Dont send to the network */ return CMD_LOCALONLY; } }; class cmd_unlockserv : public command_t { private: bool& locked; public: cmd_unlockserv (InspIRCd* Instance, bool &lock) : command_t(Instance, "UNLOCKSERV", 'o', 0), locked(lock) { this->source = "m_lockserv.so"; syntax.clear(); } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { locked = false; user->WriteServ("989 %s %s :Open for new connections", user->nick, user->server); ServerInstance->WriteOpers("*** Oper %s used UNLOCKSERV to allow for new connections", user->nick); /* Dont send to the network */ return CMD_LOCALONLY; } }; class ModuleLockserv : public Module { private: bool locked; cmd_lockserv* lockcommand; cmd_unlockserv* unlockcommand; virtual void ResetLocked() { locked = false; } public: ModuleLockserv(InspIRCd* Me) : Module(Me) { ResetLocked(); lockcommand = new cmd_lockserv(ServerInstance, locked); ServerInstance->AddCommand(lockcommand); unlockcommand = new cmd_unlockserv(ServerInstance, locked); ServerInstance->AddCommand(unlockcommand); } virtual ~ModuleLockserv() { } void Implements(char* List) { List[I_OnUserRegister] = List[I_OnRehash] = List[I_OnCheckReady] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ResetLocked(); } virtual int OnUserRegister(userrec* user) { if (locked) { userrec::QuitUser(ServerInstance, user, "Server is temporarily closed. Please try again later."); return 1; } return 0; } virtual bool OnCheckReady(userrec* user) { return !locked; } virtual Version GetVersion() { return Version(1, 0, 0, 1, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleLockserv) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.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 cmd_lockserv : public command_t
+{
+private:
+ bool& locked;
+
+public:
+ cmd_lockserv (InspIRCd* Instance, bool &lock)
+ : command_t(Instance, "LOCKSERV", 'o', 0), locked(lock)
+ {
+ this->source = "m_lockserv.so";
+ syntax.clear();
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ locked = true;
+ user->WriteServ("988 %s %s :Closed for new connections", user->nick, user->server);
+ ServerInstance->WriteOpers("*** Oper %s used LOCKSERV to temporarily close for new connections", user->nick);
+ /* Dont send to the network */
+ return CMD_LOCALONLY;
+ }
+};
+
+class cmd_unlockserv : public command_t
+{
+private:
+ bool& locked;
+
+public:
+ cmd_unlockserv (InspIRCd* Instance, bool &lock)
+ : command_t(Instance, "UNLOCKSERV", 'o', 0), locked(lock)
+ {
+ this->source = "m_lockserv.so";
+ syntax.clear();
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ locked = false;
+ user->WriteServ("989 %s %s :Open for new connections", user->nick, user->server);
+ ServerInstance->WriteOpers("*** Oper %s used UNLOCKSERV to allow for new connections", user->nick);
+ /* Dont send to the network */
+ return CMD_LOCALONLY;
+ }
+};
+
+class ModuleLockserv : public Module
+{
+private:
+ bool locked;
+ cmd_lockserv* lockcommand;
+ cmd_unlockserv* unlockcommand;
+
+ virtual void ResetLocked()
+ {
+ locked = false;
+ }
+
+public:
+ ModuleLockserv(InspIRCd* Me) : Module(Me)
+ {
+ ResetLocked();
+ lockcommand = new cmd_lockserv(ServerInstance, locked);
+ ServerInstance->AddCommand(lockcommand);
+
+ unlockcommand = new cmd_unlockserv(ServerInstance, locked);
+ ServerInstance->AddCommand(unlockcommand);
+ }
+
+ virtual ~ModuleLockserv()
+ {
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserRegister] = List[I_OnRehash] = List[I_OnCheckReady] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ResetLocked();
+ }
+
+ virtual int OnUserRegister(userrec* user)
+ {
+ if (locked)
+ {
+ userrec::QuitUser(ServerInstance, user, "Server is temporarily closed. Please try again later.");
+ return 1;
+ }
+ return 0;
+ }
+
+ virtual bool OnCheckReady(userrec* user)
+ {
+ return !locked;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 0, 0, 1, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleLockserv)
diff --git a/src/modules/m_md5.cpp b/src/modules/m_md5.cpp
index c9b062e43..3b7df8369 100644
--- a/src/modules/m_md5.cpp
+++ b/src/modules/m_md5.cpp
@@ -1 +1,322 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Allows for MD5 encrypted oper passwords */ /* $ModDep: m_hash.h */ #include "inspircd.h" #ifdef HAS_STDINT #include <stdint.h> #endif #include "users.h" #include "channels.h" #include "modules.h" #include "m_hash.h" /* The four core functions - F1 is optimized somewhat */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #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; /** An MD5 context, used by m_opermd5 */ class MD5Context : public classbase { public: word32 buf[4]; word32 bytes[2]; word32 in[16]; }; class ModuleMD5 : public Module { void byteSwap(word32 *buf, unsigned words) { byte *p = (byte *)buf; do { *buf++ = (word32)((unsigned)p[3] << 8 | p[2]) << 16 | ((unsigned)p[1] << 8 | p[0]); p += 4; } while (--words); } void MD5Init(MD5Context *ctx, unsigned int* key = NULL) { /* These are the defaults for md5 */ if (!key) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; } else { ctx->buf[0] = key[0]; ctx->buf[1] = key[1]; ctx->buf[2] = key[2]; ctx->buf[3] = key[3]; } ctx->bytes[0] = 0; ctx->bytes[1] = 0; } void MD5Update(MD5Context *ctx, byte const *buf, int len) { word32 t; /* Update byte count */ t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) ctx->bytes[1]++; /* Carry from low to high */ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ if ((unsigned)t > (unsigned)len) { memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, len); return; } /* First chunk is an odd size */ memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, (unsigned)t); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += (unsigned)t; len -= (unsigned)t; /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } void MD5Final(byte digest[16], MD5Context *ctx) { int count = (int)(ctx->bytes[0] & 0x3f); /* Bytes in ctx->in */ byte *p = (byte *)ctx->in + count; /* First unused byte */ /* Set the first char of padding to 0x80. There is always room. */ *p++ = 0x80; /* Bytes of padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; if (count < 0) { /* Padding forces an extra block */ memset(p, 0, count+8); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); p = (byte *)ctx->in; count = 56; } memset(p, 0, count+8); byteSwap(ctx->in, 14); /* Append length in bits and transform */ ctx->in[14] = ctx->bytes[0] << 3; ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; MD5Transform(ctx->buf, ctx->in); byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(ctx)); } void MD5Transform(word32 buf[4], word32 const in[16]) { register word32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } void MyMD5(void *dest, void *orig, int len, unsigned int* key) { MD5Context context; MD5Init(&context, key); MD5Update(&context, (const unsigned char*)orig, len); MD5Final((unsigned char*)dest, &context); } void GenHash(const char* src, char* dest, const char* xtab, unsigned int* key) { unsigned char bytes[16]; MyMD5((char*)bytes, (void*)src, strlen(src), key); for (int i = 0; i < 16; i++) { *dest++ = xtab[bytes[i] / 16]; *dest++ = xtab[bytes[i] % 16]; } *dest++ = 0; } unsigned int *key; char* chars; public: ModuleMD5(InspIRCd* Me) : Module(Me), key(NULL), chars(NULL) { ServerInstance->PublishInterface("HashRequest", this); } virtual ~ModuleMD5() { ServerInstance->UnpublishInterface("HashRequest", this); } void Implements(char* List) { List[I_OnRequest] = 1; } virtual char* OnRequest(Request* request) { HashRequest* MD5 = (HashRequest*)request; if (strcmp("KEY", request->GetId()) == 0) { this->key = (unsigned int*)MD5->GetKeyData(); } else if (strcmp("HEX", request->GetId()) == 0) { this->chars = (char*)MD5->GetOutputs(); } else if (strcmp("SUM", request->GetId()) == 0) { static char data[MAXBUF]; GenHash((const char*)MD5->GetHashData(), data, chars ? chars : "0123456789abcdef", key); return data; } else if (strcmp("NAME", request->GetId()) == 0) { return "md5"; } else if (strcmp("RESET", request->GetId()) == 0) { this->chars = NULL; this->key = NULL; } return NULL; } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); } }; MODULE_INIT(ModuleMD5) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Allows for MD5 encrypted oper passwords */
+/* $ModDep: m_hash.h */
+
+#include "inspircd.h"
+#ifdef HAS_STDINT
+#include <stdint.h>
+#endif
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_hash.h"
+
+/* The four core functions - F1 is optimized somewhat */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#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;
+
+/** An MD5 context, used by m_opermd5
+ */
+class MD5Context : public classbase
+{
+ public:
+ word32 buf[4];
+ word32 bytes[2];
+ word32 in[16];
+};
+
+class ModuleMD5 : public Module
+{
+ void byteSwap(word32 *buf, unsigned words)
+ {
+ byte *p = (byte *)buf;
+
+ do
+ {
+ *buf++ = (word32)((unsigned)p[3] << 8 | p[2]) << 16 |
+ ((unsigned)p[1] << 8 | p[0]);
+ p += 4;
+ } while (--words);
+ }
+
+ void MD5Init(MD5Context *ctx, unsigned int* key = NULL)
+ {
+ /* These are the defaults for md5 */
+ if (!key)
+ {
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+ }
+ else
+ {
+ ctx->buf[0] = key[0];
+ ctx->buf[1] = key[1];
+ ctx->buf[2] = key[2];
+ ctx->buf[3] = key[3];
+ }
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+ }
+
+ void MD5Update(MD5Context *ctx, byte const *buf, int len)
+ {
+ word32 t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+ if ((unsigned)t > (unsigned)len)
+ {
+ memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, len);
+ return;
+ }
+ /* First chunk is an odd size */
+ memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, (unsigned)t);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += (unsigned)t;
+ len -= (unsigned)t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64)
+ {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+ }
+
+ void MD5Final(byte digest[16], MD5Context *ctx)
+ {
+ int count = (int)(ctx->bytes[0] & 0x3f); /* Bytes in ctx->in */
+ byte *p = (byte *)ctx->in + count; /* First unused byte */
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0)
+ { /* Padding forces an extra block */
+ memset(p, 0, count+8);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ p = (byte *)ctx->in;
+ count = 56;
+ }
+ memset(p, 0, count+8);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ MD5Transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx));
+ }
+
+ void MD5Transform(word32 buf[4], word32 const in[16])
+ {
+ register word32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+ }
+
+
+ void MyMD5(void *dest, void *orig, int len, unsigned int* key)
+ {
+ MD5Context context;
+ MD5Init(&context, key);
+ MD5Update(&context, (const unsigned char*)orig, len);
+ MD5Final((unsigned char*)dest, &context);
+ }
+
+
+ void GenHash(const char* src, char* dest, const char* xtab, unsigned int* key)
+ {
+ unsigned char bytes[16];
+
+ MyMD5((char*)bytes, (void*)src, strlen(src), key);
+
+ for (int i = 0; i < 16; i++)
+ {
+ *dest++ = xtab[bytes[i] / 16];
+ *dest++ = xtab[bytes[i] % 16];
+ }
+ *dest++ = 0;
+ }
+
+ unsigned int *key;
+ char* chars;
+
+ public:
+
+ ModuleMD5(InspIRCd* Me)
+ : Module(Me), key(NULL), chars(NULL)
+ {
+ ServerInstance->PublishInterface("HashRequest", this);
+ }
+
+ virtual ~ModuleMD5()
+ {
+ ServerInstance->UnpublishInterface("HashRequest", this);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRequest] = 1;
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ HashRequest* MD5 = (HashRequest*)request;
+
+ if (strcmp("KEY", request->GetId()) == 0)
+ {
+ this->key = (unsigned int*)MD5->GetKeyData();
+ }
+ else if (strcmp("HEX", request->GetId()) == 0)
+ {
+ this->chars = (char*)MD5->GetOutputs();
+ }
+ else if (strcmp("SUM", request->GetId()) == 0)
+ {
+ static char data[MAXBUF];
+ GenHash((const char*)MD5->GetHashData(), data, chars ? chars : "0123456789abcdef", key);
+ return data;
+ }
+ else if (strcmp("NAME", request->GetId()) == 0)
+ {
+ return "md5";
+ }
+ else if (strcmp("RESET", request->GetId()) == 0)
+ {
+ this->chars = NULL;
+ this->key = NULL;
+ }
+ return NULL;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleMD5)
diff --git a/src/modules/m_messageflood.cpp b/src/modules/m_messageflood.cpp
index e5263719b..a942262ed 100644
--- a/src/modules/m_messageflood.cpp
+++ b/src/modules/m_messageflood.cpp
@@ -1 +1,304 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides channel mode +f (message flood protection) */ /** Holds flood settings and state for mode +f */ class floodsettings : public classbase { public: bool ban; int secs; int lines; time_t reset; std::map<userrec*,int> counters; floodsettings() : ban(0), secs(0), lines(0) {}; floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c) { reset = time(NULL) + secs; }; void addmessage(userrec* who) { std::map<userrec*,int>::iterator iter = counters.find(who); if (iter != counters.end()) { iter->second++; } else { counters[who] = 1; } if (time(NULL) > reset) { counters.clear(); reset = time(NULL) + secs; } } bool shouldkick(userrec* who) { std::map<userrec*,int>::iterator iter = counters.find(who); if (iter != counters.end()) { return (iter->second >= this->lines); } else return false; } void clear(userrec* who) { std::map<userrec*,int>::iterator iter = counters.find(who); if (iter != counters.end()) { counters.erase(iter); } } }; /** Handles channel mode +f */ class MsgFlood : public ModeHandler { public: MsgFlood(InspIRCd* Instance) : ModeHandler(Instance, 'f', 1, 0, false, MODETYPE_CHANNEL, false) { } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { floodsettings* x; if (channel->GetExt("flood",x)) return std::make_pair(true, (x->ban ? "*" : "")+ConvToStr(x->lines)+":"+ConvToStr(x->secs)); else return std::make_pair(false, parameter); } bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { /* When TS is equal, the alphabetically later one wins */ return (their_param < our_param); } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { floodsettings *f; if (adding) { char ndata[MAXBUF]; char* data = ndata; strlcpy(ndata,parameter.c_str(),MAXBUF); char* lines = data; char* secs = NULL; bool ban = false; if (*data == '*') { ban = true; lines++; } else { ban = false; } while (*data) { if (*data == ':') { *data = 0; data++; secs = data; break; } else data++; } if (secs) { /* Set up the flood parameters for this channel */ int nlines = atoi(lines); int nsecs = atoi(secs); if ((nlines<1) || (nsecs<1)) { source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); parameter.clear(); return MODEACTION_DENY; } else { if (!channel->GetExt("flood", f)) { parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs); floodsettings *f = new floodsettings(ban,nsecs,nlines); channel->Extend("flood",f); channel->SetMode('f', true); channel->SetModeParam('f', parameter.c_str(), true); return MODEACTION_ALLOW; } else { std::string cur_param = channel->GetModeParameter('f'); parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs); if (cur_param == parameter) { // mode params match return MODEACTION_DENY; } else { if (((nlines != f->lines) || (nsecs != f->secs)) && ((nsecs > 0) && (nlines > 0)) || (ban != f->ban)) { delete f; floodsettings *f = new floodsettings(ban,nsecs,nlines); channel->Shrink("flood"); channel->Extend("flood",f); channel->SetModeParam('f', cur_param.c_str(), false); channel->SetModeParam('f', parameter.c_str(), true); return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } } } } else { source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); parameter.clear(); return MODEACTION_DENY; } } else { if (channel->GetExt("flood", f)) { DELETE(f); channel->Shrink("flood"); channel->SetMode('f', false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleMsgFlood : public Module { MsgFlood* mf; public: ModuleMsgFlood(InspIRCd* Me) : Module(Me) { mf = new MsgFlood(ServerInstance); if (!ServerInstance->AddMode(mf, 'f')) throw ModuleException("Could not add new modes!"); } void ProcessMessages(userrec* user,chanrec* dest, const std::string &text) { if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'f') && dest->GetStatus(user) == STATUS_OP) { return; } floodsettings *f; if (dest->GetExt("flood", f)) { f->addmessage(user); if (f->shouldkick(user)) { /* Youre outttta here! */ f->clear(user); if (f->ban) { const char* parameters[3]; parameters[0] = dest->name; parameters[1] = "+b"; parameters[2] = user->MakeWildHost(); ServerInstance->SendMode(parameters,3,user); std::deque<std::string> n; /* Propogate the ban to other servers. * We dont know what protocol we may be using, * so this event is picked up by our protocol * module and formed into a ban command that * suits the protocol in use. */ n.push_back(dest->name); n.push_back("+b"); n.push_back(user->MakeWildHost()); Event rmode((char *)&n, NULL, "send_mode"); rmode.Send(ServerInstance); } char kickmessage[MAXBUF]; snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %d lines in %d secs)", f->lines, f->secs); dest->ServerKickUser(user, kickmessage, true); } } } virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_CHANNEL) { ProcessMessages(user,(chanrec*)dest,text); } } virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_CHANNEL) { ProcessMessages(user,(chanrec*)dest,text); } } void OnChannelDelete(chanrec* chan) { floodsettings* f; if (chan->GetExt("flood", f)) { DELETE(f); chan->Shrink("flood"); } } void Implements(char* List) { List[I_OnChannelDelete] = List[I_OnUserNotice] = List[I_OnUserMessage] = 1; } virtual ~ModuleMsgFlood() { ServerInstance->Modes->DelMode(mf); DELETE(mf); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleMsgFlood) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides channel mode +f (message flood protection) */
+
+/** Holds flood settings and state for mode +f
+ */
+class floodsettings : public classbase
+{
+ public:
+ bool ban;
+ int secs;
+ int lines;
+ time_t reset;
+ std::map<userrec*,int> counters;
+
+ floodsettings() : ban(0), secs(0), lines(0) {};
+ floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
+ {
+ reset = time(NULL) + secs;
+ };
+
+ void addmessage(userrec* who)
+ {
+ std::map<userrec*,int>::iterator iter = counters.find(who);
+ if (iter != counters.end())
+ {
+ iter->second++;
+ }
+ else
+ {
+ counters[who] = 1;
+ }
+ if (time(NULL) > reset)
+ {
+ counters.clear();
+ reset = time(NULL) + secs;
+ }
+ }
+
+ bool shouldkick(userrec* who)
+ {
+ std::map<userrec*,int>::iterator iter = counters.find(who);
+ if (iter != counters.end())
+ {
+ return (iter->second >= this->lines);
+ }
+ else return false;
+ }
+
+ void clear(userrec* who)
+ {
+ std::map<userrec*,int>::iterator iter = counters.find(who);
+ if (iter != counters.end())
+ {
+ counters.erase(iter);
+ }
+ }
+};
+
+/** Handles channel mode +f
+ */
+class MsgFlood : public ModeHandler
+{
+ public:
+ MsgFlood(InspIRCd* Instance) : ModeHandler(Instance, 'f', 1, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ floodsettings* x;
+ if (channel->GetExt("flood",x))
+ return std::make_pair(true, (x->ban ? "*" : "")+ConvToStr(x->lines)+":"+ConvToStr(x->secs));
+ else
+ return std::make_pair(false, parameter);
+ }
+
+ bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+ {
+ /* When TS is equal, the alphabetically later one wins */
+ return (their_param < our_param);
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ floodsettings *f;
+
+ if (adding)
+ {
+ char ndata[MAXBUF];
+ char* data = ndata;
+ strlcpy(ndata,parameter.c_str(),MAXBUF);
+ char* lines = data;
+ char* secs = NULL;
+ bool ban = false;
+ if (*data == '*')
+ {
+ ban = true;
+ lines++;
+ }
+ else
+ {
+ ban = false;
+ }
+ while (*data)
+ {
+ if (*data == ':')
+ {
+ *data = 0;
+ data++;
+ secs = data;
+ break;
+ }
+ else data++;
+ }
+ if (secs)
+ {
+ /* Set up the flood parameters for this channel */
+ int nlines = atoi(lines);
+ int nsecs = atoi(secs);
+ if ((nlines<1) || (nsecs<1))
+ {
+ source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
+ parameter.clear();
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ if (!channel->GetExt("flood", f))
+ {
+ parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
+ floodsettings *f = new floodsettings(ban,nsecs,nlines);
+ channel->Extend("flood",f);
+ channel->SetMode('f', true);
+ channel->SetModeParam('f', parameter.c_str(), true);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ std::string cur_param = channel->GetModeParameter('f');
+ parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
+ if (cur_param == parameter)
+ {
+ // mode params match
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ if (((nlines != f->lines) || (nsecs != f->secs)) && ((nsecs > 0) && (nlines > 0)) || (ban != f->ban))
+ {
+ delete f;
+ floodsettings *f = new floodsettings(ban,nsecs,nlines);
+ channel->Shrink("flood");
+ channel->Extend("flood",f);
+ channel->SetModeParam('f', cur_param.c_str(), false);
+ channel->SetModeParam('f', parameter.c_str(), true);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
+ parameter.clear();
+ return MODEACTION_DENY;
+ }
+ }
+ else
+ {
+ if (channel->GetExt("flood", f))
+ {
+ DELETE(f);
+ channel->Shrink("flood");
+ channel->SetMode('f', false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleMsgFlood : public Module
+{
+
+ MsgFlood* mf;
+
+ public:
+
+ ModuleMsgFlood(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mf = new MsgFlood(ServerInstance);
+ if (!ServerInstance->AddMode(mf, 'f'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void ProcessMessages(userrec* user,chanrec* dest, const std::string &text)
+ {
+ if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'f') && dest->GetStatus(user) == STATUS_OP)
+ {
+ return;
+ }
+
+ floodsettings *f;
+ if (dest->GetExt("flood", f))
+ {
+ f->addmessage(user);
+ if (f->shouldkick(user))
+ {
+ /* Youre outttta here! */
+ f->clear(user);
+ if (f->ban)
+ {
+ const char* parameters[3];
+ parameters[0] = dest->name;
+ parameters[1] = "+b";
+ parameters[2] = user->MakeWildHost();
+ ServerInstance->SendMode(parameters,3,user);
+ std::deque<std::string> n;
+ /* Propogate the ban to other servers.
+ * We dont know what protocol we may be using,
+ * so this event is picked up by our protocol
+ * module and formed into a ban command that
+ * suits the protocol in use.
+ */
+ n.push_back(dest->name);
+ n.push_back("+b");
+ n.push_back(user->MakeWildHost());
+ Event rmode((char *)&n, NULL, "send_mode");
+ rmode.Send(ServerInstance);
+ }
+ char kickmessage[MAXBUF];
+ snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %d lines in %d secs)", f->lines, f->secs);
+ dest->ServerKickUser(user, kickmessage, true);
+ }
+ }
+ }
+
+ virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+ {
+ if (target_type == TYPE_CHANNEL)
+ {
+ ProcessMessages(user,(chanrec*)dest,text);
+ }
+ }
+
+ virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+ {
+ if (target_type == TYPE_CHANNEL)
+ {
+ ProcessMessages(user,(chanrec*)dest,text);
+ }
+ }
+
+ void OnChannelDelete(chanrec* chan)
+ {
+ floodsettings* f;
+ if (chan->GetExt("flood", f))
+ {
+ DELETE(f);
+ chan->Shrink("flood");
+ }
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnChannelDelete] = List[I_OnUserNotice] = List[I_OnUserMessage] = 1;
+ }
+
+ virtual ~ModuleMsgFlood()
+ {
+ ServerInstance->Modes->DelMode(mf);
+ DELETE(mf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleMsgFlood)
diff --git a/src/modules/m_namesx.cpp b/src/modules/m_namesx.cpp
index 37f584331..c45d777f8 100644
--- a/src/modules/m_namesx.cpp
+++ b/src/modules/m_namesx.cpp
@@ -1 +1,127 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" static const char* dummy = "ON"; /* $ModDesc: Provides aliases of commands. */ class ModuleNamesX : public Module { public: ModuleNamesX(InspIRCd* Me) : Module(Me) { } void Implements(char* List) { List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1; } virtual ~ModuleNamesX() { } void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { if ((displayable) && (extname == "NAMESX")) proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled"); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void On005Numeric(std::string &output) { output.append(" NAMESX"); } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { irc::string c = command.c_str(); /* We don't actually create a proper command handler class for PROTOCTL, * because other modules might want to have PROTOCTL hooks too. * Therefore, we just hook its as an unvalidated command therefore we * can capture it even if it doesnt exist! :-) */ if (c == "PROTOCTL") { if ((pcnt) && (!strcasecmp(parameters[0],"NAMESX"))) { user->Extend("NAMESX",dummy); return 1; } } return 0; } virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist) { if (user->GetExt("NAMESX")) { char list[MAXBUF]; size_t dlen, curlen; dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name); int numusers = 0; char* ptr = list + dlen; if (!ulist) ulist = Ptr->GetUsers(); bool has_user = Ptr->HasUser(user); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if ((!has_user) && (i->first->IsModeSet('i'))) continue; if (i->first->Visibility && !i->first->Visibility->VisibleTo(user)) continue; size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", Ptr->GetAllPrefixChars(i->first), i->second.c_str()); /* OnUserList can change this - reset it back to normal */ i->second = i->first->nick; curlen += ptrlen; ptr += ptrlen; numusers++; if (curlen > (480-NICKMAX)) { /* list overflowed into multiple numerics */ user->WriteServ(std::string(list)); /* reset our lengths */ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name); ptr = list + dlen; ptrlen = 0; numusers = 0; } } /* if whats left in the list isnt empty, send it */ if (numusers) { user->WriteServ(std::string(list)); } user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name); return 1; } return 0; } }; MODULE_INIT(ModuleNamesX) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+static const char* dummy = "ON";
+
+/* $ModDesc: Provides aliases of commands. */
+
+class ModuleNamesX : public Module
+{
+ public:
+
+ ModuleNamesX(InspIRCd* Me)
+ : Module(Me)
+ {
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1;
+ }
+
+ virtual ~ModuleNamesX()
+ {
+ }
+
+ void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable)
+ {
+ if ((displayable) && (extname == "NAMESX"))
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled");
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" NAMESX");
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ irc::string c = command.c_str();
+ /* We don't actually create a proper command handler class for PROTOCTL,
+ * because other modules might want to have PROTOCTL hooks too.
+ * Therefore, we just hook its as an unvalidated command therefore we
+ * can capture it even if it doesnt exist! :-)
+ */
+ if (c == "PROTOCTL")
+ {
+ if ((pcnt) && (!strcasecmp(parameters[0],"NAMESX")))
+ {
+ user->Extend("NAMESX",dummy);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist)
+ {
+ if (user->GetExt("NAMESX"))
+ {
+ char list[MAXBUF];
+ size_t dlen, curlen;
+ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name);
+ int numusers = 0;
+ char* ptr = list + dlen;
+
+ if (!ulist)
+ ulist = Ptr->GetUsers();
+
+ bool has_user = Ptr->HasUser(user);
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if ((!has_user) && (i->first->IsModeSet('i')))
+ continue;
+
+ if (i->first->Visibility && !i->first->Visibility->VisibleTo(user))
+ continue;
+
+ size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", Ptr->GetAllPrefixChars(i->first), i->second.c_str());
+ /* OnUserList can change this - reset it back to normal */
+ i->second = i->first->nick;
+ curlen += ptrlen;
+ ptr += ptrlen;
+ numusers++;
+ if (curlen > (480-NICKMAX))
+ {
+ /* list overflowed into multiple numerics */
+ user->WriteServ(std::string(list));
+ /* reset our lengths */
+ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name);
+ ptr = list + dlen;
+ ptrlen = 0;
+ numusers = 0;
+ }
+ }
+ /* if whats left in the list isnt empty, send it */
+ if (numusers)
+ {
+ user->WriteServ(std::string(list));
+ }
+ user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
+ return 1;
+ }
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleNamesX)
diff --git a/src/modules/m_nicklock.cpp b/src/modules/m_nicklock.cpp
index 94c934e6f..26d5bfbd7 100644
--- a/src/modules/m_nicklock.cpp
+++ b/src/modules/m_nicklock.cpp
@@ -1 +1,159 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" /* $ModDesc: Provides the NICKLOCK command, allows an oper to chage a users nick and lock them to it until they quit */ /** Handle /NICKLOCK */ class cmd_nicklock : public command_t { char* dummy; public: cmd_nicklock (InspIRCd* Instance) : command_t(Instance,"NICKLOCK", 'o', 2) { this->source = "m_nicklock.so"; syntax = "<oldnick> <newnick>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { userrec* source = ServerInstance->FindNick(parameters[0]); irc::string server; irc::string me; // check user exists if (!source) { return CMD_FAILURE; } // check if user is locked if (source->GetExt("nick_locked", dummy)) { user->WriteServ("946 %s %s :This user's nickname is already locked.",user->nick,source->nick); return CMD_FAILURE; } // check nick is valid if (!ServerInstance->IsNick(parameters[1])) { return CMD_FAILURE; } // let others know ServerInstance->WriteOpers(std::string(user->nick)+" used NICKLOCK to change and hold "+parameters[0]+" to "+parameters[1]); if (!source->ForceNickChange(parameters[1])) { // ugh, nickchange failed for some reason -- possibly existing nick? userrec::QuitUser(ServerInstance, source, "Nickname collision"); return CMD_FAILURE; } // give them a lock flag source->Extend("nick_locked", "ON"); /* route */ return CMD_SUCCESS; } }; /** Handle /NICKUNLOCK */ class cmd_nickunlock : public command_t { public: cmd_nickunlock (InspIRCd* Instance) : command_t(Instance,"NICKUNLOCK", 'o', 1) { this->source = "m_nickunlock.so"; syntax = "<locked-nick>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* source = ServerInstance->FindNick(parameters[0]); if (source) { source->Shrink("nick_locked"); user->WriteServ("945 %s %s :Nickname now unlocked.",user->nick,source->nick); ServerInstance->WriteOpers(std::string(user->nick)+" used NICKUNLOCK on "+parameters[0]); return CMD_SUCCESS; } return CMD_FAILURE; } }; class ModuleNickLock : public Module { cmd_nicklock* cmd1; cmd_nickunlock* cmd2; char* n; public: ModuleNickLock(InspIRCd* Me) : Module(Me) { cmd1 = new cmd_nicklock(ServerInstance); cmd2 = new cmd_nickunlock(ServerInstance); ServerInstance->AddCommand(cmd1); ServerInstance->AddCommand(cmd2); } virtual ~ModuleNickLock() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnUserPreNick] = List[I_OnUserQuit] = List[I_OnCleanup] = 1; } virtual int OnUserPreNick(userrec* user, const std::string &newnick) { if (user->GetExt("nick_locked", n)) { user->WriteServ("447 %s :You cannot change your nickname (your nick is locked)",user->nick); return 1; } return 0; } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { user->Shrink("nick_locked"); } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_USER) { userrec* user = (userrec*)item; user->Shrink("nick_locked"); } } }; MODULE_INIT(ModuleNickLock) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+
+/* $ModDesc: Provides the NICKLOCK command, allows an oper to chage a users nick and lock them to it until they quit */
+
+/** Handle /NICKLOCK
+ */
+class cmd_nicklock : public command_t
+{
+ char* dummy;
+ public:
+ cmd_nicklock (InspIRCd* Instance) : command_t(Instance,"NICKLOCK", 'o', 2)
+ {
+ this->source = "m_nicklock.so";
+ syntax = "<oldnick> <newnick>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* source = ServerInstance->FindNick(parameters[0]);
+ irc::string server;
+ irc::string me;
+
+ // check user exists
+ if (!source)
+ {
+ return CMD_FAILURE;
+ }
+
+ // check if user is locked
+ if (source->GetExt("nick_locked", dummy))
+ {
+ user->WriteServ("946 %s %s :This user's nickname is already locked.",user->nick,source->nick);
+ return CMD_FAILURE;
+ }
+
+ // check nick is valid
+ if (!ServerInstance->IsNick(parameters[1]))
+ {
+ return CMD_FAILURE;
+ }
+
+ // let others know
+ ServerInstance->WriteOpers(std::string(user->nick)+" used NICKLOCK to change and hold "+parameters[0]+" to "+parameters[1]);
+
+ if (!source->ForceNickChange(parameters[1]))
+ {
+ // ugh, nickchange failed for some reason -- possibly existing nick?
+ userrec::QuitUser(ServerInstance, source, "Nickname collision");
+ return CMD_FAILURE;
+ }
+
+ // give them a lock flag
+ source->Extend("nick_locked", "ON");
+
+ /* route */
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /NICKUNLOCK
+ */
+class cmd_nickunlock : public command_t
+{
+ public:
+ cmd_nickunlock (InspIRCd* Instance) : command_t(Instance,"NICKUNLOCK", 'o', 1)
+ {
+ this->source = "m_nickunlock.so";
+ syntax = "<locked-nick>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* source = ServerInstance->FindNick(parameters[0]);
+ if (source)
+ {
+ source->Shrink("nick_locked");
+ user->WriteServ("945 %s %s :Nickname now unlocked.",user->nick,source->nick);
+ ServerInstance->WriteOpers(std::string(user->nick)+" used NICKUNLOCK on "+parameters[0]);
+ return CMD_SUCCESS;
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+
+class ModuleNickLock : public Module
+{
+ cmd_nicklock* cmd1;
+ cmd_nickunlock* cmd2;
+ char* n;
+ public:
+ ModuleNickLock(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ cmd1 = new cmd_nicklock(ServerInstance);
+ cmd2 = new cmd_nickunlock(ServerInstance);
+ ServerInstance->AddCommand(cmd1);
+ ServerInstance->AddCommand(cmd2);
+ }
+
+ virtual ~ModuleNickLock()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreNick] = List[I_OnUserQuit] = List[I_OnCleanup] = 1;
+ }
+
+ virtual int OnUserPreNick(userrec* user, const std::string &newnick)
+ {
+ if (user->GetExt("nick_locked", n))
+ {
+ user->WriteServ("447 %s :You cannot change your nickname (your nick is locked)",user->nick);
+ return 1;
+ }
+ return 0;
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ user->Shrink("nick_locked");
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ user->Shrink("nick_locked");
+ }
+ }
+};
+
+MODULE_INIT(ModuleNickLock)
diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp
index b3445155a..05dbd69ca 100644
--- a/src/modules/m_noctcp.cpp
+++ b/src/modules/m_noctcp.cpp
@@ -1 +1,107 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style channel mode +c */ class NoCTCP : public ModeHandler { public: NoCTCP(InspIRCd* Instance) : ModeHandler(Instance, 'C', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('C')) { channel->SetMode('C',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('C')) { channel->SetMode('C',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleNoCTCP : public Module { NoCTCP* nc; public: ModuleNoCTCP(InspIRCd* Me) : Module(Me) { nc = new NoCTCP(ServerInstance); if (!ServerInstance->AddMode(nc, 'C')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; } virtual int OnUserPreMessage(userrec* 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 int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user))) { chanrec* c = (chanrec*)dest; if (c->IsModeSet('C')) { if ((text.length()) && (text[0] == '\1')) { if (strncmp(text.c_str(),"\1ACTION ",8)) { user->WriteServ("492 %s %s :Can't send CTCP to channel (+C set)",user->nick, c->name); return 1; } } } } return 0; } virtual ~ModuleNoCTCP() { ServerInstance->Modes->DelMode(nc); DELETE(nc); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleNoCTCP) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style channel mode +c */
+
+class NoCTCP : public ModeHandler
+{
+ public:
+ NoCTCP(InspIRCd* Instance) : ModeHandler(Instance, 'C', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('C'))
+ {
+ channel->SetMode('C',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('C'))
+ {
+ channel->SetMode('C',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleNoCTCP : public Module
+{
+
+ NoCTCP* nc;
+
+ public:
+
+ ModuleNoCTCP(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ nc = new NoCTCP(ServerInstance);
+ if (!ServerInstance->AddMode(nc, 'C'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
+ }
+
+ virtual int OnUserPreMessage(userrec* 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 int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ {
+ chanrec* c = (chanrec*)dest;
+ if (c->IsModeSet('C'))
+ {
+ if ((text.length()) && (text[0] == '\1'))
+ {
+ if (strncmp(text.c_str(),"\1ACTION ",8))
+ {
+ user->WriteServ("492 %s %s :Can't send CTCP to channel (+C set)",user->nick, c->name);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleNoCTCP()
+ {
+ ServerInstance->Modes->DelMode(nc);
+ DELETE(nc);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleNoCTCP)
diff --git a/src/modules/m_noinvite.cpp b/src/modules/m_noinvite.cpp
index 76e2616e3..26965d319 100644
--- a/src/modules/m_noinvite.cpp
+++ b/src/modules/m_noinvite.cpp
@@ -1 +1,88 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style channel mode +V */ class NoInvite : public ModeHandler { public: NoInvite(InspIRCd* Instance) : ModeHandler(Instance, 'V', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('V')) { channel->SetMode('V',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('V')) { channel->SetMode('V',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleNoInvite : public Module { NoInvite *ni; public: ModuleNoInvite(InspIRCd* Me) : Module(Me) { ni = new NoInvite(ServerInstance); if (!ServerInstance->AddMode(ni, 'V')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreInvite] = 1; } virtual int OnUserPreInvite(userrec* user,userrec* dest,chanrec* channel) { if (channel->IsModeSet('V')) { user->WriteServ("492 %s %s :Can't invite %s to channel (+V set)",user->nick, channel->name, dest->nick); return 1; } return 0; } virtual ~ModuleNoInvite() { ServerInstance->Modes->DelMode(ni); DELETE(ni); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleNoInvite) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style channel mode +V */
+
+class NoInvite : public ModeHandler
+{
+ public:
+ NoInvite(InspIRCd* Instance) : ModeHandler(Instance, 'V', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('V'))
+ {
+ channel->SetMode('V',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('V'))
+ {
+ channel->SetMode('V',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleNoInvite : public Module
+{
+ NoInvite *ni;
+ public:
+
+ ModuleNoInvite(InspIRCd* Me) : Module(Me)
+ {
+ ni = new NoInvite(ServerInstance);
+ if (!ServerInstance->AddMode(ni, 'V'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreInvite] = 1;
+ }
+
+ virtual int OnUserPreInvite(userrec* user,userrec* dest,chanrec* channel)
+ {
+ if (channel->IsModeSet('V'))
+ {
+ user->WriteServ("492 %s %s :Can't invite %s to channel (+V set)",user->nick, channel->name, dest->nick);
+ return 1;
+ }
+ return 0;
+ }
+
+ virtual ~ModuleNoInvite()
+ {
+ ServerInstance->Modes->DelMode(ni);
+ DELETE(ni);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleNoInvite)
diff --git a/src/modules/m_nokicks.cpp b/src/modules/m_nokicks.cpp
index ac78b4d7a..315eb7399 100644
--- a/src/modules/m_nokicks.cpp
+++ b/src/modules/m_nokicks.cpp
@@ -1 +1,105 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style channel mode +Q */ class NoKicks : public ModeHandler { public: NoKicks(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('Q')) { channel->SetMode('Q',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('Q')) { channel->SetMode('Q',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleNoKicks : public Module { NoKicks* nk; public: ModuleNoKicks(InspIRCd* Me) : Module(Me) { nk = new NoKicks(ServerInstance); if (!ServerInstance->AddMode(nk, 'Q')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnAccessCheck] = 1; } virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { if (access_type == AC_KICK) { if (channel->IsModeSet('Q')) { if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server)) { // ulines can still kick with +Q in place return ACR_ALLOW; } else { // nobody else can (not even opers with override, and founders) source->WriteServ("484 %s %s :Can't kick user %s from channel (+Q set)",source->nick, channel->name,dest->nick); return ACR_DENY; } } } return ACR_DEFAULT; } virtual ~ModuleNoKicks() { ServerInstance->Modes->DelMode(nk); DELETE(nk); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleNoKicks) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style channel mode +Q */
+
+class NoKicks : public ModeHandler
+{
+ public:
+ NoKicks(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('Q'))
+ {
+ channel->SetMode('Q',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('Q'))
+ {
+ channel->SetMode('Q',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleNoKicks : public Module
+{
+
+ NoKicks* nk;
+
+ public:
+
+ ModuleNoKicks(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ nk = new NoKicks(ServerInstance);
+ if (!ServerInstance->AddMode(nk, 'Q'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnAccessCheck] = 1;
+ }
+
+ virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
+ {
+ if (access_type == AC_KICK)
+ {
+ if (channel->IsModeSet('Q'))
+ {
+ if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server))
+ {
+ // ulines can still kick with +Q in place
+ return ACR_ALLOW;
+ }
+ else
+ {
+ // nobody else can (not even opers with override, and founders)
+ source->WriteServ("484 %s %s :Can't kick user %s from channel (+Q set)",source->nick, channel->name,dest->nick);
+ return ACR_DENY;
+ }
+ }
+ }
+ return ACR_DEFAULT;
+ }
+
+ virtual ~ModuleNoKicks()
+ {
+ ServerInstance->Modes->DelMode(nk);
+ DELETE(nk);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+
+MODULE_INIT(ModuleNoKicks)
diff --git a/src/modules/m_nonicks.cpp b/src/modules/m_nonicks.cpp
index d6e6553e9..bb1843a95 100644
--- a/src/modules/m_nonicks.cpp
+++ b/src/modules/m_nonicks.cpp
@@ -1 +1,102 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "configreader.h" /* $ModDesc: Provides support for channel mode +N which prevents nick changes on channel */ class NoNicks : public ModeHandler { public: NoNicks(InspIRCd* Instance) : ModeHandler(Instance, 'N', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('N')) { channel->SetMode('N',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('N')) { channel->SetMode('N',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleNoNickChange : public Module { NoNicks* nn; public: ModuleNoNickChange(InspIRCd* Me) : Module(Me) { nn = new NoNicks(ServerInstance); ServerInstance->AddMode(nn, 'N'); } virtual ~ModuleNoNickChange() { ServerInstance->Modes->DelMode(nn); DELETE(nn); } virtual Version GetVersion() { return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnUserPreNick] = 1; } virtual int OnUserPreNick(userrec* user, const std::string &newnick) { if (IS_LOCAL(user)) { for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++) { chanrec* curr = i->first; if (curr->IsModeSet('N')) { if (CHANOPS_EXEMPT(ServerInstance, 'N') && curr->GetStatus(user) == STATUS_OP) continue; user->WriteServ("447 %s :Can't change nickname while on %s (+N is set)", user->nick, curr->name); return 1; } } } return 0; } }; MODULE_INIT(ModuleNoNickChange) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "configreader.h"
+
+/* $ModDesc: Provides support for channel mode +N which prevents nick changes on channel */
+
+class NoNicks : public ModeHandler
+{
+ public:
+ NoNicks(InspIRCd* Instance) : ModeHandler(Instance, 'N', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('N'))
+ {
+ channel->SetMode('N',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('N'))
+ {
+ channel->SetMode('N',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleNoNickChange : public Module
+{
+ NoNicks* nn;
+ public:
+ ModuleNoNickChange(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ nn = new NoNicks(ServerInstance);
+ ServerInstance->AddMode(nn, 'N');
+ }
+
+ virtual ~ModuleNoNickChange()
+ {
+ ServerInstance->Modes->DelMode(nn);
+ DELETE(nn);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreNick] = 1;
+ }
+
+ virtual int OnUserPreNick(userrec* user, const std::string &newnick)
+ {
+ if (IS_LOCAL(user))
+ {
+ for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ {
+ chanrec* curr = i->first;
+
+ if (curr->IsModeSet('N'))
+ {
+ if (CHANOPS_EXEMPT(ServerInstance, 'N') && curr->GetStatus(user) == STATUS_OP)
+ continue;
+
+ user->WriteServ("447 %s :Can't change nickname while on %s (+N is set)", user->nick, curr->name);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleNoNickChange)
diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp
index fd4c474cb..ae926b4bb 100644
--- a/src/modules/m_nonotice.cpp
+++ b/src/modules/m_nonotice.cpp
@@ -1 +1,103 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style channel mode +T */ class NoNotice : public ModeHandler { public: NoNotice(InspIRCd* Instance) : ModeHandler(Instance, 'T', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('T')) { channel->SetMode('T',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('T')) { channel->SetMode('T',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleNoNotice : public Module { NoNotice* nt; public: ModuleNoNotice(InspIRCd* Me) : Module(Me) { nt = new NoNotice(ServerInstance); if (!ServerInstance->AddMode(nt, 'T')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreNotice] = 1; } virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user))) { chanrec* c = (chanrec*)dest; if (c->IsModeSet('T')) { if ((ServerInstance->ULine(user->server)) || (c->GetStatus(user) == STATUS_OP) || (c->GetStatus(user) == STATUS_HOP)) { // ops and halfops can still /NOTICE the channel return 0; } else { user->WriteServ("404 %s %s :Can't send NOTICE to channel (+T set)",user->nick, c->name); return 1; } } } return 0; } virtual ~ModuleNoNotice() { ServerInstance->Modes->DelMode(nt); DELETE(nt); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleNoNotice) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style channel mode +T */
+
+class NoNotice : public ModeHandler
+{
+ public:
+ NoNotice(InspIRCd* Instance) : ModeHandler(Instance, 'T', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('T'))
+ {
+ channel->SetMode('T',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('T'))
+ {
+ channel->SetMode('T',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleNoNotice : public Module
+{
+
+ NoNotice* nt;
+ public:
+
+ ModuleNoNotice(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ nt = new NoNotice(ServerInstance);
+ if (!ServerInstance->AddMode(nt, 'T'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreNotice] = 1;
+ }
+
+ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ {
+ chanrec* c = (chanrec*)dest;
+ if (c->IsModeSet('T'))
+ {
+ if ((ServerInstance->ULine(user->server)) || (c->GetStatus(user) == STATUS_OP) || (c->GetStatus(user) == STATUS_HOP))
+ {
+ // ops and halfops can still /NOTICE the channel
+ return 0;
+ }
+ else
+ {
+ user->WriteServ("404 %s %s :Can't send NOTICE to channel (+T set)",user->nick, c->name);
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleNoNotice()
+ {
+ ServerInstance->Modes->DelMode(nt);
+ DELETE(nt);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleNoNotice)
diff --git a/src/modules/m_oper_hash.cpp b/src/modules/m_oper_hash.cpp
index a3989ad91..b4661741b 100644
--- a/src/modules/m_oper_hash.cpp
+++ b/src/modules/m_oper_hash.cpp
@@ -1 +1,163 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Allows for hashed oper passwords */ /* $ModDep: m_hash.h */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "m_hash.h" typedef std::map<irc::string, Module*> hashymodules; /* Handle /MKPASSWD */ class cmd_mkpasswd : public command_t { Module* Sender; hashymodules &hashers; std::deque<std::string> &names; public: cmd_mkpasswd (InspIRCd* Instance, Module* S, hashymodules &h, std::deque<std::string> &n) : command_t(Instance,"MKPASSWD", 'o', 2), Sender(S), hashers(h), names(n) { this->source = "m_oper_hash.so"; syntax = "<hashtype> <any-text>"; } void MakeHash(userrec* user, const char* algo, const char* stuff) { /* Lets see if they gave us an algorithm which has been implemented */ hashymodules::iterator x = hashers.find(algo); if (x != hashers.end()) { /* Yup, reset it first (Always ALWAYS do this) */ HashResetRequest(Sender, x->second).Send(); /* Now attempt to generate a hash */ user->WriteServ("NOTICE %s :%s hashed password for %s is %s",user->nick, algo, stuff, HashSumRequest(Sender, x->second, stuff).Send() ); } else { /* I dont do flying, bob. */ user->WriteServ("NOTICE %s :Unknown hash type, valid hash types are: %s", user->nick, irc::stringjoiner(", ", names, 0, names.size() - 1).GetJoined().c_str() ); } } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { MakeHash(user, parameters[0], parameters[1]); /* NOTE: Don't propogate this across the network! * We dont want plaintext passes going all over the place... * To make sure it goes nowhere, return CMD_FAILURE! */ return CMD_FAILURE; } }; class ModuleOperHash : public Module { cmd_mkpasswd* mycommand; ConfigReader* Conf; hashymodules hashers; /* List of modules which implement HashRequest */ std::deque<std::string> names; /* Module names which implement HashRequest */ public: ModuleOperHash(InspIRCd* Me) : Module(Me) { /* Read the config file first */ Conf = NULL; OnRehash(NULL,""); ServerInstance->UseInterface("HashRequest"); /* Find all modules which implement the interface 'HashRequest' */ modulelist* ml = ServerInstance->FindInterface("HashRequest"); /* Did we find any modules? */ if (ml) { /* Yes, enumerate them all to find out the hashing algorithm name */ for (modulelist::iterator m = ml->begin(); m != ml->end(); m++) { /* Make a request to it for its name, its implementing * HashRequest so we know its safe to do this */ std::string name = HashNameRequest(this, *m).Send(); /* Build a map of them */ hashers[name.c_str()] = *m; names.push_back(name); } } else { throw ModuleException("I can't find any modules loaded which implement the HashRequest interface! You probably forgot to load a hashing module such as m_md5.so or m_sha256.so."); } mycommand = new cmd_mkpasswd(ServerInstance, this, hashers, names); ServerInstance->AddCommand(mycommand); } virtual ~ModuleOperHash() { ServerInstance->DoneWithInterface("HashRequest"); } void Implements(char* List) { List[I_OnRehash] = List[I_OnOperCompare] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { /* Re-read configuration file */ if (Conf) delete Conf; Conf = new ConfigReader(ServerInstance); } virtual int OnOperCompare(const std::string &data, const std::string &input, int tagnumber) { /* First, lets see what hash theyre using on this oper */ std::string hashtype = Conf->ReadValue("oper", "hash", tagnumber); hashymodules::iterator x = hashers.find(hashtype.c_str()); /* Is this a valid hash name? (case insensitive) */ if (x != hashers.end()) { /* Reset the hashing module */ HashResetRequest(this, x->second).Send(); /* Compare the hash in the config to the generated hash */ if (!strcasecmp(data.c_str(), HashSumRequest(this, x->second, input.c_str()).Send())) return 1; /* No match, and must be hashed, forbid */ else return -1; } /* Not a hash, fall through to strcmp in core */ return 0; } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleOperHash) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Allows for hashed oper passwords */
+/* $ModDep: m_hash.h */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_hash.h"
+
+typedef std::map<irc::string, Module*> hashymodules;
+
+/* Handle /MKPASSWD
+ */
+class cmd_mkpasswd : public command_t
+{
+ Module* Sender;
+ hashymodules &hashers;
+ std::deque<std::string> &names;
+ public:
+ cmd_mkpasswd (InspIRCd* Instance, Module* S, hashymodules &h, std::deque<std::string> &n)
+ : command_t(Instance,"MKPASSWD", 'o', 2), Sender(S), hashers(h), names(n)
+ {
+ this->source = "m_oper_hash.so";
+ syntax = "<hashtype> <any-text>";
+ }
+
+ void MakeHash(userrec* user, const char* algo, const char* stuff)
+ {
+ /* Lets see if they gave us an algorithm which has been implemented */
+ hashymodules::iterator x = hashers.find(algo);
+ if (x != hashers.end())
+ {
+ /* Yup, reset it first (Always ALWAYS do this) */
+ HashResetRequest(Sender, x->second).Send();
+ /* Now attempt to generate a hash */
+ user->WriteServ("NOTICE %s :%s hashed password for %s is %s",user->nick, algo, stuff, HashSumRequest(Sender, x->second, stuff).Send() );
+ }
+ else
+ {
+ /* I dont do flying, bob. */
+ user->WriteServ("NOTICE %s :Unknown hash type, valid hash types are: %s", user->nick, irc::stringjoiner(", ", names, 0, names.size() - 1).GetJoined().c_str() );
+ }
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ MakeHash(user, parameters[0], parameters[1]);
+ /* NOTE: Don't propogate this across the network!
+ * We dont want plaintext passes going all over the place...
+ * To make sure it goes nowhere, return CMD_FAILURE!
+ */
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleOperHash : public Module
+{
+
+ cmd_mkpasswd* mycommand;
+ ConfigReader* Conf;
+ hashymodules hashers; /* List of modules which implement HashRequest */
+ std::deque<std::string> names; /* Module names which implement HashRequest */
+
+ public:
+
+ ModuleOperHash(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ /* Read the config file first */
+ Conf = NULL;
+ OnRehash(NULL,"");
+
+ ServerInstance->UseInterface("HashRequest");
+
+ /* Find all modules which implement the interface 'HashRequest' */
+ modulelist* ml = ServerInstance->FindInterface("HashRequest");
+
+ /* Did we find any modules? */
+ if (ml)
+ {
+ /* Yes, enumerate them all to find out the hashing algorithm name */
+ for (modulelist::iterator m = ml->begin(); m != ml->end(); m++)
+ {
+ /* Make a request to it for its name, its implementing
+ * HashRequest so we know its safe to do this
+ */
+ std::string name = HashNameRequest(this, *m).Send();
+ /* Build a map of them */
+ hashers[name.c_str()] = *m;
+ names.push_back(name);
+ }
+ }
+ else
+ {
+ throw ModuleException("I can't find any modules loaded which implement the HashRequest interface! You probably forgot to load a hashing module such as m_md5.so or m_sha256.so.");
+ }
+
+ mycommand = new cmd_mkpasswd(ServerInstance, this, hashers, names);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleOperHash()
+ {
+ ServerInstance->DoneWithInterface("HashRequest");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnOperCompare] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ /* Re-read configuration file */
+ if (Conf)
+ delete Conf;
+
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual int OnOperCompare(const std::string &data, const std::string &input, int tagnumber)
+ {
+ /* First, lets see what hash theyre using on this oper */
+ std::string hashtype = Conf->ReadValue("oper", "hash", tagnumber);
+ hashymodules::iterator x = hashers.find(hashtype.c_str());
+
+ /* Is this a valid hash name? (case insensitive) */
+ if (x != hashers.end())
+ {
+ /* Reset the hashing module */
+ HashResetRequest(this, x->second).Send();
+ /* Compare the hash in the config to the generated hash */
+ if (!strcasecmp(data.c_str(), HashSumRequest(this, x->second, input.c_str()).Send()))
+ return 1;
+ /* No match, and must be hashed, forbid */
+ else return -1;
+ }
+
+ /* Not a hash, fall through to strcmp in core */
+ return 0;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleOperHash)
diff --git a/src/modules/m_operchans.cpp b/src/modules/m_operchans.cpp
index 493904e35..6fd6c9e98 100644
--- a/src/modules/m_operchans.cpp
+++ b/src/modules/m_operchans.cpp
@@ -1 +1,97 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for oper-only chans via the +O channel mode */ class OperChans : public ModeHandler { public: /* This is an oper-only mode */ OperChans(InspIRCd* Instance) : ModeHandler(Instance, 'O', 0, 0, false, MODETYPE_CHANNEL, true) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('O')) { channel->SetMode('O',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('O')) { channel->SetMode('O',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleOperChans : public Module { OperChans* oc; public: ModuleOperChans(InspIRCd* Me) : Module(Me) { oc = new OperChans(ServerInstance); if (!ServerInstance->AddMode(oc, 'O')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreJoin] = 1; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (!IS_OPER(user)) { if (chan) { if (chan->IsModeSet('O')) { user->WriteServ("520 %s %s :Only IRC operators may join the channel %s (+O is set)",user->nick, chan->name,chan->name); return 1; } } } return 0; } virtual ~ModuleOperChans() { ServerInstance->Modes->DelMode(oc); DELETE(oc); } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR|VF_COMMON,API_VERSION); } }; MODULE_INIT(ModuleOperChans) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for oper-only chans via the +O channel mode */
+
+class OperChans : public ModeHandler
+{
+ public:
+ /* This is an oper-only mode */
+ OperChans(InspIRCd* Instance) : ModeHandler(Instance, 'O', 0, 0, false, MODETYPE_CHANNEL, true) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('O'))
+ {
+ channel->SetMode('O',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('O'))
+ {
+ channel->SetMode('O',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleOperChans : public Module
+{
+
+ OperChans* oc;
+ public:
+ ModuleOperChans(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ oc = new OperChans(ServerInstance);
+ if (!ServerInstance->AddMode(oc, 'O'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = 1;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (!IS_OPER(user))
+ {
+ if (chan)
+ {
+ if (chan->IsModeSet('O'))
+ {
+ user->WriteServ("520 %s %s :Only IRC operators may join the channel %s (+O is set)",user->nick, chan->name,chan->name);
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleOperChans()
+ {
+ ServerInstance->Modes->DelMode(oc);
+ DELETE(oc);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR|VF_COMMON,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleOperChans)
diff --git a/src/modules/m_operjoin.cpp b/src/modules/m_operjoin.cpp
index d69112eba..d12bc1932 100644
--- a/src/modules/m_operjoin.cpp
+++ b/src/modules/m_operjoin.cpp
@@ -1 +1,90 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.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; int tokenize(const string &str, std::vector<std::string> &tokens) { // skip delimiters at beginning. string::size_type lastPos = str.find_first_not_of(",", 0); // find first "non-delimiter". string::size_type pos = str.find_first_of(",", lastPos); while (string::npos != pos || 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: ModuleOperjoin(InspIRCd* Me) : Module(Me) { OnRehash(NULL, ""); } void Implements(char* List) { List[I_OnPostOper] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader* conf = new ConfigReader(ServerInstance); operChan = conf->ReadValue("operjoin", "channel", 0); operChans.clear(); if (!operChan.empty()) tokenize(operChan,operChans); DELETE(conf); } virtual ~ModuleOperjoin() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void OnPostOper(userrec* user, const std::string &opertype) { if (!IS_LOCAL(user)) return; for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++) if (ServerInstance->IsChannel(it->c_str())) chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true)); } }; MODULE_INIT(ModuleOperjoin) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.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;
+
+ int tokenize(const string &str, std::vector<std::string> &tokens)
+ {
+ // skip delimiters at beginning.
+ string::size_type lastPos = str.find_first_not_of(",", 0);
+ // find first "non-delimiter".
+ string::size_type pos = str.find_first_of(",", lastPos);
+
+ while (string::npos != pos || 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:
+ ModuleOperjoin(InspIRCd* Me) : Module(Me)
+ {
+ OnRehash(NULL, "");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPostOper] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader* conf = new ConfigReader(ServerInstance);
+
+ operChan = conf->ReadValue("operjoin", "channel", 0);
+ operChans.clear();
+ if (!operChan.empty())
+ tokenize(operChan,operChans);
+
+ DELETE(conf);
+ }
+
+ virtual ~ModuleOperjoin()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnPostOper(userrec* user, const std::string &opertype)
+ {
+ if (!IS_LOCAL(user))
+ return;
+
+ for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++)
+ if (ServerInstance->IsChannel(it->c_str()))
+ chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true));
+ }
+
+};
+
+MODULE_INIT(ModuleOperjoin)
diff --git a/src/modules/m_operlevels.cpp b/src/modules/m_operlevels.cpp
index 6d3aa7b14..918d444ac 100644
--- a/src/modules/m_operlevels.cpp
+++ b/src/modules/m_operlevels.cpp
@@ -1 +1,122 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */ class ModuleOperLevels : public Module { private: ConfigReader* conf; public: ModuleOperLevels(InspIRCd* Me) : Module(Me) { conf = new ConfigReader(ServerInstance); } virtual ~ModuleOperLevels() { DELETE(conf); } void Implements(char* List) { List[I_OnRehash] = List[I_OnKill] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { DELETE(conf); conf = new ConfigReader(ServerInstance); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual int OnKill(userrec* source, userrec* dest, const std::string &reason) { long dest_level = 0,source_level = 0; // oper killing an oper? if (IS_OPER(dest) && IS_OPER(source)) { for (int j =0; j < conf->Enumerate("type"); j++) { std::string typen = conf->ReadValue("type","name",j); if (!strcmp(typen.c_str(),dest->oper)) { dest_level = conf->ReadInteger("type","level",j,true); break; } } for (int k =0; k < conf->Enumerate("type"); k++) { std::string typen = conf->ReadValue("type","name",k); if (!strcmp(typen.c_str(),source->oper)) { source_level = conf->ReadInteger("type","level",k,true); break; } } if (dest_level > source_level) { ServerInstance->WriteOpers("Oper %s (level %d) attempted to /kill a higher oper: %s (level %d): Reason: %s",source->nick,source_level,dest->nick,dest_level,reason.c_str()); dest->WriteServ("NOTICE %s :Oper %s attempted to /kill you!",dest->nick,source->nick); source->WriteServ("481 %s :Permission Denied - Oper %s is a higher level than you",source->nick,dest->nick); return 1; } } return 0; } }; class ModuleOperLevelsFactory : public ModuleFactory { public: ModuleOperLevelsFactory() { } ~ModuleOperLevelsFactory() { } virtual Module * CreateModule(InspIRCd* Me) { return new ModuleOperLevels(Me); } }; extern "C" DllExport void * init_module( void ) { return new ModuleOperLevelsFactory; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */
+
+
+
+class ModuleOperLevels : public Module
+{
+
+ private:
+
+
+ ConfigReader* conf;
+
+ public:
+
+ ModuleOperLevels(InspIRCd* Me)
+ : Module(Me)
+ {
+
+
+ conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual ~ModuleOperLevels()
+ {
+ DELETE(conf);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnKill] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ DELETE(conf);
+ conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual int OnKill(userrec* source, userrec* dest, const std::string &reason)
+ {
+ long dest_level = 0,source_level = 0;
+
+ // oper killing an oper?
+ if (IS_OPER(dest) && IS_OPER(source))
+ {
+ for (int j =0; j < conf->Enumerate("type"); j++)
+ {
+ std::string typen = conf->ReadValue("type","name",j);
+ if (!strcmp(typen.c_str(),dest->oper))
+ {
+ dest_level = conf->ReadInteger("type","level",j,true);
+ break;
+ }
+ }
+ for (int k =0; k < conf->Enumerate("type"); k++)
+ {
+ std::string typen = conf->ReadValue("type","name",k);
+ if (!strcmp(typen.c_str(),source->oper))
+ {
+ source_level = conf->ReadInteger("type","level",k,true);
+ break;
+ }
+ }
+ if (dest_level > source_level)
+ {
+ ServerInstance->WriteOpers("Oper %s (level %d) attempted to /kill a higher oper: %s (level %d): Reason: %s",source->nick,source_level,dest->nick,dest_level,reason.c_str());
+ dest->WriteServ("NOTICE %s :Oper %s attempted to /kill you!",dest->nick,source->nick);
+ source->WriteServ("481 %s :Permission Denied - Oper %s is a higher level than you",source->nick,dest->nick);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+};
+
+class ModuleOperLevelsFactory : public ModuleFactory
+{
+ public:
+ ModuleOperLevelsFactory()
+ {
+ }
+
+ ~ModuleOperLevelsFactory()
+ {
+ }
+
+ virtual Module * CreateModule(InspIRCd* Me)
+ {
+ return new ModuleOperLevels(Me);
+ }
+
+};
+
+extern "C" DllExport void * init_module( void )
+{
+ return new ModuleOperLevelsFactory;
+}
+
diff --git a/src/modules/m_operlog.cpp b/src/modules/m_operlog.cpp
index 1a7d5bd8a..9bbdbef25 100644
--- a/src/modules/m_operlog.cpp
+++ b/src/modules/m_operlog.cpp
@@ -1 +1,75 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */ class ModuleOperLog : public Module { private: public: ModuleOperLog(InspIRCd* Me) : Module(Me) { } virtual ~ModuleOperLog() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnPreCommand] = List[I_On005Numeric] = 1; } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { /* If the command doesnt appear to be valid, we dont want to mess with it. */ if (!validated) return 0; if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command))) { command_t* thiscommand = ServerInstance->Parser->GetHandler(command); if ((thiscommand) && (thiscommand->flags_needed = 'o')) { std::string plist; for (int j = 0; j < pcnt; j++) plist.append(std::string(" ")+std::string(parameters[j])); ServerInstance->Log(DEFAULT,"OPERLOG: [%s!%s@%s] %s%s",user->nick,user->ident,user->host,command.c_str(),plist.c_str()); } } return 0; } virtual void On005Numeric(std::string &output) { output.append(" OPERLOG"); } }; MODULE_INIT(ModuleOperLog) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */
+
+class ModuleOperLog : public Module
+{
+ private:
+
+ public:
+ ModuleOperLog(InspIRCd* Me) : Module(Me)
+ {
+
+ }
+
+ virtual ~ModuleOperLog()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPreCommand] = List[I_On005Numeric] = 1;
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ /* If the command doesnt appear to be valid, we dont want to mess with it. */
+ if (!validated)
+ return 0;
+
+ if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command)))
+ {
+ command_t* thiscommand = ServerInstance->Parser->GetHandler(command);
+ if ((thiscommand) && (thiscommand->flags_needed = 'o'))
+ {
+ std::string plist;
+ for (int j = 0; j < pcnt; j++)
+ plist.append(std::string(" ")+std::string(parameters[j]));
+
+ ServerInstance->Log(DEFAULT,"OPERLOG: [%s!%s@%s] %s%s",user->nick,user->ident,user->host,command.c_str(),plist.c_str());
+ }
+ }
+
+ return 0;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" OPERLOG");
+ }
+
+};
+
+
+MODULE_INIT(ModuleOperLog)
diff --git a/src/modules/m_opermodes.cpp b/src/modules/m_opermodes.cpp
index 05d7a2b42..598f84e43 100644
--- a/src/modules/m_opermodes.cpp
+++ b/src/modules/m_opermodes.cpp
@@ -1 +1,109 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Sets (and unsets) modes on opers when they oper up */ class ModuleModesOnOper : public Module { private: ConfigReader *Conf; public: ModuleModesOnOper(InspIRCd* Me) : Module(Me) { Conf = new ConfigReader(ServerInstance); } void Implements(char* List) { List[I_OnPostOper] = List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { DELETE(Conf); Conf = new ConfigReader(ServerInstance); } virtual ~ModuleModesOnOper() { DELETE(Conf); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void OnPostOper(userrec* user, const std::string &opertype) { // whenever a user opers, go through the oper types, find their <type:modes>, // and if they have one apply their modes. The mode string can contain +modes // to add modes to the user or -modes to take modes from the user. for (int j =0; j < Conf->Enumerate("type"); j++) { std::string typen = Conf->ReadValue("type","name",j); if (!strcmp(typen.c_str(),user->oper)) { std::string ThisOpersModes = Conf->ReadValue("type","modes",j); if (!ThisOpersModes.empty()) { char first = *(ThisOpersModes.c_str()); if ((first != '+') && (first != '-')) ThisOpersModes = "+" + ThisOpersModes; std::string buf; stringstream ss(ThisOpersModes); vector<string> tokens; // split ThisOperModes into modes and mode params while (ss >> buf) tokens.push_back(buf); int size = tokens.size() + 1; const char** modes = new const char*[size]; modes[0] = user->nick; // process mode params int i = 1; for (unsigned int k = 0; k < tokens.size(); k++) { modes[i] = tokens[k].c_str(); i++; } std::deque<std::string> n; Event rmode((char *)&n, NULL, "send_mode_explicit"); for (unsigned int j = 0; j < tokens.size(); j++) n.push_back(modes[j]); rmode.Send(ServerInstance); ServerInstance->SendMode(modes, size, user); delete [] modes; } break; } } } }; MODULE_INIT(ModuleModesOnOper) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Sets (and unsets) modes on opers when they oper up */
+
+class ModuleModesOnOper : public Module
+{
+ private:
+
+
+ ConfigReader *Conf;
+
+ public:
+ ModuleModesOnOper(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnPostOper] = List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ DELETE(Conf);
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual ~ModuleModesOnOper()
+ {
+ DELETE(Conf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnPostOper(userrec* user, const std::string &opertype)
+ {
+ // whenever a user opers, go through the oper types, find their <type:modes>,
+ // and if they have one apply their modes. The mode string can contain +modes
+ // to add modes to the user or -modes to take modes from the user.
+ for (int j =0; j < Conf->Enumerate("type"); j++)
+ {
+ std::string typen = Conf->ReadValue("type","name",j);
+ if (!strcmp(typen.c_str(),user->oper))
+ {
+ std::string ThisOpersModes = Conf->ReadValue("type","modes",j);
+ if (!ThisOpersModes.empty())
+ {
+ char first = *(ThisOpersModes.c_str());
+ if ((first != '+') && (first != '-'))
+ ThisOpersModes = "+" + ThisOpersModes;
+
+ std::string buf;
+ stringstream ss(ThisOpersModes);
+ vector<string> tokens;
+
+ // split ThisOperModes into modes and mode params
+ while (ss >> buf)
+ tokens.push_back(buf);
+
+ int size = tokens.size() + 1;
+ const char** modes = new const char*[size];
+ modes[0] = user->nick;
+
+ // process mode params
+ int i = 1;
+ for (unsigned int k = 0; k < tokens.size(); k++)
+ {
+ modes[i] = tokens[k].c_str();
+ i++;
+ }
+
+ std::deque<std::string> n;
+ Event rmode((char *)&n, NULL, "send_mode_explicit");
+ for (unsigned int j = 0; j < tokens.size(); j++)
+ n.push_back(modes[j]);
+
+ rmode.Send(ServerInstance);
+ ServerInstance->SendMode(modes, size, user);
+ delete [] modes;
+ }
+ break;
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleModesOnOper)
diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp
index 7e48eefdb..55608dcb8 100644
--- a/src/modules/m_opermotd.cpp
+++ b/src/modules/m_opermotd.cpp
@@ -1 +1,116 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */ static FileReader* opermotd; CmdResult ShowOperMOTD(userrec* user) { if(!opermotd->FileSize()) { user->WriteServ(std::string("425 ") + user->nick + std::string(" :OPERMOTD file is missing")); return CMD_FAILURE; } user->WriteServ(std::string("375 ") + user->nick + std::string(" :- IRC Operators Message of the Day")); for(int i=0; i != opermotd->FileSize(); i++) { user->WriteServ(std::string("372 ") + user->nick + std::string(" :- ") + opermotd->GetLine(i)); } user->WriteServ(std::string("376 ") + user->nick + std::string(" :- End of OPERMOTD")); /* don't route me */ return CMD_LOCALONLY; } /** Handle /OPERMOTD */ class cmd_opermotd : public command_t { public: cmd_opermotd (InspIRCd* Instance) : command_t(Instance,"OPERMOTD", 'o', 0) { this->source = "m_opermotd.so"; syntax = "[<servername>]"; } CmdResult Handle (const char** parameters, int pcnt, userrec* user) { return ShowOperMOTD(user); } }; class ModuleOpermotd : public Module { cmd_opermotd* mycommand; public: void LoadOperMOTD() { ConfigReader* conf = new ConfigReader(ServerInstance); std::string filename; filename = conf->ReadValue("opermotd","file",0); if (opermotd) { delete opermotd; opermotd = NULL; } opermotd = new FileReader(ServerInstance, filename); DELETE(conf); } ModuleOpermotd(InspIRCd* Me) : Module(Me) { opermotd = NULL; mycommand = new cmd_opermotd(ServerInstance); ServerInstance->AddCommand(mycommand); opermotd = new FileReader(ServerInstance); LoadOperMOTD(); } virtual ~ModuleOpermotd() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnRehash] = List[I_OnOper] = 1; } virtual void OnOper(userrec* user, const std::string &opertype) { ShowOperMOTD(user); } virtual void OnRehash(userrec* user, const std::string &parameter) { LoadOperMOTD(); } }; MODULE_INIT(ModuleOpermotd) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */
+
+static FileReader* opermotd;
+
+CmdResult ShowOperMOTD(userrec* user)
+{
+ if(!opermotd->FileSize())
+ {
+ user->WriteServ(std::string("425 ") + user->nick + std::string(" :OPERMOTD file is missing"));
+ return CMD_FAILURE;
+ }
+
+ user->WriteServ(std::string("375 ") + user->nick + std::string(" :- IRC Operators Message of the Day"));
+
+ for(int i=0; i != opermotd->FileSize(); i++)
+ {
+ user->WriteServ(std::string("372 ") + user->nick + std::string(" :- ") + opermotd->GetLine(i));
+ }
+
+ user->WriteServ(std::string("376 ") + user->nick + std::string(" :- End of OPERMOTD"));
+
+ /* don't route me */
+ return CMD_LOCALONLY;
+}
+
+/** Handle /OPERMOTD
+ */
+class cmd_opermotd : public command_t
+{
+ public:
+ cmd_opermotd (InspIRCd* Instance) : command_t(Instance,"OPERMOTD", 'o', 0)
+ {
+ this->source = "m_opermotd.so";
+ syntax = "[<servername>]";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec* user)
+ {
+ return ShowOperMOTD(user);
+ }
+};
+
+
+class ModuleOpermotd : public Module
+{
+ cmd_opermotd* mycommand;
+ public:
+
+ void LoadOperMOTD()
+ {
+ ConfigReader* conf = new ConfigReader(ServerInstance);
+ std::string filename;
+ filename = conf->ReadValue("opermotd","file",0);
+ if (opermotd)
+ {
+ delete opermotd;
+ opermotd = NULL;
+ }
+ opermotd = new FileReader(ServerInstance, filename);
+ DELETE(conf);
+ }
+
+ ModuleOpermotd(InspIRCd* Me)
+ : Module(Me)
+ {
+ opermotd = NULL;
+ mycommand = new cmd_opermotd(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ opermotd = new FileReader(ServerInstance);
+ LoadOperMOTD();
+ }
+
+ virtual ~ModuleOpermotd()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnOper] = 1;
+ }
+
+ virtual void OnOper(userrec* user, const std::string &opertype)
+ {
+ ShowOperMOTD(user);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ LoadOperMOTD();
+ }
+};
+
+MODULE_INIT(ModuleOpermotd)
diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp
index c5b343552..be123d4fd 100644
--- a/src/modules/m_override.cpp
+++ b/src/modules/m_override.cpp
@@ -1 +1,294 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" #include "wildcard.h" /* $ModDesc: Provides support for unreal-style oper-override */ typedef std::map<std::string,std::string> override_t; class ModuleOverride : public Module { override_t overrides; bool NoisyOverride; bool OverriddenMode; int OverOps, OverDeops, OverVoices, OverDevoices, OverHalfops, OverDehalfops; public: ModuleOverride(InspIRCd* Me) : Module(Me) { // read our config options (main config file) OnRehash(NULL,""); ServerInstance->SNO->EnableSnomask('O',"OVERRIDE"); OverriddenMode = false; OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0; } virtual void OnRehash(userrec* user, const std::string &parameter) { // on a rehash we delete our classes for good measure and create them again. ConfigReader* Conf = new ConfigReader(ServerInstance); // re-read our config options on a rehash NoisyOverride = Conf->ReadFlag("override","noisy",0); overrides.clear(); for (int j =0; j < Conf->Enumerate("type"); j++) { std::string typen = Conf->ReadValue("type","name",j); std::string tokenlist = Conf->ReadValue("type","override",j); overrides[typen] = tokenlist; } DELETE(Conf); } void Implements(char* List) { List[I_OnRehash] = List[I_OnAccessCheck] = List[I_On005Numeric] = List[I_OnUserPreJoin] = List[I_OnUserPreKick] = List[I_OnPostCommand] = 1; } virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { if ((NoisyOverride) && (OverriddenMode) && (irc::string(command.c_str()) == "MODE") && (result == CMD_SUCCESS)) { int Total = OverOps + OverDeops + OverVoices + OverDevoices + OverHalfops + OverDehalfops; ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" Overriding modes: "+ServerInstance->Modes->GetLastParse()+" "+(Total ? "[Detail: " : "")+ (OverOps ? ConvToStr(OverOps)+" op"+(OverOps != 1 ? "s" : "")+" " : "")+ (OverDeops ? ConvToStr(OverDeops)+" deop"+(OverDeops != 1 ? "s" : "")+" " : "")+ (OverVoices ? ConvToStr(OverVoices)+" voice"+(OverVoices != 1 ? "s" : "")+" " : "")+ (OverDevoices ? ConvToStr(OverDevoices)+" devoice"+(OverDevoices != 1 ? "s" : "")+" " : "")+ (OverHalfops ? ConvToStr(OverHalfops)+" halfop"+(OverHalfops != 1 ? "s" : "")+" " : "")+ (OverDehalfops ? ConvToStr(OverDehalfops)+" dehalfop"+(OverDehalfops != 1 ? "s" : "") : "") +(Total ? "]" : "")); OverriddenMode = false; OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0; } } virtual void On005Numeric(std::string &output) { output.append(" OVERRIDE"); } virtual bool CanOverride(userrec* source, char* token) { // checks to see if the oper's type has <type:override> override_t::iterator j = overrides.find(source->oper); if (j != overrides.end()) { // its defined or * is set, return its value as a boolean for if the token is set return ((j->second.find(token, 0) != std::string::npos) || (j->second.find("*", 0) != std::string::npos)); } // its not defined at all, count as false return false; } virtual int OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) { if (IS_OPER(source) && CanOverride(source,"KICK")) { if (((chan->GetStatus(source) == STATUS_HOP) && (chan->GetStatus(user) == STATUS_OP)) || (chan->GetStatus(source) < STATUS_VOICE)) { ServerInstance->SNO->WriteToSnoMask('O',std::string(source->nick)+" Override-Kicked "+std::string(user->nick)+" on "+std::string(chan->name)+" ("+reason+")"); } /* Returning -1 explicitly allows the kick */ return -1; } return 0; } virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { if (IS_OPER(source)) { if (source && channel) { // Fix by brain - allow the change if they arent on channel - rely on boolean short-circuit // to not check the other items in the statement if they arent on the channel int mode = channel->GetStatus(source); switch (access_type) { case AC_DEOP: if (CanOverride(source,"MODEDEOP")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_OP)) OverDeops++; return ACR_ALLOW; } else { return ACR_DEFAULT; } break; case AC_OP: if (CanOverride(source,"MODEOP")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_OP)) OverOps++; return ACR_ALLOW; } else { return ACR_DEFAULT; } break; case AC_VOICE: if (CanOverride(source,"MODEVOICE")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_HOP)) OverVoices++; return ACR_ALLOW; } else { return ACR_DEFAULT; } break; case AC_DEVOICE: if (CanOverride(source,"MODEDEVOICE")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_HOP)) OverDevoices++; return ACR_ALLOW; } else { return ACR_DEFAULT; } break; case AC_HALFOP: if (CanOverride(source,"MODEHALFOP")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_OP)) OverHalfops++; return ACR_ALLOW; } else { return ACR_DEFAULT; } break; case AC_DEHALFOP: if (CanOverride(source,"MODEDEHALFOP")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_OP)) OverDehalfops++; return ACR_ALLOW; } else { return ACR_DEFAULT; } break; } if (CanOverride(source,"OTHERMODE")) { if (NoisyOverride) if ((!channel->HasUser(source)) || (mode < STATUS_OP)) { OverriddenMode = true; OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0; } return ACR_ALLOW; } else { return ACR_DEFAULT; } } } return ACR_DEFAULT; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (IS_OPER(user)) { if (chan) { if ((chan->modes[CM_INVITEONLY]) && (CanOverride(user,"INVITE"))) { irc::string x = chan->name; if (!user->IsInvited(x)) { /* XXX - Ugly cast for a parameter that isn't used? :< - Om */ if (NoisyOverride) chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass invite-only", cname, user->nick); ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +i on "+std::string(cname)); } return -1; } if ((*chan->key) && (CanOverride(user,"KEY"))) { if (NoisyOverride) chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel key", cname, user->nick); ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +k on "+std::string(cname)); return -1; } if ((chan->limit > 0) && (chan->GetUserCounter() >= chan->limit) && (CanOverride(user,"LIMIT"))) { if (NoisyOverride) chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel limit", cname, user->nick); ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +l on "+std::string(cname)); return -1; } if (CanOverride(user,"BANWALK")) { if (chan->IsBanned(user)) { if (NoisyOverride) chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass channel ban", cname, user->nick); ServerInstance->SNO->WriteToSnoMask('O',"%s used oper-override to bypass channel ban on %s", user->nick, cname); } return -1; } } } return 0; } virtual ~ModuleOverride() { ServerInstance->SNO->DisableSnomask('O'); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleOverride) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides support for unreal-style oper-override */
+
+typedef std::map<std::string,std::string> override_t;
+
+class ModuleOverride : public Module
+{
+
+ override_t overrides;
+ bool NoisyOverride;
+ bool OverriddenMode;
+ int OverOps, OverDeops, OverVoices, OverDevoices, OverHalfops, OverDehalfops;
+
+ public:
+
+ ModuleOverride(InspIRCd* Me)
+ : Module(Me)
+ {
+ // read our config options (main config file)
+ OnRehash(NULL,"");
+ ServerInstance->SNO->EnableSnomask('O',"OVERRIDE");
+ OverriddenMode = false;
+ OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ // on a rehash we delete our classes for good measure and create them again.
+ ConfigReader* Conf = new ConfigReader(ServerInstance);
+
+ // re-read our config options on a rehash
+ NoisyOverride = Conf->ReadFlag("override","noisy",0);
+ overrides.clear();
+ for (int j =0; j < Conf->Enumerate("type"); j++)
+ {
+ std::string typen = Conf->ReadValue("type","name",j);
+ std::string tokenlist = Conf->ReadValue("type","override",j);
+ overrides[typen] = tokenlist;
+ }
+
+ DELETE(Conf);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnAccessCheck] = List[I_On005Numeric] = List[I_OnUserPreJoin] = List[I_OnUserPreKick] = List[I_OnPostCommand] = 1;
+ }
+
+ virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
+ {
+ if ((NoisyOverride) && (OverriddenMode) && (irc::string(command.c_str()) == "MODE") && (result == CMD_SUCCESS))
+ {
+ int Total = OverOps + OverDeops + OverVoices + OverDevoices + OverHalfops + OverDehalfops;
+
+ ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" Overriding modes: "+ServerInstance->Modes->GetLastParse()+" "+(Total ? "[Detail: " : "")+
+ (OverOps ? ConvToStr(OverOps)+" op"+(OverOps != 1 ? "s" : "")+" " : "")+
+ (OverDeops ? ConvToStr(OverDeops)+" deop"+(OverDeops != 1 ? "s" : "")+" " : "")+
+ (OverVoices ? ConvToStr(OverVoices)+" voice"+(OverVoices != 1 ? "s" : "")+" " : "")+
+ (OverDevoices ? ConvToStr(OverDevoices)+" devoice"+(OverDevoices != 1 ? "s" : "")+" " : "")+
+ (OverHalfops ? ConvToStr(OverHalfops)+" halfop"+(OverHalfops != 1 ? "s" : "")+" " : "")+
+ (OverDehalfops ? ConvToStr(OverDehalfops)+" dehalfop"+(OverDehalfops != 1 ? "s" : "") : "")
+ +(Total ? "]" : ""));
+
+ OverriddenMode = false;
+ OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0;
+ }
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" OVERRIDE");
+ }
+
+ virtual bool CanOverride(userrec* source, char* token)
+ {
+ // checks to see if the oper's type has <type:override>
+ override_t::iterator j = overrides.find(source->oper);
+
+ if (j != overrides.end())
+ {
+ // its defined or * is set, return its value as a boolean for if the token is set
+ return ((j->second.find(token, 0) != std::string::npos) || (j->second.find("*", 0) != std::string::npos));
+ }
+
+ // its not defined at all, count as false
+ return false;
+ }
+
+ virtual int OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
+ {
+ if (IS_OPER(source) && CanOverride(source,"KICK"))
+ {
+ if (((chan->GetStatus(source) == STATUS_HOP) && (chan->GetStatus(user) == STATUS_OP)) || (chan->GetStatus(source) < STATUS_VOICE))
+ {
+ ServerInstance->SNO->WriteToSnoMask('O',std::string(source->nick)+" Override-Kicked "+std::string(user->nick)+" on "+std::string(chan->name)+" ("+reason+")");
+ }
+ /* Returning -1 explicitly allows the kick */
+ return -1;
+ }
+ return 0;
+ }
+
+ virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
+ {
+ if (IS_OPER(source))
+ {
+ if (source && channel)
+ {
+ // Fix by brain - allow the change if they arent on channel - rely on boolean short-circuit
+ // to not check the other items in the statement if they arent on the channel
+ int mode = channel->GetStatus(source);
+ switch (access_type)
+ {
+ case AC_DEOP:
+ if (CanOverride(source,"MODEDEOP"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_OP))
+ OverDeops++;
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ break;
+ case AC_OP:
+ if (CanOverride(source,"MODEOP"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_OP))
+ OverOps++;
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ break;
+ case AC_VOICE:
+ if (CanOverride(source,"MODEVOICE"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_HOP))
+ OverVoices++;
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ break;
+ case AC_DEVOICE:
+ if (CanOverride(source,"MODEDEVOICE"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_HOP))
+ OverDevoices++;
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ break;
+ case AC_HALFOP:
+ if (CanOverride(source,"MODEHALFOP"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_OP))
+ OverHalfops++;
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ break;
+ case AC_DEHALFOP:
+ if (CanOverride(source,"MODEDEHALFOP"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_OP))
+ OverDehalfops++;
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ break;
+ }
+
+ if (CanOverride(source,"OTHERMODE"))
+ {
+ if (NoisyOverride)
+ if ((!channel->HasUser(source)) || (mode < STATUS_OP))
+ {
+ OverriddenMode = true;
+ OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0;
+ }
+ return ACR_ALLOW;
+ }
+ else
+ {
+ return ACR_DEFAULT;
+ }
+ }
+ }
+
+ return ACR_DEFAULT;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (IS_OPER(user))
+ {
+ if (chan)
+ {
+ if ((chan->modes[CM_INVITEONLY]) && (CanOverride(user,"INVITE")))
+ {
+ irc::string x = chan->name;
+ if (!user->IsInvited(x))
+ {
+ /* XXX - Ugly cast for a parameter that isn't used? :< - Om */
+ if (NoisyOverride)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass invite-only", cname, user->nick);
+ ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +i on "+std::string(cname));
+ }
+ return -1;
+ }
+
+ if ((*chan->key) && (CanOverride(user,"KEY")))
+ {
+ if (NoisyOverride)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel key", cname, user->nick);
+ ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +k on "+std::string(cname));
+ return -1;
+ }
+
+ if ((chan->limit > 0) && (chan->GetUserCounter() >= chan->limit) && (CanOverride(user,"LIMIT")))
+ {
+ if (NoisyOverride)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel limit", cname, user->nick);
+ ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +l on "+std::string(cname));
+ return -1;
+ }
+
+ if (CanOverride(user,"BANWALK"))
+ {
+ if (chan->IsBanned(user))
+ {
+ if (NoisyOverride)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass channel ban", cname, user->nick);
+ ServerInstance->SNO->WriteToSnoMask('O',"%s used oper-override to bypass channel ban on %s", user->nick, cname);
+ }
+ return -1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleOverride()
+ {
+ ServerInstance->SNO->DisableSnomask('O');
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleOverride)
diff --git a/src/modules/m_randquote.cpp b/src/modules/m_randquote.cpp
index 8ab9aab4e..2ad7831ce 100644
--- a/src/modules/m_randquote.cpp
+++ b/src/modules/m_randquote.cpp
@@ -1 +1,138 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" static FileReader *quotes = NULL; std::string q_file; std::string prefix; std::string suffix; /* $ModDesc: Provides random Quotes on Connect. */ /** Handle /RANDQUOTE */ class cmd_randquote : public command_t { public: cmd_randquote (InspIRCd* Instance) : command_t(Instance,"RANDQUOTE", 0, 0) { this->source = "m_randquote.so"; } CmdResult Handle (const char** parameters, int pcntl, userrec *user) { std::string str; int fsize; if (q_file.empty() || quotes->Exists()) { fsize = quotes->FileSize(); str = quotes->GetLine(rand() % fsize); user->WriteServ("NOTICE %s :%s%s%s",user->nick,prefix.c_str(),str.c_str(),suffix.c_str()); } else { user->WriteServ("NOTICE %s :Your administrator specified an invalid quotes file, please bug them about this.", user->nick); return CMD_FAILURE; } return CMD_LOCALONLY; } }; /** Thrown by m_randquote */ class RandquoteException : public ModuleException { private: const std::string err; public: RandquoteException(const std::string &message) : err(message) { } ~RandquoteException() throw () { } virtual const char* GetReason() { return err.c_str(); } }; class ModuleRandQuote : public Module { private: cmd_randquote* mycommand; ConfigReader *conf; public: ModuleRandQuote(InspIRCd* Me) : Module(Me) { conf = new ConfigReader(ServerInstance); // Sort the Randomizer thingie.. srand(time(NULL)); q_file = conf->ReadValue("randquote","file",0); prefix = conf->ReadValue("randquote","prefix",0); suffix = conf->ReadValue("randquote","suffix",0); mycommand = NULL; if (q_file.empty()) { RandquoteException e("m_randquote: Quotefile not specified - Please check your config."); throw(e); } quotes = new FileReader(ServerInstance, q_file); if(!quotes->Exists()) { RandquoteException e("m_randquote: QuoteFile not Found!! Please check your config - module will not function."); throw(e); } else { /* Hidden Command -- Mode clients assume /quote sends raw data to an IRCd >:D */ mycommand = new cmd_randquote(ServerInstance); ServerInstance->AddCommand(mycommand); } } void Implements(char* List) { List[I_OnUserConnect] = 1; } virtual ~ModuleRandQuote() { DELETE(conf); DELETE(quotes); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void OnUserConnect(userrec* user) { if (mycommand) mycommand->Handle(NULL, 0, user); } }; MODULE_INIT(ModuleRandQuote) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+static FileReader *quotes = NULL;
+
+std::string q_file;
+std::string prefix;
+std::string suffix;
+
+/* $ModDesc: Provides random Quotes on Connect. */
+
+/** Handle /RANDQUOTE
+ */
+class cmd_randquote : public command_t
+{
+ public:
+ cmd_randquote (InspIRCd* Instance) : command_t(Instance,"RANDQUOTE", 0, 0)
+ {
+ this->source = "m_randquote.so";
+ }
+
+ CmdResult Handle (const char** parameters, int pcntl, userrec *user)
+ {
+ std::string str;
+ int fsize;
+
+ if (q_file.empty() || quotes->Exists())
+ {
+ fsize = quotes->FileSize();
+ str = quotes->GetLine(rand() % fsize);
+ user->WriteServ("NOTICE %s :%s%s%s",user->nick,prefix.c_str(),str.c_str(),suffix.c_str());
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :Your administrator specified an invalid quotes file, please bug them about this.", user->nick);
+ return CMD_FAILURE;
+ }
+
+ return CMD_LOCALONLY;
+ }
+};
+
+/** Thrown by m_randquote
+ */
+class RandquoteException : public ModuleException
+{
+ private:
+ const std::string err;
+ public:
+ RandquoteException(const std::string &message) : err(message) { }
+
+ ~RandquoteException() throw () { }
+
+ virtual const char* GetReason()
+ {
+ return err.c_str();
+ }
+};
+
+class ModuleRandQuote : public Module
+{
+ private:
+ cmd_randquote* mycommand;
+ ConfigReader *conf;
+ public:
+ ModuleRandQuote(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ conf = new ConfigReader(ServerInstance);
+ // Sort the Randomizer thingie..
+ srand(time(NULL));
+
+ q_file = conf->ReadValue("randquote","file",0);
+ prefix = conf->ReadValue("randquote","prefix",0);
+ suffix = conf->ReadValue("randquote","suffix",0);
+
+ mycommand = NULL;
+
+ if (q_file.empty())
+ {
+ RandquoteException e("m_randquote: Quotefile not specified - Please check your config.");
+ throw(e);
+ }
+
+ quotes = new FileReader(ServerInstance, q_file);
+ if(!quotes->Exists())
+ {
+ RandquoteException e("m_randquote: QuoteFile not Found!! Please check your config - module will not function.");
+ throw(e);
+ }
+ else
+ {
+ /* Hidden Command -- Mode clients assume /quote sends raw data to an IRCd >:D */
+ mycommand = new cmd_randquote(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserConnect] = 1;
+ }
+
+ virtual ~ModuleRandQuote()
+ {
+ DELETE(conf);
+ DELETE(quotes);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnUserConnect(userrec* user)
+ {
+ if (mycommand)
+ mycommand->Handle(NULL, 0, user);
+ }
+};
+
+MODULE_INIT(ModuleRandQuote)
diff --git a/src/modules/m_redirect.cpp b/src/modules/m_redirect.cpp
index ee2d3f004..a746644c2 100644
--- a/src/modules/m_redirect.cpp
+++ b/src/modules/m_redirect.cpp
@@ -1 +1,160 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides channel mode +L (limit redirection) */ /** Handle channel mode +L */ class Redirect : public ModeHandler { public: Redirect(InspIRCd* Instance) : ModeHandler(Instance, 'L', 1, 0, false, MODETYPE_CHANNEL, false) { } ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter) { if (channel->IsModeSet('L')) return std::make_pair(true, channel->GetModeParameter('L')); else return std::make_pair(false, parameter); } bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) { /* When TS is equal, the alphabetically later one wins */ return (their_param < our_param); } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { chanrec* c = NULL; if (!ServerInstance->IsChannel(parameter.c_str())) { source->WriteServ("403 %s %s :Invalid channel name",source->nick, parameter.c_str()); parameter.clear(); return MODEACTION_DENY; } c = ServerInstance->FindChan(parameter); if (c) { /* Fix by brain: Dont let a channel be linked to *itself* either */ if (IS_LOCAL(source)) { if ((c == channel) || (c->IsModeSet('L'))) { source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Channel already has +L). Pack of wild dogs has been unleashed.",source->nick,parameter.c_str()); parameter.clear(); return MODEACTION_DENY; } else { for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) { if ((i->second != channel) && (i->second->IsModeSet('L')) && (irc::string(i->second->GetModeParameter('L').c_str()) == irc::string(channel->name))) { source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Already forwarded here from %s). Angry monkeys dispatched.",source->nick,parameter.c_str(),i->second->name); return MODEACTION_DENY; } } } } } channel->SetMode('L', true); channel->SetModeParam('L', parameter.c_str(), true); return MODEACTION_ALLOW; } else { if (channel->IsModeSet('L')) { channel->SetMode('L', false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleRedirect : public Module { Redirect* re; public: ModuleRedirect(InspIRCd* Me) : Module(Me) { re = new Redirect(ServerInstance); if (!ServerInstance->AddMode(re, 'L')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreJoin] = 1; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (chan) { if (chan->IsModeSet('L') && chan->limit) { if (chan->GetUserCounter() >= chan->limit) { std::string channel = chan->GetModeParameter('L'); /* sometimes broken ulines can make circular or chained +L, avoid this */ chanrec* destchan = NULL; destchan = ServerInstance->FindChan(channel); if (destchan && destchan->IsModeSet('L')) { user->WriteServ("470 %s :%s is full, but has a circular redirect (+L), not following redirection to %s", user->nick, cname, channel.c_str()); return 1; } user->WriteServ("470 %s :%s has become full, so you are automatically being transferred to the linked channel %s", user->nick, cname, channel.c_str()); chanrec::JoinUser(ServerInstance, user, channel.c_str(), false, "", ServerInstance->Time(true)); return 1; } } } return 0; } virtual ~ModuleRedirect() { ServerInstance->Modes->DelMode(re); DELETE(re); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleRedirect) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides channel mode +L (limit redirection) */
+
+/** Handle channel mode +L
+ */
+class Redirect : public ModeHandler
+{
+ public:
+ Redirect(InspIRCd* Instance) : ModeHandler(Instance, 'L', 1, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
+ {
+ if (channel->IsModeSet('L'))
+ return std::make_pair(true, channel->GetModeParameter('L'));
+ else
+ return std::make_pair(false, parameter);
+ }
+
+ bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
+ {
+ /* When TS is equal, the alphabetically later one wins */
+ return (their_param < our_param);
+ }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ chanrec* c = NULL;
+
+ if (!ServerInstance->IsChannel(parameter.c_str()))
+ {
+ source->WriteServ("403 %s %s :Invalid channel name",source->nick, parameter.c_str());
+ parameter.clear();
+ return MODEACTION_DENY;
+ }
+
+ c = ServerInstance->FindChan(parameter);
+ if (c)
+ {
+ /* Fix by brain: Dont let a channel be linked to *itself* either */
+ if (IS_LOCAL(source))
+ {
+ if ((c == channel) || (c->IsModeSet('L')))
+ {
+ source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Channel already has +L). Pack of wild dogs has been unleashed.",source->nick,parameter.c_str());
+ parameter.clear();
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+ {
+ if ((i->second != channel) && (i->second->IsModeSet('L')) && (irc::string(i->second->GetModeParameter('L').c_str()) == irc::string(channel->name)))
+ {
+ source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Already forwarded here from %s). Angry monkeys dispatched.",source->nick,parameter.c_str(),i->second->name);
+ return MODEACTION_DENY;
+ }
+ }
+ }
+ }
+ }
+
+ channel->SetMode('L', true);
+ channel->SetModeParam('L', parameter.c_str(), true);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ if (channel->IsModeSet('L'))
+ {
+ channel->SetMode('L', false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+
+ }
+};
+
+class ModuleRedirect : public Module
+{
+
+ Redirect* re;
+
+ public:
+
+ ModuleRedirect(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ re = new Redirect(ServerInstance);
+ if (!ServerInstance->AddMode(re, 'L'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = 1;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (chan)
+ {
+ if (chan->IsModeSet('L') && chan->limit)
+ {
+ if (chan->GetUserCounter() >= chan->limit)
+ {
+ std::string channel = chan->GetModeParameter('L');
+
+ /* sometimes broken ulines can make circular or chained +L, avoid this */
+ chanrec* destchan = NULL;
+ destchan = ServerInstance->FindChan(channel);
+ if (destchan && destchan->IsModeSet('L'))
+ {
+ user->WriteServ("470 %s :%s is full, but has a circular redirect (+L), not following redirection to %s", user->nick, cname, channel.c_str());
+ return 1;
+ }
+
+ user->WriteServ("470 %s :%s has become full, so you are automatically being transferred to the linked channel %s", user->nick, cname, channel.c_str());
+ chanrec::JoinUser(ServerInstance, user, channel.c_str(), false, "", ServerInstance->Time(true));
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleRedirect()
+ {
+ ServerInstance->Modes->DelMode(re);
+ DELETE(re);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleRedirect)
diff --git a/src/modules/m_regonlycreate.cpp b/src/modules/m_regonlycreate.cpp
index 0d911a0bf..2174c860c 100644
--- a/src/modules/m_regonlycreate.cpp
+++ b/src/modules/m_regonlycreate.cpp
@@ -1 +1,61 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Prevents users who's nicks are not registered from creating new channels */ class ModuleRegOnlyCreate : public Module { public: ModuleRegOnlyCreate(InspIRCd* Me) : Module(Me) { } void Implements(char* List) { List[I_OnUserPreJoin] = 1; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (chan) return 0; if (IS_OPER(user)) return 0; if ((!user->IsModeSet('r')) && (!user->GetExt("accountname"))) { user->WriteServ("482 %s %s :You must have a registered nickname to create a new channel", user->nick, cname); return 1; } return 0; } virtual ~ModuleRegOnlyCreate() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleRegOnlyCreate) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Prevents users who's nicks are not registered from creating new channels */
+
+class ModuleRegOnlyCreate : public Module
+{
+ public:
+ ModuleRegOnlyCreate(InspIRCd* Me)
+ : Module(Me)
+ {
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = 1;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (chan)
+ return 0;
+
+ if (IS_OPER(user))
+ return 0;
+
+ if ((!user->IsModeSet('r')) && (!user->GetExt("accountname")))
+ {
+ user->WriteServ("482 %s %s :You must have a registered nickname to create a new channel", user->nick, cname);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual ~ModuleRegOnlyCreate()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleRegOnlyCreate)
diff --git a/src/modules/m_remove.cpp b/src/modules/m_remove.cpp
index 6d9be00ad..d28087ba8 100644
--- a/src/modules/m_remove.cpp
+++ b/src/modules/m_remove.cpp
@@ -1 +1,288 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <sstream> #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.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. * eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes. */ /** Base class for /FPART and /REMOVE */ class RemoveBase { private: bool& supportnokicks; InspIRCd* ServerInstance; protected: RemoveBase(InspIRCd* Instance, bool& snk) : supportnokicks(snk), ServerInstance(Instance) { } enum ModeLevel { PEON = 0, HALFOP = 1, OP = 2, ADMIN = 3, OWNER = 4, ULINE = 5 }; /* This little function just converts a chanmode character (U ~ & @ & +) into an integer (5 4 3 2 1 0) */ /* XXX - We should probably use the new mode prefix rank stuff * for this instead now -- Brain */ ModeLevel chartolevel(const std::string &privs) { if(privs.empty()) { return PEON; } switch (privs[0]) { case 'U': /* Ulined */ return ULINE; case '~': /* Owner */ return OWNER; case '&': /* Admin */ return ADMIN; case '@': /* Operator */ return OP; case '%': /* Halfop */ return HALFOP; default: /* Peon */ return PEON; } } CmdResult Handle (const char** parameters, int pcnt, userrec *user, bool neworder) { const char* channame; const char* username; userrec* target; chanrec* channel; ModeLevel tlevel; ModeLevel ulevel; std::string reason; std::string protectkey; std::string founderkey; bool hasnokicks; /* Set these to the parameters needed, the new version of this module switches it's parameters around * supplying a new command with the new order while keeping the old /remove with the older order. * /remove <nick> <channel> [reason ...] * /fpart <channel> <nick> [reason ...] */ channame = parameters[ neworder ? 0 : 1]; username = parameters[ neworder ? 1 : 0]; /* Look up the user we're meant to be removing from the channel */ target = ServerInstance->FindNick(username); /* And the channel we're meant to be removing them from */ channel = ServerInstance->FindChan(channame); /* Fix by brain - someone needs to learn to validate their input! */ if (!target || !channel) { user->WriteServ("401 %s %s :No such nick/channel", user->nick, !target ? username : channame); return CMD_FAILURE; } if (!channel->HasUser(target)) { user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick, target->nick, channel->name); return CMD_FAILURE; } /* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set. * Then we change the @|%|+ to & if they are +a, or ~ if they are +q */ protectkey = "cm_protect_" + std::string(channel->name); founderkey = "cm_founder_" + std::string(channel->name); if (ServerInstance->ULine(user->server) || ServerInstance->ULine(user->nick)) { ulevel = chartolevel("U"); } if (user->GetExt(founderkey)) { ulevel = chartolevel("~"); } else if (user->GetExt(protectkey)) { ulevel = chartolevel("&"); } else { ulevel = chartolevel(channel->GetPrefixChar(user)); } /* Now it's the same idea, except for the target. If they're ulined make sure they get a higher level than the sender can */ if (ServerInstance->ULine(target->server) || ServerInstance->ULine(target->nick)) { tlevel = chartolevel("U"); } else if (target->GetExt(founderkey)) { tlevel = chartolevel("~"); } else if (target->GetExt(protectkey)) { tlevel = chartolevel("&"); } else { tlevel = chartolevel(channel->GetPrefixChar(target)); } hasnokicks = (ServerInstance->FindModule("m_nokicks.so") && channel->IsModeSet('Q')); /* 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 || (ulevel == ULINE))) { /* 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. */ if ((!IS_LOCAL(user)) || ((ulevel > PEON) && (ulevel >= tlevel) && (tlevel != OWNER))) { // no you can't just go from a std::ostringstream to a std::string, Om. -nenolod // but you can do this, nenolod -brain std::string reasonparam("No reason given"); /* If a reason is given, use it */ if(pcnt > 2) { /* Join params 2 ... pcnt - 1 (inclusive) into one */ irc::stringjoiner reason_join(" ", parameters, 2, pcnt - 1); reasonparam = reason_join.GetJoined(); } /* Build up the part reason string. */ reason = std::string("Removed by ") + user->nick + ": " + reasonparam; channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name, user->nick, target->nick); target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick, user->nick, channel->name, reasonparam.c_str()); if (!channel->PartUser(target, reason.c_str())) delete channel; } else { user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick, target->nick, channel->name); 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, channel->name, target->nick); return CMD_FAILURE; } /* route me */ return CMD_SUCCESS; } }; /** Handle /REMOVE */ class cmd_remove : public command_t, public RemoveBase { public: cmd_remove(InspIRCd* Instance, bool& snk) : command_t(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk) { this->source = "m_remove.so"; syntax = "<nick> <channel> [<reason>]"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { return RemoveBase::Handle(parameters, pcnt, user, false); } }; /** Handle /FPART */ class cmd_fpart : public command_t, public RemoveBase { public: cmd_fpart(InspIRCd* Instance, bool& snk) : command_t(Instance, "FPART", 0, 2), RemoveBase(Instance, snk) { this->source = "m_remove.so"; syntax = "<channel> <nick> [<reason>]"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { return RemoveBase::Handle(parameters, pcnt, user, true); } }; class ModuleRemove : public Module { cmd_remove* mycommand; cmd_fpart* mycommand2; bool supportnokicks; public: ModuleRemove(InspIRCd* Me) : Module(Me) { mycommand = new cmd_remove(ServerInstance, supportnokicks); mycommand2 = new cmd_fpart(ServerInstance, supportnokicks); ServerInstance->AddCommand(mycommand); ServerInstance->AddCommand(mycommand2); OnRehash(NULL,""); } void Implements(char* List) { List[I_On005Numeric] = List[I_OnRehash] = 1; } virtual void On005Numeric(std::string &output) { output.append(" REMOVE"); } virtual void OnRehash(userrec* user, const std::string&) { ConfigReader conf(ServerInstance); supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0); } virtual ~ModuleRemove() { } virtual Version GetVersion() { return Version(1,1,1,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleRemove) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <sstream>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.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.
+ * eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes.
+*/
+
+/** Base class for /FPART and /REMOVE
+ */
+class RemoveBase
+{
+ private:
+ bool& supportnokicks;
+ InspIRCd* ServerInstance;
+
+ protected:
+ RemoveBase(InspIRCd* Instance, bool& snk) : supportnokicks(snk), ServerInstance(Instance)
+ {
+ }
+
+ enum ModeLevel { PEON = 0, HALFOP = 1, OP = 2, ADMIN = 3, OWNER = 4, ULINE = 5 };
+
+ /* This little function just converts a chanmode character (U ~ & @ & +) into an integer (5 4 3 2 1 0) */
+ /* XXX - We should probably use the new mode prefix rank stuff
+ * for this instead now -- Brain */
+ ModeLevel chartolevel(const std::string &privs)
+ {
+ if(privs.empty())
+ {
+ return PEON;
+ }
+
+ switch (privs[0])
+ {
+ case 'U':
+ /* Ulined */
+ return ULINE;
+ case '~':
+ /* Owner */
+ return OWNER;
+ case '&':
+ /* Admin */
+ return ADMIN;
+ case '@':
+ /* Operator */
+ return OP;
+ case '%':
+ /* Halfop */
+ return HALFOP;
+ default:
+ /* Peon */
+ return PEON;
+ }
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user, bool neworder)
+ {
+ const char* channame;
+ const char* username;
+ userrec* target;
+ chanrec* channel;
+ ModeLevel tlevel;
+ ModeLevel ulevel;
+ std::string reason;
+ std::string protectkey;
+ std::string founderkey;
+ bool hasnokicks;
+
+ /* Set these to the parameters needed, the new version of this module switches it's parameters around
+ * supplying a new command with the new order while keeping the old /remove with the older order.
+ * /remove <nick> <channel> [reason ...]
+ * /fpart <channel> <nick> [reason ...]
+ */
+ channame = parameters[ neworder ? 0 : 1];
+ username = parameters[ neworder ? 1 : 0];
+
+ /* Look up the user we're meant to be removing from the channel */
+ target = ServerInstance->FindNick(username);
+
+ /* And the channel we're meant to be removing them from */
+ channel = ServerInstance->FindChan(channame);
+
+ /* Fix by brain - someone needs to learn to validate their input! */
+ if (!target || !channel)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel", user->nick, !target ? username : channame);
+ return CMD_FAILURE;
+ }
+
+ if (!channel->HasUser(target))
+ {
+ user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick, target->nick, channel->name);
+ return CMD_FAILURE;
+ }
+
+ /* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set.
+ * Then we change the @|%|+ to & if they are +a, or ~ if they are +q */
+ protectkey = "cm_protect_" + std::string(channel->name);
+ founderkey = "cm_founder_" + std::string(channel->name);
+
+ if (ServerInstance->ULine(user->server) || ServerInstance->ULine(user->nick))
+ {
+ ulevel = chartolevel("U");
+ }
+ if (user->GetExt(founderkey))
+ {
+ ulevel = chartolevel("~");
+ }
+ else if (user->GetExt(protectkey))
+ {
+ ulevel = chartolevel("&");
+ }
+ else
+ {
+ ulevel = chartolevel(channel->GetPrefixChar(user));
+ }
+
+ /* Now it's the same idea, except for the target. If they're ulined make sure they get a higher level than the sender can */
+ if (ServerInstance->ULine(target->server) || ServerInstance->ULine(target->nick))
+ {
+ tlevel = chartolevel("U");
+ }
+ else if (target->GetExt(founderkey))
+ {
+ tlevel = chartolevel("~");
+ }
+ else if (target->GetExt(protectkey))
+ {
+ tlevel = chartolevel("&");
+ }
+ else
+ {
+ tlevel = chartolevel(channel->GetPrefixChar(target));
+ }
+
+ hasnokicks = (ServerInstance->FindModule("m_nokicks.so") && channel->IsModeSet('Q'));
+
+ /* 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 || (ulevel == ULINE)))
+ {
+ /* 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.
+ */
+ if ((!IS_LOCAL(user)) || ((ulevel > PEON) && (ulevel >= tlevel) && (tlevel != OWNER)))
+ {
+ // no you can't just go from a std::ostringstream to a std::string, Om. -nenolod
+ // but you can do this, nenolod -brain
+
+ std::string reasonparam("No reason given");
+
+ /* If a reason is given, use it */
+ if(pcnt > 2)
+ {
+ /* Join params 2 ... pcnt - 1 (inclusive) into one */
+ irc::stringjoiner reason_join(" ", parameters, 2, pcnt - 1);
+ reasonparam = reason_join.GetJoined();
+ }
+
+ /* Build up the part reason string. */
+ reason = std::string("Removed by ") + user->nick + ": " + reasonparam;
+
+ channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name, user->nick, target->nick);
+ target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick, user->nick, channel->name, reasonparam.c_str());
+
+ if (!channel->PartUser(target, reason.c_str()))
+ delete channel;
+ }
+ else
+ {
+ user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick, target->nick, channel->name);
+ 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, channel->name, target->nick);
+ return CMD_FAILURE;
+ }
+
+ /* route me */
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /REMOVE
+ */
+class cmd_remove : public command_t, public RemoveBase
+{
+ public:
+ cmd_remove(InspIRCd* Instance, bool& snk) : command_t(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk)
+ {
+ this->source = "m_remove.so";
+ syntax = "<nick> <channel> [<reason>]";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ return RemoveBase::Handle(parameters, pcnt, user, false);
+ }
+};
+
+/** Handle /FPART
+ */
+class cmd_fpart : public command_t, public RemoveBase
+{
+ public:
+ cmd_fpart(InspIRCd* Instance, bool& snk) : command_t(Instance, "FPART", 0, 2), RemoveBase(Instance, snk)
+ {
+ this->source = "m_remove.so";
+ syntax = "<channel> <nick> [<reason>]";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ return RemoveBase::Handle(parameters, pcnt, user, true);
+ }
+};
+
+class ModuleRemove : public Module
+{
+ cmd_remove* mycommand;
+ cmd_fpart* mycommand2;
+ bool supportnokicks;
+
+
+ public:
+ ModuleRemove(InspIRCd* Me)
+ : Module(Me)
+ {
+ mycommand = new cmd_remove(ServerInstance, supportnokicks);
+ mycommand2 = new cmd_fpart(ServerInstance, supportnokicks);
+ ServerInstance->AddCommand(mycommand);
+ ServerInstance->AddCommand(mycommand2);
+ OnRehash(NULL,"");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_On005Numeric] = List[I_OnRehash] = 1;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" REMOVE");
+ }
+
+ virtual void OnRehash(userrec* user, const std::string&)
+ {
+ ConfigReader conf(ServerInstance);
+ supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0);
+ }
+
+ virtual ~ModuleRemove()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,1,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleRemove)
diff --git a/src/modules/m_restrictbanned.cpp b/src/modules/m_restrictbanned.cpp
index 5535ca464..9315385a1 100644
--- a/src/modules/m_restrictbanned.cpp
+++ b/src/modules/m_restrictbanned.cpp
@@ -1 +1,98 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Restricts banned users in a channel. May not speak, etc. */ class ModuleRestrictBanned : public Module { private: public: ModuleRestrictBanned(InspIRCd* Me) : Module(Me) { } virtual ~ModuleRestrictBanned() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnLocalTopicChange] = List[I_OnUserPreNick] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1; } int CheckRestricted(userrec *user, chanrec *channel, const std::string &action) { /* aren't local? we don't care. */ if (!IS_LOCAL(user)) return 0; if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user)) { /* banned, boned. drop the message. */ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not " + action + ", as you are banned on channel " + channel->name); return 1; } return 0; } virtual int OnUserPreNick(userrec *user, const std::string &newnick) { /* if they aren't local, we don't care */ if (!IS_LOCAL(user)) return 0; /* bit of a special case. */ for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++) { if (CheckRestricted(user, i->first, "change your nickname") == 1) return 1; } return 0; } virtual int OnLocalTopicChange(userrec *user, chanrec *channel, const std::string &topic) { return CheckRestricted(user, channel, "change the topic"); } virtual int OnUserPreMessage(userrec* 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 int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (target_type == TYPE_CHANNEL) { chanrec *channel = (chanrec *)dest; return CheckRestricted(user, channel, "message the channel"); } return 0; } }; MODULE_INIT(ModuleRestrictBanned) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Restricts banned users in a channel. May not speak, etc. */
+
+class ModuleRestrictBanned : public Module
+{
+ private:
+ public:
+ ModuleRestrictBanned(InspIRCd* Me) : Module(Me)
+ {
+ }
+
+ virtual ~ModuleRestrictBanned()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnLocalTopicChange] = List[I_OnUserPreNick] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1;
+ }
+
+ int CheckRestricted(userrec *user, chanrec *channel, const std::string &action)
+ {
+ /* aren't local? we don't care. */
+ if (!IS_LOCAL(user))
+ return 0;
+
+ if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user))
+ {
+ /* banned, boned. drop the message. */
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not " + action + ", as you are banned on channel " + channel->name);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int OnUserPreNick(userrec *user, const std::string &newnick)
+ {
+ /* if they aren't local, we don't care */
+ if (!IS_LOCAL(user))
+ return 0;
+
+ /* bit of a special case. */
+ for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ {
+ if (CheckRestricted(user, i->first, "change your nickname") == 1)
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int OnLocalTopicChange(userrec *user, chanrec *channel, const std::string &topic)
+ {
+ return CheckRestricted(user, channel, "change the topic");
+ }
+
+ virtual int OnUserPreMessage(userrec* 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 int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (target_type == TYPE_CHANNEL)
+ {
+ chanrec *channel = (chanrec *)dest;
+
+ return CheckRestricted(user, channel, "message the channel");
+ }
+
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleRestrictBanned)
diff --git a/src/modules/m_restrictchans.cpp b/src/modules/m_restrictchans.cpp
index 1fc44f22b..1f2416d44 100644
--- a/src/modules/m_restrictchans.cpp
+++ b/src/modules/m_restrictchans.cpp
@@ -1 +1,85 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Only opers may create new channels if this module is loaded */ class ModuleRestrictChans : public Module { std::map<irc::string,int> allowchans; void ReadConfig() { ConfigReader* MyConf = new ConfigReader(ServerInstance); allowchans.clear(); for (int i = 0; i < MyConf->Enumerate("allowchannel"); i++) { std::string txt; txt = MyConf->ReadValue("allowchannel", "name", i); irc::string channel = txt.c_str(); allowchans[channel] = 1; } DELETE(MyConf); } public: ModuleRestrictChans(InspIRCd* Me) : Module(Me) { ReadConfig(); } virtual void OnRehash(userrec* user, const std::string &parameter) { ReadConfig(); } void Implements(char* List) { List[I_OnUserPreJoin] = List[I_OnRehash] = 1; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { irc::string x = cname; // user is not an oper and its not in the allow list if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end())) { // channel does not yet exist (record is null, about to be created IF we were to allow it) if (!chan) { user->WriteServ("530 %s %s :Only IRC operators may create new channels",user->nick,cname,cname); return 1; } } return 0; } virtual ~ModuleRestrictChans() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleRestrictChans) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Only opers may create new channels if this module is loaded */
+
+class ModuleRestrictChans : public Module
+{
+
+
+ std::map<irc::string,int> allowchans;
+
+ void ReadConfig()
+ {
+ ConfigReader* MyConf = new ConfigReader(ServerInstance);
+ allowchans.clear();
+ for (int i = 0; i < MyConf->Enumerate("allowchannel"); i++)
+ {
+ std::string txt;
+ txt = MyConf->ReadValue("allowchannel", "name", i);
+ irc::string channel = txt.c_str();
+ allowchans[channel] = 1;
+ }
+ DELETE(MyConf);
+ }
+
+ public:
+ ModuleRestrictChans(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ ReadConfig();
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ReadConfig();
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = List[I_OnRehash] = 1;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ irc::string x = cname;
+ // user is not an oper and its not in the allow list
+ if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end()))
+ {
+ // channel does not yet exist (record is null, about to be created IF we were to allow it)
+ if (!chan)
+ {
+ user->WriteServ("530 %s %s :Only IRC operators may create new channels",user->nick,cname,cname);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleRestrictChans()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleRestrictChans)
diff --git a/src/modules/m_restrictmsg.cpp b/src/modules/m_restrictmsg.cpp
index c05e320fe..0e95660b2 100644
--- a/src/modules/m_restrictmsg.cpp
+++ b/src/modules/m_restrictmsg.cpp
@@ -1 +1,75 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */ class ModuleRestrictMsg : public Module { public: ModuleRestrictMsg(InspIRCd* Me) : Module(Me) { } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if ((target_type == TYPE_USER) && (IS_LOCAL(user))) { userrec* u = (userrec*)dest; // message allowed if: // (1) the sender is opered // (2) the recipient is opered // anything else, blocked. if (IS_OPER(u) || IS_OPER(user)) { return 0; } user->WriteServ("531 %s %s :You are not permitted to send private messages to this user",user->nick,u->nick); return 1; } // however, we must allow channel messages... return 0; } virtual int OnUserPreNotice(userrec* 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() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleRestrictMsg) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */
+
+
+class ModuleRestrictMsg : public Module
+{
+
+ public:
+
+ ModuleRestrictMsg(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
+ {
+ userrec* u = (userrec*)dest;
+
+ // message allowed if:
+ // (1) the sender is opered
+ // (2) the recipient is opered
+ // anything else, blocked.
+ if (IS_OPER(u) || IS_OPER(user))
+ {
+ return 0;
+ }
+ user->WriteServ("531 %s %s :You are not permitted to send private messages to this user",user->nick,u->nick);
+ return 1;
+ }
+
+ // however, we must allow channel messages...
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleRestrictMsg)
diff --git a/src/modules/m_safelist.cpp b/src/modules/m_safelist.cpp
index abd782c86..4adfc0011 100644
--- a/src/modules/m_safelist.cpp
+++ b/src/modules/m_safelist.cpp
@@ -1 +1,268 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" /** Holds a users m_safelist state */ class ListData : public classbase { public: long list_start; long list_position; bool list_ended; const std::string glob; int minusers; int maxusers; ListData() : list_start(0), list_position(0), list_ended(false) {}; ListData(long pos, time_t t, const std::string &pattern, int mi, int ma) : list_start(t), list_position(pos), list_ended(false), glob(pattern), minusers(mi), maxusers(ma) {}; }; /* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */ class ModuleSafeList : public Module { time_t ThrottleSecs; size_t ServerNameSize; int global_listing; int LimitList; public: ModuleSafeList(InspIRCd* Me) : Module(Me) { OnRehash(NULL, ""); } virtual ~ModuleSafeList() { } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader MyConf(ServerInstance); ThrottleSecs = MyConf.ReadInteger("safelist", "throttle", "60", 0, true); LimitList = MyConf.ReadInteger("safelist", "maxlisters", "50", 0, true); ServerNameSize = strlen(ServerInstance->Config->ServerName) + 4; global_listing = 0; } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } void Implements(char* List) { List[I_OnBufferFlushed] = List[I_OnPreCommand] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnRehash] = 1; } /* * OnPreCommand() * Intercept the LIST command. */ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { /* If the command doesnt appear to be valid, we dont want to mess with it. */ if (!validated) return 0; if (command == "LIST") { return this->HandleList(parameters, pcnt, user); } return 0; } /* * HandleList() * Handle (override) the LIST command. */ int HandleList(const char** parameters, int pcnt, userrec* user) { int minusers = 0, maxusers = 0; if (global_listing >= LimitList) { user->WriteServ("NOTICE %s :*** Server load is currently too heavy. Please try again later.", user->nick); user->WriteServ("321 %s Channel :Users Name",user->nick); user->WriteServ("323 %s :End of channel list.",user->nick); return 1; } /* First, let's check if the user is currently /list'ing */ ListData *ld; user->GetExt("safelist_cache", ld); if (ld) { /* user is already /list'ing, we don't want to do shit. */ return 1; } /* Work around mIRC suckyness. YOU SUCK, KHALED! */ if (pcnt == 1) { if (*parameters[0] == '<') { maxusers = atoi(parameters[0]+1); ServerInstance->Log(DEBUG,"Max users: %d", maxusers); pcnt = 0; } else if (*parameters[0] == '>') { minusers = atoi(parameters[0]+1); ServerInstance->Log(DEBUG,"Min users: %d", minusers); pcnt = 0; } } time_t* last_list_time; user->GetExt("safelist_last", last_list_time); if (last_list_time) { if (ServerInstance->Time() < (*last_list_time)+ThrottleSecs) { user->WriteServ("NOTICE %s :*** Woah there, slow down a little, you can't /LIST so often!",user->nick); user->WriteServ("321 %s Channel :Users Name",user->nick); user->WriteServ("323 %s :End of channel list.",user->nick); return 1; } DELETE(last_list_time); user->Shrink("safelist_last"); } /* * start at channel 0! ;) */ ld = new ListData(0,ServerInstance->Time(), pcnt ? parameters[0] : "*", minusers, maxusers); user->Extend("safelist_cache", ld); time_t* llt = new time_t; *llt = ServerInstance->Time(); user->Extend("safelist_last", llt); user->WriteServ("321 %s Channel :Users Name",user->nick); global_listing++; return 1; } virtual void OnBufferFlushed(userrec* user) { char buffer[MAXBUF]; ListData* ld; if (user->GetExt("safelist_cache", ld)) { chanrec* chan = NULL; long amount_sent = 0; do { chan = ServerInstance->GetChannelIndex(ld->list_position); bool has_user = (chan && chan->HasUser(user)); long users = chan ? chan->GetUserCounter() : 0; bool too_few = (ld->minusers && (users <= ld->minusers)); bool too_many = (ld->maxusers && (users >= ld->maxusers)); if (chan && (too_many || too_few)) { ld->list_position++; continue; } if ((chan) && (chan->modes[CM_PRIVATE])) { bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str()))); if ((users) && (display)) { int counter = snprintf(buffer, MAXBUF, "322 %s *", user->nick); amount_sent += counter + ServerNameSize; user->WriteServ(std::string(buffer)); } } else if ((chan) && (((!(chan->modes[CM_PRIVATE])) && (!(chan->modes[CM_SECRET]))) || (has_user))) { bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str()))); if ((users) && (display)) { int counter = snprintf(buffer, MAXBUF, "322 %s %s %ld :[+%s] %s",user->nick, chan->name, users, chan->ChanModes(has_user), chan->topic); amount_sent += counter + ServerNameSize; user->WriteServ(std::string(buffer)); } } else { if (!chan) { if (!ld->list_ended) { ld->list_ended = true; user->WriteServ("323 %s :End of channel list.",user->nick); } } } ld->list_position++; } while ((chan != NULL) && (amount_sent < (user->sendqmax / 4))); if (ld->list_ended) { user->Shrink("safelist_cache"); DELETE(ld); global_listing--; } } } virtual void OnCleanup(int target_type, void* item) { if(target_type == TYPE_USER) { userrec* u = (userrec*)item; ListData* ld; u->GetExt("safelist_cache", ld); if (ld) { u->Shrink("safelist_cache"); DELETE(ld); global_listing--; } time_t* last_list_time; u->GetExt("safelist_last", last_list_time); if (last_list_time) { DELETE(last_list_time); u->Shrink("safelist_last"); } } } virtual void On005Numeric(std::string &output) { output.append(" SAFELIST"); } virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) { this->OnCleanup(TYPE_USER,user); } }; MODULE_INIT(ModuleSafeList) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+/** Holds a users m_safelist state
+ */
+class ListData : public classbase
+{
+ public:
+ long list_start;
+ long list_position;
+ bool list_ended;
+ const std::string glob;
+ int minusers;
+ int maxusers;
+
+ ListData() : list_start(0), list_position(0), list_ended(false) {};
+ ListData(long pos, time_t t, const std::string &pattern, int mi, int ma) : list_start(t), list_position(pos), list_ended(false), glob(pattern), minusers(mi), maxusers(ma) {};
+};
+
+/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */
+
+class ModuleSafeList : public Module
+{
+ time_t ThrottleSecs;
+ size_t ServerNameSize;
+ int global_listing;
+ int LimitList;
+ public:
+ ModuleSafeList(InspIRCd* Me) : Module(Me)
+ {
+ OnRehash(NULL, "");
+ }
+
+ virtual ~ModuleSafeList()
+ {
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader MyConf(ServerInstance);
+ ThrottleSecs = MyConf.ReadInteger("safelist", "throttle", "60", 0, true);
+ LimitList = MyConf.ReadInteger("safelist", "maxlisters", "50", 0, true);
+ ServerNameSize = strlen(ServerInstance->Config->ServerName) + 4;
+ global_listing = 0;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnBufferFlushed] = List[I_OnPreCommand] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnRehash] = 1;
+ }
+
+ /*
+ * OnPreCommand()
+ * Intercept the LIST command.
+ */
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ /* If the command doesnt appear to be valid, we dont want to mess with it. */
+ if (!validated)
+ return 0;
+
+ if (command == "LIST")
+ {
+ return this->HandleList(parameters, pcnt, user);
+ }
+ return 0;
+ }
+
+ /*
+ * HandleList()
+ * Handle (override) the LIST command.
+ */
+ int HandleList(const char** parameters, int pcnt, userrec* user)
+ {
+ int minusers = 0, maxusers = 0;
+
+ if (global_listing >= LimitList)
+ {
+ user->WriteServ("NOTICE %s :*** Server load is currently too heavy. Please try again later.", user->nick);
+ user->WriteServ("321 %s Channel :Users Name",user->nick);
+ user->WriteServ("323 %s :End of channel list.",user->nick);
+ return 1;
+ }
+
+ /* First, let's check if the user is currently /list'ing */
+ ListData *ld;
+ user->GetExt("safelist_cache", ld);
+
+ if (ld)
+ {
+ /* user is already /list'ing, we don't want to do shit. */
+ return 1;
+ }
+
+ /* Work around mIRC suckyness. YOU SUCK, KHALED! */
+ if (pcnt == 1)
+ {
+ if (*parameters[0] == '<')
+ {
+ maxusers = atoi(parameters[0]+1);
+ ServerInstance->Log(DEBUG,"Max users: %d", maxusers);
+ pcnt = 0;
+ }
+ else if (*parameters[0] == '>')
+ {
+ minusers = atoi(parameters[0]+1);
+ ServerInstance->Log(DEBUG,"Min users: %d", minusers);
+ pcnt = 0;
+ }
+ }
+
+ time_t* last_list_time;
+ user->GetExt("safelist_last", last_list_time);
+ if (last_list_time)
+ {
+ if (ServerInstance->Time() < (*last_list_time)+ThrottleSecs)
+ {
+ user->WriteServ("NOTICE %s :*** Woah there, slow down a little, you can't /LIST so often!",user->nick);
+ user->WriteServ("321 %s Channel :Users Name",user->nick);
+ user->WriteServ("323 %s :End of channel list.",user->nick);
+ return 1;
+ }
+
+ DELETE(last_list_time);
+ user->Shrink("safelist_last");
+ }
+
+
+ /*
+ * start at channel 0! ;)
+ */
+ ld = new ListData(0,ServerInstance->Time(), pcnt ? parameters[0] : "*", minusers, maxusers);
+ user->Extend("safelist_cache", ld);
+
+ time_t* llt = new time_t;
+ *llt = ServerInstance->Time();
+ user->Extend("safelist_last", llt);
+
+ user->WriteServ("321 %s Channel :Users Name",user->nick);
+
+ global_listing++;
+
+ return 1;
+ }
+
+ virtual void OnBufferFlushed(userrec* user)
+ {
+ char buffer[MAXBUF];
+ ListData* ld;
+ if (user->GetExt("safelist_cache", ld))
+ {
+ chanrec* chan = NULL;
+ long amount_sent = 0;
+ do
+ {
+ chan = ServerInstance->GetChannelIndex(ld->list_position);
+ bool has_user = (chan && chan->HasUser(user));
+ long users = chan ? chan->GetUserCounter() : 0;
+
+ bool too_few = (ld->minusers && (users <= ld->minusers));
+ bool too_many = (ld->maxusers && (users >= ld->maxusers));
+
+ if (chan && (too_many || too_few))
+ {
+ ld->list_position++;
+ continue;
+ }
+
+ if ((chan) && (chan->modes[CM_PRIVATE]))
+ {
+ bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str())));
+ if ((users) && (display))
+ {
+ int counter = snprintf(buffer, MAXBUF, "322 %s *", user->nick);
+ amount_sent += counter + ServerNameSize;
+ user->WriteServ(std::string(buffer));
+ }
+ }
+ else if ((chan) && (((!(chan->modes[CM_PRIVATE])) && (!(chan->modes[CM_SECRET]))) || (has_user)))
+ {
+ bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str())));
+ if ((users) && (display))
+ {
+ int counter = snprintf(buffer, MAXBUF, "322 %s %s %ld :[+%s] %s",user->nick, chan->name, users, chan->ChanModes(has_user), chan->topic);
+ amount_sent += counter + ServerNameSize;
+ user->WriteServ(std::string(buffer));
+ }
+ }
+ else
+ {
+ if (!chan)
+ {
+ if (!ld->list_ended)
+ {
+ ld->list_ended = true;
+ user->WriteServ("323 %s :End of channel list.",user->nick);
+ }
+ }
+ }
+ ld->list_position++;
+ }
+ while ((chan != NULL) && (amount_sent < (user->sendqmax / 4)));
+ if (ld->list_ended)
+ {
+ user->Shrink("safelist_cache");
+ DELETE(ld);
+ global_listing--;
+ }
+ }
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if(target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)item;
+ ListData* ld;
+ u->GetExt("safelist_cache", ld);
+ if (ld)
+ {
+ u->Shrink("safelist_cache");
+ DELETE(ld);
+ global_listing--;
+ }
+ time_t* last_list_time;
+ u->GetExt("safelist_last", last_list_time);
+ if (last_list_time)
+ {
+ DELETE(last_list_time);
+ u->Shrink("safelist_last");
+ }
+ }
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" SAFELIST");
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
+ {
+ this->OnCleanup(TYPE_USER,user);
+ }
+
+};
+
+MODULE_INIT(ModuleSafeList)
diff --git a/src/modules/m_sajoin.cpp b/src/modules/m_sajoin.cpp
index 2b143606f..0c9822fb9 100644
--- a/src/modules/m_sajoin.cpp
+++ b/src/modules/m_sajoin.cpp
@@ -1 +1,114 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style SAJOIN command */ /** Handle /SAJOIN */ class cmd_sajoin : public command_t { public: cmd_sajoin (InspIRCd* Instance) : command_t(Instance,"SAJOIN", 'o', 2) { this->source = "m_sajoin.so"; syntax = "<nick> <channel>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* dest = ServerInstance->FindNick(parameters[0]); if (dest) { if (ServerInstance->ULine(dest->server)) { user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); return CMD_FAILURE; } if (!ServerInstance->IsChannel(parameters[1])) { /* we didn't need to check this for each character ;) */ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Invalid characters in channel name"); return CMD_FAILURE; } /* For local users, we send the JoinUser which may create a channel and set its TS. * For non-local users, we just return CMD_SUCCESS, knowing this will propogate it where it needs to be * and then that server will generate the users JOIN or FJOIN instead. */ if (IS_LOCAL(dest)) { chanrec::JoinUser(ServerInstance, dest, parameters[1], true, "", ServerInstance->Time(true)); /* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propogate */ chanrec* n = ServerInstance->FindChan(parameters[1]); if (n) { if (n->HasUser(dest)) { ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]); return CMD_SUCCESS; } else { user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]+" (User is probably banned, or blocking modes)"); return CMD_FAILURE; } } else { user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]); return CMD_FAILURE; } } else { ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]); return CMD_SUCCESS; } } else { user->WriteServ("NOTICE "+std::string(user->nick)+" :*** No such nickname "+parameters[0]); return CMD_FAILURE; } } }; class ModuleSajoin : public Module { cmd_sajoin* mycommand; public: ModuleSajoin(InspIRCd* Me) : Module(Me) { mycommand = new cmd_sajoin(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSajoin() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSajoin) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style SAJOIN command */
+
+/** Handle /SAJOIN
+ */
+class cmd_sajoin : public command_t
+{
+ public:
+ cmd_sajoin (InspIRCd* Instance) : command_t(Instance,"SAJOIN", 'o', 2)
+ {
+ this->source = "m_sajoin.so";
+ syntax = "<nick> <channel>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+ if (dest)
+ {
+ if (ServerInstance->ULine(dest->server))
+ {
+ user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
+ return CMD_FAILURE;
+ }
+ if (!ServerInstance->IsChannel(parameters[1]))
+ {
+ /* we didn't need to check this for each character ;) */
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Invalid characters in channel name");
+ return CMD_FAILURE;
+ }
+
+ /* For local users, we send the JoinUser which may create a channel and set its TS.
+ * For non-local users, we just return CMD_SUCCESS, knowing this will propogate it where it needs to be
+ * and then that server will generate the users JOIN or FJOIN instead.
+ */
+ if (IS_LOCAL(dest))
+ {
+ chanrec::JoinUser(ServerInstance, dest, parameters[1], true, "", ServerInstance->Time(true));
+ /* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propogate */
+ chanrec* n = ServerInstance->FindChan(parameters[1]);
+ if (n)
+ {
+ if (n->HasUser(dest))
+ {
+ ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]);
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]+" (User is probably banned, or blocking modes)");
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]);
+ return CMD_SUCCESS;
+ }
+ }
+ else
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** No such nickname "+parameters[0]);
+ return CMD_FAILURE;
+ }
+ }
+};
+
+class ModuleSajoin : public Module
+{
+ cmd_sajoin* mycommand;
+ public:
+ ModuleSajoin(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_sajoin(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSajoin()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSajoin)
diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp
index f48e078b1..88d0d666e 100644
--- a/src/modules/m_samode.cpp
+++ b/src/modules/m_samode.cpp
@@ -1 +1,98 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Provides more advanced UnrealIRCd SAMODE command */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /** Handle /SAMODE */ class cmd_samode : public command_t { public: cmd_samode (InspIRCd* Instance) : command_t(Instance,"SAMODE", 'o', 2) { this->source = "m_samode.so"; syntax = "<target> <modes> {<mode-parameters>}"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { /* * Handles an SAMODE request. Notifies all +s users. */ userrec* n = new userrec(ServerInstance); n->SetFd(FD_MAGIC_NUMBER); ServerInstance->SendMode(parameters,pcnt,n); delete n; if (ServerInstance->Modes->GetLastParse().length()) { ServerInstance->WriteOpers("*** " + std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse()); std::deque<std::string> n; irc::spacesepstream spaced(ServerInstance->Modes->GetLastParse()); std::string one = "*"; while ((one = spaced.GetToken()) != "") n.push_back(one); Event rmode((char *)&n, NULL, "send_mode"); rmode.Send(ServerInstance); n.clear(); n.push_back(std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse()); Event rmode2((char *)&n, NULL, "send_opers"); rmode2.Send(ServerInstance); /* XXX: Yes, this is right. We dont want to propogate the * actual SAMODE command, just the MODE command generated * by the send_mode */ return CMD_LOCALONLY; } else { user->WriteServ("NOTICE %s :*** Invalid SAMODE sequence.", user->nick); } return CMD_FAILURE; } }; class ModuleSaMode : public Module { cmd_samode* mycommand; public: ModuleSaMode(InspIRCd* Me) : Module(Me) { mycommand = new cmd_samode(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSaMode() { } virtual Version GetVersion() { return Version(1,1,2,2,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSaMode) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Provides more advanced UnrealIRCd SAMODE command */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/** Handle /SAMODE
+ */
+class cmd_samode : public command_t
+{
+ public:
+ cmd_samode (InspIRCd* Instance) : command_t(Instance,"SAMODE", 'o', 2)
+ {
+ this->source = "m_samode.so";
+ syntax = "<target> <modes> {<mode-parameters>}";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ /*
+ * Handles an SAMODE request. Notifies all +s users.
+ */
+
+ userrec* n = new userrec(ServerInstance);
+ n->SetFd(FD_MAGIC_NUMBER);
+ ServerInstance->SendMode(parameters,pcnt,n);
+ delete n;
+
+ if (ServerInstance->Modes->GetLastParse().length())
+ {
+ ServerInstance->WriteOpers("*** " + std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse());
+
+ std::deque<std::string> n;
+ irc::spacesepstream spaced(ServerInstance->Modes->GetLastParse());
+ std::string one = "*";
+ while ((one = spaced.GetToken()) != "")
+ n.push_back(one);
+
+ Event rmode((char *)&n, NULL, "send_mode");
+ rmode.Send(ServerInstance);
+
+ n.clear();
+ n.push_back(std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse());
+ Event rmode2((char *)&n, NULL, "send_opers");
+ rmode2.Send(ServerInstance);
+
+ /* XXX: Yes, this is right. We dont want to propogate the
+ * actual SAMODE command, just the MODE command generated
+ * by the send_mode
+ */
+ return CMD_LOCALONLY;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Invalid SAMODE sequence.", user->nick);
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleSaMode : public Module
+{
+ cmd_samode* mycommand;
+ public:
+ ModuleSaMode(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_samode(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSaMode()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,2,2,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSaMode)
diff --git a/src/modules/m_sanick.cpp b/src/modules/m_sanick.cpp
index 8810550ae..715d978c3 100644
--- a/src/modules/m_sanick.cpp
+++ b/src/modules/m_sanick.cpp
@@ -1 +1,97 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for SANICK command */ /** Handle /SANICK */ class cmd_sanick : public command_t { public: cmd_sanick (InspIRCd* Instance) : command_t(Instance,"SANICK", 'o', 2) { this->source = "m_sanick.so"; syntax = "<nick> <new-nick>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* source = ServerInstance->FindNick(parameters[0]); if (source) { if (ServerInstance->ULine(source->server)) { user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); return CMD_FAILURE; } std::string oldnick = user->nick; if (ServerInstance->IsNick(parameters[1])) { if (source->ForceNickChange(parameters[1])) { ServerInstance->WriteOpers("*** " + oldnick+" used SANICK to change "+std::string(parameters[0])+" to "+parameters[1]); return CMD_SUCCESS; } else { /* We couldnt change the nick */ ServerInstance->WriteOpers("*** " + oldnick+" failed SANICK (from "+std::string(parameters[0])+" to "+parameters[1]+")"); return CMD_FAILURE; } } else { user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[1]); } return CMD_FAILURE; } else { user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick, parameters[0]); } return CMD_FAILURE; } }; class ModuleSanick : public Module { cmd_sanick* mycommand; public: ModuleSanick(InspIRCd* Me) : Module(Me) { mycommand = new cmd_sanick(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSanick() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSanick) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for SANICK command */
+
+/** Handle /SANICK
+ */
+class cmd_sanick : public command_t
+{
+ public:
+ cmd_sanick (InspIRCd* Instance) : command_t(Instance,"SANICK", 'o', 2)
+ {
+ this->source = "m_sanick.so";
+ syntax = "<nick> <new-nick>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* source = ServerInstance->FindNick(parameters[0]);
+ if (source)
+ {
+ if (ServerInstance->ULine(source->server))
+ {
+ user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
+ return CMD_FAILURE;
+ }
+ std::string oldnick = user->nick;
+ if (ServerInstance->IsNick(parameters[1]))
+ {
+ if (source->ForceNickChange(parameters[1]))
+ {
+ ServerInstance->WriteOpers("*** " + oldnick+" used SANICK to change "+std::string(parameters[0])+" to "+parameters[1]);
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ /* We couldnt change the nick */
+ ServerInstance->WriteOpers("*** " + oldnick+" failed SANICK (from "+std::string(parameters[0])+" to "+parameters[1]+")");
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[1]);
+ }
+
+ return CMD_FAILURE;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick, parameters[0]);
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+
+class ModuleSanick : public Module
+{
+ cmd_sanick* mycommand;
+ public:
+ ModuleSanick(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_sanick(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSanick()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSanick)
diff --git a/src/modules/m_sapart.cpp b/src/modules/m_sapart.cpp
index 4d663e822..829607e58 100644
--- a/src/modules/m_sapart.cpp
+++ b/src/modules/m_sapart.cpp
@@ -1 +1,113 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style SAPART command */ /** Handle /SAPART */ class cmd_sapart : public command_t { public: cmd_sapart (InspIRCd* Instance) : command_t(Instance,"SAPART", 'o', 2) { this->source = "m_sapart.so"; syntax = "<nick> <channel>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* dest = ServerInstance->FindNick(parameters[0]); chanrec* channel = ServerInstance->FindChan(parameters[1]); if (dest && channel) { if (ServerInstance->ULine(dest->server)) { user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); return CMD_FAILURE; } /* For local clients, directly part them generating a PART message. For remote clients, * just return CMD_SUCCESS knowing the protocol module will route the SAPART to the users * local server and that will generate the PART instead */ if (IS_LOCAL(dest)) { if (!channel->PartUser(dest, dest->nick)) delete channel; chanrec* n = ServerInstance->FindChan(parameters[1]); if (!n) { ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAPART to make "+dest->nick+" part "+parameters[1]); return CMD_SUCCESS; } else { if (!n->HasUser(dest)) { ServerInstance->WriteOpers("*** "+std::string(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, dest->nick, parameters[1]); return CMD_FAILURE; } } } else { ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAPART to make "+dest->nick+" part "+parameters[1]); } return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick); } return CMD_FAILURE; } }; class ModuleSapart : public Module { cmd_sapart* mycommand; public: ModuleSapart(InspIRCd* Me) : Module(Me) { mycommand = new cmd_sapart(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSapart() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSapart) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style SAPART command */
+
+/** Handle /SAPART
+ */
+class cmd_sapart : public command_t
+{
+ public:
+ cmd_sapart (InspIRCd* Instance) : command_t(Instance,"SAPART", 'o', 2)
+ {
+ this->source = "m_sapart.so";
+ syntax = "<nick> <channel>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+ chanrec* channel = ServerInstance->FindChan(parameters[1]);
+ if (dest && channel)
+ {
+ if (ServerInstance->ULine(dest->server))
+ {
+ user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
+ return CMD_FAILURE;
+ }
+
+ /* For local clients, directly part them generating a PART message. For remote clients,
+ * just return CMD_SUCCESS knowing the protocol module will route the SAPART to the users
+ * local server and that will generate the PART instead
+ */
+ if (IS_LOCAL(dest))
+ {
+ if (!channel->PartUser(dest, dest->nick))
+ delete channel;
+ chanrec* n = ServerInstance->FindChan(parameters[1]);
+ if (!n)
+ {
+ ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAPART to make "+dest->nick+" part "+parameters[1]);
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ if (!n->HasUser(dest))
+ {
+ ServerInstance->WriteOpers("*** "+std::string(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, dest->nick, parameters[1]);
+ return CMD_FAILURE;
+ }
+ }
+ }
+ else
+ {
+ ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAPART to make "+dest->nick+" part "+parameters[1]);
+ }
+
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick);
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+
+class ModuleSapart : public Module
+{
+ cmd_sapart* mycommand;
+ public:
+ ModuleSapart(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_sapart(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSapart()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSapart)
+
diff --git a/src/modules/m_saquit.cpp b/src/modules/m_saquit.cpp
index 36d511af9..d2fa8e89b 100644
--- a/src/modules/m_saquit.cpp
+++ b/src/modules/m_saquit.cpp
@@ -1 +1,82 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */ /** Handle /SAQUIT */ class cmd_saquit : public command_t { public: cmd_saquit (InspIRCd* Instance) : command_t(Instance,"SAQUIT",'o',2) { this->source = "m_saquit.so"; syntax = "<nick> <reason>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* dest = ServerInstance->FindNick(parameters[0]); if (dest) { if (ServerInstance->ULine(dest->server)) { user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); return CMD_FAILURE; } irc::stringjoiner reason_join(" ", parameters, 1, pcnt - 1); std::string line = reason_join.GetJoined(); ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAQUIT to make "+std::string(dest->nick)+" quit with a reason of "+line); userrec::QuitUser(ServerInstance, dest, line); return CMD_SUCCESS; } else { user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[0]); } return CMD_FAILURE; } }; class ModuleSaquit : public Module { cmd_saquit* mycommand; public: ModuleSaquit(InspIRCd* Me) : Module(Me) { mycommand = new cmd_saquit(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSaquit() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSaquit) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */
+
+/** Handle /SAQUIT
+ */
+class cmd_saquit : public command_t
+{
+ public:
+ cmd_saquit (InspIRCd* Instance) : command_t(Instance,"SAQUIT",'o',2)
+ {
+ this->source = "m_saquit.so";
+ syntax = "<nick> <reason>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+ if (dest)
+ {
+ if (ServerInstance->ULine(dest->server))
+ {
+ user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
+ return CMD_FAILURE;
+ }
+ irc::stringjoiner reason_join(" ", parameters, 1, pcnt - 1);
+ std::string line = reason_join.GetJoined();
+
+ ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAQUIT to make "+std::string(dest->nick)+" quit with a reason of "+line);
+ userrec::QuitUser(ServerInstance, dest, line);
+
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[0]);
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleSaquit : public Module
+{
+ cmd_saquit* mycommand;
+ public:
+ ModuleSaquit(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_saquit(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSaquit()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSaquit)
diff --git a/src/modules/m_securelist.cpp b/src/modules/m_securelist.cpp
index 264090311..8761716c0 100644
--- a/src/modules/m_securelist.cpp
+++ b/src/modules/m_securelist.cpp
@@ -1 +1,97 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */ class ModuleSecureList : public Module { private: std::vector<std::string> allowlist; time_t WaitTime; public: ModuleSecureList(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); } virtual ~ModuleSecureList() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } void OnRehash(userrec* user, const std::string &parameter) { ConfigReader* MyConf = new ConfigReader(ServerInstance); allowlist.clear(); for (int i = 0; i < MyConf->Enumerate("securehost"); i++) allowlist.push_back(MyConf->ReadValue("securehost", "exception", i)); WaitTime = MyConf->ReadInteger("securelist", "waittime", "60", 0, true); DELETE(MyConf); } void Implements(char* List) { List[I_OnRehash] = List[I_OnPreCommand] = List[I_On005Numeric] = 1; } /* * OnPreCommand() * Intercept the LIST command. */ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { /* If the command doesnt appear to be valid, we dont want to mess with it. */ if (!validated) return 0; if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user))) { /* Normally wouldnt be allowed here, are they exempt? */ for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++) if (ServerInstance->MatchText(user->MakeHost(), *x)) return 0; /* Not exempt, BOOK EM DANNO! */ user->WriteServ("NOTICE %s :*** You cannot list within the first %d seconds of connecting. Please try again later.",user->nick, WaitTime); /* 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->WriteServ("321 %s Channel :Users Name",user->nick); user->WriteServ("323 %s :End of channel list.",user->nick); return 1; } return 0; } virtual void On005Numeric(std::string &output) { output.append(" SECURELIST"); } virtual Priority Prioritize() { return (Priority)ServerInstance->PriorityBefore("m_safelist.so"); } }; MODULE_INIT(ModuleSecureList) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */
+
+class ModuleSecureList : public Module
+{
+ private:
+ std::vector<std::string> allowlist;
+ time_t WaitTime;
+ public:
+ ModuleSecureList(InspIRCd* Me) : Module(Me)
+ {
+ OnRehash(NULL,"");
+ }
+
+ virtual ~ModuleSecureList()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+ void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader* MyConf = new ConfigReader(ServerInstance);
+ allowlist.clear();
+ for (int i = 0; i < MyConf->Enumerate("securehost"); i++)
+ allowlist.push_back(MyConf->ReadValue("securehost", "exception", i));
+ WaitTime = MyConf->ReadInteger("securelist", "waittime", "60", 0, true);
+ DELETE(MyConf);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnPreCommand] = List[I_On005Numeric] = 1;
+ }
+
+ /*
+ * OnPreCommand()
+ * Intercept the LIST command.
+ */
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ /* If the command doesnt appear to be valid, we dont want to mess with it. */
+ if (!validated)
+ return 0;
+
+ if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user)))
+ {
+ /* Normally wouldnt be allowed here, are they exempt? */
+ for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++)
+ if (ServerInstance->MatchText(user->MakeHost(), *x))
+ return 0;
+
+ /* Not exempt, BOOK EM DANNO! */
+ user->WriteServ("NOTICE %s :*** You cannot list within the first %d seconds of connecting. Please try again later.",user->nick, WaitTime);
+ /* 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->WriteServ("321 %s Channel :Users Name",user->nick);
+ user->WriteServ("323 %s :End of channel list.",user->nick);
+ return 1;
+ }
+ return 0;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" SECURELIST");
+ }
+
+ virtual Priority Prioritize()
+ {
+ return (Priority)ServerInstance->PriorityBefore("m_safelist.so");
+ }
+
+};
+
+MODULE_INIT(ModuleSecureList)
diff --git a/src/modules/m_seenicks.cpp b/src/modules/m_seenicks.cpp
index 2e7755810..215cd34b0 100644
--- a/src/modules/m_seenicks.cpp
+++ b/src/modules/m_seenicks.cpp
@@ -1 +1,55 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "configreader.h" /* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks */ class ModuleSeeNicks : public Module { public: ModuleSeeNicks(InspIRCd* Me) : Module(Me) { ServerInstance->SNO->EnableSnomask('n',"NICK"); ServerInstance->SNO->EnableSnomask('N',"REMOTENICK"); } virtual ~ModuleSeeNicks() { ServerInstance->SNO->DisableSnomask('n'); ServerInstance->SNO->DisableSnomask('N'); } virtual Version GetVersion() { return Version(1,1,0,1, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnUserPostNick] = 1; } virtual void OnUserPostNick(userrec* user, const std::string &oldnick) { ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick); } }; MODULE_INIT(ModuleSeeNicks) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "configreader.h"
+
+/* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks */
+
+class ModuleSeeNicks : public Module
+{
+ public:
+ ModuleSeeNicks(InspIRCd* Me)
+ : Module(Me)
+ {
+ ServerInstance->SNO->EnableSnomask('n',"NICK");
+ ServerInstance->SNO->EnableSnomask('N',"REMOTENICK");
+ }
+
+ virtual ~ModuleSeeNicks()
+ {
+ ServerInstance->SNO->DisableSnomask('n');
+ ServerInstance->SNO->DisableSnomask('N');
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPostNick] = 1;
+ }
+
+ virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
+ {
+ ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick);
+ }
+};
+
+MODULE_INIT(ModuleSeeNicks)
diff --git a/src/modules/m_services.cpp b/src/modules/m_services.cpp
index d429b2860..22b5dfcb5 100644
--- a/src/modules/m_services.cpp
+++ b/src/modules/m_services.cpp
@@ -1 +1,310 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" static bool kludgeme = false; /* $ModDesc: Povides support for services +r user/chan modes and more */ /** Channel mode +r - mark a channel as identified */ class Channel_r : public ModeHandler { public: Channel_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { // only a u-lined server may add or remove the +r mode. if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.')))) { channel->SetMode('r',adding); return MODEACTION_ALLOW; } else { source->WriteServ("500 %s :Only a server may modify the +r channel mode", source->nick); return MODEACTION_DENY; } } }; /** User mode +r - mark a user as identified */ class User_r : public ModeHandler { public: User_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if ((kludgeme) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.')))) { if ((adding && !dest->IsModeSet('r')) || (!adding && dest->IsModeSet('r'))) { dest->SetMode('r',adding); return MODEACTION_ALLOW; } return MODEACTION_DENY; } else { source->WriteServ("500 %s :Only a server may modify the +r user mode", source->nick); return MODEACTION_DENY; } } }; /** Channel mode +R - registered users only */ class Channel_R : public ModeHandler { public: Channel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('R')) { channel->SetMode('R',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('R')) { channel->SetMode('R',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** User mode +R - only allow PRIVMSG and NOTICE from registered users */ class User_R : public ModeHandler { public: User_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!dest->IsModeSet('R')) { dest->SetMode('R',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('R')) { dest->SetMode('R',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Channel mode +M - only allow privmsg and notice to channel from registered users */ class Channel_M : public ModeHandler { public: Channel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('M')) { channel->SetMode('M',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('M')) { channel->SetMode('M',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Dreamnforge-like services support */ class ModuleServices : public Module { Channel_r* m1; Channel_R* m2; Channel_M* m3; User_r* m4; User_R* m5; public: ModuleServices(InspIRCd* Me) : Module(Me) { m1 = new Channel_r(ServerInstance); m2 = new Channel_R(ServerInstance); m3 = new Channel_M(ServerInstance); m4 = new User_r(ServerInstance); m5 = new User_R(ServerInstance); if (!ServerInstance->AddMode(m1, 'r') || !ServerInstance->AddMode(m2, 'R') || !ServerInstance->AddMode(m3, 'M') || !ServerInstance->AddMode(m4, 'r') || !ServerInstance->AddMode(m5, 'R')) { throw ModuleException("Could not add user and channel modes!"); } kludgeme = false; } /* <- :stitch.chatspike.net 307 w00t w00t :is a registered nick */ virtual void OnWhois(userrec* source, userrec* dest) { if (dest->IsModeSet('r')) { /* user is registered */ ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick, dest->nick); } } void Implements(char* List) { List[I_OnWhois] = List[I_OnUserPostNick] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1; } virtual void OnUserPostNick(userrec* user, const std::string &oldnick) { /* On nickchange, if they have +r, remove it */ if (user->IsModeSet('r')) { const char* modechange[2]; modechange[0] = user->nick; modechange[1] = "-r"; kludgeme = true; ServerInstance->SendMode(modechange,2,user); kludgeme = false; } } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (!IS_LOCAL(user)) return 0; if (target_type == TYPE_CHANNEL) { chanrec* c = (chanrec*)dest; if ((c->IsModeSet('M')) && (!user->IsModeSet('r'))) { if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) { // user is ulined, can speak regardless return 0; } // user messaging a +M channel and is not registered user->WriteServ("477 %s %s :You need a registered nickname to speak on this channel", user->nick, c->name); return 1; } } if (target_type == TYPE_USER) { userrec* u = (userrec*)dest; if ((u->IsModeSet('R')) && (!user->IsModeSet('r'))) { if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) { // user is ulined, can speak regardless return 0; } // user messaging a +R user and is not registered user->WriteServ("477 %s %s :You need a registered nickname to message this user", user->nick, u->nick); return 1; } } return 0; } virtual int OnUserPreNotice(userrec* 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 int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if (chan) { if (chan->IsModeSet('R')) { if (!user->IsModeSet('r')) { if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) { // user is ulined, won't be stopped from joining return 0; } // joining a +R channel and not identified user->WriteServ("477 %s %s :You need a registered nickname to join this channel", user->nick, chan->name); return 1; } } } return 0; } virtual ~ModuleServices() { kludgeme = true; ServerInstance->Modes->DelMode(m1); ServerInstance->Modes->DelMode(m2); ServerInstance->Modes->DelMode(m3); ServerInstance->Modes->DelMode(m4); ServerInstance->Modes->DelMode(m5); DELETE(m1); DELETE(m2); DELETE(m3); DELETE(m4); DELETE(m5); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleServices) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+static bool kludgeme = false;
+
+/* $ModDesc: Povides support for services +r user/chan modes and more */
+
+/** Channel mode +r - mark a channel as identified
+ */
+class Channel_r : public ModeHandler
+{
+
+ public:
+ Channel_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ // only a u-lined server may add or remove the +r mode.
+ if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.'))))
+ {
+ channel->SetMode('r',adding);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ source->WriteServ("500 %s :Only a server may modify the +r channel mode", source->nick);
+ return MODEACTION_DENY;
+ }
+ }
+};
+
+/** User mode +r - mark a user as identified
+ */
+class User_r : public ModeHandler
+{
+
+ public:
+ User_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if ((kludgeme) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.'))))
+ {
+ if ((adding && !dest->IsModeSet('r')) || (!adding && dest->IsModeSet('r')))
+ {
+ dest->SetMode('r',adding);
+ return MODEACTION_ALLOW;
+ }
+ return MODEACTION_DENY;
+ }
+ else
+ {
+ source->WriteServ("500 %s :Only a server may modify the +r user mode", source->nick);
+ return MODEACTION_DENY;
+ }
+ }
+};
+
+/** Channel mode +R - registered users only
+ */
+class Channel_R : public ModeHandler
+{
+ public:
+ Channel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('R'))
+ {
+ channel->SetMode('R',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('R'))
+ {
+ channel->SetMode('R',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** User mode +R - only allow PRIVMSG and NOTICE from registered users
+ */
+class User_R : public ModeHandler
+{
+ public:
+ User_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!dest->IsModeSet('R'))
+ {
+ dest->SetMode('R',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('R'))
+ {
+ dest->SetMode('R',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** Channel mode +M - only allow privmsg and notice to channel from registered users
+ */
+class Channel_M : public ModeHandler
+{
+ public:
+ Channel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('M'))
+ {
+ channel->SetMode('M',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('M'))
+ {
+ channel->SetMode('M',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** Dreamnforge-like services support
+ */
+class ModuleServices : public Module
+{
+
+ Channel_r* m1;
+ Channel_R* m2;
+ Channel_M* m3;
+ User_r* m4;
+ User_R* m5;
+ public:
+ ModuleServices(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ m1 = new Channel_r(ServerInstance);
+ m2 = new Channel_R(ServerInstance);
+ m3 = new Channel_M(ServerInstance);
+ m4 = new User_r(ServerInstance);
+ m5 = new User_R(ServerInstance);
+
+ if (!ServerInstance->AddMode(m1, 'r') || !ServerInstance->AddMode(m2, 'R') || !ServerInstance->AddMode(m3, 'M')
+ || !ServerInstance->AddMode(m4, 'r') || !ServerInstance->AddMode(m5, 'R'))
+ {
+ throw ModuleException("Could not add user and channel modes!");
+ }
+
+ kludgeme = false;
+ }
+
+ /* <- :stitch.chatspike.net 307 w00t w00t :is a registered nick */
+ virtual void OnWhois(userrec* source, userrec* dest)
+ {
+ if (dest->IsModeSet('r'))
+ {
+ /* user is registered */
+ ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick, dest->nick);
+ }
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnWhois] = List[I_OnUserPostNick] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1;
+ }
+
+ virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
+ {
+ /* On nickchange, if they have +r, remove it */
+ if (user->IsModeSet('r'))
+ {
+ const char* modechange[2];
+ modechange[0] = user->nick;
+ modechange[1] = "-r";
+ kludgeme = true;
+ ServerInstance->SendMode(modechange,2,user);
+ kludgeme = false;
+ }
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (!IS_LOCAL(user))
+ return 0;
+
+ if (target_type == TYPE_CHANNEL)
+ {
+ chanrec* c = (chanrec*)dest;
+ if ((c->IsModeSet('M')) && (!user->IsModeSet('r')))
+ {
+ if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
+ {
+ // user is ulined, can speak regardless
+ return 0;
+ }
+ // user messaging a +M channel and is not registered
+ user->WriteServ("477 %s %s :You need a registered nickname to speak on this channel", user->nick, c->name);
+ return 1;
+ }
+ }
+ if (target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)dest;
+ if ((u->IsModeSet('R')) && (!user->IsModeSet('r')))
+ {
+ if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
+ {
+ // user is ulined, can speak regardless
+ return 0;
+ }
+ // user messaging a +R user and is not registered
+ user->WriteServ("477 %s %s :You need a registered nickname to message this user", user->nick, u->nick);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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 int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if (chan)
+ {
+ if (chan->IsModeSet('R'))
+ {
+ if (!user->IsModeSet('r'))
+ {
+ if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
+ {
+ // user is ulined, won't be stopped from joining
+ return 0;
+ }
+ // joining a +R channel and not identified
+ user->WriteServ("477 %s %s :You need a registered nickname to join this channel", user->nick, chan->name);
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleServices()
+ {
+ kludgeme = true;
+ ServerInstance->Modes->DelMode(m1);
+ ServerInstance->Modes->DelMode(m2);
+ ServerInstance->Modes->DelMode(m3);
+ ServerInstance->Modes->DelMode(m4);
+ ServerInstance->Modes->DelMode(m5);
+ DELETE(m1);
+ DELETE(m2);
+ DELETE(m3);
+ DELETE(m4);
+ DELETE(m5);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+
+MODULE_INIT(ModuleServices)
diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp
index 3d32e3156..cff0d7698 100644
--- a/src/modules/m_services_account.cpp
+++ b/src/modules/m_services_account.cpp
@@ -1 +1,332 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" /* $ModDesc: Povides support for ircu-style services accounts, including chmode +R, etc. */ /** Channel mode +R - unidentified users cannot join */ class AChannel_R : public ModeHandler { public: AChannel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('R')) { channel->SetMode('R',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('R')) { channel->SetMode('R',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** User mode +R - unidentified users cannot message */ class AUser_R : public ModeHandler { public: AUser_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!dest->IsModeSet('R')) { dest->SetMode('R',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('R')) { dest->SetMode('R',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Channel mode +M - unidentified users cannot message channel */ class AChannel_M : public ModeHandler { public: AChannel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('M')) { channel->SetMode('M',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('M')) { channel->SetMode('M',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleServicesAccount : public Module { AChannel_R* m1; AChannel_M* m2; AUser_R* m3; public: ModuleServicesAccount(InspIRCd* Me) : Module(Me) { m1 = new AChannel_R(ServerInstance); m2 = new AChannel_M(ServerInstance); m3 = new AUser_R(ServerInstance); if (!ServerInstance->AddMode(m1, 'R') || !ServerInstance->AddMode(m2, 'M') || !ServerInstance->AddMode(m3, 'R')) throw ModuleException("Could not add new modes!"); } /* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */ virtual void OnWhois(userrec* source, userrec* dest) { std::string *account; dest->GetExt("accountname", account); if (account) { ServerInstance->SendWhoisLine(source, dest, 330, "%s %s %s :is logged in as", source->nick, dest->nick, account->c_str()); } } void Implements(char* List) { List[I_OnWhois] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1; List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnDecodeMetaData] = 1; } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { std::string *account; if (!IS_LOCAL(user)) return 0; user->GetExt("accountname", account); if (target_type == TYPE_CHANNEL) { chanrec* c = (chanrec*)dest; if ((c->IsModeSet('M')) && (!account)) { if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) { // user is ulined, can speak regardless return 0; } // user messaging a +M channel and is not registered user->WriteServ("477 "+std::string(user->nick)+" "+std::string(c->name)+" :You need to be identified to a registered account to message this channel"); return 1; } } if (target_type == TYPE_USER) { userrec* u = (userrec*)dest; if ((u->modes['R'-65]) && (!account)) { if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) { // user is ulined, can speak regardless return 0; } // user messaging a +R user and is not registered user->WriteServ("477 "+std::string(user->nick)+" "+std::string(u->nick)+" :You need to be identified to a registered account to message this user"); return 1; } } return 0; } virtual int OnUserPreNotice(userrec* 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 int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { std::string *account; user->GetExt("accountname", account); if (chan) { if (chan->IsModeSet('R')) { if (!account) { if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) { // user is ulined, won't be stopped from joining return 0; } // joining a +R channel and not identified user->WriteServ("477 "+std::string(user->nick)+" "+std::string(chan->name)+" :You need to be identified to a registered account to join this channel"); return 1; } } } return 0; } // Whenever the linking module wants to send out data, but doesnt know what the data // represents (e.g. it is metadata, added to a userrec or chanrec by a module) then // this method is called. We should use the ProtoSendMetaData function after we've // corrected decided how the data should look, to send the metadata on its way if // it is ours. virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) { // check if the linking module wants to know about OUR metadata if (extname == "accountname") { // check if this user has an swhois field to send std::string* account; user->GetExt("accountname", account); if (account) { // remove any accidental leading/trailing spaces trim(*account); // call this function in the linking module, let it format the data how it // sees fit, and send it on its way. We dont need or want to know how. proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*account); } } } // when a user quits, tidy up their metadata virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) { std::string* account; user->GetExt("accountname", account); if (account) { user->Shrink("accountname"); delete account; } } // if the module is unloaded, tidy up all our dangling metadata virtual void OnCleanup(int target_type, void* item) { if (target_type == TYPE_USER) { userrec* user = (userrec*)item; std::string* account; user->GetExt("accountname", account); if (account) { user->Shrink("accountname"); delete account; } } } // 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. virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { // check if its our metadata key, and its associated with a user if ((target_type == TYPE_USER) && (extname == "accountname")) { userrec* dest = (userrec*)target; /* logging them out? */ if (extdata.empty()) { std::string* account; dest->GetExt("accountname", account); if (account) { dest->Shrink("accountname"); delete account; } } else { // if they dont already have an accountname field, accept the remote server's std::string* text; if (!dest->GetExt("accountname", text)) { text = new std::string(extdata); // remove any accidental leading/trailing spaces trim(*text); dest->Extend("accountname", text); } } } } virtual ~ModuleServicesAccount() { ServerInstance->Modes->DelMode(m1); ServerInstance->Modes->DelMode(m2); ServerInstance->Modes->DelMode(m3); DELETE(m1); DELETE(m2); DELETE(m3); } virtual Version GetVersion() { return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleServicesAccount) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+
+/* $ModDesc: Povides support for ircu-style services accounts, including chmode +R, etc. */
+
+/** Channel mode +R - unidentified users cannot join
+ */
+class AChannel_R : public ModeHandler
+{
+ public:
+ AChannel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('R'))
+ {
+ channel->SetMode('R',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('R'))
+ {
+ channel->SetMode('R',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** User mode +R - unidentified users cannot message
+ */
+class AUser_R : public ModeHandler
+{
+ public:
+ AUser_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!dest->IsModeSet('R'))
+ {
+ dest->SetMode('R',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('R'))
+ {
+ dest->SetMode('R',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** Channel mode +M - unidentified users cannot message channel
+ */
+class AChannel_M : public ModeHandler
+{
+ public:
+ AChannel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('M'))
+ {
+ channel->SetMode('M',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('M'))
+ {
+ channel->SetMode('M',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleServicesAccount : public Module
+{
+
+ AChannel_R* m1;
+ AChannel_M* m2;
+ AUser_R* m3;
+ public:
+ ModuleServicesAccount(InspIRCd* Me) : Module(Me)
+ {
+
+ m1 = new AChannel_R(ServerInstance);
+ m2 = new AChannel_M(ServerInstance);
+ m3 = new AUser_R(ServerInstance);
+ if (!ServerInstance->AddMode(m1, 'R') || !ServerInstance->AddMode(m2, 'M') || !ServerInstance->AddMode(m3, 'R'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ /* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
+ virtual void OnWhois(userrec* source, userrec* dest)
+ {
+ std::string *account;
+ dest->GetExt("accountname", account);
+
+ if (account)
+ {
+ ServerInstance->SendWhoisLine(source, dest, 330, "%s %s %s :is logged in as", source->nick, dest->nick, account->c_str());
+ }
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnWhois] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1;
+ List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnDecodeMetaData] = 1;
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ std::string *account;
+
+ if (!IS_LOCAL(user))
+ return 0;
+
+ user->GetExt("accountname", account);
+
+ if (target_type == TYPE_CHANNEL)
+ {
+ chanrec* c = (chanrec*)dest;
+
+ if ((c->IsModeSet('M')) && (!account))
+ {
+ if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
+ {
+ // user is ulined, can speak regardless
+ return 0;
+ }
+
+ // user messaging a +M channel and is not registered
+ user->WriteServ("477 "+std::string(user->nick)+" "+std::string(c->name)+" :You need to be identified to a registered account to message this channel");
+ return 1;
+ }
+ }
+ if (target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)dest;
+
+ if ((u->modes['R'-65]) && (!account))
+ {
+ if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
+ {
+ // user is ulined, can speak regardless
+ return 0;
+ }
+
+ // user messaging a +R user and is not registered
+ user->WriteServ("477 "+std::string(user->nick)+" "+std::string(u->nick)+" :You need to be identified to a registered account to message this user");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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 int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ std::string *account;
+ user->GetExt("accountname", account);
+
+ if (chan)
+ {
+ if (chan->IsModeSet('R'))
+ {
+ if (!account)
+ {
+ if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
+ {
+ // user is ulined, won't be stopped from joining
+ return 0;
+ }
+ // joining a +R channel and not identified
+ user->WriteServ("477 "+std::string(user->nick)+" "+std::string(chan->name)+" :You need to be identified to a registered account to join this channel");
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ // Whenever the linking module wants to send out data, but doesnt know what the data
+ // represents (e.g. it is metadata, added to a userrec or chanrec by a module) then
+ // this method is called. We should use the ProtoSendMetaData function after we've
+ // corrected decided how the data should look, to send the metadata on its way if
+ // it is ours.
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
+ {
+ // check if the linking module wants to know about OUR metadata
+ if (extname == "accountname")
+ {
+ // check if this user has an swhois field to send
+ std::string* account;
+ user->GetExt("accountname", account);
+ if (account)
+ {
+ // remove any accidental leading/trailing spaces
+ trim(*account);
+
+ // call this function in the linking module, let it format the data how it
+ // sees fit, and send it on its way. We dont need or want to know how.
+ proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*account);
+ }
+ }
+ }
+
+ // when a user quits, tidy up their metadata
+ virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
+ {
+ std::string* account;
+ user->GetExt("accountname", account);
+ if (account)
+ {
+ user->Shrink("accountname");
+ delete account;
+ }
+ }
+
+ // if the module is unloaded, tidy up all our dangling metadata
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ std::string* account;
+ user->GetExt("accountname", account);
+ if (account)
+ {
+ user->Shrink("accountname");
+ delete account;
+ }
+ }
+ }
+
+ // 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.
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ // check if its our metadata key, and its associated with a user
+ if ((target_type == TYPE_USER) && (extname == "accountname"))
+ {
+ userrec* dest = (userrec*)target;
+
+ /* logging them out? */
+ if (extdata.empty())
+ {
+ std::string* account;
+ dest->GetExt("accountname", account);
+ if (account)
+ {
+ dest->Shrink("accountname");
+ delete account;
+ }
+ }
+ else
+ {
+ // if they dont already have an accountname field, accept the remote server's
+ std::string* text;
+ if (!dest->GetExt("accountname", text))
+ {
+ text = new std::string(extdata);
+ // remove any accidental leading/trailing spaces
+ trim(*text);
+ dest->Extend("accountname", text);
+ }
+ }
+ }
+ }
+
+ virtual ~ModuleServicesAccount()
+ {
+ ServerInstance->Modes->DelMode(m1);
+ ServerInstance->Modes->DelMode(m2);
+ ServerInstance->Modes->DelMode(m3);
+ DELETE(m1);
+ DELETE(m2);
+ DELETE(m3);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleServicesAccount)
diff --git a/src/modules/m_sethost.cpp b/src/modules/m_sethost.cpp
index 833f8e684..e69a944e7 100644
--- a/src/modules/m_sethost.cpp
+++ b/src/modules/m_sethost.cpp
@@ -1 +1,108 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for the SETHOST command */ /** Handle /SETHOST */ class cmd_sethost : public command_t { private: char* hostmap; public: cmd_sethost (InspIRCd* Instance, char* hmap) : command_t(Instance,"SETHOST",'o',1), hostmap(hmap) { this->source = "m_sethost.so"; syntax = "<new-hostname>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { size_t len = 0; for (const char* x = parameters[0]; *x; x++, len++) { if (!hostmap[(unsigned char)*x]) { user->WriteServ("NOTICE "+std::string(user->nick)+" :*** SETHOST: Invalid characters in hostname"); return CMD_FAILURE; } } if (len == 0) { user->WriteServ("NOTICE %s :*** SETHOST: Host must be specified", user->nick); return CMD_FAILURE; } if (len > 64) { user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick); return CMD_FAILURE; } if (user->ChangeDisplayedHost(parameters[0])) { ServerInstance->WriteOpers(std::string(user->nick)+" used SETHOST to change their displayed host to "+user->dhost); return CMD_SUCCESS; } return CMD_FAILURE; } }; class ModuleSetHost : public Module { cmd_sethost* mycommand; char hostmap[256]; public: ModuleSetHost(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); mycommand = new cmd_sethost(ServerInstance, hostmap); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_OnRehash] = 1; } void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); std::string hmap = Conf.ReadValue("hostname", "charmap", 0); if (hmap.empty()) hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789"; memset(&hostmap, 0, 255); for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++) hostmap[(unsigned char)*n] = 1; } virtual ~ModuleSetHost() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSetHost) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for the SETHOST command */
+
+/** Handle /SETHOST
+ */
+class cmd_sethost : public command_t
+{
+ private:
+ char* hostmap;
+ public:
+ cmd_sethost (InspIRCd* Instance, char* hmap) : command_t(Instance,"SETHOST",'o',1), hostmap(hmap)
+ {
+ this->source = "m_sethost.so";
+ syntax = "<new-hostname>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ size_t len = 0;
+ for (const char* x = parameters[0]; *x; x++, len++)
+ {
+ if (!hostmap[(unsigned char)*x])
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :*** SETHOST: Invalid characters in hostname");
+ return CMD_FAILURE;
+ }
+ }
+ if (len == 0)
+ {
+ user->WriteServ("NOTICE %s :*** SETHOST: Host must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+ if (len > 64)
+ {
+ user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick);
+ return CMD_FAILURE;
+ }
+ if (user->ChangeDisplayedHost(parameters[0]))
+ {
+ ServerInstance->WriteOpers(std::string(user->nick)+" used SETHOST to change their displayed host to "+user->dhost);
+ return CMD_SUCCESS;
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+
+class ModuleSetHost : public Module
+{
+ cmd_sethost* mycommand;
+ char hostmap[256];
+ public:
+ ModuleSetHost(InspIRCd* Me)
+ : Module(Me)
+ {
+ OnRehash(NULL,"");
+ mycommand = new cmd_sethost(ServerInstance, hostmap);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = 1;
+ }
+
+ void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+ std::string hmap = Conf.ReadValue("hostname", "charmap", 0);
+
+ if (hmap.empty())
+ hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789";
+
+ memset(&hostmap, 0, 255);
+ for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
+ hostmap[(unsigned char)*n] = 1;
+ }
+
+ virtual ~ModuleSetHost()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSetHost)
diff --git a/src/modules/m_setident.cpp b/src/modules/m_setident.cpp
index f512a1f59..3f33061cd 100644
--- a/src/modules/m_setident.cpp
+++ b/src/modules/m_setident.cpp
@@ -1 +1,83 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" /* $ModDesc: Provides support for the SETIDENT command */ /** Handle /SETIDENT */ class cmd_setident : public command_t { public: cmd_setident (InspIRCd* Instance) : command_t(Instance,"SETIDENT", 'o', 1) { this->source = "m_setident.so"; syntax = "<new-ident>"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { if (!*parameters[0]) { user->WriteServ("NOTICE %s :*** SETIDENT: Ident must be specified", user->nick); return CMD_FAILURE; } if (strlen(parameters[0]) > IDENTMAX) { user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick); return CMD_FAILURE; } if (!ServerInstance->IsIdent(parameters[0])) { user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick); return CMD_FAILURE; } user->ChangeIdent(parameters[0]); ServerInstance->WriteOpers("%s used SETIDENT to change their ident to '%s'", user->nick, user->ident); return CMD_SUCCESS; } }; class ModuleSetIdent : public Module { cmd_setident* mycommand; public: ModuleSetIdent(InspIRCd* Me) : Module(Me) { mycommand = new cmd_setident(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSetIdent() { } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSetIdent) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for the SETIDENT command */
+
+/** Handle /SETIDENT
+ */
+class cmd_setident : public command_t
+{
+ public:
+ cmd_setident (InspIRCd* Instance) : command_t(Instance,"SETIDENT", 'o', 1)
+ {
+ this->source = "m_setident.so";
+ syntax = "<new-ident>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ if (!*parameters[0])
+ {
+ user->WriteServ("NOTICE %s :*** SETIDENT: Ident must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (strlen(parameters[0]) > IDENTMAX)
+ {
+ user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (!ServerInstance->IsIdent(parameters[0]))
+ {
+ user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick);
+ return CMD_FAILURE;
+ }
+
+ user->ChangeIdent(parameters[0]);
+ ServerInstance->WriteOpers("%s used SETIDENT to change their ident to '%s'", user->nick, user->ident);
+
+ return CMD_SUCCESS;
+ }
+};
+
+
+class ModuleSetIdent : public Module
+{
+ cmd_setident* mycommand;
+
+ public:
+ ModuleSetIdent(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_setident(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSetIdent()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+
+MODULE_INIT(ModuleSetIdent)
diff --git a/src/modules/m_setidle.cpp b/src/modules/m_setidle.cpp
index 917368d7b..e16369aa4 100644
--- a/src/modules/m_setidle.cpp
+++ b/src/modules/m_setidle.cpp
@@ -1 +1,74 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Allows opers to set their idle time */ /** Handle /SETIDLE */ class cmd_setidle : public command_t { public: cmd_setidle (InspIRCd* Instance) : command_t(Instance,"SETIDLE", 'o', 1) { this->source = "m_setidle.so"; syntax = "<duration>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { time_t idle = ServerInstance->Duration(parameters[0]); if (idle < 1) { user->WriteServ("948 %s :Invalid idle time.",user->nick); return CMD_FAILURE; } user->idle_lastmsg = (ServerInstance->Time() - idle); // minor tweak - we cant have signon time shorter than our idle time! if (user->signon > user->idle_lastmsg) user->signon = user->idle_lastmsg; ServerInstance->WriteOpers(std::string(user->nick)+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds"); user->WriteServ("944 %s :Idle time set.",user->nick); return CMD_LOCALONLY; } }; class ModuleSetIdle : public Module { cmd_setidle* mycommand; public: ModuleSetIdle(InspIRCd* Me) : Module(Me) { mycommand = new cmd_setidle(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSetIdle() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSetIdle) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Allows opers to set their idle time */
+
+/** Handle /SETIDLE
+ */
+class cmd_setidle : public command_t
+{
+ public:
+ cmd_setidle (InspIRCd* Instance) : command_t(Instance,"SETIDLE", 'o', 1)
+ {
+ this->source = "m_setidle.so";
+ syntax = "<duration>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ time_t idle = ServerInstance->Duration(parameters[0]);
+ if (idle < 1)
+ {
+ user->WriteServ("948 %s :Invalid idle time.",user->nick);
+ return CMD_FAILURE;
+ }
+ user->idle_lastmsg = (ServerInstance->Time() - idle);
+ // minor tweak - we cant have signon time shorter than our idle time!
+ if (user->signon > user->idle_lastmsg)
+ user->signon = user->idle_lastmsg;
+ ServerInstance->WriteOpers(std::string(user->nick)+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds");
+ user->WriteServ("944 %s :Idle time set.",user->nick);
+
+ return CMD_LOCALONLY;
+ }
+};
+
+
+class ModuleSetIdle : public Module
+{
+ cmd_setidle* mycommand;
+ public:
+ ModuleSetIdle(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_setidle(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSetIdle()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSetIdle)
diff --git a/src/modules/m_setname.cpp b/src/modules/m_setname.cpp
index 3f525622f..586c6f84e 100644
--- a/src/modules/m_setname.cpp
+++ b/src/modules/m_setname.cpp
@@ -1 +1,80 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for the SETNAME command */ class cmd_setname : public command_t { public: cmd_setname (InspIRCd* Instance) : command_t(Instance,"SETNAME", 0, 1) { this->source = "m_setname.so"; syntax = "<new-gecos>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { if (!*parameters[0]) { user->WriteServ("NOTICE %s :*** SETNAME: GECOS must be specified", user->nick); return CMD_FAILURE; } if (strlen(parameters[0]) > MAXGECOS) { user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick); return CMD_FAILURE; } if (user->ChangeName(parameters[0])) { ServerInstance->WriteOpers("%s used SETNAME to change their GECOS to %s", user->nick, parameters[0]); return CMD_SUCCESS; } return CMD_SUCCESS; } }; class ModuleSetName : public Module { cmd_setname* mycommand; public: ModuleSetName(InspIRCd* Me) : Module(Me) { mycommand = new cmd_setname(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleSetName() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSetName) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for the SETNAME command */
+
+
+
+class cmd_setname : public command_t
+{
+ public:
+ cmd_setname (InspIRCd* Instance) : command_t(Instance,"SETNAME", 0, 1)
+ {
+ this->source = "m_setname.so";
+ syntax = "<new-gecos>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ if (!*parameters[0])
+ {
+ user->WriteServ("NOTICE %s :*** SETNAME: GECOS must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (strlen(parameters[0]) > MAXGECOS)
+ {
+ user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (user->ChangeName(parameters[0]))
+ {
+ ServerInstance->WriteOpers("%s used SETNAME to change their GECOS to %s", user->nick, parameters[0]);
+ return CMD_SUCCESS;
+ }
+
+ return CMD_SUCCESS;
+ }
+};
+
+
+class ModuleSetName : public Module
+{
+ cmd_setname* mycommand;
+ public:
+ ModuleSetName(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_setname(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleSetName()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleSetName)
diff --git a/src/modules/m_sha256.cpp b/src/modules/m_sha256.cpp
index 0dfef40b9..547e7655c 100644
--- a/src/modules/m_sha256.cpp
+++ b/src/modules/m_sha256.cpp
@@ -1 +1,296 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* m_sha256 - Based on m_opersha256 written by Special <john@yarbbles.com> * Modified and improved by Craig Edwards, December 2006. * * * FIPS 180-2 SHA-224/256/384/512 implementation * Last update: 05/23/2005 * Issue date: 04/30/2005 * * Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $ModDesc: Allows for SHA-256 encrypted oper passwords */ /* $ModDep: m_hash.h */ #include "inspircd.h" #ifdef HAS_STDINT #include <stdint.h> #endif #include "users.h" #include "channels.h" #include "modules.h" #include "m_hash.h" #ifndef HAS_STDINT typedef unsigned int uint32_t; #endif /** An sha 256 context, used by m_opersha256 */ class SHA256Context : public classbase { public: unsigned int tot_len; unsigned int len; unsigned char block[2 * SHA256_BLOCK_SIZE]; uint32_t h[8]; }; #define SHFR(x, n) (x >> n) #define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) #define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) #define CH(x, y, z) ((x & y) ^ (~x & z)) #define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) #define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) #define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) #define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) #define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) #define UNPACK32(x, str) \ { \ *((str) + 3) = (uint8_t) ((x) ); \ *((str) + 2) = (uint8_t) ((x) >> 8); \ *((str) + 1) = (uint8_t) ((x) >> 16); \ *((str) + 0) = (uint8_t) ((x) >> 24); \ } #define PACK32(str, x) \ { \ *(x) = ((uint32_t) *((str) + 3) ) \ | ((uint32_t) *((str) + 2) << 8) \ | ((uint32_t) *((str) + 1) << 16) \ | ((uint32_t) *((str) + 0) << 24); \ } /* Macros used for loops unrolling */ #define SHA256_SCR(i) \ { \ w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + SHA256_F3(w[i - 15]) + w[i - 16]; \ } const unsigned int sha256_h0[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; uint32_t sha256_k[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; class ModuleSHA256 : public Module { void SHA256Init(SHA256Context *ctx, const unsigned int* key) { if (key) { for (int i = 0; i < 8; i++) ctx->h[i] = key[i]; } else { for (int i = 0; i < 8; i++) ctx->h[i] = sha256_h0[i]; } ctx->len = 0; ctx->tot_len = 0; } void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned int block_nb) { uint32_t w[64]; uint32_t wv[8]; unsigned char *sub_block; for (unsigned int i = 1; i <= block_nb; i++) { int j; sub_block = message + ((i - 1) << 6); for (j = 0; j < 16; j++) PACK32(&sub_block[j << 2], &w[j]); for (j = 16; j < 64; j++) SHA256_SCR(j); for (j = 0; j < 8; j++) wv[j] = ctx->h[j]; for (j = 0; j < 64; j++) { uint32_t t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]; uint32_t t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); wv[7] = wv[6]; wv[6] = wv[5]; wv[5] = wv[4]; wv[4] = wv[3] + t1; wv[3] = wv[2]; wv[2] = wv[1]; wv[1] = wv[0]; wv[0] = t1 + t2; } for (j = 0; j < 8; j++) ctx->h[j] += wv[j]; } } void SHA256Update(SHA256Context *ctx, unsigned char *message, unsigned int len) { unsigned int rem_len = SHA256_BLOCK_SIZE - ctx->len; memcpy(&ctx->block[ctx->len], message, rem_len); if (ctx->len + len < SHA256_BLOCK_SIZE) { ctx->len += len; return; } unsigned int new_len = len - rem_len; unsigned int block_nb = new_len / SHA256_BLOCK_SIZE; unsigned char *shifted_message = message + rem_len; SHA256Transform(ctx, ctx->block, 1); SHA256Transform(ctx, shifted_message, block_nb); rem_len = new_len % SHA256_BLOCK_SIZE; memcpy(ctx->block, &shifted_message[block_nb << 6],rem_len); ctx->len = rem_len; ctx->tot_len += (block_nb + 1) << 6; } void SHA256Final(SHA256Context *ctx, unsigned char *digest) { unsigned int block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE))); unsigned int len_b = (ctx->tot_len + ctx->len) << 3; unsigned int pm_len = block_nb << 6; memset(ctx->block + ctx->len, 0, pm_len - ctx->len); ctx->block[ctx->len] = 0x80; UNPACK32(len_b, ctx->block + pm_len - 4); SHA256Transform(ctx, ctx->block, block_nb); for (int i = 0 ; i < 8; i++) UNPACK32(ctx->h[i], &digest[i << 2]); } void SHA256(const char *src, char *dest, int len, const char* hxc, const unsigned int* key = NULL) { // Generate the hash unsigned char bytehash[SHA256_DIGEST_SIZE]; SHA256Context ctx; SHA256Init(&ctx, key); SHA256Update(&ctx, (unsigned char *)src, (unsigned int)len); SHA256Final(&ctx, bytehash); // Convert it to hex for (int i = 0, j = 0; i < SHA256_DIGEST_SIZE; i++) { dest[j++] = hxc[bytehash[i] / 16]; dest[j++] = hxc[bytehash[i] % 16]; dest[j] = '\0'; } } unsigned int* key; char* chars; public: ModuleSHA256(InspIRCd* Me) : Module(Me), key(NULL), chars(NULL) { ServerInstance->PublishInterface("HashRequest", this); } virtual ~ModuleSHA256() { ServerInstance->UnpublishInterface("HashRequest", this); } void Implements(char *List) { List[I_OnRequest] = 1; } virtual char* OnRequest(Request* request) { HashRequest* SHA = (HashRequest*)request; if (strcmp("KEY", request->GetId()) == 0) { this->key = (unsigned int*)SHA->GetKeyData(); } else if (strcmp("HEX", request->GetId()) == 0) { this->chars = (char*)SHA->GetOutputs(); } else if (strcmp("SUM", request->GetId()) == 0) { static char data[MAXBUF]; SHA256((const char*)SHA->GetHashData(), data, strlen(SHA->GetHashData()), chars ? chars : "0123456789abcdef", key); return data; } else if (strcmp("NAME", request->GetId()) == 0) { return "sha256"; } else if (strcmp("RESET", request->GetId()) == 0) { this->chars = NULL; this->key = NULL; } return NULL; } virtual Version GetVersion() { return Version(1, 1, 0, 1, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION); } }; MODULE_INIT(ModuleSHA256) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* m_sha256 - Based on m_opersha256 written by Special <john@yarbbles.com>
+ * Modified and improved by Craig Edwards, December 2006.
+ *
+ *
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 05/23/2005
+ * Issue date: 04/30/2005
+ *
+ * Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $ModDesc: Allows for SHA-256 encrypted oper passwords */
+/* $ModDep: m_hash.h */
+
+#include "inspircd.h"
+#ifdef HAS_STDINT
+#include <stdint.h>
+#endif
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "m_hash.h"
+
+#ifndef HAS_STDINT
+typedef unsigned int uint32_t;
+#endif
+
+/** An sha 256 context, used by m_opersha256
+ */
+class SHA256Context : public classbase
+{
+ public:
+ unsigned int tot_len;
+ unsigned int len;
+ unsigned char block[2 * SHA256_BLOCK_SIZE];
+ uint32_t h[8];
+};
+
+#define SHFR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define CH(x, y, z) ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
+#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
+
+#define UNPACK32(x, str) \
+{ \
+ *((str) + 3) = (uint8_t) ((x) ); \
+ *((str) + 2) = (uint8_t) ((x) >> 8); \
+ *((str) + 1) = (uint8_t) ((x) >> 16); \
+ *((str) + 0) = (uint8_t) ((x) >> 24); \
+}
+
+#define PACK32(str, x) \
+{ \
+ *(x) = ((uint32_t) *((str) + 3) ) \
+ | ((uint32_t) *((str) + 2) << 8) \
+ | ((uint32_t) *((str) + 1) << 16) \
+ | ((uint32_t) *((str) + 0) << 24); \
+}
+
+/* Macros used for loops unrolling */
+
+#define SHA256_SCR(i) \
+{ \
+ w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \
+ + SHA256_F3(w[i - 15]) + w[i - 16]; \
+}
+
+const unsigned int sha256_h0[8] =
+{
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+};
+
+uint32_t sha256_k[64] =
+{
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+class ModuleSHA256 : public Module
+{
+ void SHA256Init(SHA256Context *ctx, const unsigned int* key)
+ {
+ if (key)
+ {
+ for (int i = 0; i < 8; i++)
+ ctx->h[i] = key[i];
+ }
+ else
+ {
+ for (int i = 0; i < 8; i++)
+ ctx->h[i] = sha256_h0[i];
+ }
+ ctx->len = 0;
+ ctx->tot_len = 0;
+ }
+
+ void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned int block_nb)
+ {
+ uint32_t w[64];
+ uint32_t wv[8];
+ unsigned char *sub_block;
+ for (unsigned int i = 1; i <= block_nb; i++)
+ {
+ int j;
+ sub_block = message + ((i - 1) << 6);
+
+ for (j = 0; j < 16; j++)
+ PACK32(&sub_block[j << 2], &w[j]);
+ for (j = 16; j < 64; j++)
+ SHA256_SCR(j);
+ for (j = 0; j < 8; j++)
+ wv[j] = ctx->h[j];
+ for (j = 0; j < 64; j++)
+ {
+ uint32_t t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j];
+ uint32_t t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+ wv[7] = wv[6];
+ wv[6] = wv[5];
+ wv[5] = wv[4];
+ wv[4] = wv[3] + t1;
+ wv[3] = wv[2];
+ wv[2] = wv[1];
+ wv[1] = wv[0];
+ wv[0] = t1 + t2;
+ }
+ for (j = 0; j < 8; j++)
+ ctx->h[j] += wv[j];
+ }
+ }
+
+ void SHA256Update(SHA256Context *ctx, unsigned char *message, unsigned int len)
+ {
+ unsigned int rem_len = SHA256_BLOCK_SIZE - ctx->len;
+ memcpy(&ctx->block[ctx->len], message, rem_len);
+ if (ctx->len + len < SHA256_BLOCK_SIZE)
+ {
+ ctx->len += len;
+ return;
+ }
+ unsigned int new_len = len - rem_len;
+ unsigned int block_nb = new_len / SHA256_BLOCK_SIZE;
+ unsigned char *shifted_message = message + rem_len;
+ SHA256Transform(ctx, ctx->block, 1);
+ SHA256Transform(ctx, shifted_message, block_nb);
+ rem_len = new_len % SHA256_BLOCK_SIZE;
+ memcpy(ctx->block, &shifted_message[block_nb << 6],rem_len);
+ ctx->len = rem_len;
+ ctx->tot_len += (block_nb + 1) << 6;
+ }
+
+ void SHA256Final(SHA256Context *ctx, unsigned char *digest)
+ {
+ unsigned int block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE)));
+ unsigned int len_b = (ctx->tot_len + ctx->len) << 3;
+ unsigned int pm_len = block_nb << 6;
+ memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+ ctx->block[ctx->len] = 0x80;
+ UNPACK32(len_b, ctx->block + pm_len - 4);
+ SHA256Transform(ctx, ctx->block, block_nb);
+ for (int i = 0 ; i < 8; i++)
+ UNPACK32(ctx->h[i], &digest[i << 2]);
+ }
+
+ void SHA256(const char *src, char *dest, int len, const char* hxc, const unsigned int* key = NULL)
+ {
+ // Generate the hash
+ unsigned char bytehash[SHA256_DIGEST_SIZE];
+ SHA256Context ctx;
+ SHA256Init(&ctx, key);
+ SHA256Update(&ctx, (unsigned char *)src, (unsigned int)len);
+ SHA256Final(&ctx, bytehash);
+ // Convert it to hex
+ for (int i = 0, j = 0; i < SHA256_DIGEST_SIZE; i++)
+ {
+ dest[j++] = hxc[bytehash[i] / 16];
+ dest[j++] = hxc[bytehash[i] % 16];
+ dest[j] = '\0';
+ }
+ }
+
+ unsigned int* key;
+ char* chars;
+
+ public:
+
+ ModuleSHA256(InspIRCd* Me) : Module(Me), key(NULL), chars(NULL)
+ {
+ ServerInstance->PublishInterface("HashRequest", this);
+ }
+
+ virtual ~ModuleSHA256()
+ {
+ ServerInstance->UnpublishInterface("HashRequest", this);
+ }
+
+ void Implements(char *List)
+ {
+ List[I_OnRequest] = 1;
+ }
+
+ virtual char* OnRequest(Request* request)
+ {
+ HashRequest* SHA = (HashRequest*)request;
+ if (strcmp("KEY", request->GetId()) == 0)
+ {
+ this->key = (unsigned int*)SHA->GetKeyData();
+ }
+ else if (strcmp("HEX", request->GetId()) == 0)
+ {
+ this->chars = (char*)SHA->GetOutputs();
+ }
+ else if (strcmp("SUM", request->GetId()) == 0)
+ {
+ static char data[MAXBUF];
+ SHA256((const char*)SHA->GetHashData(), data, strlen(SHA->GetHashData()), chars ? chars : "0123456789abcdef", key);
+ return data;
+ }
+ else if (strcmp("NAME", request->GetId()) == 0)
+ {
+ return "sha256";
+ }
+ else if (strcmp("RESET", request->GetId()) == 0)
+ {
+ this->chars = NULL;
+ this->key = NULL;
+ }
+ return NULL;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 1, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSHA256)
+
diff --git a/src/modules/m_showwhois.cpp b/src/modules/m_showwhois.cpp
index 676962818..cb6a0ffb0 100644
--- a/src/modules/m_showwhois.cpp
+++ b/src/modules/m_showwhois.cpp
@@ -1 +1,109 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */ /** Handle user mode +W */ class SeeWhois : public ModeHandler { public: SeeWhois(InspIRCd* Instance) : ModeHandler(Instance, 'W', 0, 0, false, MODETYPE_USER, true) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if (source != dest) return MODEACTION_DENY; if (adding) { if (!dest->IsModeSet('W')) { dest->SetMode('W',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('W')) { dest->SetMode('W',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleShowwhois : public Module { SeeWhois* sw; public: ModuleShowwhois(InspIRCd* Me) : Module(Me) { sw = new SeeWhois(ServerInstance); if (!ServerInstance->AddMode(sw, 'W')) throw ModuleException("Could not add new modes!"); } ~ModuleShowwhois() { ServerInstance->Modes->DelMode(sw); DELETE(sw); } void Implements(char* List) { List[I_OnWhois] = 1; } virtual Version GetVersion() { return Version(1,1,0,3,VF_COMMON|VF_VENDOR,API_VERSION); } virtual void OnWhois(userrec* source, userrec* dest) { if ((dest->IsModeSet('W')) && (source != dest)) { if (IS_LOCAL(dest)) { dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you.",dest->nick,source->nick,source->ident,source->host); } else { std::deque<std::string> params; params.push_back(dest->nick); std::string msg = ":"; msg = msg + dest->server + " NOTICE " + dest->nick + " :*** " + source->nick + " (" + source->ident + "@" + source->host + ") did a /whois on you."; params.push_back(msg); Event ev((char *) &params, NULL, "send_push"); ev.Send(ServerInstance); } } } }; MODULE_INIT(ModuleShowwhois) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */
+
+/** Handle user mode +W
+ */
+class SeeWhois : public ModeHandler
+{
+ public:
+ SeeWhois(InspIRCd* Instance) : ModeHandler(Instance, 'W', 0, 0, false, MODETYPE_USER, true) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ /* Only opers can change other users modes */
+ if (source != dest)
+ return MODEACTION_DENY;
+
+ if (adding)
+ {
+ if (!dest->IsModeSet('W'))
+ {
+ dest->SetMode('W',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('W'))
+ {
+ dest->SetMode('W',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+class ModuleShowwhois : public Module
+{
+
+ SeeWhois* sw;
+
+ public:
+
+ ModuleShowwhois(InspIRCd* Me) : Module(Me)
+ {
+
+ sw = new SeeWhois(ServerInstance);
+ if (!ServerInstance->AddMode(sw, 'W'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ ~ModuleShowwhois()
+ {
+ ServerInstance->Modes->DelMode(sw);
+ DELETE(sw);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnWhois] = 1;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,3,VF_COMMON|VF_VENDOR,API_VERSION);
+ }
+
+ virtual void OnWhois(userrec* source, userrec* dest)
+ {
+ if ((dest->IsModeSet('W')) && (source != dest))
+ {
+ if (IS_LOCAL(dest))
+ {
+ dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you.",dest->nick,source->nick,source->ident,source->host);
+ }
+ else
+ {
+ std::deque<std::string> params;
+ params.push_back(dest->nick);
+ std::string msg = ":";
+ msg = msg + dest->server + " NOTICE " + dest->nick + " :*** " + source->nick + " (" + source->ident + "@" + source->host + ") did a /whois on you.";
+ params.push_back(msg);
+ Event ev((char *) &params, NULL, "send_push");
+ ev.Send(ServerInstance);
+ }
+ }
+ }
+
+};
+
+MODULE_INIT(ModuleShowwhois)
diff --git a/src/modules/m_silence.cpp b/src/modules/m_silence.cpp
index 3becb06f2..b05689056 100644
--- a/src/modules/m_silence.cpp
+++ b/src/modules/m_silence.cpp
@@ -1 +1,215 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "wildcard.h" /* $ModDesc: Provides support for the /SILENCE command */ // This typedef holds a silence list. Each user may or may not have a // silencelist, if a silence list is empty for a user, he/she does not // have one of these structures associated with their user record. typedef std::map<irc::string, time_t> silencelist; class cmd_silence : public command_t { unsigned int& maxsilence; public: cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max) { this->source = "m_silence.so"; syntax = "{[+|-]<mask>}"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { if (!pcnt) { // no parameters, show the current silence list. // Use Extensible::GetExt to fetch the silence list silencelist* sl; user->GetExt("silence_list", sl); // if the user has a silence list associated with their user record, show it if (sl) { for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) { user->WriteServ("271 %s %s %s :%lu",user->nick, user->nick, c->first.c_str(), (unsigned long)c->second); } } user->WriteServ("272 %s :End of Silence List",user->nick); return CMD_SUCCESS; } else if (pcnt > 0) { // one or more parameters, add or delete entry from the list (only the first parameter is used) std::string mask = parameters[0] + 1; char action = *parameters[0]; if (!mask.length()) { // 'SILENCE +' or 'SILENCE -', assume *!*@* mask = "*!*@*"; } ModeParser::CleanMask(mask); if (action == '-') { // fetch their silence list silencelist* sl; user->GetExt("silence_list", sl); // does it contain any entries and does it exist? if (sl) { silencelist::iterator i = sl->find(mask.c_str()); if (i != sl->end()) { sl->erase(i); user->WriteServ("950 %s %s :Removed %s from silence list",user->nick, user->nick, mask.c_str()); if (!sl->size()) { // tidy up -- if a user's list is empty, theres no use having it // hanging around in the user record. DELETE(sl); user->Shrink("silence_list"); } } else user->WriteServ("952 %s %s :%s does not exist on your silence list",user->nick, user->nick, mask.c_str()); } } else if (action == '+') { // fetch the user's current silence list silencelist* sl; user->GetExt("silence_list", sl); // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one. if (!sl) { sl = new silencelist; user->Extend("silence_list", sl); } silencelist::iterator n = sl->find(mask.c_str()); if (n != sl->end()) { user->WriteServ("952 %s %s :%s is already on your silence list",user->nick, user->nick, mask.c_str()); return CMD_FAILURE; } if (sl->size() >= maxsilence) { user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick, mask.c_str()); return CMD_FAILURE; } sl->insert(std::make_pair<irc::string, time_t>(mask.c_str(), ServerInstance->Time())); user->WriteServ("951 %s %s :Added %s to silence list",user->nick, user->nick, mask.c_str()); return CMD_SUCCESS; } } return CMD_SUCCESS; } }; class ModuleSilence : public Module { cmd_silence* mycommand; unsigned int maxsilence; public: ModuleSilence(InspIRCd* Me) : Module(Me), maxsilence(32) { OnRehash(NULL, ""); mycommand = new cmd_silence(ServerInstance, maxsilence); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_OnRehash] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true); if (!maxsilence) maxsilence = 32; } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { // when the user quits tidy up any silence list they might have just to keep things tidy // and to prevent a HONKING BIG MEMORY LEAK! silencelist* sl; user->GetExt("silence_list", sl); if (sl) { DELETE(sl); user->Shrink("silence_list"); } } virtual void On005Numeric(std::string &output) { // we don't really have a limit... output = output + " SILENCE=" + ConvToStr(maxsilence); } virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { // im not sure how unreal's silence operates but ours is sensible. It blocks notices and // privmsgs from people on the silence list, directed privately at the user. // channel messages are unaffected (ever tried to follow the flow of conversation in // a channel when you've set an ignore on the two most talkative people?) if ((target_type == TYPE_USER) && (IS_LOCAL(user))) { userrec* u = (userrec*)dest; silencelist* sl; u->GetExt("silence_list", sl); if (sl) { for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) { if (match(user->GetFullHost(), c->first.c_str())) { return 1; } } } } return 0; } virtual int OnUserPreMessage(userrec* 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 ~ModuleSilence() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSilence) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides support for the /SILENCE command */
+
+// This typedef holds a silence list. Each user may or may not have a
+// silencelist, if a silence list is empty for a user, he/she does not
+// have one of these structures associated with their user record.
+typedef std::map<irc::string, time_t> silencelist;
+
+class cmd_silence : public command_t
+{
+ unsigned int& maxsilence;
+ public:
+ cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max)
+ {
+ this->source = "m_silence.so";
+ syntax = "{[+|-]<mask>}";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ if (!pcnt)
+ {
+ // no parameters, show the current silence list.
+ // Use Extensible::GetExt to fetch the silence list
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ // if the user has a silence list associated with their user record, show it
+ if (sl)
+ {
+ for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
+ {
+ user->WriteServ("271 %s %s %s :%lu",user->nick, user->nick, c->first.c_str(), (unsigned long)c->second);
+ }
+ }
+ user->WriteServ("272 %s :End of Silence List",user->nick);
+
+ return CMD_SUCCESS;
+ }
+ else if (pcnt > 0)
+ {
+ // one or more parameters, add or delete entry from the list (only the first parameter is used)
+ std::string mask = parameters[0] + 1;
+ char action = *parameters[0];
+
+ if (!mask.length())
+ {
+ // 'SILENCE +' or 'SILENCE -', assume *!*@*
+ mask = "*!*@*";
+ }
+
+ ModeParser::CleanMask(mask);
+
+ if (action == '-')
+ {
+ // fetch their silence list
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ // does it contain any entries and does it exist?
+ if (sl)
+ {
+ silencelist::iterator i = sl->find(mask.c_str());
+ if (i != sl->end())
+ {
+ sl->erase(i);
+ user->WriteServ("950 %s %s :Removed %s from silence list",user->nick, user->nick, mask.c_str());
+ if (!sl->size())
+ {
+ // tidy up -- if a user's list is empty, theres no use having it
+ // hanging around in the user record.
+ DELETE(sl);
+ user->Shrink("silence_list");
+ }
+ }
+ else
+ user->WriteServ("952 %s %s :%s does not exist on your silence list",user->nick, user->nick, mask.c_str());
+ }
+ }
+ else if (action == '+')
+ {
+ // fetch the user's current silence list
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
+ if (!sl)
+ {
+ sl = new silencelist;
+ user->Extend("silence_list", sl);
+ }
+ silencelist::iterator n = sl->find(mask.c_str());
+ if (n != sl->end())
+ {
+ user->WriteServ("952 %s %s :%s is already on your silence list",user->nick, user->nick, mask.c_str());
+ return CMD_FAILURE;
+ }
+ if (sl->size() >= maxsilence)
+ {
+ user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick, mask.c_str());
+ return CMD_FAILURE;
+ }
+ sl->insert(std::make_pair<irc::string, time_t>(mask.c_str(), ServerInstance->Time()));
+ user->WriteServ("951 %s %s :Added %s to silence list",user->nick, user->nick, mask.c_str());
+ return CMD_SUCCESS;
+ }
+ }
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleSilence : public Module
+{
+
+ cmd_silence* mycommand;
+ unsigned int maxsilence;
+ public:
+
+ ModuleSilence(InspIRCd* Me)
+ : Module(Me), maxsilence(32)
+ {
+ OnRehash(NULL, "");
+ mycommand = new cmd_silence(ServerInstance, maxsilence);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+ maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true);
+ if (!maxsilence)
+ maxsilence = 32;
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ // when the user quits tidy up any silence list they might have just to keep things tidy
+ // and to prevent a HONKING BIG MEMORY LEAK!
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ if (sl)
+ {
+ DELETE(sl);
+ user->Shrink("silence_list");
+ }
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ // we don't really have a limit...
+ output = output + " SILENCE=" + ConvToStr(maxsilence);
+ }
+
+ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ // im not sure how unreal's silence operates but ours is sensible. It blocks notices and
+ // privmsgs from people on the silence list, directed privately at the user.
+ // channel messages are unaffected (ever tried to follow the flow of conversation in
+ // a channel when you've set an ignore on the two most talkative people?)
+ if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
+ {
+ userrec* u = (userrec*)dest;
+ silencelist* sl;
+ u->GetExt("silence_list", sl);
+ if (sl)
+ {
+ for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
+ {
+ if (match(user->GetFullHost(), c->first.c_str()))
+ {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreMessage(userrec* 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 ~ModuleSilence()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSilence)
diff --git a/src/modules/m_silence_ext.cpp b/src/modules/m_silence_ext.cpp
index 7b1588043..06eee9dd4 100644
--- a/src/modules/m_silence_ext.cpp
+++ b/src/modules/m_silence_ext.cpp
@@ -1 +1,372 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "wildcard.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> * * example that blocks all except private messages * /SILENCE +*!*@* a * /SILENCE +*!*@* px * * example that blocks all invites except from channel services * /SILENCE +*!*@* i * /SILENCE +chanserv!services@chatters.net ix * * example that blocks some bad dude from private, notice and inviting you * /SILENCE +*!kiddie@lamerz.net pin * * TODO: possibly have add and remove check for existing host and only modify flags according to * what's been changed instead of having to remove first, then add if you want to change * an entry. */ // pair of hostmask and flags typedef std::pair<std::string, int> silenceset; // deque list of pairs typedef std::deque<silenceset> silencelist; // intmasks for flags static int SILENCE_PRIVATE = 0x0001; /* p private messages */ static int SILENCE_CHANNEL = 0x0002; /* c channel messages */ static int SILENCE_INVITE = 0x0004; /* i invites */ static int SILENCE_NOTICE = 0x0008; /* n notices */ static int SILENCE_CNOTICE = 0x0010; /* t channel notices */ static int SILENCE_ALL = 0x0020; /* a all, (pcint) */ static int SILENCE_EXCLUDE = 0x0040; /* x exclude this pattern */ class cmd_silence : public command_t { unsigned int& maxsilence; public: cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max) { this->source = "m_silence_ext.so"; syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { if (!pcnt) { // no parameters, show the current silence list. // Use Extensible::GetExt to fetch the silence list silencelist* sl; user->GetExt("silence_list", sl); // if the user has a silence list associated with their user record, show it if (sl) { for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) { user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str()); } } user->WriteServ("272 %s :End of Silence List",user->nick); return CMD_LOCALONLY; } else if (pcnt > 0) { // one or more parameters, add or delete entry from the list (only the first parameter is used) std::string mask = parameters[0] + 1; char action = *parameters[0]; // Default is private and notice so clients do not break int pattern = CompilePattern("pn"); // if pattern supplied, use it if (pcnt > 1) { pattern = CompilePattern(parameters[1]); } if (!mask.length()) { // 'SILENCE +' or 'SILENCE -', assume *!*@* mask = "*!*@*"; } ModeParser::CleanMask(mask); if (action == '-') { // fetch their silence list silencelist* sl; user->GetExt("silence_list", sl); // does it contain any entries and does it exist? if (sl) { for (silencelist::iterator i = sl->begin(); i != sl->end(); i++) { // search through for the item irc::string listitem = i->first.c_str(); if (listitem == mask && i->second == pattern) { sl->erase(i); user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); if (!sl->size()) { DELETE(sl); user->Shrink("silence_list"); } break; } } } user->WriteServ("952 %s %s :%s %s does not exist on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); } else if (action == '+') { // fetch the user's current silence list silencelist* sl; user->GetExt("silence_list", sl); // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one. if (!sl) { sl = new silencelist; user->Extend("silence_list", sl); } if (sl->size() > maxsilence) { user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick); return CMD_FAILURE; } for (silencelist::iterator n = sl->begin(); n != sl->end(); n++) { irc::string listitem = n->first.c_str(); if (listitem == mask && n->second == pattern) { user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); return CMD_FAILURE; } } if (((pattern & SILENCE_EXCLUDE) > 0)) { sl->push_front(silenceset(mask,pattern)); } else { sl->push_back(silenceset(mask,pattern)); } user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); return CMD_LOCALONLY; } } return CMD_LOCALONLY; } /* turn the nice human readable pattern into a mask */ int CompilePattern(const char* pattern) { int p = 0; for (const char* n = pattern; *n; n++) { switch (*n) { case 'p': p |= SILENCE_PRIVATE; break; case 'c': p |= SILENCE_CHANNEL; break; case 'i': p |= SILENCE_INVITE; break; case 'n': p |= SILENCE_NOTICE; break; case 't': p |= SILENCE_CNOTICE; break; case 'a': p |= SILENCE_ALL; break; case 'x': p |= SILENCE_EXCLUDE; break; default: break; } } return p; } /* turn the mask into a nice human readable format */ std::string DecompPattern (const int pattern) { std::string out; if ((pattern & SILENCE_PRIVATE) > 0) out += ",privatemessages"; if ((pattern & SILENCE_CHANNEL) > 0) out += ",channelmessages"; if ((pattern & SILENCE_INVITE) > 0) out += ",invites"; if ((pattern & SILENCE_NOTICE) > 0) out += ",privatenotices"; if ((pattern & SILENCE_CNOTICE) > 0) out += ",channelnotices"; if ((pattern & SILENCE_ALL) > 0) out = ",all"; if ((pattern & SILENCE_EXCLUDE) > 0) out += ",exclude"; return "<" + out.substr(1) + ">"; } }; class ModuleSilence : public Module { cmd_silence* mycommand; unsigned int maxsilence; public: ModuleSilence(InspIRCd* Me) : Module(Me), maxsilence(32) { OnRehash(NULL, ""); mycommand = new cmd_silence(ServerInstance,maxsilence); ServerInstance->AddCommand(mycommand); } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true); if (!maxsilence) maxsilence = 32; } void Implements(char* List) { List[I_OnRehash] = List[I_OnBuildExemptList] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = List[I_OnUserPreInvite] = 1; } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { // when the user quits tidy up any silence list they might have just to keep things tidy silencelist* sl; user->GetExt("silence_list", sl); if (sl) { DELETE(sl); user->Shrink("silence_list"); } } virtual void On005Numeric(std::string &output) { // we don't really have a limit... output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence); } virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) { int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE); CUList *ulist; switch (status) { case '@': ulist = chan->GetOppedUsers(); break; case '%': ulist = chan->GetHalfoppedUsers(); break; case '+': ulist = chan->GetVoicedUsers(); break; default: ulist = chan->GetUsers(); break; } for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (IS_LOCAL(i->first)) { if (MatchPattern(i->first, sender, public_silence) == 1) { exempt_list[i->first] = i->first->nick; } } } } virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type) { if (!IS_LOCAL(user)) return 0; if (target_type == TYPE_USER) { return MatchPattern((userrec*)dest, user, silence_type); } else if (target_type == TYPE_CHANNEL) { chanrec* chan = (chanrec*)dest; if (chan) { this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list); } } return 0; } virtual int OnUserPreMessage(userrec* 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); } virtual int OnUserPreNotice(userrec* 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); } virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) { return MatchPattern(dest, source, SILENCE_INVITE); } int MatchPattern(userrec* dest, userrec* source, int pattern) { silencelist* sl; dest->GetExt("silence_list", sl); if (sl) { for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) { if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (ServerInstance->MatchText(source->GetFullHost(), c->first))) return !(((c->second & SILENCE_EXCLUDE) > 0)); } } return 0; } virtual ~ModuleSilence() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSilence) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "wildcard.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>
+ *
+ * example that blocks all except private messages
+ * /SILENCE +*!*@* a
+ * /SILENCE +*!*@* px
+ *
+ * example that blocks all invites except from channel services
+ * /SILENCE +*!*@* i
+ * /SILENCE +chanserv!services@chatters.net ix
+ *
+ * example that blocks some bad dude from private, notice and inviting you
+ * /SILENCE +*!kiddie@lamerz.net pin
+ *
+ * TODO: possibly have add and remove check for existing host and only modify flags according to
+ * what's been changed instead of having to remove first, then add if you want to change
+ * an entry.
+ */
+
+// pair of hostmask and flags
+typedef std::pair<std::string, int> silenceset;
+
+// deque list of pairs
+typedef std::deque<silenceset> silencelist;
+
+// intmasks for flags
+static int SILENCE_PRIVATE = 0x0001; /* p private messages */
+static int SILENCE_CHANNEL = 0x0002; /* c channel messages */
+static int SILENCE_INVITE = 0x0004; /* i invites */
+static int SILENCE_NOTICE = 0x0008; /* n notices */
+static int SILENCE_CNOTICE = 0x0010; /* t channel notices */
+static int SILENCE_ALL = 0x0020; /* a all, (pcint) */
+static int SILENCE_EXCLUDE = 0x0040; /* x exclude this pattern */
+
+
+class cmd_silence : public command_t
+{
+ unsigned int& maxsilence;
+ public:
+ cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max)
+ {
+ this->source = "m_silence_ext.so";
+ syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ if (!pcnt)
+ {
+ // no parameters, show the current silence list.
+ // Use Extensible::GetExt to fetch the silence list
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ // if the user has a silence list associated with their user record, show it
+ if (sl)
+ {
+ for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
+ {
+ user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str());
+ }
+ }
+ user->WriteServ("272 %s :End of Silence List",user->nick);
+
+ return CMD_LOCALONLY;
+ }
+ else if (pcnt > 0)
+ {
+ // one or more parameters, add or delete entry from the list (only the first parameter is used)
+ std::string mask = parameters[0] + 1;
+ char action = *parameters[0];
+ // Default is private and notice so clients do not break
+ int pattern = CompilePattern("pn");
+
+ // if pattern supplied, use it
+ if (pcnt > 1) {
+ pattern = CompilePattern(parameters[1]);
+ }
+
+ if (!mask.length())
+ {
+ // 'SILENCE +' or 'SILENCE -', assume *!*@*
+ mask = "*!*@*";
+ }
+
+ ModeParser::CleanMask(mask);
+
+ if (action == '-')
+ {
+ // fetch their silence list
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ // does it contain any entries and does it exist?
+ if (sl)
+ {
+ for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
+ {
+ // search through for the item
+ irc::string listitem = i->first.c_str();
+ if (listitem == mask && i->second == pattern)
+ {
+ sl->erase(i);
+ user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
+ if (!sl->size())
+ {
+ DELETE(sl);
+ user->Shrink("silence_list");
+ }
+ break;
+ }
+ }
+ }
+ user->WriteServ("952 %s %s :%s %s does not exist on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
+ }
+ else if (action == '+')
+ {
+ // fetch the user's current silence list
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
+ if (!sl)
+ {
+ sl = new silencelist;
+ user->Extend("silence_list", sl);
+ }
+ if (sl->size() > maxsilence)
+ {
+ user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick);
+ return CMD_FAILURE;
+ }
+ for (silencelist::iterator n = sl->begin(); n != sl->end(); n++)
+ {
+ irc::string listitem = n->first.c_str();
+ if (listitem == mask && n->second == pattern)
+ {
+ user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
+ return CMD_FAILURE;
+ }
+ }
+ if (((pattern & SILENCE_EXCLUDE) > 0))
+ {
+ sl->push_front(silenceset(mask,pattern));
+ }
+ else
+ {
+ sl->push_back(silenceset(mask,pattern));
+ }
+ user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
+ return CMD_LOCALONLY;
+ }
+ }
+ return CMD_LOCALONLY;
+ }
+
+ /* turn the nice human readable pattern into a mask */
+ int CompilePattern(const char* pattern)
+ {
+ int p = 0;
+ for (const char* n = pattern; *n; n++)
+ {
+ switch (*n)
+ {
+ case 'p':
+ p |= SILENCE_PRIVATE;
+ break;
+ case 'c':
+ p |= SILENCE_CHANNEL;
+ break;
+ case 'i':
+ p |= SILENCE_INVITE;
+ break;
+ case 'n':
+ p |= SILENCE_NOTICE;
+ break;
+ case 't':
+ p |= SILENCE_CNOTICE;
+ break;
+ case 'a':
+ p |= SILENCE_ALL;
+ break;
+ case 'x':
+ p |= SILENCE_EXCLUDE;
+ break;
+ default:
+ break;
+ }
+ }
+ return p;
+ }
+
+ /* turn the mask into a nice human readable format */
+ std::string DecompPattern (const int pattern)
+ {
+ std::string out;
+ if ((pattern & SILENCE_PRIVATE) > 0)
+ out += ",privatemessages";
+ if ((pattern & SILENCE_CHANNEL) > 0)
+ out += ",channelmessages";
+ if ((pattern & SILENCE_INVITE) > 0)
+ out += ",invites";
+ if ((pattern & SILENCE_NOTICE) > 0)
+ out += ",privatenotices";
+ if ((pattern & SILENCE_CNOTICE) > 0)
+ out += ",channelnotices";
+ if ((pattern & SILENCE_ALL) > 0)
+ out = ",all";
+ if ((pattern & SILENCE_EXCLUDE) > 0)
+ out += ",exclude";
+ return "<" + out.substr(1) + ">";
+ }
+
+};
+
+class ModuleSilence : public Module
+{
+ cmd_silence* mycommand;
+ unsigned int maxsilence;
+ public:
+
+ ModuleSilence(InspIRCd* Me)
+ : Module(Me), maxsilence(32)
+ {
+ OnRehash(NULL, "");
+ mycommand = new cmd_silence(ServerInstance,maxsilence);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+ maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true);
+ if (!maxsilence)
+ maxsilence = 32;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnBuildExemptList] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = List[I_OnUserPreInvite] = 1;
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ // when the user quits tidy up any silence list they might have just to keep things tidy
+ silencelist* sl;
+ user->GetExt("silence_list", sl);
+ if (sl)
+ {
+ DELETE(sl);
+ user->Shrink("silence_list");
+ }
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ // we don't really have a limit...
+ output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
+ }
+
+ virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list)
+ {
+ int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
+ CUList *ulist;
+ switch (status)
+ {
+ case '@':
+ ulist = chan->GetOppedUsers();
+ break;
+ case '%':
+ ulist = chan->GetHalfoppedUsers();
+ break;
+ case '+':
+ ulist = chan->GetVoicedUsers();
+ break;
+ default:
+ ulist = chan->GetUsers();
+ break;
+ }
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (IS_LOCAL(i->first))
+ {
+ if (MatchPattern(i->first, sender, public_silence) == 1)
+ {
+ exempt_list[i->first] = i->first->nick;
+ }
+ }
+ }
+ }
+
+ virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
+ {
+ if (!IS_LOCAL(user))
+ return 0;
+
+ if (target_type == TYPE_USER)
+ {
+ return MatchPattern((userrec*)dest, user, silence_type);
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ chanrec* chan = (chanrec*)dest;
+ if (chan)
+ {
+ this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list);
+ }
+ }
+ return 0;
+ }
+
+ virtual int OnUserPreMessage(userrec* 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);
+ }
+
+ virtual int OnUserPreNotice(userrec* 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);
+ }
+
+ virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel)
+ {
+ return MatchPattern(dest, source, SILENCE_INVITE);
+ }
+
+ int MatchPattern(userrec* dest, userrec* source, int pattern)
+ {
+ silencelist* sl;
+ dest->GetExt("silence_list", sl);
+ if (sl)
+ {
+ for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
+ {
+ if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (ServerInstance->MatchText(source->GetFullHost(), c->first)))
+ return !(((c->second & SILENCE_EXCLUDE) > 0));
+ }
+ }
+ return 0;
+ }
+
+ virtual ~ModuleSilence()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSilence)
diff --git a/src/modules/m_spanningtree/README b/src/modules/m_spanningtree/README
index 76c678c1f..ff23e0381 100644
--- a/src/modules/m_spanningtree/README
+++ b/src/modules/m_spanningtree/README
@@ -1 +1,24 @@
-m_spanningtree -------------- This directory contains all files required to build the m_spanningtree.so module. Directories like this one starting with m_ and containing .cpp files are aggregated together by the makefile to form a single .so, with the same name as the directory. This directory contains the following files: * handshaketimer.cpp Code for detecting end of ziplink/ssl handshake * handshaketimer.h Header for above code * link.h Contains the definition of the Link block class * main.cpp The main group of classes and code for the module class * main.h The header for the main file * resolvers.h The header file that defines certain DNS utility classes * treesocket.cpp Contains code that inherits InspSocket into a server socket * treesocket.h Header definitions for above code * treeserver.cpp Contains code that defines the behaviour of a single server * treeserver.h Header definitions for above code * utils.cpp Contains general and message routing utility classes * utils.h Header code for general and message routing utilities Have fun -- Brain :-) \ No newline at end of file
+m_spanningtree
+--------------
+
+This directory contains all files required to build the m_spanningtree.so module.
+Directories like this one starting with m_ and containing .cpp files are aggregated
+together by the makefile to form a single .so, with the same name as the directory.
+
+This directory contains the following files:
+
+* handshaketimer.cpp Code for detecting end of ziplink/ssl handshake
+* handshaketimer.h Header for above code
+* link.h Contains the definition of the Link block class
+* main.cpp The main group of classes and code for the module class
+* main.h The header for the main file
+* resolvers.h The header file that defines certain DNS utility classes
+* treesocket.cpp Contains code that inherits InspSocket into a server socket
+* treesocket.h Header definitions for above code
+* treeserver.cpp Contains code that defines the behaviour of a single server
+* treeserver.h Header definitions for above code
+* utils.cpp Contains general and message routing utility classes
+* utils.h Header code for general and message routing utilities
+
+Have fun
+ -- Brain :-)
diff --git a/src/modules/m_spanningtree/handshaketimer.cpp b/src/modules/m_spanningtree/handshaketimer.cpp
index 93856f467..4aeb1da88 100644
--- a/src/modules/m_spanningtree/handshaketimer.cpp
+++ b/src/modules/m_spanningtree/handshaketimer.cpp
@@ -1 +1,62 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/handshaketimer.h" /* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ HandshakeTimer::HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay) : InspTimer(delay, time(NULL)), Instance(Inst), sock(s), lnk(l), Utils(u) { thefd = sock->GetFd(); } void HandshakeTimer::Tick(time_t TIME) { if (Instance->SE->GetRef(thefd) == sock) { if (!sock->GetHook()) { sock->SendCapabilities(); } else { if (sock->GetHook() && InspSocketHSCompleteRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send()) { InspSocketAttachCertRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send(); sock->SendCapabilities(); } else { Instance->Timers->AddTimer(new HandshakeTimer(Instance, sock, lnk, Utils, 1)); } } } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
+
+HandshakeTimer::HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay) : InspTimer(delay, time(NULL)), Instance(Inst), sock(s), lnk(l), Utils(u)
+{
+ thefd = sock->GetFd();
+}
+
+void HandshakeTimer::Tick(time_t TIME)
+{
+ if (Instance->SE->GetRef(thefd) == sock)
+ {
+ if (!sock->GetHook())
+ {
+ sock->SendCapabilities();
+ }
+ else
+ {
+ if (sock->GetHook() && InspSocketHSCompleteRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send())
+ {
+ InspSocketAttachCertRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send();
+ sock->SendCapabilities();
+ }
+ else
+ {
+ Instance->Timers->AddTimer(new HandshakeTimer(Instance, sock, lnk, Utils, 1));
+ }
+ }
+ }
+}
+
diff --git a/src/modules/m_spanningtree/handshaketimer.h b/src/modules/m_spanningtree/handshaketimer.h
index e94fe67d7..496102dda 100644
--- a/src/modules/m_spanningtree/handshaketimer.h
+++ b/src/modules/m_spanningtree/handshaketimer.h
@@ -1 +1,37 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __HANDSHAKE_TIMER_H__ #define __HANDSHAKE_TIMER_H__ #include "inspircd.h" #include "timer.h" class SpanningTreeUtilities; class TreeSocket; class Link; class HandshakeTimer : public InspTimer { private: InspIRCd* Instance; TreeSocket* sock; Link* lnk; SpanningTreeUtilities* Utils; int thefd; public: HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay); virtual void Tick(time_t TIME); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __HANDSHAKE_TIMER_H__
+#define __HANDSHAKE_TIMER_H__
+
+#include "inspircd.h"
+#include "timer.h"
+
+class SpanningTreeUtilities;
+class TreeSocket;
+class Link;
+
+class HandshakeTimer : public InspTimer
+{
+ private:
+ InspIRCd* Instance;
+ TreeSocket* sock;
+ Link* lnk;
+ SpanningTreeUtilities* Utils;
+ int thefd;
+ public:
+ HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay);
+ virtual void Tick(time_t TIME);
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/link.h b/src/modules/m_spanningtree/link.h
index 9636d565f..3de326153 100644
--- a/src/modules/m_spanningtree/link.h
+++ b/src/modules/m_spanningtree/link.h
@@ -1 +1,42 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __LINK_H__ #define __LINK_H__ /** The Link class might as well be a struct, * but this is C++ and we don't believe in structs (!). * It holds the entire information of one <link> * tag from the main config file. We maintain a list * of them, and populate the list on rehash/load. */ class Link : public classbase { public: irc::string Name; std::string IPAddr; int Port; std::string SendPass; std::string RecvPass; std::string AllowMask; unsigned long AutoConnect; time_t NextConnectTime; bool HiddenFromStats; std::string FailOver; std::string Hook; int Timeout; std::string Bind; bool Hidden; }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __LINK_H__
+#define __LINK_H__
+
+/** The Link class might as well be a struct,
+ * but this is C++ and we don't believe in structs (!).
+ * It holds the entire information of one <link>
+ * tag from the main config file. We maintain a list
+ * of them, and populate the list on rehash/load.
+ */
+class Link : public classbase
+{
+ public:
+ irc::string Name;
+ std::string IPAddr;
+ int Port;
+ std::string SendPass;
+ std::string RecvPass;
+ std::string AllowMask;
+ unsigned long AutoConnect;
+ time_t NextConnectTime;
+ bool HiddenFromStats;
+ std::string FailOver;
+ std::string Hook;
+ int Timeout;
+ std::string Bind;
+ bool Hidden;
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index 352cae870..1cc18dae6 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -1 +1,1392 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Provides a spanning tree server link protocol */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/timesynctimer.h" #include "m_spanningtree/resolvers.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/rconnect.h" #include "m_spanningtree/rsquit.h" /* $ModDep: m_spanningtree/timesynctimer.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 m_spanningtree/rconnect.h m_spanningtree/rsquit.h */ ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me) : Module(Me), max_local(0), max_global(0) { ServerInstance->UseInterface("InspSocketHook"); Utils = new SpanningTreeUtilities(Me, this); command_rconnect = new cmd_rconnect(ServerInstance, this, Utils); ServerInstance->AddCommand(command_rconnect); command_rsquit = new cmd_rsquit(ServerInstance, this, Utils); ServerInstance->AddCommand(command_rsquit); if (Utils->EnableTimeSync) { SyncTimer = new TimeSyncTimer(ServerInstance, this); ServerInstance->Timers->AddTimer(SyncTimer); } else SyncTimer = NULL; RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils); ServerInstance->Timers->AddTimer(RefreshTimer); } void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops) { std::string Parent = Utils->TreeRoot->GetName(); if (Current->GetParent()) { Parent = Current->GetParent()->GetName(); } for (unsigned int q = 0; q < Current->ChildCount(); q++) { if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))) { if (*user->oper) { ShowLinks(Current->GetChild(q),user,hops+1); } } else { ShowLinks(Current->GetChild(q),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().c_str())) && (!IS_OPER(user))) return; /* Or if the server is hidden and they're not an oper */ else if ((Current->Hidden) && (!IS_OPER(user))) return; user->WriteServ("364 %s %s %s :%d %s", user->nick,Current->GetName().c_str(), (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(), (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops, Current->GetDesc().c_str()); } int ModuleSpanningTree::CountLocalServs() { return Utils->TreeRoot->ChildCount(); } int ModuleSpanningTree::CountServs() { return Utils->serverlist.size(); } void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user) { ShowLinks(Utils->TreeRoot,user,0); user->WriteServ("365 %s * :End of /LINKS list.",user->nick); return; } void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user) { unsigned int n_users = ServerInstance->UserCount(); /* Only update these when someone wants to see them, more efficient */ if ((unsigned int)ServerInstance->LocalUserCount() > max_local) max_local = ServerInstance->LocalUserCount(); if (n_users > max_global) max_global = n_users; unsigned int ulined_count = 0; unsigned int ulined_local_count = 0; /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden, * locally and globally (locally means directly connected to us) */ if ((Utils->HideULines) && (!*user->oper)) { for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++) { if (ServerInstance->ULine(q->second->GetName().c_str())) { ulined_count++; if (q->second->GetParent() == Utils->TreeRoot) ulined_local_count++; } } } user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs()); if (ServerInstance->OperCount()) user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount()); if (ServerInstance->UnregisteredUserCount()) user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount()); if (ServerInstance->ChannelCount()) user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount()); user->WriteServ("255 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs()); user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local); user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global); return; } std::string ModuleSpanningTree::TimeToStr(time_t secs) { time_t mins_up = secs / 60; time_t hours_up = mins_up / 60; time_t days_up = hours_up / 24; secs = secs % 60; mins_up = mins_up % 60; hours_up = hours_up % 24; return ((days_up ? (ConvToStr(days_up) + "d") : std::string("")) + (hours_up ? (ConvToStr(hours_up) + "h") : std::string("")) + (mins_up ? (ConvToStr(mins_up) + "m") : std::string("")) + ConvToStr(secs) + "s"); } const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current) { time_t secs_up = ServerInstance->Time() - Current->age; return (" [Up: " + TimeToStr(secs_up) + " Lag: "+ConvToStr(Current->rtt)+"s]"); } // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS. void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers) { if (line < 128) { for (int t = 0; t < depth; t++) { matrix[line][t] = ' '; } // For Aligning, we need to work out exactly how deep this thing is, and produce // a 'Spacer' String to compensate. char spacer[40]; memset(spacer,' ',40); if ((40 - Current->GetName().length() - depth) > 1) { spacer[40 - Current->GetName().length() - depth] = '\0'; } else { spacer[5] = '\0'; } float percent; char text[128]; /* Neat and tidy default values, as we're dealing with a matrix not a simple string */ memset(text, 0, 128); if (ServerInstance->clientlist->size() == 0) { // If there are no users, WHO THE HELL DID THE /MAP?!?!?! percent = 0; } else { percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100; } const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : ""; snprintf(text, 126, "%s %s%5d [%5.2f%%]%s", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str()); totusers += Current->GetUserCount(); totservers++; strlcpy(&matrix[line][depth],text,126); line++; for (unsigned int q = 0; q < Current->ChildCount(); q++) { if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))) { if (*user->oper) { ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers); } } else { ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers); } } } } int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user) { if (pcnt > 0) { if (match(ServerInstance->Config->ServerName, parameters[0])) return 0; /* Remote MOTD, the server is within the 1st parameter */ std::deque<std::string> params; params.push_back(parameters[0]); /* Send it out remotely, generate no reply yet */ TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { params[0] = s->GetName(); Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName()); } else user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); return 1; } return 0; } int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user) { if (pcnt > 0) { if (match(ServerInstance->Config->ServerName, parameters[0])) return 0; /* Remote ADMIN, the server is within the 1st parameter */ std::deque<std::string> params; params.push_back(parameters[0]); /* Send it out remotely, generate no reply yet */ TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { params[0] = s->GetName(); Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName()); } else user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); return 1; } return 0; } int ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user) { if (pcnt > 0) { if (match(ServerInstance->Config->ServerName, parameters[0])) return 0; std::deque<std::string> params; params.push_back(parameters[0]); TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { params[0] = s->GetName(); Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName()); } else user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); return 1; } return 0; } int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user) { if (pcnt > 1) { if (match(ServerInstance->Config->ServerName, parameters[1])) return 0; /* Remote STATS, the server is within the 2nd parameter */ std::deque<std::string> params; params.push_back(parameters[0]); params.push_back(parameters[1]); /* Send it out remotely, generate no reply yet */ TreeServer* s = Utils->FindServerMask(parameters[1]); if (s) { params[1] = s->GetName(); Utils->DoOneToOne(user->nick, "STATS", params, s->GetName()); } else { user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]); } return 1; } return 0; } // 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. void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user) { // This array represents a virtual screen which we will // "scratch" draw to, as the console device of an irc // client does not provide for a proper terminal. float totusers = 0; float totservers = 0; char matrix[128][128]; for (unsigned int t = 0; t < 128; t++) { matrix[t][0] = '\0'; } line = 0; // The only recursive bit is called here. ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers); // Process each line one by one. The algorithm has a limit of // 128 servers (which is far more than a spanning tree should have // anyway, so we're ok). This limit can be raised simply by making // the character matrix deeper, 128 rows taking 10k of memory. for (int l = 1; l < line; 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 (matrix[l][first_nonspace] == ' ') { first_nonspace++; } 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. matrix[l][first_nonspace] = '-'; matrix[l][first_nonspace-1] = '`'; int l2 = l - 1; // Draw upwards until we hit the parent server, causing possibly // other corners (`-) to become branches (|-) while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`')) { matrix[l2][first_nonspace-1] = '|'; l2--; } } // dump the whole lot to the user. This is the easy bit, honest. for (int t = 0; t < line; t++) { user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]); } float avg_users = totusers / totservers; user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users); user->WriteServ("007 %s :End of /MAP",user->nick); return; } int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user) { TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { if (s == Utils->TreeRoot) { user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]); return 1; } TreeSocket* sock = s->GetSocket(); if (sock) { ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick); sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost()); ServerInstance->SE->DelFd(sock); sock->Close(); } else { if (IS_LOCAL(user)) user->WriteServ("NOTICE %s :*** WARNING: Using SQUIT to split remote servers is deprecated. Please use RSQUIT instead.",user->nick); } } else { user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]); } return 1; } int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user) { if ((IS_LOCAL(user)) && (pcnt)) { TreeServer* found = Utils->FindServerMask(parameters[0]); if (found) { // we dont' override for local server if (found == Utils->TreeRoot) return 0; std::deque<std::string> params; params.push_back(found->GetName()); params.push_back(user->nick); Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName()); } else { user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]); } } return 1; } int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user) { if ((IS_LOCAL(user)) && (pcnt > 1)) { userrec* remote = ServerInstance->FindNick(parameters[1]); if ((remote) && (remote->GetFd() < 0)) { std::deque<std::string> params; params.push_back(parameters[1]); Utils->DoOneToOne(user->nick,"IDLE",params,remote->server); return 1; } else if (!remote) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]); user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]); return 1; } } return 0; } void ModuleSpanningTree::DoPingChecks(time_t curtime) { for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++) { TreeServer* serv = Utils->TreeRoot->GetChild(j); TreeSocket* sock = serv->GetSocket(); if (sock) { if (curtime >= serv->NextPingTime()) { if (serv->AnsweredLastPing()) { sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName()); serv->SetNextPingTime(curtime + 60); serv->LastPing = curtime; serv->Warned = false; } else { /* they didnt answer, boot them */ sock->SendError("Ping timeout"); sock->Squit(serv,"Ping timeout"); /*** XXX SOCKET CULL ***/ return; } } else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (60 - Utils->PingWarnTime)) && (!serv->AnsweredLastPing())) { /* The server hasnt responded, send a warning to opers */ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime); serv->Warned = true; } } } /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data. * This prevents lost REMOTECONNECT notices */ for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) Utils->SetRemoteBursting(i->second, false); } void ModuleSpanningTree::ConnectServer(Link* x) { bool ipvalid = true; QueryType start_type = DNS_QUERY_A; #ifdef IPV6 start_type = DNS_QUERY_AAAA; if (strchr(x->IPAddr.c_str(),':')) { in6_addr n; if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1) ipvalid = false; } else #endif { in_addr n; if (inet_aton(x->IPAddr.c_str(),&n) < 1) ipvalid = false; } /* Do we already have an IP? If so, no need to resolve it. */ if (ipvalid) { /* Gave a hook, but it wasnt one we know */ if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end())) return; TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Bind, x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]); if (newsocket->GetFd() > -1) { /* Handled automatically on success */ } else { ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno)); delete newsocket; Utils->DoFailOver(x); } } else { try { bool cached; ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type); ServerInstance->AddResolver(snr, cached); } catch (ModuleException& e) { ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason()); Utils->DoFailOver(x); } } } void ModuleSpanningTree::AutoConnectServers(time_t curtime) { for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) { if ((x->AutoConnect) && (curtime >= x->NextConnectTime)) { x->NextConnectTime = curtime + x->AutoConnect; TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str()); if (x->FailOver.length()) { TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str()); if (CheckFailOver) { /* The failover for this server is currently a member of the network. * The failover probably succeeded, where the main link did not. * Don't try the main link until the failover is gone again. */ continue; } } if (!CheckDupe) { // an autoconnected server is not connected. Check if its time to connect it ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect); this->ConnectServer(&(*x)); } } } } int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user) { // we've already checked if pcnt > 0, so this is safe TreeServer* found = Utils->FindServerMask(parameters[0]); if (found) { std::string Version = found->GetVersion(); user->WriteServ("351 %s :%s",user->nick,Version.c_str()); if (found == Utils->TreeRoot) { ServerInstance->Config->Send005(user); } } else { user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]); } return 1; } int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user) { for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) { if (ServerInstance->MatchText(x->Name.c_str(),parameters[0])) { TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str()); if (!CheckDupe) { user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port); ConnectServer(&(*x)); return 1; } else { user->WriteServ("NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str()); return 1; } } } user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]); return 1; } void ModuleSpanningTree::BroadcastTimeSync() { if (Utils->MasterTime) { std::deque<std::string> params; params.push_back(ConvToStr(ServerInstance->Time(false))); params.push_back("FORCE"); Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params); } } int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results) { if ((statschar == 'c') || (statschar == 'n')) { for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++) { results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s'); if (statschar == 'c') results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str()); } results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report"); ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); return 1; } if (statschar == 'p') { /* show all server ports, after showing client ports. -- w00t */ for (unsigned int i = 0; i < Utils->Bindings.size(); i++) { std::string ip = Utils->Bindings[i]->IP; if (ip.empty()) ip = "*"; std::string transport("plaintext"); if (Utils->Bindings[i]->GetHook()) transport = InspSocketNameRequest(this, Utils->Bindings[i]->GetHook()).Send(); results.push_back(ConvToStr(ServerInstance->Config->ServerName) + " 249 "+user->nick+" :" + ip + ":" + ConvToStr(Utils->Bindings[i]->port)+ " (server, " + transport + ")"); } } return 0; } int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { /* If the command doesnt appear to be valid, we dont want to mess with it. */ if (!validated) return 0; if (command == "CONNECT") { return this->HandleConnect(parameters,pcnt,user); } else if (command == "STATS") { return this->HandleStats(parameters,pcnt,user); } else if (command == "MOTD") { return this->HandleMotd(parameters,pcnt,user); } else if (command == "ADMIN") { return this->HandleAdmin(parameters,pcnt,user); } else if (command == "SQUIT") { return this->HandleSquit(parameters,pcnt,user); } else if (command == "MAP") { this->HandleMap(parameters,pcnt,user); return 1; } else if ((command == "TIME") && (pcnt > 0)) { return this->HandleTime(parameters,pcnt,user); } else if (command == "LUSERS") { this->HandleLusers(parameters,pcnt,user); return 1; } else if (command == "LINKS") { this->HandleLinks(parameters,pcnt,user); return 1; } else if (command == "WHOIS") { if (pcnt > 1) { // remote whois return this->HandleRemoteWhois(parameters,pcnt,user); } } else if ((command == "VERSION") && (pcnt > 0)) { this->HandleVersion(parameters,pcnt,user); return 1; } else if ((command == "MODULES") && (pcnt > 0)) { return this->HandleModules(parameters,pcnt,user); } return 0; } void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user))) { // this bit of code cleverly routes all module commands // to all remote severs *automatically* so that modules // can just handle commands locally, without having // to have any special provision in place for remote // commands and linking protocols. std::deque<std::string> params; params.clear(); for (int j = 0; j < pcnt; j++) { if (strchr(parameters[j],' ')) { params.push_back(":" + std::string(parameters[j])); } else { params.push_back(std::string(parameters[j])); } } Utils->DoOneToMany(user->nick,command,params); } } void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description) { TreeServer* s = Utils->FindServer(servername); if (s) { description = s->GetDesc(); } } void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel) { if (IS_LOCAL(source)) { std::deque<std::string> params; params.push_back(dest->nick); params.push_back(channel->name); Utils->DoOneToMany(source->nick,"INVITE",params); } } void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { std::deque<std::string> params; params.push_back(chan->name); params.push_back(":"+topic); Utils->DoOneToMany(user->nick,"TOPIC",params); } void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text) { if (IS_LOCAL(user)) { std::deque<std::string> params; params.push_back(":"+text); Utils->DoOneToMany(user->nick,"WALLOPS",params); } } void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_USER) { userrec* d = (userrec*)dest; if ((d->GetFd() < 0) && (IS_LOCAL(user))) { std::deque<std::string> params; params.clear(); params.push_back(d->nick); params.push_back(":"+text); Utils->DoOneToOne(user->nick,"NOTICE",params,d->server); } } else if (target_type == TYPE_CHANNEL) { if (IS_LOCAL(user)) { chanrec *c = (chanrec*)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->nick)+" NOTICE "+cname+" :"+text); } } } } else if (target_type == TYPE_SERVER) { if (IS_LOCAL(user)) { char* target = (char*)dest; std::deque<std::string> par; par.push_back(target); par.push_back(":"+text); Utils->DoOneToMany(user->nick,"NOTICE",par); } } } void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_USER) { // route private messages which are targetted at clients only to the server // which needs to receive them userrec* d = (userrec*)dest; if ((d->GetFd() < 0) && (IS_LOCAL(user))) { std::deque<std::string> params; params.clear(); params.push_back(d->nick); params.push_back(":"+text); Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server); } } else if (target_type == TYPE_CHANNEL) { if (IS_LOCAL(user)) { chanrec *c = (chanrec*)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->nick)+" PRIVMSG "+cname+" :"+text); } } } } else if (target_type == TYPE_SERVER) { if (IS_LOCAL(user)) { char* target = (char*)dest; std::deque<std::string> par; par.push_back(target); par.push_back(":"+text); Utils->DoOneToMany(user->nick,"PRIVMSG",par); } } } void ModuleSpanningTree::OnBackgroundTimer(time_t curtime) { AutoConnectServers(curtime); DoPingChecks(curtime); } void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel, bool &silent) { // Only do this for local users if (IS_LOCAL(user)) { if (channel->GetUserCounter() == 1) { std::deque<std::string> 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. params.push_back(channel->name); params.push_back(ConvToStr(channel->age)); params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick)); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params); /* First user in, sync the modes for the channel */ params.pop_back(); params.push_back(channel->ChanModes(true)); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params); } else { std::deque<std::string> params; params.push_back(channel->name); params.push_back(ConvToStr(channel->age)); Utils->DoOneToMany(user->nick,"JOIN",params); } } } void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost) { // only occurs for local clients if (user->registered != REG_ALL) return; std::deque<std::string> params; params.push_back(newhost); Utils->DoOneToMany(user->nick,"FHOST",params); } void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos) { // only occurs for local clients if (user->registered != REG_ALL) return; std::deque<std::string> params; params.push_back(gecos); Utils->DoOneToMany(user->nick,"FNAME",params); } void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { if (IS_LOCAL(user)) { std::deque<std::string> params; params.push_back(channel->name); if (!partmessage.empty()) params.push_back(":"+partmessage); Utils->DoOneToMany(user->nick,"PART",params); } } void ModuleSpanningTree::OnUserConnect(userrec* user) { char agestr[MAXBUF]; if (IS_LOCAL(user)) { std::deque<std::string> params; snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age); params.push_back(agestr); params.push_back(user->nick); params.push_back(user->host); params.push_back(user->dhost); params.push_back(user->ident); params.push_back("+"+std::string(user->FormatModes())); params.push_back(user->GetIPString()); params.push_back(":"+std::string(user->fullname)); Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params); // User is Local, change needs to be reflected! TreeServer* SourceServer = Utils->FindServer(user->server); if (SourceServer) { SourceServer->AddUserCount(); } } } void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) { std::deque<std::string> params; if (oper_message != reason) { params.push_back(":"+oper_message); Utils->DoOneToMany(user->nick,"OPERQUIT",params); } params.clear(); params.push_back(":"+reason); Utils->DoOneToMany(user->nick,"QUIT",params); } // Regardless, We need to modify the user Counts.. TreeServer* SourceServer = Utils->FindServer(user->server); if (SourceServer) { SourceServer->DelUserCount(); } } void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick) { if (IS_LOCAL(user)) { std::deque<std::string> params; params.push_back(user->nick); Utils->DoOneToMany(oldnick,"NICK",params); } } void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { if ((source) && (IS_LOCAL(source))) { std::deque<std::string> params; params.push_back(chan->name); params.push_back(user->nick); params.push_back(":"+reason); Utils->DoOneToMany(source->nick,"KICK",params); } else if (!source) { std::deque<std::string> params; params.push_back(chan->name); params.push_back(user->nick); params.push_back(":"+reason); Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params); } } void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason) { std::deque<std::string> params; params.push_back(":"+reason); Utils->DoOneToMany(dest->nick,"OPERQUIT",params); params.clear(); params.push_back(dest->nick); params.push_back(":"+reason); dest->SetOperQuit(operreason); Utils->DoOneToMany(source->nick,"KILL",params); } void ModuleSpanningTree::OnRehash(userrec* user, const std::string &parameter) { if (!parameter.empty()) { std::deque<std::string> params; params.push_back(parameter); Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params); // check for self if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter)) { ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName); ServerInstance->RehashServer(); } } Utils->ReadConfiguration(false); InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance); } // note: the protocol does not allow direct umode +o except // via NICK with 8 params. sending OPERTYPE infers +o modechange // locally. void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype) { if (IS_LOCAL(user)) { std::deque<std::string> params; params.push_back(opertype); Utils->DoOneToMany(user->nick,"OPERTYPE",params); } } void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason) { if (!source) { /* Server-set lines */ char data[MAXBUF]; snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false), (unsigned long)duration, reason.c_str()); std::deque<std::string> params; params.push_back(data); Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params); } else { if (IS_LOCAL(source)) { char type[8]; snprintf(type,8,"%cLINE",linetype); std::string stype = type; if (adding) { char sduration[MAXBUF]; snprintf(sduration,MAXBUF,"%ld",duration); std::deque<std::string> params; params.push_back(host); params.push_back(sduration); params.push_back(":"+reason); Utils->DoOneToMany(source->nick,stype,params); } else { std::deque<std::string> params; params.push_back(host); Utils->DoOneToMany(source->nick,stype,params); } } } } void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { OnLine(source,hostmask,true,'G',duration,reason); } void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask) { OnLine(source,ipmask,true,'Z',duration,reason); } void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask) { OnLine(source,nickmask,true,'Q',duration,reason); } void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { OnLine(source,hostmask,true,'E',duration,reason); } void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask) { OnLine(source,hostmask,false,'G',0,""); } void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask) { OnLine(source,ipmask,false,'Z',0,""); } void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask) { OnLine(source,nickmask,false,'Q',0,""); } void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask) { OnLine(source,hostmask,false,'E',0,""); } void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text) { if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) { std::deque<std::string> params; std::string command; if (target_type == TYPE_USER) { userrec* u = (userrec*)dest; params.push_back(u->nick); params.push_back(text); command = "MODE"; } else { chanrec* c = (chanrec*)dest; params.push_back(c->name); params.push_back(ConvToStr(c->age)); params.push_back(text); command = "FMODE"; } Utils->DoOneToMany(user->nick, command, params); } } void ModuleSpanningTree::OnSetAway(userrec* user) { if (IS_LOCAL(user)) { std::deque<std::string> params; params.push_back(":"+std::string(user->awaymsg)); Utils->DoOneToMany(user->nick,"AWAY",params); } } void ModuleSpanningTree::OnCancelAway(userrec* user) { if (IS_LOCAL(user)) { std::deque<std::string> params; params.clear(); Utils->DoOneToMany(user->nick,"AWAY",params); } } void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) { TreeSocket* s = (TreeSocket*)opaque; if (target) { if (target_type == TYPE_USER) { userrec* u = (userrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline); } else { chanrec* c = (chanrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline); } } } void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { TreeSocket* s = (TreeSocket*)opaque; if (target) { if (target_type == TYPE_USER) { userrec* u = (userrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata); } else if (target_type == TYPE_CHANNEL) { chanrec* c = (chanrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata); } } if (target_type == TYPE_OTHER) { s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata); } } void ModuleSpanningTree::OnEvent(Event* event) { std::deque<std::string>* params = (std::deque<std::string>*)event->GetData(); if (event->GetEventID() == "send_metadata") { if (params->size() < 3) return; (*params)[2] = ":" + (*params)[2]; Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params); } else if (event->GetEventID() == "send_topic") { if (params->size() < 2) return; (*params)[1] = ":" + (*params)[1]; params->insert(params->begin() + 1,ServerInstance->Config->ServerName); params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true))); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params); } else if (event->GetEventID() == "send_mode") { if (params->size() < 2) return; // Insert the TS value of the object, either userrec or chanrec time_t ourTS = 0; userrec* a = ServerInstance->FindNick((*params)[0]); if (a) { ourTS = a->age; Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params); return; } else { chanrec* a = ServerInstance->FindChan((*params)[0]); if (a) { ourTS = a->age; params->insert(params->begin() + 1,ConvToStr(ourTS)); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params); } } } else if (event->GetEventID() == "send_mode_explicit") { if (params->size() < 2) return; Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params); } else if (event->GetEventID() == "send_opers") { if (params->size() < 1) return; (*params)[0] = ":" + (*params)[0]; Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params); } else if (event->GetEventID() == "send_modeset") { if (params->size() < 2) return; (*params)[1] = ":" + (*params)[1]; Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params); } else if (event->GetEventID() == "send_snoset") { if (params->size() < 2) return; (*params)[1] = ":" + (*params)[1]; Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params); } else if (event->GetEventID() == "send_push") { if (params->size() < 2) return; userrec *a = ServerInstance->FindNick((*params)[0]); if (!a) return; (*params)[1] = ":" + (*params)[1]; Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server); } } ModuleSpanningTree::~ModuleSpanningTree() { /* This will also free the listeners */ delete Utils; if (SyncTimer) ServerInstance->Timers->DelTimer(SyncTimer); ServerInstance->Timers->DelTimer(RefreshTimer); ServerInstance->DoneWithInterface("InspSocketHook"); } Version ModuleSpanningTree::GetVersion() { return Version(1,1,0,2,VF_VENDOR,API_VERSION); } void ModuleSpanningTree::Implements(char* List) { List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1; List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1; List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1; List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1; List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1; List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1; List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1; } /* It is IMPORTANT that m_spanningtree is the last module in the chain * 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 * the module call queue. */ Priority ModuleSpanningTree::Prioritize() { return PRIORITY_LAST; } MODULE_INIT(ModuleSpanningTree) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Provides a spanning tree server link protocol */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/timesynctimer.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/rconnect.h"
+#include "m_spanningtree/rsquit.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 m_spanningtree/rconnect.h m_spanningtree/rsquit.h */
+
+ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
+ : Module(Me), max_local(0), max_global(0)
+{
+ ServerInstance->UseInterface("InspSocketHook");
+ Utils = new SpanningTreeUtilities(Me, this);
+ command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
+ ServerInstance->AddCommand(command_rconnect);
+ command_rsquit = new cmd_rsquit(ServerInstance, this, Utils);
+ ServerInstance->AddCommand(command_rsquit);
+ if (Utils->EnableTimeSync)
+ {
+ SyncTimer = new TimeSyncTimer(ServerInstance, this);
+ ServerInstance->Timers->AddTimer(SyncTimer);
+ }
+ else
+ SyncTimer = NULL;
+
+ RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils);
+ ServerInstance->Timers->AddTimer(RefreshTimer);
+}
+
+void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)
+{
+ std::string Parent = Utils->TreeRoot->GetName();
+ if (Current->GetParent())
+ {
+ Parent = Current->GetParent()->GetName();
+ }
+ for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ {
+ if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
+ {
+ if (*user->oper)
+ {
+ ShowLinks(Current->GetChild(q),user,hops+1);
+ }
+ }
+ else
+ {
+ ShowLinks(Current->GetChild(q),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().c_str())) && (!IS_OPER(user)))
+ return;
+ /* Or if the server is hidden and they're not an oper */
+ else if ((Current->Hidden) && (!IS_OPER(user)))
+ return;
+
+ user->WriteServ("364 %s %s %s :%d %s", user->nick,Current->GetName().c_str(),
+ (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(),
+ (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
+ Current->GetDesc().c_str());
+}
+
+int ModuleSpanningTree::CountLocalServs()
+{
+ return Utils->TreeRoot->ChildCount();
+}
+
+int ModuleSpanningTree::CountServs()
+{
+ return Utils->serverlist.size();
+}
+
+void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)
+{
+ ShowLinks(Utils->TreeRoot,user,0);
+ user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
+ return;
+}
+
+void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)
+{
+ unsigned int n_users = ServerInstance->UserCount();
+
+ /* Only update these when someone wants to see them, more efficient */
+ if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
+ max_local = ServerInstance->LocalUserCount();
+ if (n_users > max_global)
+ max_global = n_users;
+
+ unsigned int ulined_count = 0;
+ unsigned int ulined_local_count = 0;
+
+ /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
+ * locally and globally (locally means directly connected to us)
+ */
+ if ((Utils->HideULines) && (!*user->oper))
+ {
+ for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
+ {
+ if (ServerInstance->ULine(q->second->GetName().c_str()))
+ {
+ ulined_count++;
+ if (q->second->GetParent() == Utils->TreeRoot)
+ ulined_local_count++;
+ }
+ }
+ }
+ user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
+ if (ServerInstance->OperCount())
+ user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
+ if (ServerInstance->UnregisteredUserCount())
+ user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
+ if (ServerInstance->ChannelCount())
+ user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
+ user->WriteServ("255 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
+ user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
+ user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);
+ return;
+}
+
+std::string ModuleSpanningTree::TimeToStr(time_t secs)
+{
+ time_t mins_up = secs / 60;
+ time_t hours_up = mins_up / 60;
+ time_t days_up = hours_up / 24;
+ secs = secs % 60;
+ mins_up = mins_up % 60;
+ hours_up = hours_up % 24;
+ return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))
+ + (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))
+ + (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))
+ + ConvToStr(secs) + "s");
+}
+
+const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)
+{
+ time_t secs_up = ServerInstance->Time() - Current->age;
+ return (" [Up: " + TimeToStr(secs_up) + " Lag: "+ConvToStr(Current->rtt)+"s]");
+}
+
+// WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
+void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers)
+{
+ if (line < 128)
+ {
+ for (int t = 0; t < depth; t++)
+ {
+ matrix[line][t] = ' ';
+ }
+ // For Aligning, we need to work out exactly how deep this thing is, and produce
+ // a 'Spacer' String to compensate.
+ char spacer[40];
+ memset(spacer,' ',40);
+ if ((40 - Current->GetName().length() - depth) > 1) {
+ spacer[40 - Current->GetName().length() - depth] = '\0';
+ }
+ else
+ {
+ spacer[5] = '\0';
+ }
+ float percent;
+ char text[128];
+ /* Neat and tidy default values, as we're dealing with a matrix not a simple string */
+ memset(text, 0, 128);
+
+ if (ServerInstance->clientlist->size() == 0) {
+ // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
+ percent = 0;
+ }
+ else
+ {
+ percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;
+ }
+ const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";
+ snprintf(text, 126, "%s %s%5d [%5.2f%%]%s", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str());
+ totusers += Current->GetUserCount();
+ totservers++;
+ strlcpy(&matrix[line][depth],text,126);
+ line++;
+ for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ {
+ if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
+ {
+ if (*user->oper)
+ {
+ ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
+ }
+ }
+ else
+ {
+ ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
+ }
+ }
+ }
+}
+
+int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)
+{
+ if (pcnt > 0)
+ {
+ if (match(ServerInstance->Config->ServerName, parameters[0]))
+ return 0;
+
+ /* Remote MOTD, the server is within the 1st parameter */
+ std::deque<std::string> params;
+ params.push_back(parameters[0]);
+ /* Send it out remotely, generate no reply yet */
+ TreeServer* s = Utils->FindServerMask(parameters[0]);
+ if (s)
+ {
+ params[0] = s->GetName();
+ Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());
+ }
+ else
+ user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
+ return 1;
+ }
+ return 0;
+}
+
+int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)
+{
+ if (pcnt > 0)
+ {
+ if (match(ServerInstance->Config->ServerName, parameters[0]))
+ return 0;
+
+ /* Remote ADMIN, the server is within the 1st parameter */
+ std::deque<std::string> params;
+ params.push_back(parameters[0]);
+ /* Send it out remotely, generate no reply yet */
+ TreeServer* s = Utils->FindServerMask(parameters[0]);
+ if (s)
+ {
+ params[0] = s->GetName();
+ Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());
+ }
+ else
+ user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
+ return 1;
+ }
+ return 0;
+}
+
+int ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user)
+{
+ if (pcnt > 0)
+ {
+ if (match(ServerInstance->Config->ServerName, parameters[0]))
+ return 0;
+
+ std::deque<std::string> params;
+ params.push_back(parameters[0]);
+ TreeServer* s = Utils->FindServerMask(parameters[0]);
+ if (s)
+ {
+ params[0] = s->GetName();
+ Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName());
+ }
+ else
+ user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
+ return 1;
+ }
+ return 0;
+}
+
+int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)
+{
+ if (pcnt > 1)
+ {
+ if (match(ServerInstance->Config->ServerName, parameters[1]))
+ return 0;
+
+ /* Remote STATS, the server is within the 2nd parameter */
+ std::deque<std::string> params;
+ params.push_back(parameters[0]);
+ params.push_back(parameters[1]);
+ /* Send it out remotely, generate no reply yet */
+
+ TreeServer* s = Utils->FindServerMask(parameters[1]);
+ if (s)
+ {
+ params[1] = s->GetName();
+ Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());
+ }
+ else
+ {
+ user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+// 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.
+void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)
+{
+ // This array represents a virtual screen which we will
+ // "scratch" draw to, as the console device of an irc
+ // client does not provide for a proper terminal.
+ float totusers = 0;
+ float totservers = 0;
+ char matrix[128][128];
+ for (unsigned int t = 0; t < 128; t++)
+ {
+ matrix[t][0] = '\0';
+ }
+ line = 0;
+ // The only recursive bit is called here.
+ ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);
+ // Process each line one by one. The algorithm has a limit of
+ // 128 servers (which is far more than a spanning tree should have
+ // anyway, so we're ok). This limit can be raised simply by making
+ // the character matrix deeper, 128 rows taking 10k of memory.
+ for (int l = 1; l < line; 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 (matrix[l][first_nonspace] == ' ')
+ {
+ first_nonspace++;
+ }
+ 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.
+ matrix[l][first_nonspace] = '-';
+ matrix[l][first_nonspace-1] = '`';
+ int l2 = l - 1;
+ // Draw upwards until we hit the parent server, causing possibly
+ // other corners (`-) to become branches (|-)
+ while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
+ {
+ matrix[l2][first_nonspace-1] = '|';
+ l2--;
+ }
+ }
+ // dump the whole lot to the user. This is the easy bit, honest.
+ for (int t = 0; t < line; t++)
+ {
+ user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);
+ }
+ float avg_users = totusers / totservers;
+ user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users);
+ user->WriteServ("007 %s :End of /MAP",user->nick);
+ return;
+}
+
+int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)
+{
+ TreeServer* s = Utils->FindServerMask(parameters[0]);
+ if (s)
+ {
+ if (s == Utils->TreeRoot)
+ {
+ user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
+ return 1;
+ }
+ TreeSocket* sock = s->GetSocket();
+ if (sock)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
+ sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
+ ServerInstance->SE->DelFd(sock);
+ sock->Close();
+ }
+ else
+ {
+ if (IS_LOCAL(user))
+ user->WriteServ("NOTICE %s :*** WARNING: Using SQUIT to split remote servers is deprecated. Please use RSQUIT instead.",user->nick);
+ }
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
+ }
+ return 1;
+}
+
+int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)
+{
+ if ((IS_LOCAL(user)) && (pcnt))
+ {
+ TreeServer* found = Utils->FindServerMask(parameters[0]);
+ if (found)
+ {
+ // we dont' override for local server
+ if (found == Utils->TreeRoot)
+ return 0;
+
+ std::deque<std::string> params;
+ params.push_back(found->GetName());
+ params.push_back(user->nick);
+ Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());
+ }
+ else
+ {
+ user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
+ }
+ }
+ return 1;
+}
+
+int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
+{
+ if ((IS_LOCAL(user)) && (pcnt > 1))
+ {
+ userrec* remote = ServerInstance->FindNick(parameters[1]);
+ if ((remote) && (remote->GetFd() < 0))
+ {
+ std::deque<std::string> params;
+ params.push_back(parameters[1]);
+ Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);
+ return 1;
+ }
+ else if (!remote)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
+ user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ModuleSpanningTree::DoPingChecks(time_t curtime)
+{
+ for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
+ {
+ TreeServer* serv = Utils->TreeRoot->GetChild(j);
+ TreeSocket* sock = serv->GetSocket();
+ if (sock)
+ {
+ if (curtime >= serv->NextPingTime())
+ {
+ if (serv->AnsweredLastPing())
+ {
+ sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());
+ serv->SetNextPingTime(curtime + 60);
+ serv->LastPing = curtime;
+ serv->Warned = false;
+ }
+ else
+ {
+ /* they didnt answer, boot them */
+ sock->SendError("Ping timeout");
+ sock->Squit(serv,"Ping timeout");
+ /*** XXX SOCKET CULL ***/
+ return;
+ }
+ }
+ else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (60 - Utils->PingWarnTime)) && (!serv->AnsweredLastPing()))
+ {
+ /* The server hasnt responded, send a warning to opers */
+ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime);
+ serv->Warned = true;
+ }
+ }
+ }
+
+ /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
+ * This prevents lost REMOTECONNECT notices
+ */
+ for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
+ Utils->SetRemoteBursting(i->second, false);
+}
+
+void ModuleSpanningTree::ConnectServer(Link* x)
+{
+ bool ipvalid = true;
+ QueryType start_type = DNS_QUERY_A;
+#ifdef IPV6
+ start_type = DNS_QUERY_AAAA;
+ if (strchr(x->IPAddr.c_str(),':'))
+ {
+ in6_addr n;
+ if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
+ ipvalid = false;
+ }
+ else
+#endif
+ {
+ in_addr n;
+ if (inet_aton(x->IPAddr.c_str(),&n) < 1)
+ ipvalid = false;
+ }
+
+ /* Do we already have an IP? If so, no need to resolve it. */
+ if (ipvalid)
+ {
+ /* Gave a hook, but it wasnt one we know */
+ if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
+ return;
+ TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Bind, x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
+ if (newsocket->GetFd() > -1)
+ {
+ /* Handled automatically on success */
+ }
+ else
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
+ delete newsocket;
+ Utils->DoFailOver(x);
+ }
+ }
+ else
+ {
+ try
+ {
+ bool cached;
+ ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type);
+ ServerInstance->AddResolver(snr, cached);
+ }
+ catch (ModuleException& e)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
+ Utils->DoFailOver(x);
+ }
+ }
+}
+
+void ModuleSpanningTree::AutoConnectServers(time_t curtime)
+{
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
+ {
+ x->NextConnectTime = curtime + x->AutoConnect;
+ TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
+ if (x->FailOver.length())
+ {
+ TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
+ if (CheckFailOver)
+ {
+ /* The failover for this server is currently a member of the network.
+ * The failover probably succeeded, where the main link did not.
+ * Don't try the main link until the failover is gone again.
+ */
+ continue;
+ }
+ }
+ if (!CheckDupe)
+ {
+ // an autoconnected server is not connected. Check if its time to connect it
+ ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
+ this->ConnectServer(&(*x));
+ }
+ }
+ }
+}
+
+int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)
+{
+ // we've already checked if pcnt > 0, so this is safe
+ TreeServer* found = Utils->FindServerMask(parameters[0]);
+ if (found)
+ {
+ std::string Version = found->GetVersion();
+ user->WriteServ("351 %s :%s",user->nick,Version.c_str());
+ if (found == Utils->TreeRoot)
+ {
+ ServerInstance->Config->Send005(user);
+ }
+ }
+ else
+ {
+ user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
+ }
+ return 1;
+}
+
+int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)
+{
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
+ {
+ TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
+ if (!CheckDupe)
+ {
+ user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
+ ConnectServer(&(*x));
+ return 1;
+ }
+ else
+ {
+ user->WriteServ("NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str());
+ return 1;
+ }
+ }
+ }
+ user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
+ return 1;
+}
+
+void ModuleSpanningTree::BroadcastTimeSync()
+{
+ if (Utils->MasterTime)
+ {
+ std::deque<std::string> params;
+ params.push_back(ConvToStr(ServerInstance->Time(false)));
+ params.push_back("FORCE");
+ Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
+ }
+}
+
+int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)
+{
+ if ((statschar == 'c') || (statschar == 'n'))
+ {
+ for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
+ {
+ results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
+ if (statschar == 'c')
+ results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
+ }
+ results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
+ return 1;
+ }
+
+ if (statschar == 'p')
+ {
+ /* show all server ports, after showing client ports. -- w00t */
+
+ for (unsigned int i = 0; i < Utils->Bindings.size(); i++)
+ {
+ std::string ip = Utils->Bindings[i]->IP;
+ if (ip.empty())
+ ip = "*";
+
+ std::string transport("plaintext");
+ if (Utils->Bindings[i]->GetHook())
+ transport = InspSocketNameRequest(this, Utils->Bindings[i]->GetHook()).Send();
+
+ results.push_back(ConvToStr(ServerInstance->Config->ServerName) + " 249 "+user->nick+" :" + ip + ":" + ConvToStr(Utils->Bindings[i]->port)+
+ " (server, " + transport + ")");
+ }
+ }
+ return 0;
+}
+
+int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+{
+ /* If the command doesnt appear to be valid, we dont want to mess with it. */
+ if (!validated)
+ return 0;
+
+ if (command == "CONNECT")
+ {
+ return this->HandleConnect(parameters,pcnt,user);
+ }
+ else if (command == "STATS")
+ {
+ return this->HandleStats(parameters,pcnt,user);
+ }
+ else if (command == "MOTD")
+ {
+ return this->HandleMotd(parameters,pcnt,user);
+ }
+ else if (command == "ADMIN")
+ {
+ return this->HandleAdmin(parameters,pcnt,user);
+ }
+ else if (command == "SQUIT")
+ {
+ return this->HandleSquit(parameters,pcnt,user);
+ }
+ else if (command == "MAP")
+ {
+ this->HandleMap(parameters,pcnt,user);
+ return 1;
+ }
+ else if ((command == "TIME") && (pcnt > 0))
+ {
+ return this->HandleTime(parameters,pcnt,user);
+ }
+ else if (command == "LUSERS")
+ {
+ this->HandleLusers(parameters,pcnt,user);
+ return 1;
+ }
+ else if (command == "LINKS")
+ {
+ this->HandleLinks(parameters,pcnt,user);
+ return 1;
+ }
+ else if (command == "WHOIS")
+ {
+ if (pcnt > 1)
+ {
+ // remote whois
+ return this->HandleRemoteWhois(parameters,pcnt,user);
+ }
+ }
+ else if ((command == "VERSION") && (pcnt > 0))
+ {
+ this->HandleVersion(parameters,pcnt,user);
+ return 1;
+ }
+ else if ((command == "MODULES") && (pcnt > 0))
+ {
+ return this->HandleModules(parameters,pcnt,user);
+ }
+ return 0;
+}
+
+void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
+{
+ if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
+ {
+ // this bit of code cleverly routes all module commands
+ // to all remote severs *automatically* so that modules
+ // can just handle commands locally, without having
+ // to have any special provision in place for remote
+ // commands and linking protocols.
+ std::deque<std::string> params;
+ params.clear();
+ for (int j = 0; j < pcnt; j++)
+ {
+ if (strchr(parameters[j],' '))
+ {
+ params.push_back(":" + std::string(parameters[j]));
+ }
+ else
+ {
+ params.push_back(std::string(parameters[j]));
+ }
+ }
+ Utils->DoOneToMany(user->nick,command,params);
+ }
+}
+
+void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
+{
+ TreeServer* s = Utils->FindServer(servername);
+ if (s)
+ {
+ description = s->GetDesc();
+ }
+}
+
+void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
+{
+ if (IS_LOCAL(source))
+ {
+ std::deque<std::string> params;
+ params.push_back(dest->nick);
+ params.push_back(channel->name);
+ Utils->DoOneToMany(source->nick,"INVITE",params);
+ }
+}
+
+void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)
+{
+ std::deque<std::string> params;
+ params.push_back(chan->name);
+ params.push_back(":"+topic);
+ Utils->DoOneToMany(user->nick,"TOPIC",params);
+}
+
+void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(":"+text);
+ Utils->DoOneToMany(user->nick,"WALLOPS",params);
+ }
+}
+
+void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+{
+ if (target_type == TYPE_USER)
+ {
+ userrec* d = (userrec*)dest;
+ if ((d->GetFd() < 0) && (IS_LOCAL(user)))
+ {
+ std::deque<std::string> params;
+ params.clear();
+ params.push_back(d->nick);
+ params.push_back(":"+text);
+ Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);
+ }
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ if (IS_LOCAL(user))
+ {
+ chanrec *c = (chanrec*)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->nick)+" NOTICE "+cname+" :"+text);
+ }
+ }
+ }
+ }
+ else if (target_type == TYPE_SERVER)
+ {
+ if (IS_LOCAL(user))
+ {
+ char* target = (char*)dest;
+ std::deque<std::string> par;
+ par.push_back(target);
+ par.push_back(":"+text);
+ Utils->DoOneToMany(user->nick,"NOTICE",par);
+ }
+ }
+}
+
+void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+{
+ if (target_type == TYPE_USER)
+ {
+ // route private messages which are targetted at clients only to the server
+ // which needs to receive them
+ userrec* d = (userrec*)dest;
+ if ((d->GetFd() < 0) && (IS_LOCAL(user)))
+ {
+ std::deque<std::string> params;
+ params.clear();
+ params.push_back(d->nick);
+ params.push_back(":"+text);
+ Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);
+ }
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ if (IS_LOCAL(user))
+ {
+ chanrec *c = (chanrec*)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->nick)+" PRIVMSG "+cname+" :"+text);
+ }
+ }
+ }
+ }
+ else if (target_type == TYPE_SERVER)
+ {
+ if (IS_LOCAL(user))
+ {
+ char* target = (char*)dest;
+ std::deque<std::string> par;
+ par.push_back(target);
+ par.push_back(":"+text);
+ Utils->DoOneToMany(user->nick,"PRIVMSG",par);
+ }
+ }
+}
+
+void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
+{
+ AutoConnectServers(curtime);
+ DoPingChecks(curtime);
+}
+
+void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel, bool &silent)
+{
+ // Only do this for local users
+ if (IS_LOCAL(user))
+ {
+ if (channel->GetUserCounter() == 1)
+ {
+ std::deque<std::string> 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.
+ params.push_back(channel->name);
+ params.push_back(ConvToStr(channel->age));
+ params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
+ /* First user in, sync the modes for the channel */
+ params.pop_back();
+ params.push_back(channel->ChanModes(true));
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
+ }
+ else
+ {
+ std::deque<std::string> params;
+ params.push_back(channel->name);
+ params.push_back(ConvToStr(channel->age));
+ Utils->DoOneToMany(user->nick,"JOIN",params);
+ }
+ }
+}
+
+void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)
+{
+ // only occurs for local clients
+ if (user->registered != REG_ALL)
+ return;
+ std::deque<std::string> params;
+ params.push_back(newhost);
+ Utils->DoOneToMany(user->nick,"FHOST",params);
+}
+
+void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)
+{
+ // only occurs for local clients
+ if (user->registered != REG_ALL)
+ return;
+ std::deque<std::string> params;
+ params.push_back(gecos);
+ Utils->DoOneToMany(user->nick,"FNAME",params);
+}
+
+void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(channel->name);
+ if (!partmessage.empty())
+ params.push_back(":"+partmessage);
+ Utils->DoOneToMany(user->nick,"PART",params);
+ }
+}
+
+void ModuleSpanningTree::OnUserConnect(userrec* user)
+{
+ char agestr[MAXBUF];
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
+ params.push_back(agestr);
+ params.push_back(user->nick);
+ params.push_back(user->host);
+ params.push_back(user->dhost);
+ params.push_back(user->ident);
+ params.push_back("+"+std::string(user->FormatModes()));
+ params.push_back(user->GetIPString());
+ params.push_back(":"+std::string(user->fullname));
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);
+ // User is Local, change needs to be reflected!
+ TreeServer* SourceServer = Utils->FindServer(user->server);
+ if (SourceServer)
+ {
+ SourceServer->AddUserCount();
+ }
+ }
+}
+
+void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+{
+ if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+ {
+ std::deque<std::string> params;
+
+ if (oper_message != reason)
+ {
+ params.push_back(":"+oper_message);
+ Utils->DoOneToMany(user->nick,"OPERQUIT",params);
+ }
+ params.clear();
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(user->nick,"QUIT",params);
+ }
+ // Regardless, We need to modify the user Counts..
+ TreeServer* SourceServer = Utils->FindServer(user->server);
+ if (SourceServer)
+ {
+ SourceServer->DelUserCount();
+ }
+}
+
+void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(user->nick);
+ Utils->DoOneToMany(oldnick,"NICK",params);
+ }
+}
+
+void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
+{
+ if ((source) && (IS_LOCAL(source)))
+ {
+ std::deque<std::string> params;
+ params.push_back(chan->name);
+ params.push_back(user->nick);
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(source->nick,"KICK",params);
+ }
+ else if (!source)
+ {
+ std::deque<std::string> params;
+ params.push_back(chan->name);
+ params.push_back(user->nick);
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);
+ }
+}
+
+void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason)
+{
+ std::deque<std::string> params;
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(dest->nick,"OPERQUIT",params);
+ params.clear();
+ params.push_back(dest->nick);
+ params.push_back(":"+reason);
+ dest->SetOperQuit(operreason);
+ Utils->DoOneToMany(source->nick,"KILL",params);
+}
+
+void ModuleSpanningTree::OnRehash(userrec* user, const std::string &parameter)
+{
+ if (!parameter.empty())
+ {
+ std::deque<std::string> params;
+ params.push_back(parameter);
+ Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);
+ // check for self
+ if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
+ {
+ ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
+ ServerInstance->RehashServer();
+ }
+ }
+ Utils->ReadConfiguration(false);
+ InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
+}
+
+// note: the protocol does not allow direct umode +o except
+// via NICK with 8 params. sending OPERTYPE infers +o modechange
+// locally.
+void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(opertype);
+ Utils->DoOneToMany(user->nick,"OPERTYPE",params);
+ }
+}
+
+void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
+{
+ if (!source)
+ {
+ /* Server-set lines */
+ char data[MAXBUF];
+ snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
+ (unsigned long)duration, reason.c_str());
+ std::deque<std::string> params;
+ params.push_back(data);
+ Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
+ }
+ else
+ {
+ if (IS_LOCAL(source))
+ {
+ char type[8];
+ snprintf(type,8,"%cLINE",linetype);
+ std::string stype = type;
+ if (adding)
+ {
+ char sduration[MAXBUF];
+ snprintf(sduration,MAXBUF,"%ld",duration);
+ std::deque<std::string> params;
+ params.push_back(host);
+ params.push_back(sduration);
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(source->nick,stype,params);
+ }
+ else
+ {
+ std::deque<std::string> params;
+ params.push_back(host);
+ Utils->DoOneToMany(source->nick,stype,params);
+ }
+ }
+ }
+}
+
+void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
+{
+ OnLine(source,hostmask,true,'G',duration,reason);
+}
+
+void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
+{
+ OnLine(source,ipmask,true,'Z',duration,reason);
+}
+
+void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
+{
+ OnLine(source,nickmask,true,'Q',duration,reason);
+}
+
+void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
+{
+ OnLine(source,hostmask,true,'E',duration,reason);
+}
+
+void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
+{
+ OnLine(source,hostmask,false,'G',0,"");
+}
+
+void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
+{
+ OnLine(source,ipmask,false,'Z',0,"");
+}
+
+void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
+{
+ OnLine(source,nickmask,false,'Q',0,"");
+}
+
+void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
+{
+ OnLine(source,hostmask,false,'E',0,"");
+}
+
+void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
+{
+ if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+ {
+ std::deque<std::string> params;
+ std::string command;
+
+ if (target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)dest;
+ params.push_back(u->nick);
+ params.push_back(text);
+ command = "MODE";
+ }
+ else
+ {
+ chanrec* c = (chanrec*)dest;
+ params.push_back(c->name);
+ params.push_back(ConvToStr(c->age));
+ params.push_back(text);
+ command = "FMODE";
+ }
+ Utils->DoOneToMany(user->nick, command, params);
+ }
+}
+
+void ModuleSpanningTree::OnSetAway(userrec* user)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(":"+std::string(user->awaymsg));
+ Utils->DoOneToMany(user->nick,"AWAY",params);
+ }
+}
+
+void ModuleSpanningTree::OnCancelAway(userrec* user)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.clear();
+ Utils->DoOneToMany(user->nick,"AWAY",params);
+ }
+}
+
+void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
+{
+ TreeSocket* s = (TreeSocket*)opaque;
+ if (target)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);
+ }
+ else
+ {
+ chanrec* c = (chanrec*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);
+ }
+ }
+}
+
+void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
+{
+ TreeSocket* s = (TreeSocket*)opaque;
+ if (target)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* u = (userrec*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ chanrec* c = (chanrec*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);
+ }
+ }
+ if (target_type == TYPE_OTHER)
+ {
+ s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);
+ }
+}
+
+void ModuleSpanningTree::OnEvent(Event* event)
+{
+ std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
+ if (event->GetEventID() == "send_metadata")
+ {
+ if (params->size() < 3)
+ return;
+ (*params)[2] = ":" + (*params)[2];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);
+ }
+ else if (event->GetEventID() == "send_topic")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
+ params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
+ }
+ else if (event->GetEventID() == "send_mode")
+ {
+ if (params->size() < 2)
+ return;
+ // Insert the TS value of the object, either userrec or chanrec
+ time_t ourTS = 0;
+ userrec* a = ServerInstance->FindNick((*params)[0]);
+ if (a)
+ {
+ ourTS = a->age;
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
+ return;
+ }
+ else
+ {
+ chanrec* a = ServerInstance->FindChan((*params)[0]);
+ if (a)
+ {
+ ourTS = a->age;
+ params->insert(params->begin() + 1,ConvToStr(ourTS));
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
+ }
+ }
+ }
+ else if (event->GetEventID() == "send_mode_explicit")
+ {
+ if (params->size() < 2)
+ return;
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
+ }
+ else if (event->GetEventID() == "send_opers")
+ {
+ if (params->size() < 1)
+ return;
+ (*params)[0] = ":" + (*params)[0];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_modeset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_snoset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_push")
+ {
+ if (params->size() < 2)
+ return;
+
+ userrec *a = ServerInstance->FindNick((*params)[0]);
+
+ if (!a)
+ return;
+
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
+ }
+}
+
+ModuleSpanningTree::~ModuleSpanningTree()
+{
+ /* This will also free the listeners */
+ delete Utils;
+ if (SyncTimer)
+ ServerInstance->Timers->DelTimer(SyncTimer);
+
+ ServerInstance->Timers->DelTimer(RefreshTimer);
+
+ ServerInstance->DoneWithInterface("InspSocketHook");
+}
+
+Version ModuleSpanningTree::GetVersion()
+{
+ return Version(1,1,0,2,VF_VENDOR,API_VERSION);
+}
+
+void ModuleSpanningTree::Implements(char* List)
+{
+ List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
+ List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
+ List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
+ List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
+ List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
+ List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
+ List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
+}
+
+/* It is IMPORTANT that m_spanningtree is the last module in the chain
+ * 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
+ * the module call queue.
+ */
+Priority ModuleSpanningTree::Prioritize()
+{
+ return PRIORITY_LAST;
+}
+
+MODULE_INIT(ModuleSpanningTree)
+
diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h
index 5bfb73e6a..c184ef076 100644
--- a/src/modules/m_spanningtree/main.h
+++ b/src/modules/m_spanningtree/main.h
@@ -1 +1,198 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __ST_MAIN__ #define __ST_MAIN__ #include "inspircd.h" #include "modules.h" /** If you make a change which breaks the protocol, increment this. * If you completely change the protocol, completely change the number. * * IMPORTANT: If you make changes, document your changes here, without fail: * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions * * Failure to document your protocol changes will result in a painfully * painful death by pain. You have been warned. */ const long ProtocolVersion = 1105; /** Forward declarations */ class cmd_rconnect; class cmd_rsquit; class SpanningTreeUtilities; class TimeSyncTimer; class CacheRefreshTimer; class TreeServer; class Link; /** This is the main class for the spanningtree module */ class ModuleSpanningTree : public Module { int line; int NumServers; unsigned int max_local; unsigned int max_global; cmd_rconnect* command_rconnect; cmd_rsquit* command_rsquit; SpanningTreeUtilities* Utils; public: /** Timer for clock syncs */ TimeSyncTimer *SyncTimer; CacheRefreshTimer *RefreshTimer; /** Constructor */ ModuleSpanningTree(InspIRCd* Me); /** Shows /LINKS */ void ShowLinks(TreeServer* Current, userrec* user, int hops); /** Counts local servers */ int CountLocalServs(); /** Counts local and remote servers */ int CountServs(); /** Handle LINKS command */ void HandleLinks(const char** parameters, int pcnt, userrec* user); /** Handle LUSERS command */ void HandleLusers(const char** parameters, int pcnt, userrec* user); /** Show MAP output to a user (recursive) */ void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers); /** Handle remote MOTD */ int HandleMotd(const char** parameters, int pcnt, userrec* user); /** Handle remote ADMIN */ int HandleAdmin(const char** parameters, int pcnt, userrec* user); /** Handle remote STATS */ int HandleStats(const char** parameters, int pcnt, userrec* user); /** Handle MAP command */ void HandleMap(const char** parameters, int pcnt, userrec* user); /** Handle SQUIT */ int HandleSquit(const char** parameters, int pcnt, userrec* user); /** Handle TIME */ int HandleTime(const char** parameters, int pcnt, userrec* user); /** Handle remote WHOIS */ int HandleRemoteWhois(const char** parameters, int pcnt, userrec* user); /** Handle remote MODULES */ int HandleModules(const char** parameters, int pcnt, userrec* user); /** Ping all local servers */ void DoPingChecks(time_t curtime); /** Connect a server locally */ void ConnectServer(Link* x); /** Check if any servers are due to be autoconnected */ void AutoConnectServers(time_t curtime); /** Handle remote VERSON */ int HandleVersion(const char** parameters, int pcnt, userrec* user); /** Handle CONNECT */ int HandleConnect(const char** parameters, int pcnt, userrec* user); /** Send out time sync to all servers */ void BroadcastTimeSync(); /** 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); /** ** *** MODULE EVENTS *** **/ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line); virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line); virtual void OnGetServerDescription(const std::string &servername,std::string &description); virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel); virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic); virtual void OnWallops(userrec* user, const std::string &text); virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); virtual void OnBackgroundTimer(time_t curtime); virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent); virtual void OnChangeHost(userrec* user, const std::string &newhost); virtual void OnChangeName(userrec* user, const std::string &gecos); virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent); virtual void OnUserConnect(userrec* user); virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message); virtual void OnUserPostNick(userrec* user, const std::string &oldnick); virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent); virtual void OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason); virtual void OnRehash(userrec* user, const std::string &parameter); virtual void OnOper(userrec* user, const std::string &opertype); void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason); virtual void OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); virtual void OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask); virtual void OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask); virtual void OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); virtual void OnDelGLine(userrec* source, const std::string &hostmask); virtual void OnDelZLine(userrec* source, const std::string &ipmask); virtual void OnDelQLine(userrec* source, const std::string &nickmask); virtual void OnDelELine(userrec* source, const std::string &hostmask); virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text); virtual int OnStats(char statschar, userrec* user, string_list &results); virtual void OnSetAway(userrec* user); virtual void OnCancelAway(userrec* user); virtual void ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline); virtual void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata); virtual void OnEvent(Event* event); virtual ~ModuleSpanningTree(); virtual Version GetVersion(); void Implements(char* List); Priority Prioritize(); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __ST_MAIN__
+#define __ST_MAIN__
+
+#include "inspircd.h"
+#include "modules.h"
+
+/** If you make a change which breaks the protocol, increment this.
+ * If you completely change the protocol, completely change the number.
+ *
+ * IMPORTANT: If you make changes, document your changes here, without fail:
+ * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions
+ *
+ * Failure to document your protocol changes will result in a painfully
+ * painful death by pain. You have been warned.
+ */
+const long ProtocolVersion = 1105;
+
+/** Forward declarations
+ */
+class cmd_rconnect;
+class cmd_rsquit;
+class SpanningTreeUtilities;
+class TimeSyncTimer;
+class CacheRefreshTimer;
+class TreeServer;
+class Link;
+
+/** This is the main class for the spanningtree module
+ */
+class ModuleSpanningTree : public Module
+{
+ int line;
+ int NumServers;
+ unsigned int max_local;
+ unsigned int max_global;
+ cmd_rconnect* command_rconnect;
+ cmd_rsquit* command_rsquit;
+ SpanningTreeUtilities* Utils;
+
+ public:
+ /** Timer for clock syncs
+ */
+ TimeSyncTimer *SyncTimer;
+
+ CacheRefreshTimer *RefreshTimer;
+
+ /** Constructor
+ */
+ ModuleSpanningTree(InspIRCd* Me);
+
+ /** Shows /LINKS
+ */
+ void ShowLinks(TreeServer* Current, userrec* user, int hops);
+
+ /** Counts local servers
+ */
+ int CountLocalServs();
+
+ /** Counts local and remote servers
+ */
+ int CountServs();
+
+ /** Handle LINKS command
+ */
+ void HandleLinks(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle LUSERS command
+ */
+ void HandleLusers(const char** parameters, int pcnt, userrec* user);
+
+ /** Show MAP output to a user (recursive)
+ */
+ void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers);
+
+ /** Handle remote MOTD
+ */
+ int HandleMotd(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle remote ADMIN
+ */
+ int HandleAdmin(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle remote STATS
+ */
+ int HandleStats(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle MAP command
+ */
+ void HandleMap(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle SQUIT
+ */
+ int HandleSquit(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle TIME
+ */
+ int HandleTime(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle remote WHOIS
+ */
+ int HandleRemoteWhois(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle remote MODULES
+ */
+ int HandleModules(const char** parameters, int pcnt, userrec* user);
+
+ /** Ping all local servers
+ */
+ void DoPingChecks(time_t curtime);
+
+ /** Connect a server locally
+ */
+ void ConnectServer(Link* x);
+
+ /** Check if any servers are due to be autoconnected
+ */
+ void AutoConnectServers(time_t curtime);
+
+ /** Handle remote VERSON
+ */
+ int HandleVersion(const char** parameters, int pcnt, userrec* user);
+
+ /** Handle CONNECT
+ */
+ int HandleConnect(const char** parameters, int pcnt, userrec* user);
+
+ /** Send out time sync to all servers
+ */
+ void BroadcastTimeSync();
+
+ /** 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);
+
+ /**
+ ** *** MODULE EVENTS ***
+ **/
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line);
+ virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line);
+ virtual void OnGetServerDescription(const std::string &servername,std::string &description);
+ virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel);
+ virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic);
+ virtual void OnWallops(userrec* user, const std::string &text);
+ virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
+ virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
+ virtual void OnBackgroundTimer(time_t curtime);
+ virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent);
+ virtual void OnChangeHost(userrec* user, const std::string &newhost);
+ virtual void OnChangeName(userrec* user, const std::string &gecos);
+ virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent);
+ virtual void OnUserConnect(userrec* user);
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message);
+ virtual void OnUserPostNick(userrec* user, const std::string &oldnick);
+ virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent);
+ virtual void OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason);
+ virtual void OnRehash(userrec* user, const std::string &parameter);
+ virtual void OnOper(userrec* user, const std::string &opertype);
+ void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
+ virtual void OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
+ virtual void OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask);
+ virtual void OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask);
+ virtual void OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
+ virtual void OnDelGLine(userrec* source, const std::string &hostmask);
+ virtual void OnDelZLine(userrec* source, const std::string &ipmask);
+ virtual void OnDelQLine(userrec* source, const std::string &nickmask);
+ virtual void OnDelELine(userrec* source, const std::string &hostmask);
+ virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text);
+ virtual int OnStats(char statschar, userrec* user, string_list &results);
+ virtual void OnSetAway(userrec* user);
+ virtual void OnCancelAway(userrec* user);
+ virtual void ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline);
+ virtual void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata);
+ virtual void OnEvent(Event* event);
+ virtual ~ModuleSpanningTree();
+ virtual Version GetVersion();
+ void Implements(char* List);
+ Priority Prioritize();
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/rconnect.cpp b/src/modules/m_spanningtree/rconnect.cpp
index 88b1fde8b..5500ccdc0 100644
--- a/src/modules/m_spanningtree/rconnect.cpp
+++ b/src/modules/m_spanningtree/rconnect.cpp
@@ -1 +1,67 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/timesynctimer.h" #include "m_spanningtree/resolvers.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/rconnect.h" /* $ModDep: m_spanningtree/timesynctimer.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 m_spanningtree/rconnect.h */ cmd_rconnect::cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util) { this->source = "m_spanningtree.so"; syntax = "<remote-server-mask> <target-server-mask>"; } CmdResult cmd_rconnect::Handle (const char** parameters, int pcnt, userrec *user) { if (IS_LOCAL(user)) { if (!Utils->FindServerMask(parameters[0])) { user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]); return CMD_FAILURE; } user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]); } /* Is this aimed at our server? */ if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0])) { /* Yes, initiate the given connect */ ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]); const char* para[1]; para[0] = parameters[1]; std::string original_command = std::string("CONNECT ") + parameters[1]; Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command); } return CMD_SUCCESS; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/timesynctimer.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/rconnect.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 m_spanningtree/rconnect.h */
+
+cmd_rconnect::cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util)
+{
+ this->source = "m_spanningtree.so";
+ syntax = "<remote-server-mask> <target-server-mask>";
+}
+
+CmdResult cmd_rconnect::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (IS_LOCAL(user))
+ {
+ if (!Utils->FindServerMask(parameters[0]))
+ {
+ user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]);
+ }
+
+ /* Is this aimed at our server? */
+ if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
+ {
+ /* Yes, initiate the given connect */
+ ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]);
+ const char* para[1];
+ para[0] = parameters[1];
+ std::string original_command = std::string("CONNECT ") + parameters[1];
+ Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command);
+ }
+ return CMD_SUCCESS;
+}
+
diff --git a/src/modules/m_spanningtree/rconnect.h b/src/modules/m_spanningtree/rconnect.h
index fca96f4a8..77e271949 100644
--- a/src/modules/m_spanningtree/rconnect.h
+++ b/src/modules/m_spanningtree/rconnect.h
@@ -1 +1,28 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __RCONNECT_H__ #define __RCONNECT_H__ /** Handle /RCONNECT */ class cmd_rconnect : public command_t { Module* Creator; /* Creator */ SpanningTreeUtilities* Utils; /* Utility class */ public: cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util); CmdResult Handle (const char** parameters, int pcnt, userrec *user); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __RCONNECT_H__
+#define __RCONNECT_H__
+
+/** Handle /RCONNECT
+ */
+class cmd_rconnect : public command_t
+{
+ Module* Creator; /* Creator */
+ SpanningTreeUtilities* Utils; /* Utility class */
+ public:
+ cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util);
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user);
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/resolvers.cpp b/src/modules/m_spanningtree/resolvers.cpp
index 80971c699..0d94da99f 100644
--- a/src/modules/m_spanningtree/resolvers.cpp
+++ b/src/modules/m_spanningtree/resolvers.cpp
@@ -1 +1,88 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/resolvers.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" /* $ModDep: m_spanningtree/timesynctimer.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 */ /** This class is used to resolve server hostnames during /connect and autoconnect. * As of 1.1, the resolver system is seperated out from InspSocket, 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(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt) : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(Util), query(qt), host(hostname), mine(me) { /* Nothing in here, folks */ } void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) { /* Initiate the connection, now that we have an IP to use. * Passing a hostname directly to InspSocket causes it to * just bail and set its FD to -1. */ TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str()); if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */ { if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) == Utils->hooks.end())) return; TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(), MyLink.Bind, MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]); if (newsocket->GetFd() > -1) { /* We're all OK */ } else { /* Something barfed, show the opers */ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno)); delete newsocket; Utils->DoFailOver(&MyLink); } } } void ServernameResolver::OnError(ResolverError e, const std::string &errormessage) { /* Ooops! */ if (query == DNS_QUERY_AAAA) { bool cached; ServernameResolver* snr = new ServernameResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A); ServerInstance->AddResolver(snr, cached); return; } ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str()); Utils->DoFailOver(&MyLink); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 */
+
+/** This class is used to resolve server hostnames during /connect and autoconnect.
+ * As of 1.1, the resolver system is seperated out from InspSocket, 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(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt) : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(Util), query(qt), host(hostname), mine(me)
+{
+ /* Nothing in here, folks */
+}
+
+void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+{
+ /* Initiate the connection, now that we have an IP to use.
+ * Passing a hostname directly to InspSocket causes it to
+ * just bail and set its FD to -1.
+ */
+ TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str());
+ if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
+ {
+
+ if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) == Utils->hooks.end()))
+ return;
+
+ TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(),
+ MyLink.Bind, MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]);
+ if (newsocket->GetFd() > -1)
+ {
+ /* We're all OK */
+ }
+ else
+ {
+ /* Something barfed, show the opers */
+ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
+ delete newsocket;
+ Utils->DoFailOver(&MyLink);
+ }
+ }
+}
+
+void ServernameResolver::OnError(ResolverError e, const std::string &errormessage)
+{
+ /* Ooops! */
+ if (query == DNS_QUERY_AAAA)
+ {
+ bool cached;
+ ServernameResolver* snr = new ServernameResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A);
+ ServerInstance->AddResolver(snr, cached);
+ return;
+ }
+ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
+ Utils->DoFailOver(&MyLink);
+}
+
diff --git a/src/modules/m_spanningtree/resolvers.h b/src/modules/m_spanningtree/resolvers.h
index 0ba9d6bd6..06fd05bad 100644
--- a/src/modules/m_spanningtree/resolvers.h
+++ b/src/modules/m_spanningtree/resolvers.h
@@ -1 +1,90 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __RESOLVERS__H__ #define __RESOLVERS__H__ #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "inspircd.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/link.h" /** Handle resolving of server IPs for the cache */ class SecurityIPResolver : public Resolver { private: Link MyLink; SpanningTreeUtilities* Utils; Module* mine; std::string host; QueryType query; public: SecurityIPResolver(Module* me, SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt) : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt) { } void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) { Utils->ValidIPs.push_back(result); } void OnError(ResolverError e, const std::string &errormessage) { if (query == DNS_QUERY_AAAA) { bool cached; SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A); ServerInstance->AddResolver(res, cached); return; } ServerInstance->Log(DEFAULT,"Could not resolve IP associated with Link '%s': %s",MyLink.Name.c_str(),errormessage.c_str()); } }; /** This class is used to resolve server hostnames during /connect and autoconnect. * As of 1.1, the resolver system is seperated out from InspSocket, 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. */ class ServernameResolver : public Resolver { private: /** A copy of the Link tag info for what we're connecting to. * We take a copy, rather than using a pointer, just in case the * admin takes the tag away and rehashes while the domain is resolving. */ Link MyLink; SpanningTreeUtilities* Utils; QueryType query; std::string host; Module* mine; public: ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, 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); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __RESOLVERS__H__
+#define __RESOLVERS__H__
+
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "inspircd.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/link.h"
+
+/** Handle resolving of server IPs for the cache
+ */
+class SecurityIPResolver : public Resolver
+{
+ private:
+ Link MyLink;
+ SpanningTreeUtilities* Utils;
+ Module* mine;
+ std::string host;
+ QueryType query;
+ public:
+ SecurityIPResolver(Module* me, SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt)
+ : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt)
+ {
+ }
+
+ void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ {
+ Utils->ValidIPs.push_back(result);
+ }
+
+ void OnError(ResolverError e, const std::string &errormessage)
+ {
+ if (query == DNS_QUERY_AAAA)
+ {
+ bool cached;
+ SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A);
+ ServerInstance->AddResolver(res, cached);
+ return;
+ }
+ ServerInstance->Log(DEFAULT,"Could not resolve IP associated with Link '%s': %s",MyLink.Name.c_str(),errormessage.c_str());
+ }
+};
+
+/** This class is used to resolve server hostnames during /connect and autoconnect.
+ * As of 1.1, the resolver system is seperated out from InspSocket, 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.
+ */
+class ServernameResolver : public Resolver
+{
+ private:
+ /** A copy of the Link tag info for what we're connecting to.
+ * We take a copy, rather than using a pointer, just in case the
+ * admin takes the tag away and rehashes while the domain is resolving.
+ */
+ Link MyLink;
+ SpanningTreeUtilities* Utils;
+ QueryType query;
+ std::string host;
+ Module* mine;
+ public:
+ ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, 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);
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/rsquit.cpp b/src/modules/m_spanningtree/rsquit.cpp
index 7bb6abfc1..5f3d33fc0 100644
--- a/src/modules/m_spanningtree/rsquit.cpp
+++ b/src/modules/m_spanningtree/rsquit.cpp
@@ -1 +1,123 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/timesynctimer.h" #include "m_spanningtree/resolvers.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/rsquit.h" /* $ModDep: m_spanningtree/timesynctimer.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 m_spanningtree/rsquit.h */ cmd_rsquit::cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RSQUIT", 'o', 1), Creator(Callback), Utils(Util) { this->source = "m_spanningtree.so"; syntax = "<remote-server-mask> [target-server-mask]"; } CmdResult cmd_rsquit::Handle (const char** parameters, int pcnt, userrec *user) { if (IS_LOCAL(user)) { if (!Utils->FindServerMask(parameters[0])) { user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]); return CMD_FAILURE; } if (pcnt > 1) user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit to \002%s\002 to squit server \002%s\002.",user->nick,parameters[0],parameters[1]); else user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit for server \002%s\002.",user->nick,parameters[0]); } TreeServer* s = (pcnt > 1) ? Utils->FindServerMask(parameters[1]) : Utils->FindServerMask(parameters[0]); if (pcnt > 1) { if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0])) { if (s) { if (s == Utils->TreeRoot) { NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[1])+" matches local server name)"); return CMD_FAILURE; } TreeSocket* sock = s->GetSocket(); if (!sock) { NoticeUser(user, "*** RSQUIT: Server \002"+ConvToStr(parameters[1])+"\002 isn't connected to \002"+ConvToStr(parameters[0])+"\002."); return CMD_FAILURE; } ServerInstance->SNO->WriteToSnoMask('l',"Remote SQUIT from %s matching \002%s\002, squitting server \002%s\002",user->nick,parameters[0],parameters[1]); const char* para[1]; para[0] = parameters[1]; std::string original_command = std::string("SQUIT ") + parameters[1]; Creator->OnPreCommand("SQUIT", para, 1, user, true, original_command); return CMD_LOCALONLY; } } } else { if (s) { if (s == Utils->TreeRoot) { NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[0])+" matches local server name)"); return CMD_FAILURE; } TreeSocket* sock = s->GetSocket(); if (sock) { ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick); sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost()); ServerInstance->SE->DelFd(sock); sock->Close(); return CMD_LOCALONLY; } } } return CMD_SUCCESS; } void cmd_rsquit::NoticeUser(userrec* user, const std::string &msg) { if (IS_LOCAL(user)) { user->WriteServ("NOTICE %s :%s",user->nick,msg.c_str()); } else { std::deque<std::string> params; params.push_back(user->nick); params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg); Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", params, user->server); } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/timesynctimer.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/rsquit.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 m_spanningtree/rsquit.h */
+
+cmd_rsquit::cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RSQUIT", 'o', 1), Creator(Callback), Utils(Util)
+{
+ this->source = "m_spanningtree.so";
+ syntax = "<remote-server-mask> [target-server-mask]";
+}
+
+CmdResult cmd_rsquit::Handle (const char** parameters, int pcnt, userrec *user)
+{
+ if (IS_LOCAL(user))
+ {
+ if (!Utils->FindServerMask(parameters[0]))
+ {
+ user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+ if (pcnt > 1)
+ user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit to \002%s\002 to squit server \002%s\002.",user->nick,parameters[0],parameters[1]);
+ else
+ user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit for server \002%s\002.",user->nick,parameters[0]);
+ }
+
+ TreeServer* s = (pcnt > 1) ? Utils->FindServerMask(parameters[1]) : Utils->FindServerMask(parameters[0]);
+
+ if (pcnt > 1)
+ {
+ if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
+ {
+ if (s)
+ {
+ if (s == Utils->TreeRoot)
+ {
+ NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[1])+" matches local server name)");
+ return CMD_FAILURE;
+ }
+ TreeSocket* sock = s->GetSocket();
+ if (!sock)
+ {
+ NoticeUser(user, "*** RSQUIT: Server \002"+ConvToStr(parameters[1])+"\002 isn't connected to \002"+ConvToStr(parameters[0])+"\002.");
+ return CMD_FAILURE;
+ }
+ ServerInstance->SNO->WriteToSnoMask('l',"Remote SQUIT from %s matching \002%s\002, squitting server \002%s\002",user->nick,parameters[0],parameters[1]);
+ const char* para[1];
+ para[0] = parameters[1];
+ std::string original_command = std::string("SQUIT ") + parameters[1];
+ Creator->OnPreCommand("SQUIT", para, 1, user, true, original_command);
+ return CMD_LOCALONLY;
+ }
+ }
+ }
+ else
+ {
+ if (s)
+ {
+ if (s == Utils->TreeRoot)
+ {
+ NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[0])+" matches local server name)");
+ return CMD_FAILURE;
+ }
+ TreeSocket* sock = s->GetSocket();
+ if (sock)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
+ sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
+ ServerInstance->SE->DelFd(sock);
+ sock->Close();
+ return CMD_LOCALONLY;
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+void cmd_rsquit::NoticeUser(userrec* user, const std::string &msg)
+{
+ if (IS_LOCAL(user))
+ {
+ user->WriteServ("NOTICE %s :%s",user->nick,msg.c_str());
+ }
+ else
+ {
+ std::deque<std::string> params;
+ params.push_back(user->nick);
+ params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg);
+ Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", params, user->server);
+ }
+}
diff --git a/src/modules/m_spanningtree/rsquit.h b/src/modules/m_spanningtree/rsquit.h
index ed9eb83d4..81e9bc2b7 100644
--- a/src/modules/m_spanningtree/rsquit.h
+++ b/src/modules/m_spanningtree/rsquit.h
@@ -1 +1,29 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __RSQUIT_H__ #define __RSQUIT_H__ /** Handle /RCONNECT */ class cmd_rsquit : public command_t { Module* Creator; /* Creator */ SpanningTreeUtilities* Utils; /* Utility class */ public: cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util); CmdResult Handle (const char** parameters, int pcnt, userrec *user); void NoticeUser(userrec* user, const std::string &msg); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __RSQUIT_H__
+#define __RSQUIT_H__
+
+/** Handle /RCONNECT
+ */
+class cmd_rsquit : public command_t
+{
+ Module* Creator; /* Creator */
+ SpanningTreeUtilities* Utils; /* Utility class */
+ public:
+ cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util);
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user);
+ void NoticeUser(userrec* user, const std::string &msg);
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/timesynctimer.cpp b/src/modules/m_spanningtree/timesynctimer.cpp
index 8ecb84a4b..af615e91e 100644
--- a/src/modules/m_spanningtree/timesynctimer.cpp
+++ b/src/modules/m_spanningtree/timesynctimer.cpp
@@ -1 +1,52 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/timesynctimer.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" /* $ModDep: m_spanningtree/timesynctimer.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 */ TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(600, Inst->Time(), true), Instance(Inst), Module(Mod) { } void TimeSyncTimer::Tick(time_t TIME) { Module->BroadcastTimeSync(); } CacheRefreshTimer::CacheRefreshTimer(InspIRCd *Inst, SpanningTreeUtilities *Util) : InspTimer(3600, Inst->Time(), true), Instance(Inst), Utils(Util) { } void CacheRefreshTimer::Tick(time_t TIME) { Utils->RefreshIPCache(); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/timesynctimer.h"
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 */
+
+TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(600, Inst->Time(), true), Instance(Inst), Module(Mod)
+{
+}
+
+void TimeSyncTimer::Tick(time_t TIME)
+{
+ Module->BroadcastTimeSync();
+}
+
+CacheRefreshTimer::CacheRefreshTimer(InspIRCd *Inst, SpanningTreeUtilities *Util) : InspTimer(3600, Inst->Time(), true), Instance(Inst), Utils(Util)
+{
+}
+
+void CacheRefreshTimer::Tick(time_t TIME)
+{
+ Utils->RefreshIPCache();
+}
+
diff --git a/src/modules/m_spanningtree/timesynctimer.h b/src/modules/m_spanningtree/timesynctimer.h
index dd23ee171..434ee253c 100644
--- a/src/modules/m_spanningtree/timesynctimer.h
+++ b/src/modules/m_spanningtree/timesynctimer.h
@@ -1 +1,47 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __TIMESYNC_H__ #define __TIMESYNC_H__ #include "timer.h" class ModuleSpanningTree; class SpanningTreeUtilities; class InspIRCd; /** Create a timer which recurs every second, we inherit from InspTimer. * InspTimer is only one-shot however, so at the end of each Tick() we simply * insert another of ourselves into the pending queue :) */ class TimeSyncTimer : public InspTimer { private: InspIRCd *Instance; ModuleSpanningTree *Module; public: TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod); virtual void Tick(time_t TIME); }; class CacheRefreshTimer : public InspTimer { private: InspIRCd *Instance; SpanningTreeUtilities *Utils; public: CacheRefreshTimer(InspIRCd *Instance, SpanningTreeUtilities* Util); virtual void Tick(time_t TIME); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __TIMESYNC_H__
+#define __TIMESYNC_H__
+
+#include "timer.h"
+
+class ModuleSpanningTree;
+class SpanningTreeUtilities;
+class InspIRCd;
+
+/** Create a timer which recurs every second, we inherit from InspTimer.
+ * InspTimer is only one-shot however, so at the end of each Tick() we simply
+ * insert another of ourselves into the pending queue :)
+ */
+class TimeSyncTimer : public InspTimer
+{
+ private:
+ InspIRCd *Instance;
+ ModuleSpanningTree *Module;
+ public:
+ TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod);
+ virtual void Tick(time_t TIME);
+};
+
+class CacheRefreshTimer : public InspTimer
+{
+ private:
+ InspIRCd *Instance;
+ SpanningTreeUtilities *Utils;
+ public:
+ CacheRefreshTimer(InspIRCd *Instance, SpanningTreeUtilities* Util);
+ virtual void Tick(time_t TIME);
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/treeserver.cpp b/src/modules/m_spanningtree/treeserver.cpp
index 670f7e420..b5cac1802 100644
--- a/src/modules/m_spanningtree/treeserver.cpp
+++ b/src/modules/m_spanningtree/treeserver.cpp
@@ -1 +1,325 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" /* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */ TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance) : ServerInstance(Instance), Utils(Util) { Parent = NULL; ServerName.clear(); ServerDesc.clear(); VersionString.clear(); UserCount = OperCount = 0; rtt = LastPing = 0; Hidden = false; VersionString = ServerInstance->GetVersionString(); } /** 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, InspIRCd* Instance, std::string Name, std::string Desc) : ServerInstance(Instance), ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util) { Parent = NULL; VersionString.clear(); UserCount = ServerInstance->UserCount(); OperCount = ServerInstance->OperCount(); VersionString = ServerInstance->GetVersionString(); Route = NULL; Socket = NULL; /* Fix by brain */ rtt = LastPing = 0; Hidden = false; AddHashEntry(); } /** When we create a new server, we call this constructor to initialize it. * This constructor initializes the server's Route and Parent, and sets up * its ping counters so that it will be pinged one minute from now. */ TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide) : ServerInstance(Instance), Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), Hidden(Hide) { VersionString.clear(); UserCount = OperCount = 0; this->SetNextPingTime(time(NULL) + 60); this->SetPingFlag(); rtt = LastPing = 0; /* find the 'route' for this server (e.g. the one directly connected * to the local server, which we can use to reach it) * * In the following example, consider we have just added a TreeServer * class for server G on our network, of which we are server A. * To route traffic to G (marked with a *) we must send the data to * B (marked with a +) so this algorithm initializes the 'Route' * value to point at whichever server traffic must be routed through * to get here. If we were to try this algorithm with server B, * the Route pointer would point at its own object ('this'). * * A * / \ * + B C * / \ \ * D E F * / \ * * G H * * We only run this algorithm when a server is created, as * the routes remain constant while ever the server exists, and * do not need to be re-calculated. */ Route = Above; if (Route == Utils->TreeRoot) { Route = this; } else { while (this->Route->GetParent() != Utils->TreeRoot) { this->Route = Route->GetParent(); } } /* Because recursive code is slow and takes a lot of resources, * we store two representations of the server tree. The first * is a recursive structure where each server references its * children and its parent, which is used for netbursts and * netsplits to dump the whole dataset to the other server, * and the second is used for very fast lookups when routing * messages and is instead a hash_map, where each item can * be referenced by its server name. The AddHashEntry() * call below automatically inserts each TreeServer class * into the hash_map as it is created. There is a similar * maintainance call in the destructor to tidy up deleted * servers. */ this->AddHashEntry(); } int TreeServer::QuitUsers(const std::string &reason) { const char* reason_s = reason.c_str(); std::vector<userrec*> time_to_die; for (user_hash::iterator n = ServerInstance->clientlist->begin(); n != ServerInstance->clientlist->end(); n++) { if (!strcmp(n->second->server, this->ServerName.c_str())) { time_to_die.push_back(n->second); } } for (std::vector<userrec*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++) { userrec* a = (userrec*)*n; if (!IS_LOCAL(a)) { if (ServerInstance->Config->HideSplits) userrec::QuitUser(ServerInstance, a, "*.net *.split", reason_s); else userrec::QuitUser(ServerInstance, a, reason_s); if (this->Utils->quiet_bursts) ServerInstance->GlobalCulls.MakeSilent(a); } } 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(); } std::string TreeServer::GetDesc() { return ServerDesc; } std::string TreeServer::GetVersion() { return VersionString; } void TreeServer::SetNextPingTime(time_t t) { this->NextPing = t; LastPingWasGood = false; } time_t TreeServer::NextPingTime() { return NextPing; } bool TreeServer::AnsweredLastPing() { return LastPingWasGood; } void TreeServer::SetPingFlag() { LastPingWasGood = true; } int TreeServer::GetUserCount() { return UserCount; } void TreeServer::AddUserCount() { UserCount++; } void TreeServer::DelUserCount() { UserCount--; } int TreeServer::GetOperCount() { return OperCount; } TreeSocket* TreeServer::GetSocket() { return Socket; } TreeServer* TreeServer::GetParent() { return Parent; } void TreeServer::SetVersion(const std::string &Version) { VersionString = Version; } unsigned int TreeServer::ChildCount() { return Children.size(); } TreeServer* TreeServer::GetChild(unsigned int n) { if (n < Children.size()) { /* Make sure they cant request * an out-of-range object. After * all we know what these programmer * types are like *grin*. */ return Children[n]; } else { return NULL; } } void TreeServer::AddChild(TreeServer* Child) { Children.push_back(Child); } bool TreeServer::DelChild(TreeServer* Child) { for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++) { if (*a == Child) { Children.erase(a); return true; } } return false; } /** Removes child nodes of this node, and of that node, etc etc. * This is used during netsplits to automatically tidy up the * server tree. It is slow, we don't use it for much else. */ bool TreeServer::Tidy() { bool stillchildren = true; while (stillchildren) { stillchildren = false; for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++) { TreeServer* s = (TreeServer*)*a; s->Tidy(); Children.erase(a); DELETE(s); stillchildren = true; break; } } return true; } TreeServer::~TreeServer() { /* We'd better tidy up after ourselves, eh? */ this->DelHashEntry(); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+
+/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */
+
+TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance) : ServerInstance(Instance), Utils(Util)
+{
+ Parent = NULL;
+ ServerName.clear();
+ ServerDesc.clear();
+ VersionString.clear();
+ UserCount = OperCount = 0;
+ rtt = LastPing = 0;
+ Hidden = false;
+ VersionString = ServerInstance->GetVersionString();
+}
+
+/** 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, InspIRCd* Instance, std::string Name, std::string Desc) : ServerInstance(Instance), ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util)
+{
+ Parent = NULL;
+ VersionString.clear();
+ UserCount = ServerInstance->UserCount();
+ OperCount = ServerInstance->OperCount();
+ VersionString = ServerInstance->GetVersionString();
+ Route = NULL;
+ Socket = NULL; /* Fix by brain */
+ rtt = LastPing = 0;
+ Hidden = false;
+ AddHashEntry();
+}
+
+/** When we create a new server, we call this constructor to initialize it.
+ * This constructor initializes the server's Route and Parent, and sets up
+ * its ping counters so that it will be pinged one minute from now.
+ */
+TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide)
+ : ServerInstance(Instance), Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), Hidden(Hide)
+{
+ VersionString.clear();
+ UserCount = OperCount = 0;
+ this->SetNextPingTime(time(NULL) + 60);
+ this->SetPingFlag();
+ rtt = LastPing = 0;
+ /* find the 'route' for this server (e.g. the one directly connected
+ * to the local server, which we can use to reach it)
+ *
+ * In the following example, consider we have just added a TreeServer
+ * class for server G on our network, of which we are server A.
+ * To route traffic to G (marked with a *) we must send the data to
+ * B (marked with a +) so this algorithm initializes the 'Route'
+ * value to point at whichever server traffic must be routed through
+ * to get here. If we were to try this algorithm with server B,
+ * the Route pointer would point at its own object ('this').
+ *
+ * A
+ * / \
+ * + B C
+ * / \ \
+ * D E F
+ * / \
+ * * G H
+ *
+ * We only run this algorithm when a server is created, as
+ * the routes remain constant while ever the server exists, and
+ * do not need to be re-calculated.
+ */
+
+ Route = Above;
+ if (Route == Utils->TreeRoot)
+ {
+ Route = this;
+ }
+ else
+ {
+ while (this->Route->GetParent() != Utils->TreeRoot)
+ {
+ this->Route = Route->GetParent();
+ }
+ }
+
+ /* Because recursive code is slow and takes a lot of resources,
+ * we store two representations of the server tree. The first
+ * is a recursive structure where each server references its
+ * children and its parent, which is used for netbursts and
+ * netsplits to dump the whole dataset to the other server,
+ * and the second is used for very fast lookups when routing
+ * messages and is instead a hash_map, where each item can
+ * be referenced by its server name. The AddHashEntry()
+ * call below automatically inserts each TreeServer class
+ * into the hash_map as it is created. There is a similar
+ * maintainance call in the destructor to tidy up deleted
+ * servers.
+ */
+
+ this->AddHashEntry();
+}
+
+int TreeServer::QuitUsers(const std::string &reason)
+{
+ const char* reason_s = reason.c_str();
+ std::vector<userrec*> time_to_die;
+ for (user_hash::iterator n = ServerInstance->clientlist->begin(); n != ServerInstance->clientlist->end(); n++)
+ {
+ if (!strcmp(n->second->server, this->ServerName.c_str()))
+ {
+ time_to_die.push_back(n->second);
+ }
+ }
+ for (std::vector<userrec*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++)
+ {
+ userrec* a = (userrec*)*n;
+ if (!IS_LOCAL(a))
+ {
+ if (ServerInstance->Config->HideSplits)
+ userrec::QuitUser(ServerInstance, a, "*.net *.split", reason_s);
+ else
+ userrec::QuitUser(ServerInstance, a, reason_s);
+
+ if (this->Utils->quiet_bursts)
+ ServerInstance->GlobalCulls.MakeSilent(a);
+ }
+ }
+ 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();
+}
+
+std::string TreeServer::GetDesc()
+{
+ return ServerDesc;
+}
+
+std::string TreeServer::GetVersion()
+{
+ return VersionString;
+}
+
+void TreeServer::SetNextPingTime(time_t t)
+{
+ this->NextPing = t;
+ LastPingWasGood = false;
+}
+
+time_t TreeServer::NextPingTime()
+{
+ return NextPing;
+}
+
+bool TreeServer::AnsweredLastPing()
+{
+ return LastPingWasGood;
+}
+
+void TreeServer::SetPingFlag()
+{
+ LastPingWasGood = true;
+}
+
+int TreeServer::GetUserCount()
+{
+ return UserCount;
+}
+
+void TreeServer::AddUserCount()
+{
+ UserCount++;
+}
+
+void TreeServer::DelUserCount()
+{
+ UserCount--;
+}
+
+int TreeServer::GetOperCount()
+{
+ return OperCount;
+}
+
+TreeSocket* TreeServer::GetSocket()
+{
+ return Socket;
+}
+
+TreeServer* TreeServer::GetParent()
+{
+ return Parent;
+}
+
+void TreeServer::SetVersion(const std::string &Version)
+{
+ VersionString = Version;
+}
+
+unsigned int TreeServer::ChildCount()
+{
+ return Children.size();
+}
+
+TreeServer* TreeServer::GetChild(unsigned int n)
+{
+ if (n < Children.size())
+ {
+ /* Make sure they cant request
+ * an out-of-range object. After
+ * all we know what these programmer
+ * types are like *grin*.
+ */
+ return Children[n];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void TreeServer::AddChild(TreeServer* Child)
+{
+ Children.push_back(Child);
+}
+
+bool TreeServer::DelChild(TreeServer* Child)
+{
+ for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
+ {
+ if (*a == Child)
+ {
+ Children.erase(a);
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Removes child nodes of this node, and of that node, etc etc.
+ * This is used during netsplits to automatically tidy up the
+ * server tree. It is slow, we don't use it for much else.
+ */
+bool TreeServer::Tidy()
+{
+ bool stillchildren = true;
+ while (stillchildren)
+ {
+ stillchildren = false;
+ for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
+ {
+ TreeServer* s = (TreeServer*)*a;
+ s->Tidy();
+ Children.erase(a);
+ DELETE(s);
+ stillchildren = true;
+ break;
+ }
+ }
+ return true;
+}
+
+TreeServer::~TreeServer()
+{
+ /* We'd better tidy up after ourselves, eh? */
+ this->DelHashEntry();
+}
+
+
diff --git a/src/modules/m_spanningtree/treeserver.h b/src/modules/m_spanningtree/treeserver.h
index e942c1acc..514d6bc07 100644
--- a/src/modules/m_spanningtree/treeserver.h
+++ b/src/modules/m_spanningtree/treeserver.h
@@ -1 +1,186 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __TREESERVER_H__ #define __TREESERVER_H__ /** Each server in the tree is represented by one class of * type TreeServer. A locally connected TreeServer can * have a class of type TreeSocket associated with it, for * remote servers, the TreeSocket entry will be NULL. * Each server also maintains a pointer to its parent * (NULL if this server is ours, at the top of the tree) * and a pointer to its "Route" (see the comments in the * constructors below), and also a dynamic list of pointers * to its children which can be iterated recursively * if required. Creating or deleting objects of type i* TreeServer automatically maintains the hash_map of * TreeServer items, deleting and inserting them as they * are created and destroyed. */ class TreeServer : public classbase { InspIRCd* ServerInstance; /* Creator */ 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 */ int UserCount; /* Not used in this version */ int OperCount; /* Not used in this version */ 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 */ public: bool Warned; /* True if we've warned opers about high latency on this server */ /** We don't use this constructor. Its a dummy, and won't cause any insertion * of the TreeServer into the hash_map. See below for the two we DO use. */ TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance); /** 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, InspIRCd* Instance, std::string Name, std::string Desc); /** 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, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide); int QuitUsers(const std::string &reason); /** This method is used to add the structure to the * hash_map for linear searches. It is only called * by the constructors. */ void AddHashEntry(); /** 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 DelHashEntry(); /** 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(); /** Get server description (GECOS) */ std::string GetDesc(); /** Get server version string */ std::string GetVersion(); /** Set time we are next due to ping this server */ void SetNextPingTime(time_t t); /** Get the time we are next due to ping this server */ time_t NextPingTime(); /** Time of last ping used to calculate this->rtt below */ time_t LastPing; /** Round trip time of last ping */ time_t rtt; /** True if this server is hidden */ bool Hidden; /** True if the server answered their last ping */ bool AnsweredLastPing(); /** Set the server as responding to its last ping */ void SetPingFlag(); /** Get the number of users on this server for MAP */ int GetUserCount(); /** Increment the user counter */ void AddUserCount(); /** Decrement the user counter */ void DelUserCount(); /** Get the oper count for this server */ int GetOperCount(); /** Get the TreeSocket pointer for local servers. * For remote servers, this returns NULL. */ TreeSocket* GetSocket(); /** Get the parent server. * For the root node, this returns NULL. */ TreeServer* GetParent(); /** Set the server version string */ void SetVersion(const std::string &Version); /** Return number of child servers */ unsigned int ChildCount(); /** Return a child server indexed 0..n */ TreeServer* GetChild(unsigned int n); /** Add a child server */ void AddChild(TreeServer* Child); /** Delete a child server, return false if it didn't exist. */ bool DelChild(TreeServer* Child); /** 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. */ bool Tidy(); /** Destructor */ ~TreeServer(); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __TREESERVER_H__
+#define __TREESERVER_H__
+
+/** Each server in the tree is represented by one class of
+ * type TreeServer. A locally connected TreeServer can
+ * have a class of type TreeSocket associated with it, for
+ * remote servers, the TreeSocket entry will be NULL.
+ * Each server also maintains a pointer to its parent
+ * (NULL if this server is ours, at the top of the tree)
+ * and a pointer to its "Route" (see the comments in the
+ * constructors below), and also a dynamic list of pointers
+ * to its children which can be iterated recursively
+ * if required. Creating or deleting objects of type
+ i* TreeServer automatically maintains the hash_map of
+ * TreeServer items, deleting and inserting them as they
+ * are created and destroyed.
+ */
+class TreeServer : public classbase
+{
+ InspIRCd* ServerInstance; /* Creator */
+ 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 */
+ int UserCount; /* Not used in this version */
+ int OperCount; /* Not used in this version */
+ 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 */
+
+ public:
+
+ bool Warned; /* True if we've warned opers about high latency on this server */
+
+ /** We don't use this constructor. Its a dummy, and won't cause any insertion
+ * of the TreeServer into the hash_map. See below for the two we DO use.
+ */
+ TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance);
+
+ /** 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, InspIRCd* Instance, std::string Name, std::string Desc);
+
+ /** 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, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide);
+
+ int QuitUsers(const std::string &reason);
+
+ /** This method is used to add the structure to the
+ * hash_map for linear searches. It is only called
+ * by the constructors.
+ */
+ void AddHashEntry();
+
+ /** 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 DelHashEntry();
+
+ /** 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();
+
+ /** Get server description (GECOS)
+ */
+ std::string GetDesc();
+
+ /** Get server version string
+ */
+ std::string GetVersion();
+
+ /** Set time we are next due to ping this server
+ */
+ void SetNextPingTime(time_t t);
+
+ /** Get the time we are next due to ping this server
+ */
+ time_t NextPingTime();
+
+ /** Time of last ping used to calculate this->rtt below
+ */
+ time_t LastPing;
+
+ /** Round trip time of last ping
+ */
+ time_t rtt;
+
+ /** True if this server is hidden
+ */
+ bool Hidden;
+
+ /** True if the server answered their last ping
+ */
+ bool AnsweredLastPing();
+
+ /** Set the server as responding to its last ping
+ */
+ void SetPingFlag();
+
+ /** Get the number of users on this server for MAP
+ */
+ int GetUserCount();
+
+ /** Increment the user counter
+ */
+ void AddUserCount();
+
+ /** Decrement the user counter
+ */
+ void DelUserCount();
+
+ /** Get the oper count for this server
+ */
+ int GetOperCount();
+
+ /** Get the TreeSocket pointer for local servers.
+ * For remote servers, this returns NULL.
+ */
+ TreeSocket* GetSocket();
+
+ /** Get the parent server.
+ * For the root node, this returns NULL.
+ */
+ TreeServer* GetParent();
+
+ /** Set the server version string
+ */
+ void SetVersion(const std::string &Version);
+
+ /** Return number of child servers
+ */
+ unsigned int ChildCount();
+
+ /** Return a child server indexed 0..n
+ */
+ TreeServer* GetChild(unsigned int n);
+
+ /** Add a child server
+ */
+ void AddChild(TreeServer* Child);
+
+ /** Delete a child server, return false if it didn't exist.
+ */
+ bool DelChild(TreeServer* Child);
+
+ /** 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.
+ */
+ bool Tidy();
+
+ /** Destructor
+ */
+ ~TreeServer();
+
+};
+
+#endif
diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h
index bd99c1480..fae22638d 100644
--- a/src/modules/m_spanningtree/treesocket.h
+++ b/src/modules/m_spanningtree/treesocket.h
@@ -1 +1,413 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __TREESOCKET_H__ #define __TREESOCKET_H__ #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "inspircd.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_spanningtree/utils.h" /* * The server list in InspIRCd is maintained as two structures * which hold the data in different ways. Most of the time, we * want to very quicky obtain three pieces of information: * * (1) The information on a server * (2) The information on the server we must send data through * to actually REACH the server we're after * (3) Potentially, the child/parent objects of this server * * The InspIRCd spanning protocol provides easy access to these * by storing the data firstly in a recursive structure, where * each item references its parent item, and a dynamic list * of child items, and another structure which stores the items * hashed, linearly. This means that if we want to find a server * by name quickly, we can look it up in the hash, avoiding * any O(n) lookups. If however, during a split or sync, we want * to apply an operation to a server, and any of its child objects * we can resort to recursion to walk the tree structure. * Any socket can have one of five states at any one time. * The LISTENER state indicates a socket which is listening * for connections. It cannot receive data itself, only incoming * sockets. * The CONNECTING state indicates an outbound socket which is * waiting to be writeable. * The WAIT_AUTH_1 state indicates the socket is outbound and * has successfully connected, but has not yet sent and received * SERVER strings. * The WAIT_AUTH_2 state indicates that the socket is inbound * (allocated by a LISTENER) but has not yet sent and received * SERVER strings. * The CONNECTED state represents a fully authorized, fully * connected server. */ enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED }; /** Every SERVER connection inbound or outbound is represented by * an object of type TreeSocket. * TreeSockets, being inherited from InspSocket, can be tied into * the core socket engine, and we cn therefore receive activity events * for them, just like activex objects on speed. (yes really, that * is a technical term!) Each of these which relates to a locally * connected server is assocated with it, by hooking it onto a * TreeSocket class using its constructor. In this way, we can * maintain a list of servers, some of which are directly connected, * some of which are not. */ class TreeSocket : public InspSocket { SpanningTreeUtilities* Utils; /* Utility class */ std::string myhost; /* Canonical hostname */ std::string in_buffer; /* Input buffer */ ServerState LinkState; /* Link state */ std::string InboundServerName; /* Server name sent to us by other side */ std::string InboundDescription; /* Server description (GECOS) sent to us by the other side */ int num_lost_users; /* Users lost in split */ int num_lost_servers; /* Servers lost in split */ time_t NextPing; /* Time when we are due to ping this server */ bool LastPingWasGood; /* Responded to last ping we sent? */ bool bursting; /* True if not finished bursting yet */ unsigned int keylength; /* Is this still used? */ std::string ModuleList; /* Module list of other server from CAPAB */ std::map<std::string,std::string> CapKeys; /* CAPAB keys from other server */ Module* Hook; /* I/O hooking module that we're attached to for this socket */ std::string ourchallenge; /* Challenge sent for challenge/response */ std::string theirchallenge; /* Challenge recv for challenge/response */ std::string OutboundPass; /* Outbound password */ bool sentcapab; /* Have sent CAPAB already */ public: /** Because most of the I/O gubbins are encapsulated within * InspSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. */ TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod = NULL); /** Because most of the I/O gubbins are encapsulated within * InspSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. */ TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod = NULL); /** 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, InspIRCd* SI, int newfd, char* ip, Module* HookMod = NULL); /** Get link state */ ServerState GetLinkState(); /** Get challenge set in our CAPAB for challenge/response */ const std::string& GetOurChallenge(); /** Get challenge set in our CAPAB for challenge/response */ void SetOurChallenge(const std::string &c); /** Get challenge set in their CAPAB for challenge/response */ const std::string& GetTheirChallenge(); /** Get challenge set in their CAPAB for challenge/response */ void SetTheirChallenge(const std::string &c); /** Compare two passwords based on authentication scheme */ bool ComparePass(const std::string &ours, const std::string &theirs); /** Return the module which we are hooking to for I/O encapsulation */ Module* GetHook(); /** Destructor */ ~TreeSocket(); /** Generate random string used for challenge-response auth */ std::string RandString(unsigned int length); /** Construct a password, optionally hashed with the other side's * challenge string */ std::string MakePass(const std::string &password, const std::string &challenge); /** When an outbound connection finishes connecting, we receive * this event, and must send our SERVER string to 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. */ virtual bool OnConnected(); /** Handle socket error event */ virtual void OnError(InspSocketError e); /** Sends an error to the remote server, and displays it locally to show * that it was sent. */ void SendError(const std::string &errormessage); /** Handle socket disconnect event */ virtual int OnDisconnect(); /** 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); /** Returns my capabilities as a string */ std::string MyCapabilities(); /** Send my capabilities to the remote side */ void SendCapabilities(); /* Check a comma seperated list for an item */ bool HasItem(const std::string &list, const std::string &item); /* Isolate and return the elements that are different between two comma seperated lists */ std::string ListDifference(const std::string &one, const std::string &two); bool Capab(const std::deque<std::string> &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); /** 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); /** FMODE command - server mode with timestamp checks */ bool ForceMode(const std::string &source, std::deque<std::string> &params); /** FTOPIC command */ bool ForceTopic(const std::string &source, std::deque<std::string> &params); /** FJOIN, similar to TS6 SJOIN, but not quite. */ bool ForceJoin(const std::string &source, std::deque<std::string> &params); /** NICK command */ bool IntroduceClient(const std::string &source, std::deque<std::string> &params); /** 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. */ void SendFJoins(TreeServer* Current, chanrec* c); /** Send G, Q, Z and E lines */ void SendXLines(TreeServer* Current); /** Send channel modes and topics */ void SendChannelModes(TreeServer* Current); /** send all users and their oper state/modes */ void SendUsers(TreeServer* Current); /** 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 * users require their servers to exist, and channels require their * users to exist. You get the idea. */ void DoBurst(TreeServer* s); /** This function is called when we receive data from a remote * server. We buffer the data in a std::string (it doesnt stay * there for long), reading using InspSocket::Read() which can * read up to 16 kilobytes in one operation. * * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES * THE SOCKET OBJECT FOR US. */ virtual bool OnDataReady(); /** Send one or more complete lines down the socket */ int WriteLine(std::string line); /** Handle ERROR command */ bool Error(std::deque<std::string> &params); /** remote MOTD. leet, huh? */ bool Motd(const std::string &prefix, std::deque<std::string> &params); /** remote ADMIN. leet, huh? */ bool Admin(const std::string &prefix, std::deque<std::string> &params); /** Remote MODULES */ bool Modules(const std::string &prefix, std::deque<std::string> &params); bool Stats(const std::string &prefix, std::deque<std::string> &params); /** Because the core won't let users or even SERVERS set +o, * we use the OPERTYPE command to do this. */ bool OperType(const std::string &prefix, std::deque<std::string> &params); /** Because Andy insists that services-compatible servers must * implement SVSNICK and SVSJOIN, that's exactly what we do :p */ bool ForceNick(const std::string &prefix, std::deque<std::string> &params); bool OperQuit(const std::string &prefix, std::deque<std::string> &params); /** SVSJOIN */ bool ServiceJoin(const std::string &prefix, std::deque<std::string> &params); /** REHASH */ bool RemoteRehash(const std::string &prefix, std::deque<std::string> &params); /** KILL */ bool RemoteKill(const std::string &prefix, std::deque<std::string> &params); /** PONG */ bool LocalPong(const std::string &prefix, std::deque<std::string> &params); /** METADATA */ bool MetaData(const std::string &prefix, std::deque<std::string> &params); /** VERSION */ bool ServerVersion(const std::string &prefix, std::deque<std::string> &params); /** CHGHOST */ bool ChangeHost(const std::string &prefix, std::deque<std::string> &params); /** ADDLINE */ bool AddLine(const std::string &prefix, std::deque<std::string> &params); /** CHGNAME */ bool ChangeName(const std::string &prefix, std::deque<std::string> &params); /** WHOIS */ bool Whois(const std::string &prefix, std::deque<std::string> &params); /** PUSH */ bool Push(const std::string &prefix, std::deque<std::string> &params); /** SETTIME */ bool HandleSetTime(const std::string &prefix, std::deque<std::string> &params); /** TIME */ bool Time(const std::string &prefix, std::deque<std::string> &params); /** PING */ bool LocalPing(const std::string &prefix, std::deque<std::string> &params); /** 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. */ bool RemoveStatus(const std::string &prefix, std::deque<std::string> &params); /** <- (remote) <- SERVER */ bool RemoteServer(const std::string &prefix, std::deque<std::string> &params); /** (local) -> SERVER */ bool Outbound_Reply_Server(std::deque<std::string> &params); /** (local) <- SERVER */ bool Inbound_Server(std::deque<std::string> &params); /** Handle netsplit */ void Split(const std::string &line, std::deque<std::string> &n); /** Process complete line from buffer */ bool ProcessLine(std::string &line); /** Get this server's name */ virtual std::string GetName(); /** Handle socket timeout from connect() */ virtual void OnTimeout(); /** Handle socket close event */ virtual void OnClose(); /** Handle incoming connection event */ virtual int OnIncomingConnection(int newsock, char* ip); }; /* Used to validate the value lengths of multiple parameters for a command */ struct cmd_validation { const char* item; size_t param; size_t length; }; /* Used to validate the length values in CAPAB CAPABILITIES */ struct cap_validation { const char* reason; const char* key; size_t size; }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __TREESOCKET_H__
+#define __TREESOCKET_H__
+
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "inspircd.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/utils.h"
+
+/*
+ * The server list in InspIRCd is maintained as two structures
+ * which hold the data in different ways. Most of the time, we
+ * want to very quicky obtain three pieces of information:
+ *
+ * (1) The information on a server
+ * (2) The information on the server we must send data through
+ * to actually REACH the server we're after
+ * (3) Potentially, the child/parent objects of this server
+ *
+ * The InspIRCd spanning protocol provides easy access to these
+ * by storing the data firstly in a recursive structure, where
+ * each item references its parent item, and a dynamic list
+ * of child items, and another structure which stores the items
+ * hashed, linearly. This means that if we want to find a server
+ * by name quickly, we can look it up in the hash, avoiding
+ * any O(n) lookups. If however, during a split or sync, we want
+ * to apply an operation to a server, and any of its child objects
+ * we can resort to recursion to walk the tree structure.
+ * Any socket can have one of five states at any one time.
+ * The LISTENER state indicates a socket which is listening
+ * for connections. It cannot receive data itself, only incoming
+ * sockets.
+ * The CONNECTING state indicates an outbound socket which is
+ * waiting to be writeable.
+ * The WAIT_AUTH_1 state indicates the socket is outbound and
+ * has successfully connected, but has not yet sent and received
+ * SERVER strings.
+ * The WAIT_AUTH_2 state indicates that the socket is inbound
+ * (allocated by a LISTENER) but has not yet sent and received
+ * SERVER strings.
+ * The CONNECTED state represents a fully authorized, fully
+ * connected server.
+ */
+enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
+
+/** Every SERVER connection inbound or outbound is represented by
+ * an object of type TreeSocket.
+ * TreeSockets, being inherited from InspSocket, can be tied into
+ * the core socket engine, and we cn therefore receive activity events
+ * for them, just like activex objects on speed. (yes really, that
+ * is a technical term!) Each of these which relates to a locally
+ * connected server is assocated with it, by hooking it onto a
+ * TreeSocket class using its constructor. In this way, we can
+ * maintain a list of servers, some of which are directly connected,
+ * some of which are not.
+ */
+class TreeSocket : public InspSocket
+{
+ SpanningTreeUtilities* Utils; /* Utility class */
+ std::string myhost; /* Canonical hostname */
+ std::string in_buffer; /* Input buffer */
+ ServerState LinkState; /* Link state */
+ std::string InboundServerName; /* Server name sent to us by other side */
+ std::string InboundDescription; /* Server description (GECOS) sent to us by the other side */
+ int num_lost_users; /* Users lost in split */
+ int num_lost_servers; /* Servers lost in split */
+ time_t NextPing; /* Time when we are due to ping this server */
+ bool LastPingWasGood; /* Responded to last ping we sent? */
+ bool bursting; /* True if not finished bursting yet */
+ unsigned int keylength; /* Is this still used? */
+ std::string ModuleList; /* Module list of other server from CAPAB */
+ std::map<std::string,std::string> CapKeys; /* CAPAB keys from other server */
+ Module* Hook; /* I/O hooking module that we're attached to for this socket */
+ std::string ourchallenge; /* Challenge sent for challenge/response */
+ std::string theirchallenge; /* Challenge recv for challenge/response */
+ std::string OutboundPass; /* Outbound password */
+ bool sentcapab; /* Have sent CAPAB already */
+ public:
+
+ /** Because most of the I/O gubbins are encapsulated within
+ * InspSocket, we just call the superclass constructor for
+ * most of the action, and append a few of our own values
+ * to it.
+ */
+ TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod = NULL);
+
+ /** Because most of the I/O gubbins are encapsulated within
+ * InspSocket, we just call the superclass constructor for
+ * most of the action, and append a few of our own values
+ * to it.
+ */
+ TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod = NULL);
+
+ /** 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, InspIRCd* SI, int newfd, char* ip, Module* HookMod = NULL);
+
+ /** Get link state
+ */
+ ServerState GetLinkState();
+
+ /** Get challenge set in our CAPAB for challenge/response
+ */
+ const std::string& GetOurChallenge();
+
+ /** Get challenge set in our CAPAB for challenge/response
+ */
+ void SetOurChallenge(const std::string &c);
+
+ /** Get challenge set in their CAPAB for challenge/response
+ */
+ const std::string& GetTheirChallenge();
+
+ /** Get challenge set in their CAPAB for challenge/response
+ */
+ void SetTheirChallenge(const std::string &c);
+
+ /** Compare two passwords based on authentication scheme
+ */
+ bool ComparePass(const std::string &ours, const std::string &theirs);
+
+ /** Return the module which we are hooking to for I/O encapsulation
+ */
+ Module* GetHook();
+
+ /** Destructor
+ */
+ ~TreeSocket();
+
+ /** Generate random string used for challenge-response auth
+ */
+ std::string RandString(unsigned int length);
+
+ /** Construct a password, optionally hashed with the other side's
+ * challenge string
+ */
+ std::string MakePass(const std::string &password, const std::string &challenge);
+
+ /** When an outbound connection finishes connecting, we receive
+ * this event, and must send our SERVER string to 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.
+ */
+ virtual bool OnConnected();
+
+ /** Handle socket error event
+ */
+ virtual void OnError(InspSocketError e);
+
+ /** Sends an error to the remote server, and displays it locally to show
+ * that it was sent.
+ */
+ void SendError(const std::string &errormessage);
+
+ /** Handle socket disconnect event
+ */
+ virtual int OnDisconnect();
+
+ /** 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);
+
+ /** Returns my capabilities as a string
+ */
+ std::string MyCapabilities();
+
+ /** Send my capabilities to the remote side
+ */
+ void SendCapabilities();
+
+ /* Check a comma seperated list for an item */
+ bool HasItem(const std::string &list, const std::string &item);
+
+ /* Isolate and return the elements that are different between two comma seperated lists */
+ std::string ListDifference(const std::string &one, const std::string &two);
+
+ bool Capab(const std::deque<std::string> &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);
+
+ /** 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);
+
+ /** FMODE command - server mode with timestamp checks */
+ bool ForceMode(const std::string &source, std::deque<std::string> &params);
+
+ /** FTOPIC command */
+ bool ForceTopic(const std::string &source, std::deque<std::string> &params);
+
+ /** FJOIN, similar to TS6 SJOIN, but not quite. */
+ bool ForceJoin(const std::string &source, std::deque<std::string> &params);
+
+ /** NICK command */
+ bool IntroduceClient(const std::string &source, std::deque<std::string> &params);
+
+ /** 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.
+ */
+ void SendFJoins(TreeServer* Current, chanrec* c);
+
+ /** Send G, Q, Z and E lines */
+ void SendXLines(TreeServer* Current);
+
+ /** Send channel modes and topics */
+ void SendChannelModes(TreeServer* Current);
+
+ /** send all users and their oper state/modes */
+ void SendUsers(TreeServer* Current);
+
+ /** 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
+ * users require their servers to exist, and channels require their
+ * users to exist. You get the idea.
+ */
+ void DoBurst(TreeServer* s);
+
+ /** This function is called when we receive data from a remote
+ * server. We buffer the data in a std::string (it doesnt stay
+ * there for long), reading using InspSocket::Read() which can
+ * read up to 16 kilobytes in one operation.
+ *
+ * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
+ * THE SOCKET OBJECT FOR US.
+ */
+ virtual bool OnDataReady();
+
+ /** Send one or more complete lines down the socket
+ */
+ int WriteLine(std::string line);
+
+ /** Handle ERROR command */
+ bool Error(std::deque<std::string> &params);
+
+ /** remote MOTD. leet, huh? */
+ bool Motd(const std::string &prefix, std::deque<std::string> &params);
+
+ /** remote ADMIN. leet, huh? */
+ bool Admin(const std::string &prefix, std::deque<std::string> &params);
+
+ /** Remote MODULES */
+ bool Modules(const std::string &prefix, std::deque<std::string> &params);
+
+ bool Stats(const std::string &prefix, std::deque<std::string> &params);
+
+ /** Because the core won't let users or even SERVERS set +o,
+ * we use the OPERTYPE command to do this.
+ */
+ bool OperType(const std::string &prefix, std::deque<std::string> &params);
+
+ /** Because Andy insists that services-compatible servers must
+ * implement SVSNICK and SVSJOIN, that's exactly what we do :p
+ */
+ bool ForceNick(const std::string &prefix, std::deque<std::string> &params);
+
+ bool OperQuit(const std::string &prefix, std::deque<std::string> &params);
+
+ /** SVSJOIN
+ */
+ bool ServiceJoin(const std::string &prefix, std::deque<std::string> &params);
+
+ /** REHASH
+ */
+ bool RemoteRehash(const std::string &prefix, std::deque<std::string> &params);
+
+ /** KILL
+ */
+ bool RemoteKill(const std::string &prefix, std::deque<std::string> &params);
+
+ /** PONG
+ */
+ bool LocalPong(const std::string &prefix, std::deque<std::string> &params);
+
+ /** METADATA
+ */
+ bool MetaData(const std::string &prefix, std::deque<std::string> &params);
+
+ /** VERSION
+ */
+ bool ServerVersion(const std::string &prefix, std::deque<std::string> &params);
+
+ /** CHGHOST
+ */
+ bool ChangeHost(const std::string &prefix, std::deque<std::string> &params);
+
+ /** ADDLINE
+ */
+ bool AddLine(const std::string &prefix, std::deque<std::string> &params);
+
+ /** CHGNAME
+ */
+ bool ChangeName(const std::string &prefix, std::deque<std::string> &params);
+
+ /** WHOIS
+ */
+ bool Whois(const std::string &prefix, std::deque<std::string> &params);
+
+ /** PUSH
+ */
+ bool Push(const std::string &prefix, std::deque<std::string> &params);
+
+ /** SETTIME
+ */
+ bool HandleSetTime(const std::string &prefix, std::deque<std::string> &params);
+
+ /** TIME
+ */
+ bool Time(const std::string &prefix, std::deque<std::string> &params);
+
+ /** PING
+ */
+ bool LocalPing(const std::string &prefix, std::deque<std::string> &params);
+
+ /** 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.
+ */
+ bool RemoveStatus(const std::string &prefix, std::deque<std::string> &params);
+
+ /** <- (remote) <- SERVER
+ */
+ bool RemoteServer(const std::string &prefix, std::deque<std::string> &params);
+
+ /** (local) -> SERVER
+ */
+ bool Outbound_Reply_Server(std::deque<std::string> &params);
+
+ /** (local) <- SERVER
+ */
+ bool Inbound_Server(std::deque<std::string> &params);
+
+ /** Handle netsplit
+ */
+ void Split(const std::string &line, std::deque<std::string> &n);
+
+ /** Process complete line from buffer
+ */
+ bool ProcessLine(std::string &line);
+
+ /** Get this server's name
+ */
+ virtual std::string GetName();
+
+ /** Handle socket timeout from connect()
+ */
+ virtual void OnTimeout();
+
+ /** Handle socket close event
+ */
+ virtual void OnClose();
+
+ /** Handle incoming connection event
+ */
+ virtual int OnIncomingConnection(int newsock, char* ip);
+};
+
+/* Used to validate the value lengths of multiple parameters for a command */
+struct cmd_validation
+{
+ const char* item;
+ size_t param;
+ size_t length;
+};
+
+/* Used to validate the length values in CAPAB CAPABILITIES */
+struct cap_validation
+{
+ const char* reason;
+ const char* key;
+ size_t size;
+};
+
+#endif
+
diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp
index ad2588cab..a907bb440 100644
--- a/src/modules/m_spanningtree/treesocket1.cpp
+++ b/src/modules/m_spanningtree/treesocket1.cpp
@@ -1 +1,1273 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "m_hash.h" #include "socketengine.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/resolvers.h" #include "m_spanningtree/handshaketimer.h" /* $ModDep: m_spanningtree/timesynctimer.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 m_hash.h */ /** Because most of the I/O gubbins are encapsulated within * InspSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. */ TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod) : InspSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod) { myhost = host; this->LinkState = LISTENER; theirchallenge.clear(); ourchallenge.clear(); if (listening && Hook) InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); } TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod) : InspSocket(SI, host, port, listening, maxtime, bindto), Utils(Util), Hook(HookMod) { myhost = ServerName; theirchallenge.clear(); ourchallenge.clear(); this->LinkState = CONNECTING; if (Hook) InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); } /** 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::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod) : InspSocket(SI, newfd, ip), Utils(Util), Hook(HookMod) { this->LinkState = WAIT_AUTH_1; theirchallenge.clear(); ourchallenge.clear(); sentcapab = false; /* If we have a transport module hooked to the parent, hook the same module to this * socket, and set a timer waiting for handshake before we send CAPAB etc. */ if (Hook) InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils, 1)); } ServerState TreeSocket::GetLinkState() { return this->LinkState; } Module* TreeSocket::GetHook() { return this->Hook; } TreeSocket::~TreeSocket() { if (Hook) InspSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send(); Utils->DelBurstingServer(this); } const std::string& TreeSocket::GetOurChallenge() { return this->ourchallenge; } void TreeSocket::SetOurChallenge(const std::string &c) { this->ourchallenge = c; } const std::string& TreeSocket::GetTheirChallenge() { return this->theirchallenge; } void TreeSocket::SetTheirChallenge(const std::string &c) { this->theirchallenge = c; } std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge) { /* 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 * HMAC challenge/response. */ Module* sha256 = Instance->FindModule("m_sha256.so"); if (Utils->ChallengeResponse && sha256 && !challenge.empty()) { /* XXX: This is how HMAC is supposed to be done: * * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) ) * * Note that we are encoding the hex hash, not the binary * output of the hash which is slightly different to standard. * * Don't ask me why its always 0x5c and 0x36... it just is. */ std::string hmac1, hmac2; for (size_t n = 0; n < password.length(); n++) { hmac1 += static_cast<char>(password[n] ^ 0x5C); hmac2 += static_cast<char>(password[n] ^ 0x36); } hmac2 += challenge; HashResetRequest(Utils->Creator, sha256).Send(); hmac2 = HashSumRequest(Utils->Creator, sha256, hmac2).Send(); HashResetRequest(Utils->Creator, sha256).Send(); std::string hmac = hmac1 + hmac2; hmac = HashSumRequest(Utils->Creator, sha256, hmac).Send(); return "HMAC-SHA256:"+ hmac; } else if (!challenge.empty() && !sha256) Instance->Log(DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!"); return password; } /** When an outbound connection finishes connecting, we receive * this event, and must send our SERVER string to 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. */ bool TreeSocket::OnConnected() { if (this->LinkState == CONNECTING) { /* we do not need to change state here. */ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) { if (x->Name == this->myhost) { this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] started."); if (Hook) { InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+x->Hook+"\2"); } this->OutboundPass = x->SendPass; sentcapab = false; /* found who we're supposed to be connecting to, send the neccessary gubbins. */ if (this->GetHook()) Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils, 1)); else this->SendCapabilities(); return true; } } } /* There is a (remote) chance that between the /CONNECT and the connection * being accepted, some muppet has removed the <link> block and rehashed. * If that happens the connection hangs here until it's closed. Unlikely * and rather harmless. */ this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2 lost link tag(!)"); return true; } void TreeSocket::OnError(InspSocketError e) { Link* MyLink; switch (e) { case I_ERR_CONNECT: this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused"); MyLink = Utils->FindLink(myhost); if (MyLink) Utils->DoFailOver(MyLink); break; case I_ERR_SOCKET: this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Could not create socket"); break; case I_ERR_BIND: this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Error binding socket to address or port"); break; case I_ERR_WRITE: this->Instance->SNO->WriteToSnoMask('l',"Connection failed: I/O error on connection"); break; case I_ERR_NOMOREFDS: this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Operating system is out of file descriptors!"); break; default: if ((errno) && (errno != EINPROGRESS) && (errno != EAGAIN)) { std::string errstr = strerror(errno); this->Instance->SNO->WriteToSnoMask('l',"Connection to \002"+myhost+"\002 failed with OS error: " + errstr); } break; } } int TreeSocket::OnDisconnect() { /* For the same reason as above, we don't * handle OnDisconnect() */ return true; } /** 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 TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops) { char command[1024]; for (unsigned int q = 0; q < Current->ChildCount(); q++) { TreeServer* recursive_server = Current->GetChild(q); if (recursive_server != s) { snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str()); this->WriteLine(command); this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion()); /* down to next level */ this->SendServers(recursive_server, s, hops+1); } } } std::string TreeSocket::MyCapabilities() { std::vector<std::string> modlist; std::string capabilities; for (int i = 0; i <= this->Instance->GetModuleCount(); i++) { if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON) modlist.push_back(this->Instance->Config->module_names[i]); } sort(modlist.begin(),modlist.end()); for (unsigned int i = 0; i < modlist.size(); i++) { if (i) capabilities = capabilities + ","; capabilities = capabilities + modlist[i]; } return capabilities; } std::string TreeSocket::RandString(unsigned int length) { char* randombuf = new char[length+1]; std::string out; #ifdef WINDOWS int fd = -1; #else int fd = open("/dev/urandom", O_RDONLY, 0); #endif if (fd >= 0) { #ifndef WINDOWS read(fd, randombuf, length); close(fd); #endif } else { for (unsigned int i = 0; i < length; i++) randombuf[i] = rand(); } for (unsigned int i = 0; i < length; i++) { char randchar = static_cast<char>((randombuf[i] & 0x7F) | 0x21); out += (randchar == '=' ? '_' : randchar); } delete[] randombuf; return out; } void TreeSocket::SendCapabilities() { if (sentcapab) return; sentcapab = true; irc::commasepstream modulelist(MyCapabilities()); this->WriteLine("CAPAB START"); /* Send module names, split at 509 length */ std::string item = "*"; std::string line = "CAPAB MODULES "; while ((item = modulelist.GetToken()) != "") { if (line.length() + item.length() + 1 > 509) { this->WriteLine(line); line = "CAPAB MODULES "; } if (line != "CAPAB MODULES ") line.append(","); line.append(item); } if (line != "CAPAB MODULES ") this->WriteLine(line); int ip6 = 0; int ip6support = 0; #ifdef IPV6 ip6 = 1; #endif #ifdef SUPPORT_IP6LINKS ip6support = 1; #endif std::string extra; /* Do we have sha256 available? If so, we send a challenge */ if (Utils->ChallengeResponse && (Instance->FindModule("m_sha256.so"))) { this->SetOurChallenge(RandString(20)); extra = " CHALLENGE=" + this->GetOurChallenge(); } this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes()); this->WriteLine("CAPAB END"); } /* Check a comma seperated list for an item */ bool TreeSocket::HasItem(const std::string &list, const std::string &item) { irc::commasepstream seplist(list); std::string item2 = "*"; while ((item2 = seplist.GetToken()) != "") { if (item2 == item) return true; } return false; } /* Isolate and return the elements that are different between two comma seperated lists */ std::string TreeSocket::ListDifference(const std::string &one, const std::string &two) { irc::commasepstream list_one(one); std::string item = "*"; std::string result; while ((item = list_one.GetToken()) != "") { if (!HasItem(two, item)) { result.append(" "); result.append(item); } } return result; } void TreeSocket::SendError(const std::string &errormessage) { /* Display the error locally as well as sending it remotely */ this->WriteLine("ERROR :"+errormessage); this->Instance->SNO->WriteToSnoMask('l',"Sent \2ERROR\2 to "+this->InboundServerName+": "+errormessage); /* One last attempt to make sure the error reaches its target */ this->FlushWriteBuffer(); } bool TreeSocket::Capab(const std::deque<std::string> &params) { if (params.size() < 1) { this->SendError("Invalid number of parameters for CAPAB - Mismatched version"); return false; } if (params[0] == "START") { this->ModuleList.clear(); this->CapKeys.clear(); } else if (params[0] == "END") { std::string reason; int ip6support = 0; #ifdef SUPPORT_IP6LINKS ip6support = 1; #endif /* Compare ModuleList and check CapKeys... * Maybe this could be tidier? -- Brain */ if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length())) { std::string diff = ListDifference(this->ModuleList, this->MyCapabilities()); if (!diff.length()) { diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList); } else { diff = "this server:" + diff; } if (diff.length() == 12) reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists."; else reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff; } cap_validation valid_capab[] = { {"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX}, {"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX}, {"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX}, {"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES}, {"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT}, {"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC}, {"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK}, {"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS}, {"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY}, {"", "", 0} }; if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support)))) reason = "We don't both support linking to IPV6 servers"; if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support)) reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers"; if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion))))) { if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion); else reason = "Protocol version not specified"; } if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes()) reason = "One or more of the prefixes on the remote server are invalid on this server."; if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop)))) reason = "We don't both have halfop support enabled/disabled identically"; for (int x = 0; valid_capab[x].size; ++x) { if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) && (this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size))))) reason = valid_capab[x].reason; } /* Challenge response, store their challenge for our password */ std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE"); if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->FindModule("m_sha256.so"))) { /* Challenge-response is on now */ this->SetTheirChallenge(n->second); if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING)) { this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc); } } else { /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */ if (this->LinkState == CONNECTING) this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 :"+this->Instance->Config->ServerDesc); } if (reason.length()) { this->SendError("CAPAB negotiation failed: "+reason); return false; } } else if ((params[0] == "MODULES") && (params.size() == 2)) { if (!this->ModuleList.length()) { this->ModuleList.append(params[1]); } else { this->ModuleList.append(","); this->ModuleList.append(params[1]); } } else if ((params[0] == "CAPABILITIES") && (params.size() == 2)) { irc::tokenstream capabs(params[1]); std::string item; bool more = true; while ((more = capabs.GetToken(item))) { /* Process each key/value pair */ std::string::size_type equals = item.rfind('='); if (equals != std::string::npos) { std::string var = item.substr(0, equals); std::string value = item.substr(equals+1, item.length()); CapKeys[var] = value; } } } return true; } /** 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) { /* 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* recursive_server = Current->GetChild(q); this->SquitServer(from,recursive_server); } /* 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) { if ((Current) && (Current != Utils->TreeRoot)) { Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server"); rmode.Send(Instance); std::deque<std::string> params; params.push_back(Current->GetName()); params.push_back(":"+reason); Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName()); if (Current->GetParent() == Utils->TreeRoot) { this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split: "+reason); } else { this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason); } num_lost_servers = 0; num_lost_users = 0; std::string from = Current->GetParent()->GetName()+" "+Current->GetName(); SquitServer(from, Current); Current->Tidy(); Current->GetParent()->DelChild(Current); DELETE(Current); this->Instance->SNO->WriteToSnoMask('l',"Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers); } else Instance->Log(DEFAULT,"Squit from unknown server"); } /** FMODE command - server mode with timestamp checks */ bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> &params) { /* Chances are this is a 1.0 FMODE without TS */ if (params.size() < 3) { /* No modes were in the command, probably a channel with no modes set on it */ return true; } bool smode = false; std::string sourceserv; /* Are we dealing with an FMODE from a user, or from a server? */ userrec* who = this->Instance->FindNick(source); if (who) { /* FMODE from a user, set sourceserv to the users server name */ sourceserv = who->server; } else { /* FMODE from a server, create a fake user to receive mode feedback */ who = new userrec(this->Instance); who->SetFd(FD_MAGIC_NUMBER); smode = true; /* Setting this flag tells us we should free the userrec later */ sourceserv = source; /* Set sourceserv to the actual source string */ } const char* modelist[64]; time_t TS = 0; int n = 0; memset(&modelist,0,sizeof(modelist)); 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[n++] = params[q].c_str(); } } /* Extract the TS value of the object, either userrec or chanrec */ userrec* dst = this->Instance->FindNick(params[0]); chanrec* chan = NULL; time_t ourTS = 0; if (dst) { ourTS = dst->age; } else { chan = this->Instance->FindChan(params[0]); if (chan) { ourTS = chan->age; } else /* Oops, channel doesnt exist! */ return true; } if (!TS) { Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped."); Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str()); return true; } /* TS is equal or less: Merge the mode changes into ours and pass on. */ if (TS <= ourTS) { if ((TS < ourTS) && (!dst)) Instance->Log(DEFAULT,"*** BUG *** Channel TS sent in FMODE to %s is %lu which is not equal to %lu!", params[0].c_str(), TS, ourTS); if (smode) { this->Instance->SendMode(modelist, n, who); } else { this->Instance->CallCommandHandler("MODE", modelist, n, who); } /* HOT POTATO! PASS IT ON! */ Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv); } /* If the TS is greater than ours, we drop the mode and dont pass it anywhere. */ if (smode) DELETE(who); return true; } /** FTOPIC command */ bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> &params) { if (params.size() != 4) return true; time_t ts = atoi(params[1].c_str()); std::string nsource = source; chanrec* c = this->Instance->FindChan(params[0]); if (c) { if ((ts >= c->topicset) || (!*c->topic)) { std::string oldtopic = c->topic; strlcpy(c->topic,params[3].c_str(),MAXTOPIC); strlcpy(c->setby,params[2].c_str(),127); c->topicset = ts; /* if the topic text is the same as the current topic, * dont bother to send the TOPIC command out, just silently * update the set time and set nick. */ if (oldtopic != params[3]) { userrec* user = this->Instance->FindNick(source); if (!user) { c->WriteChannelWithServ(Instance->Config->ServerName, "TOPIC %s :%s", c->name, c->topic); } else { c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic); nsource = user->server; } /* all done, send it on its way */ params[3] = ":" + params[3]; Utils->DoOneToAllButSender(source,"FTOPIC",params,nsource); } } } return true; } /** FJOIN, similar to TS6 SJOIN, but not quite. */ bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> &params) { /* 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 * winner. The side with the higher timestamp loses, from this point on we * will call this side the loser or losing side. This should be familiar to * anyone who's dealt with dreamforge or TS6 before. * * When two sides of a split heal and this occurs, the following things * will happen: * * If the timestamps are exactly equal, both sides merge their privilages * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been * re-created during a split, this is safe to do. * * If the timestamps are NOT equal, the losing side removes all of its * modes from the channel, before introducing new users into the channel * which are listed in the FJOIN command's parameters. The losing side then * LOWERS its timestamp value of the channel to match that of the winning * side, and the modes of the users of the winning side are merged in with * the losing side. * * 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. :-) * * NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN, * FJOIN does not contain the simple-modes such as +iklmnsp. Why not, * you ask? Well, quite simply because we don't need to. They'll be sent * after the FJOIN by FMODE, and FMODE is timestamped, so in the event * the losing side sends any modes for the channel which shouldnt win, * they wont as their timestamp will be too high :-) */ if (params.size() < 3) return true; irc::modestacker modestack(true); /* Modes to apply from the users in the user list */ userrec* 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[2]); /* Users from the user list */ bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */ chanrec* chan = this->Instance->FindChan(channel); /* The channel we're sending joins to */ time_t ourTS = chan ? chan->age : Instance->Time(true)+600; /* The TS of our side of the link */ bool created = !chan; /* True if the channel doesnt exist here yet */ std::string item; /* One item in the list of nicks */ params[2] = ":" + params[2]; Utils->DoOneToAllButSender(source,"FJOIN",params,source); if (!TS) { Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped."); Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str()); return true; } /* If our TS is less than theirs, we dont accept their modes */ if (ourTS < TS) apply_other_sides_modes = false; /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */ if (ourTS > TS) { std::deque<std::string> param_list; if (Utils->AnnounceTSChange && chan) chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS); ourTS = TS; if (!created) { chan->age = TS; param_list.push_back(channel); this->RemoveStatus(Instance->Config->ServerName, param_list); } } /* Now, process every 'prefixes,nick' pair */ while (users.GetToken(item)) { const char* usr = item.c_str(); if (usr && *usr) { const char* permissions = usr; /* Iterate through all the prefix values, convert them from prefixes to mode letters */ std::string modes; while ((*permissions) && (*permissions != ',')) { ModeHandler* mh = Instance->Modes->FindPrefix(*permissions); if (mh) modes = modes + mh->GetModeChar(); else { this->SendError(std::string("Invalid prefix '")+(*permissions)+"' in FJOIN"); return false; } usr++; permissions++; } /* Advance past the comma, to the nick */ usr++; /* Check the user actually exists */ who = this->Instance->FindNick(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() != this)) continue; /* Add any permissions this user had to the mode stack */ for (std::string::iterator x = modes.begin(); x != modes.end(); ++x) modestack.Push(*x, who->nick); chanrec::JoinUser(this->Instance, who, channel.c_str(), true, "", TS); } else { Instance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str()); continue; } } } /* Flush mode stacker if we lost the FJOIN or had equal TS */ if (apply_other_sides_modes) { std::deque<std::string> stackresult; const char* mode_junk[MAXMODES+2]; userrec* n = new userrec(Instance); n->SetFd(FD_MAGIC_NUMBER); mode_junk[0] = channel.c_str(); while (modestack.GetStackedLine(stackresult)) { for (size_t j = 0; j < stackresult.size(); j++) { mode_junk[j+1] = stackresult[j].c_str(); } Instance->SendMode(mode_junk, stackresult.size() + 1, n); } delete n; } return true; } /** NICK command */ bool TreeSocket::IntroduceClient(const std::string &source, std::deque<std::string> &params) { /** Do we have enough parameters: * NICK age nick host dhost ident +modes ip :gecos */ if (params.size() != 8) { this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+"?)"); return true; } time_t age = ConvToInt(params[0]); const char* tempnick = params[1].c_str(); cmd_validation valid[] = { {"Nickname", 1, NICKMAX}, {"Hostname", 2, 64}, {"Displayed hostname", 3, 64}, {"Ident", 4, IDENTMAX}, {"GECOS", 7, MAXGECOS}, {"", 0, 0} }; TreeServer* remoteserver = Utils->FindServer(source); if (!remoteserver) { this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Unknown server "+source+")"); return true; } /* Check parameters for validity before introducing the client, discovered by dmb */ if (!age) { this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Invalid TS?)"); return true; } for (size_t x = 0; valid[x].length; ++x) { if (params[valid[x].param].length() > valid[x].length) { this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (" + valid[x].item + " > " + ConvToStr(valid[x].length) + ")"); return true; } } /** Our client looks ok, lets introduce it now */ Instance->Log(DEBUG,"New remote client %s",tempnick); user_hash::iterator iter = this->Instance->clientlist->find(tempnick); if (iter != this->Instance->clientlist->end()) { /* nick collision */ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision"); userrec::QuitUser(this->Instance, iter->second, "Nickname collision"); return true; } userrec* _new = new userrec(this->Instance); (*(this->Instance->clientlist))[tempnick] = _new; _new->SetFd(FD_MAGIC_NUMBER); strlcpy(_new->nick, tempnick,NICKMAX-1); strlcpy(_new->host, params[2].c_str(),64); strlcpy(_new->dhost, params[3].c_str(),64); _new->server = this->Instance->FindServerNamePtr(source.c_str()); strlcpy(_new->ident, params[4].c_str(),IDENTMAX); strlcpy(_new->fullname, params[7].c_str(),MAXGECOS); _new->registered = REG_ALL; _new->signon = age; /* we need to remove the + from the modestring, so we can do our stuff */ std::string::size_type pos_after_plus = params[5].find_first_not_of('+'); if (pos_after_plus != std::string::npos) params[5] = params[5].substr(pos_after_plus); for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++) { _new->modes[(*v)-65] = 1; /* For each mode thats set, increase counter */ ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER); if (mh) mh->ChangeCount(1); } /* now we've done with modes processing, put the + back for remote servers */ params[5] = "+" + params[5]; #ifdef SUPPORT_IP6LINKS if (params[6].find_first_of(":") != std::string::npos) _new->SetSockAddr(AF_INET6, params[6].c_str(), 0); else #endif _new->SetSockAddr(AF_INET, params[6].c_str(), 0); Instance->AddGlobalClone(_new); bool dosend = !(((this->Utils->quiet_bursts) && (this->bursting || Utils->FindRemoteBurstServer(remoteserver))) || (this->Instance->SilentULine(_new->server))); if (dosend) this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s] [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString(), _new->fullname); params[7] = ":" + params[7]; Utils->DoOneToAllButSender(source,"NICK", params, source); // Increment the Source Servers User Count.. TreeServer* SourceServer = Utils->FindServer(source); if (SourceServer) { SourceServer->AddUserCount(); } FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new)); return true; } /** 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. */ void TreeSocket::SendFJoins(TreeServer* Current, chanrec* c) { std::string buffer; char list[MAXBUF]; std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age); size_t dlen, curlen; dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age); int numusers = 0; char* ptr = list + dlen; CUList *ulist = c->GetUsers(); std::string modes; std::string params; for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { // The first parameter gets a : before it size_t ptrlen = snprintf(ptr, MAXBUF, " %s%s,%s", !numusers ? ":" : "", c->GetAllPrefixChars(i->first), i->first->nick); curlen += ptrlen; ptr += ptrlen; numusers++; if (curlen > (480-NICKMAX)) { buffer.append(list).append("\r\n"); dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age); ptr = list + dlen; ptrlen = 0; numusers = 0; } } if (numusers) buffer.append(list).append("\r\n"); buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true)).append("\r\n"); int linesize = 1; for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++) { int size = strlen(b->data) + 2; int currsize = linesize + size; if (currsize <= 350) { modes.append("b"); params.append(" ").append(b->data); linesize += size; } if ((params.length() >= MAXMODES) || (currsize > 350)) { /* Wrap at MAXMODES */ buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n"); modes.clear(); params.clear(); linesize = 1; } } /* Only send these if there are any */ if (!modes.empty()) buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params); this->WriteLine(buffer); } /** Send G, Q, Z and E lines */ void TreeSocket::SendXLines(TreeServer* Current) { char data[MAXBUF]; std::string buffer; std::string n = this->Instance->Config->ServerName; const char* sn = n.c_str(); /* Yes, these arent too nice looking, but they get the job done */ for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++) { snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } if (!buffer.empty()) this->WriteLine(buffer); } /** Send channel modes and topics */ void TreeSocket::SendChannelModes(TreeServer* Current) { char data[MAXBUF]; std::deque<std::string> list; std::string n = this->Instance->Config->ServerName; const char* sn = n.c_str(); Instance->Log(DEBUG,"Sending channels and modes, %d to send", this->Instance->chanlist->size()); for (chan_hash::iterator c = this->Instance->chanlist->begin(); c != this->Instance->chanlist->end(); c++) { SendFJoins(Current, c->second); if (*c->second->topic) { snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",sn,c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic); this->WriteLine(data); } FOREACH_MOD_I(this->Instance,I_OnSyncChannel,OnSyncChannel(c->second,(Module*)Utils->Creator,(void*)this)); list.clear(); c->second->GetExtList(list); for (unsigned int j = 0; j < list.size(); j++) { FOREACH_MOD_I(this->Instance,I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)Utils->Creator,(void*)this,list[j])); } } } /** send all users and their oper state/modes */ void TreeSocket::SendUsers(TreeServer* Current) { char data[MAXBUF]; std::deque<std::string> list; std::string dataline; for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++) { if (u->second->registered == REG_ALL) { snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),u->second->GetIPString(),u->second->fullname); this->WriteLine(data); if (*u->second->oper) { snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->nick, u->second->oper); this->WriteLine(data); } if (*u->second->awaymsg) { snprintf(data,MAXBUF,":%s AWAY :%s", u->second->nick, u->second->awaymsg); this->WriteLine(data); } } } for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++) { FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this)); list.clear(); u->second->GetExtList(list); for (unsigned int j = 0; j < list.size(); j++) { FOREACH_MOD_I(this->Instance,I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)Utils->Creator,(void*)this,list[j])); } } } /** 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 * users require their servers to exist, and channels require their * users to exist. You get the idea. */ void TreeSocket::DoBurst(TreeServer* s) { std::string name = s->GetName(); std::string burst = "BURST "+ConvToStr(Instance->Time(true)); std::string endburst = "ENDBURST"; this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s).", name.c_str(), this->GetTheirChallenge().empty() ? "plaintext password" : "SHA256-HMAC challenge-response"); this->WriteLine(burst); /* send our version string */ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString()); /* Send server tree */ this->SendServers(Utils->TreeRoot,s,1); /* Send users and their oper status */ this->SendUsers(s); /* Send everything else (channel modes, xlines etc) */ this->SendChannelModes(s); this->SendXLines(s); FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)Utils->Creator,(void*)this)); this->WriteLine(endburst); this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2."); } /** This function is called when we receive data from a remote * server. We buffer the data in a std::string (it doesnt stay * there for long), reading using InspSocket::Read() which can * read up to 16 kilobytes in one operation. * * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES * THE SOCKET OBJECT FOR US. */ bool TreeSocket::OnDataReady() { char* data = this->Read(); /* Check that the data read is a valid pointer and it has some content */ if (data && *data) { this->in_buffer.append(data); /* While there is at least one new line in the buffer, * do something useful (we hope!) with it. */ while (in_buffer.find("\n") != std::string::npos) { std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1); in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n")); /* Use rfind here not find, as theres more * chance of the \r being near the end of the * string, not the start. */ if (ret.find("\r") != std::string::npos) ret = in_buffer.substr(0,in_buffer.find("\r")-1); /* Process this one, abort if it * didnt return true. */ if (!this->ProcessLine(ret)) { return false; } } return true; } /* EAGAIN returns an empty but non-NULL string, so this * evaluates to TRUE for EAGAIN but to FALSE for EOF. */ return (data && !*data); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 m_hash.h */
+
+
+/** Because most of the I/O gubbins are encapsulated within
+ * InspSocket, we just call the superclass constructor for
+ * most of the action, and append a few of our own values
+ * to it.
+ */
+TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod)
+ : InspSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod)
+{
+ myhost = host;
+ this->LinkState = LISTENER;
+ theirchallenge.clear();
+ ourchallenge.clear();
+ if (listening && Hook)
+ InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
+}
+
+TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod)
+ : InspSocket(SI, host, port, listening, maxtime, bindto), Utils(Util), Hook(HookMod)
+{
+ myhost = ServerName;
+ theirchallenge.clear();
+ ourchallenge.clear();
+ this->LinkState = CONNECTING;
+ if (Hook)
+ InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
+}
+
+/** 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::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod)
+ : InspSocket(SI, newfd, ip), Utils(Util), Hook(HookMod)
+{
+ this->LinkState = WAIT_AUTH_1;
+ theirchallenge.clear();
+ ourchallenge.clear();
+ sentcapab = false;
+ /* If we have a transport module hooked to the parent, hook the same module to this
+ * socket, and set a timer waiting for handshake before we send CAPAB etc.
+ */
+ if (Hook)
+ InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
+
+ Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils, 1));
+}
+
+ServerState TreeSocket::GetLinkState()
+{
+ return this->LinkState;
+}
+
+Module* TreeSocket::GetHook()
+{
+ return this->Hook;
+}
+
+TreeSocket::~TreeSocket()
+{
+ if (Hook)
+ InspSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send();
+
+ Utils->DelBurstingServer(this);
+}
+
+const std::string& TreeSocket::GetOurChallenge()
+{
+ return this->ourchallenge;
+}
+
+void TreeSocket::SetOurChallenge(const std::string &c)
+{
+ this->ourchallenge = c;
+}
+
+const std::string& TreeSocket::GetTheirChallenge()
+{
+ return this->theirchallenge;
+}
+
+void TreeSocket::SetTheirChallenge(const std::string &c)
+{
+ this->theirchallenge = c;
+}
+
+std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge)
+{
+ /* 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
+ * HMAC challenge/response.
+ */
+ Module* sha256 = Instance->FindModule("m_sha256.so");
+ if (Utils->ChallengeResponse && sha256 && !challenge.empty())
+ {
+ /* XXX: This is how HMAC is supposed to be done:
+ *
+ * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
+ *
+ * Note that we are encoding the hex hash, not the binary
+ * output of the hash which is slightly different to standard.
+ *
+ * Don't ask me why its always 0x5c and 0x36... it just is.
+ */
+ std::string hmac1, hmac2;
+
+ for (size_t n = 0; n < password.length(); n++)
+ {
+ hmac1 += static_cast<char>(password[n] ^ 0x5C);
+ hmac2 += static_cast<char>(password[n] ^ 0x36);
+ }
+
+ hmac2 += challenge;
+ HashResetRequest(Utils->Creator, sha256).Send();
+ hmac2 = HashSumRequest(Utils->Creator, sha256, hmac2).Send();
+
+ HashResetRequest(Utils->Creator, sha256).Send();
+ std::string hmac = hmac1 + hmac2;
+ hmac = HashSumRequest(Utils->Creator, sha256, hmac).Send();
+
+ return "HMAC-SHA256:"+ hmac;
+ }
+ else if (!challenge.empty() && !sha256)
+ Instance->Log(DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
+
+ return password;
+}
+
+/** When an outbound connection finishes connecting, we receive
+ * this event, and must send our SERVER string to 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.
+ */
+bool TreeSocket::OnConnected()
+{
+ if (this->LinkState == CONNECTING)
+ {
+ /* we do not need to change state here. */
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if (x->Name == this->myhost)
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] started.");
+ if (Hook)
+ {
+ InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+x->Hook+"\2");
+ }
+ this->OutboundPass = x->SendPass;
+ sentcapab = false;
+
+ /* found who we're supposed to be connecting to, send the neccessary gubbins. */
+ if (this->GetHook())
+ Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils, 1));
+ else
+ this->SendCapabilities();
+
+ return true;
+ }
+ }
+ }
+ /* There is a (remote) chance that between the /CONNECT and the connection
+ * being accepted, some muppet has removed the <link> block and rehashed.
+ * If that happens the connection hangs here until it's closed. Unlikely
+ * and rather harmless.
+ */
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2 lost link tag(!)");
+ return true;
+}
+
+void TreeSocket::OnError(InspSocketError e)
+{
+ Link* MyLink;
+
+ switch (e)
+ {
+ case I_ERR_CONNECT:
+ this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused");
+ MyLink = Utils->FindLink(myhost);
+ if (MyLink)
+ Utils->DoFailOver(MyLink);
+ break;
+ case I_ERR_SOCKET:
+ this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Could not create socket");
+ break;
+ case I_ERR_BIND:
+ this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Error binding socket to address or port");
+ break;
+ case I_ERR_WRITE:
+ this->Instance->SNO->WriteToSnoMask('l',"Connection failed: I/O error on connection");
+ break;
+ case I_ERR_NOMOREFDS:
+ this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Operating system is out of file descriptors!");
+ break;
+ default:
+ if ((errno) && (errno != EINPROGRESS) && (errno != EAGAIN))
+ {
+ std::string errstr = strerror(errno);
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to \002"+myhost+"\002 failed with OS error: " + errstr);
+ }
+ break;
+ }
+}
+
+int TreeSocket::OnDisconnect()
+{
+ /* For the same reason as above, we don't
+ * handle OnDisconnect()
+ */
+ return true;
+}
+
+/** 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 TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
+{
+ char command[1024];
+ for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ {
+ TreeServer* recursive_server = Current->GetChild(q);
+ if (recursive_server != s)
+ {
+ snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
+ this->WriteLine(command);
+ this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
+ /* down to next level */
+ this->SendServers(recursive_server, s, hops+1);
+ }
+ }
+}
+
+std::string TreeSocket::MyCapabilities()
+{
+ std::vector<std::string> modlist;
+ std::string capabilities;
+ for (int i = 0; i <= this->Instance->GetModuleCount(); i++)
+ {
+ if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON)
+ modlist.push_back(this->Instance->Config->module_names[i]);
+ }
+ sort(modlist.begin(),modlist.end());
+ for (unsigned int i = 0; i < modlist.size(); i++)
+ {
+ if (i)
+ capabilities = capabilities + ",";
+ capabilities = capabilities + modlist[i];
+ }
+ return capabilities;
+}
+
+std::string TreeSocket::RandString(unsigned int length)
+{
+ char* randombuf = new char[length+1];
+ std::string out;
+#ifdef WINDOWS
+ int fd = -1;
+#else
+ int fd = open("/dev/urandom", O_RDONLY, 0);
+#endif
+
+ if (fd >= 0)
+ {
+#ifndef WINDOWS
+ read(fd, randombuf, length);
+ close(fd);
+#endif
+ }
+ else
+ {
+ for (unsigned int i = 0; i < length; i++)
+ randombuf[i] = rand();
+ }
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ char randchar = static_cast<char>((randombuf[i] & 0x7F) | 0x21);
+ out += (randchar == '=' ? '_' : randchar);
+ }
+
+ delete[] randombuf;
+ return out;
+}
+
+void TreeSocket::SendCapabilities()
+{
+ if (sentcapab)
+ return;
+
+ sentcapab = true;
+ irc::commasepstream modulelist(MyCapabilities());
+ this->WriteLine("CAPAB START");
+
+ /* Send module names, split at 509 length */
+ std::string item = "*";
+ std::string line = "CAPAB MODULES ";
+ while ((item = modulelist.GetToken()) != "")
+ {
+ if (line.length() + item.length() + 1 > 509)
+ {
+ this->WriteLine(line);
+ line = "CAPAB MODULES ";
+ }
+
+ if (line != "CAPAB MODULES ")
+ line.append(",");
+
+ line.append(item);
+ }
+ if (line != "CAPAB MODULES ")
+ this->WriteLine(line);
+
+ int ip6 = 0;
+ int ip6support = 0;
+#ifdef IPV6
+ ip6 = 1;
+#endif
+#ifdef SUPPORT_IP6LINKS
+ ip6support = 1;
+#endif
+ std::string extra;
+ /* Do we have sha256 available? If so, we send a challenge */
+ if (Utils->ChallengeResponse && (Instance->FindModule("m_sha256.so")))
+ {
+ this->SetOurChallenge(RandString(20));
+ extra = " CHALLENGE=" + this->GetOurChallenge();
+ }
+
+ this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes());
+
+ this->WriteLine("CAPAB END");
+}
+
+/* Check a comma seperated list for an item */
+bool TreeSocket::HasItem(const std::string &list, const std::string &item)
+{
+ irc::commasepstream seplist(list);
+ std::string item2 = "*";
+ while ((item2 = seplist.GetToken()) != "")
+ {
+ if (item2 == item)
+ return true;
+ }
+ return false;
+}
+
+/* Isolate and return the elements that are different between two comma seperated lists */
+std::string TreeSocket::ListDifference(const std::string &one, const std::string &two)
+{
+ irc::commasepstream list_one(one);
+ std::string item = "*";
+ std::string result;
+ while ((item = list_one.GetToken()) != "")
+ {
+ if (!HasItem(two, item))
+ {
+ result.append(" ");
+ result.append(item);
+ }
+ }
+ return result;
+}
+
+void TreeSocket::SendError(const std::string &errormessage)
+{
+ /* Display the error locally as well as sending it remotely */
+ this->WriteLine("ERROR :"+errormessage);
+ this->Instance->SNO->WriteToSnoMask('l',"Sent \2ERROR\2 to "+this->InboundServerName+": "+errormessage);
+ /* One last attempt to make sure the error reaches its target */
+ this->FlushWriteBuffer();
+}
+
+bool TreeSocket::Capab(const std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ {
+ this->SendError("Invalid number of parameters for CAPAB - Mismatched version");
+ return false;
+ }
+ if (params[0] == "START")
+ {
+ this->ModuleList.clear();
+ this->CapKeys.clear();
+ }
+ else if (params[0] == "END")
+ {
+ std::string reason;
+ int ip6support = 0;
+#ifdef SUPPORT_IP6LINKS
+ ip6support = 1;
+#endif
+ /* Compare ModuleList and check CapKeys...
+ * Maybe this could be tidier? -- Brain
+ */
+ if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length()))
+ {
+ std::string diff = ListDifference(this->ModuleList, this->MyCapabilities());
+ if (!diff.length())
+ {
+ diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList);
+ }
+ else
+ {
+ diff = "this server:" + diff;
+ }
+ if (diff.length() == 12)
+ reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists.";
+ else
+ reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff;
+ }
+
+ cap_validation valid_capab[] = {
+ {"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX},
+ {"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX},
+ {"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX},
+ {"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES},
+ {"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT},
+ {"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC},
+ {"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK},
+ {"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS},
+ {"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY},
+ {"", "", 0}
+ };
+
+ if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support))))
+ reason = "We don't both support linking to IPV6 servers";
+ if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support))
+ reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers";
+ if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
+ {
+ if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
+ reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
+ else
+ reason = "Protocol version not specified";
+ }
+
+ if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes())
+ reason = "One or more of the prefixes on the remote server are invalid on this server.";
+
+ if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
+ reason = "We don't both have halfop support enabled/disabled identically";
+
+ for (int x = 0; valid_capab[x].size; ++x)
+ {
+ if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) &&
+ (this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size)))))
+ reason = valid_capab[x].reason;
+ }
+
+ /* Challenge response, store their challenge for our password */
+ std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE");
+ if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->FindModule("m_sha256.so")))
+ {
+ /* Challenge-response is on now */
+ this->SetTheirChallenge(n->second);
+ if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING))
+ {
+ this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc);
+ }
+ }
+ else
+ {
+ /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
+ if (this->LinkState == CONNECTING)
+ this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 :"+this->Instance->Config->ServerDesc);
+ }
+
+ if (reason.length())
+ {
+ this->SendError("CAPAB negotiation failed: "+reason);
+ return false;
+ }
+ }
+ else if ((params[0] == "MODULES") && (params.size() == 2))
+ {
+ if (!this->ModuleList.length())
+ {
+ this->ModuleList.append(params[1]);
+ }
+ else
+ {
+ this->ModuleList.append(",");
+ this->ModuleList.append(params[1]);
+ }
+ }
+
+ else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
+ {
+ irc::tokenstream capabs(params[1]);
+ std::string item;
+ bool more = true;
+ while ((more = capabs.GetToken(item)))
+ {
+ /* Process each key/value pair */
+ std::string::size_type equals = item.rfind('=');
+ if (equals != std::string::npos)
+ {
+ std::string var = item.substr(0, equals);
+ std::string value = item.substr(equals+1, item.length());
+ CapKeys[var] = value;
+ }
+ }
+ }
+ return true;
+}
+
+/** 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)
+{
+ /* 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* recursive_server = Current->GetChild(q);
+ this->SquitServer(from,recursive_server);
+ }
+ /* 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)
+{
+ if ((Current) && (Current != Utils->TreeRoot))
+ {
+ Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server");
+ rmode.Send(Instance);
+
+ std::deque<std::string> params;
+ params.push_back(Current->GetName());
+ params.push_back(":"+reason);
+ Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
+ if (Current->GetParent() == Utils->TreeRoot)
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split: "+reason);
+ }
+ else
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
+ }
+ num_lost_servers = 0;
+ num_lost_users = 0;
+ std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
+ SquitServer(from, Current);
+ Current->Tidy();
+ Current->GetParent()->DelChild(Current);
+ DELETE(Current);
+ this->Instance->SNO->WriteToSnoMask('l',"Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
+ }
+ else
+ Instance->Log(DEFAULT,"Squit from unknown server");
+}
+
+/** FMODE command - server mode with timestamp checks */
+bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> &params)
+{
+ /* Chances are this is a 1.0 FMODE without TS */
+ if (params.size() < 3)
+ {
+ /* No modes were in the command, probably a channel with no modes set on it */
+ return true;
+ }
+
+ bool smode = false;
+ std::string sourceserv;
+ /* Are we dealing with an FMODE from a user, or from a server? */
+ userrec* who = this->Instance->FindNick(source);
+ if (who)
+ {
+ /* FMODE from a user, set sourceserv to the users server name */
+ sourceserv = who->server;
+ }
+ else
+ {
+ /* FMODE from a server, create a fake user to receive mode feedback */
+ who = new userrec(this->Instance);
+ who->SetFd(FD_MAGIC_NUMBER);
+ smode = true; /* Setting this flag tells us we should free the userrec later */
+ sourceserv = source; /* Set sourceserv to the actual source string */
+ }
+ const char* modelist[64];
+ time_t TS = 0;
+ int n = 0;
+ memset(&modelist,0,sizeof(modelist));
+ 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[n++] = params[q].c_str();
+ }
+
+ }
+ /* Extract the TS value of the object, either userrec or chanrec */
+ userrec* dst = this->Instance->FindNick(params[0]);
+ chanrec* chan = NULL;
+ time_t ourTS = 0;
+ if (dst)
+ {
+ ourTS = dst->age;
+ }
+ else
+ {
+ chan = this->Instance->FindChan(params[0]);
+ if (chan)
+ {
+ ourTS = chan->age;
+ }
+ else
+ /* Oops, channel doesnt exist! */
+ return true;
+ }
+
+ if (!TS)
+ {
+ Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
+ Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str());
+ return true;
+ }
+
+ /* TS is equal or less: Merge the mode changes into ours and pass on.
+ */
+ if (TS <= ourTS)
+ {
+ if ((TS < ourTS) && (!dst))
+ Instance->Log(DEFAULT,"*** BUG *** Channel TS sent in FMODE to %s is %lu which is not equal to %lu!", params[0].c_str(), TS, ourTS);
+
+ if (smode)
+ {
+ this->Instance->SendMode(modelist, n, who);
+ }
+ else
+ {
+ this->Instance->CallCommandHandler("MODE", modelist, n, who);
+ }
+ /* HOT POTATO! PASS IT ON! */
+ Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv);
+ }
+ /* If the TS is greater than ours, we drop the mode and dont pass it anywhere.
+ */
+
+ if (smode)
+ DELETE(who);
+
+ return true;
+}
+
+/** FTOPIC command */
+bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> &params)
+{
+ if (params.size() != 4)
+ return true;
+ time_t ts = atoi(params[1].c_str());
+ std::string nsource = source;
+ chanrec* c = this->Instance->FindChan(params[0]);
+ if (c)
+ {
+ if ((ts >= c->topicset) || (!*c->topic))
+ {
+ std::string oldtopic = c->topic;
+ strlcpy(c->topic,params[3].c_str(),MAXTOPIC);
+ strlcpy(c->setby,params[2].c_str(),127);
+ c->topicset = ts;
+ /* if the topic text is the same as the current topic,
+ * dont bother to send the TOPIC command out, just silently
+ * update the set time and set nick.
+ */
+ if (oldtopic != params[3])
+ {
+ userrec* user = this->Instance->FindNick(source);
+ if (!user)
+ {
+ c->WriteChannelWithServ(Instance->Config->ServerName, "TOPIC %s :%s", c->name, c->topic);
+ }
+ else
+ {
+ c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic);
+ nsource = user->server;
+ }
+ /* all done, send it on its way */
+ params[3] = ":" + params[3];
+ Utils->DoOneToAllButSender(source,"FTOPIC",params,nsource);
+ }
+ }
+
+ }
+ return true;
+}
+
+/** FJOIN, similar to TS6 SJOIN, but not quite. */
+bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> &params)
+{
+ /* 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
+ * winner. The side with the higher timestamp loses, from this point on we
+ * will call this side the loser or losing side. This should be familiar to
+ * anyone who's dealt with dreamforge or TS6 before.
+ *
+ * When two sides of a split heal and this occurs, the following things
+ * will happen:
+ *
+ * If the timestamps are exactly equal, both sides merge their privilages
+ * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been
+ * re-created during a split, this is safe to do.
+ *
+ * If the timestamps are NOT equal, the losing side removes all of its
+ * modes from the channel, before introducing new users into the channel
+ * which are listed in the FJOIN command's parameters. The losing side then
+ * LOWERS its timestamp value of the channel to match that of the winning
+ * side, and the modes of the users of the winning side are merged in with
+ * the losing side.
+ *
+ * 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. :-)
+ *
+ * NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN,
+ * FJOIN does not contain the simple-modes such as +iklmnsp. Why not,
+ * you ask? Well, quite simply because we don't need to. They'll be sent
+ * after the FJOIN by FMODE, and FMODE is timestamped, so in the event
+ * the losing side sends any modes for the channel which shouldnt win,
+ * they wont as their timestamp will be too high :-)
+ */
+
+ if (params.size() < 3)
+ return true;
+
+ irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
+ userrec* 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[2]); /* Users from the user list */
+ bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
+ chanrec* chan = this->Instance->FindChan(channel); /* The channel we're sending joins to */
+ time_t ourTS = chan ? chan->age : Instance->Time(true)+600; /* The TS of our side of the link */
+ bool created = !chan; /* True if the channel doesnt exist here yet */
+ std::string item; /* One item in the list of nicks */
+
+ params[2] = ":" + params[2];
+ Utils->DoOneToAllButSender(source,"FJOIN",params,source);
+
+ if (!TS)
+ {
+ Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
+ Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str());
+ return true;
+ }
+
+ /* If our TS is less than theirs, we dont accept their modes */
+ if (ourTS < TS)
+ apply_other_sides_modes = false;
+
+ /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
+ if (ourTS > TS)
+ {
+ std::deque<std::string> param_list;
+ if (Utils->AnnounceTSChange && chan)
+ chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
+ ourTS = TS;
+ if (!created)
+ {
+ chan->age = TS;
+ param_list.push_back(channel);
+ this->RemoveStatus(Instance->Config->ServerName, param_list);
+ }
+ }
+
+ /* Now, process every 'prefixes,nick' pair */
+ while (users.GetToken(item))
+ {
+ const char* usr = item.c_str();
+ if (usr && *usr)
+ {
+ const char* permissions = usr;
+ /* Iterate through all the prefix values, convert them from prefixes to mode letters */
+ std::string modes;
+ while ((*permissions) && (*permissions != ','))
+ {
+ ModeHandler* mh = Instance->Modes->FindPrefix(*permissions);
+ if (mh)
+ modes = modes + mh->GetModeChar();
+ else
+ {
+ this->SendError(std::string("Invalid prefix '")+(*permissions)+"' in FJOIN");
+ return false;
+ }
+ usr++;
+ permissions++;
+ }
+ /* Advance past the comma, to the nick */
+ usr++;
+
+ /* Check the user actually exists */
+ who = this->Instance->FindNick(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() != this))
+ continue;
+
+ /* Add any permissions this user had to the mode stack */
+ for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
+ modestack.Push(*x, who->nick);
+
+ chanrec::JoinUser(this->Instance, who, channel.c_str(), true, "", TS);
+ }
+ else
+ {
+ Instance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str());
+ continue;
+ }
+ }
+ }
+
+ /* Flush mode stacker if we lost the FJOIN or had equal TS */
+ if (apply_other_sides_modes)
+ {
+ std::deque<std::string> stackresult;
+ const char* mode_junk[MAXMODES+2];
+ userrec* n = new userrec(Instance);
+ n->SetFd(FD_MAGIC_NUMBER);
+ mode_junk[0] = channel.c_str();
+
+ while (modestack.GetStackedLine(stackresult))
+ {
+ for (size_t j = 0; j < stackresult.size(); j++)
+ {
+ mode_junk[j+1] = stackresult[j].c_str();
+ }
+ Instance->SendMode(mode_junk, stackresult.size() + 1, n);
+ }
+
+ delete n;
+ }
+
+ return true;
+}
+
+/** NICK command */
+bool TreeSocket::IntroduceClient(const std::string &source, std::deque<std::string> &params)
+{
+ /** Do we have enough parameters:
+ * NICK age nick host dhost ident +modes ip :gecos
+ */
+ if (params.size() != 8)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+"?)");
+ return true;
+ }
+
+ time_t age = ConvToInt(params[0]);
+ const char* tempnick = params[1].c_str();
+
+ cmd_validation valid[] = { {"Nickname", 1, NICKMAX}, {"Hostname", 2, 64}, {"Displayed hostname", 3, 64}, {"Ident", 4, IDENTMAX}, {"GECOS", 7, MAXGECOS}, {"", 0, 0} };
+
+ TreeServer* remoteserver = Utils->FindServer(source);
+ if (!remoteserver)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Unknown server "+source+")");
+ return true;
+ }
+
+ /* Check parameters for validity before introducing the client, discovered by dmb */
+ if (!age)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Invalid TS?)");
+ return true;
+ }
+ for (size_t x = 0; valid[x].length; ++x)
+ {
+ if (params[valid[x].param].length() > valid[x].length)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (" + valid[x].item + " > " + ConvToStr(valid[x].length) + ")");
+ return true;
+ }
+ }
+
+ /** Our client looks ok, lets introduce it now
+ */
+ Instance->Log(DEBUG,"New remote client %s",tempnick);
+ user_hash::iterator iter = this->Instance->clientlist->find(tempnick);
+
+ if (iter != this->Instance->clientlist->end())
+ {
+ /* nick collision */
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision");
+ userrec::QuitUser(this->Instance, iter->second, "Nickname collision");
+ return true;
+ }
+
+ userrec* _new = new userrec(this->Instance);
+ (*(this->Instance->clientlist))[tempnick] = _new;
+ _new->SetFd(FD_MAGIC_NUMBER);
+ strlcpy(_new->nick, tempnick,NICKMAX-1);
+ strlcpy(_new->host, params[2].c_str(),64);
+ strlcpy(_new->dhost, params[3].c_str(),64);
+ _new->server = this->Instance->FindServerNamePtr(source.c_str());
+ strlcpy(_new->ident, params[4].c_str(),IDENTMAX);
+ strlcpy(_new->fullname, params[7].c_str(),MAXGECOS);
+ _new->registered = REG_ALL;
+ _new->signon = age;
+
+ /* we need to remove the + from the modestring, so we can do our stuff */
+ std::string::size_type pos_after_plus = params[5].find_first_not_of('+');
+ if (pos_after_plus != std::string::npos)
+ params[5] = params[5].substr(pos_after_plus);
+
+ for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++)
+ {
+ _new->modes[(*v)-65] = 1;
+ /* For each mode thats set, increase counter */
+ ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER);
+ if (mh)
+ mh->ChangeCount(1);
+ }
+
+ /* now we've done with modes processing, put the + back for remote servers */
+ params[5] = "+" + params[5];
+
+#ifdef SUPPORT_IP6LINKS
+ if (params[6].find_first_of(":") != std::string::npos)
+ _new->SetSockAddr(AF_INET6, params[6].c_str(), 0);
+ else
+#endif
+ _new->SetSockAddr(AF_INET, params[6].c_str(), 0);
+
+ Instance->AddGlobalClone(_new);
+
+ bool dosend = !(((this->Utils->quiet_bursts) && (this->bursting || Utils->FindRemoteBurstServer(remoteserver))) || (this->Instance->SilentULine(_new->server)));
+
+ if (dosend)
+ this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s] [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString(), _new->fullname);
+
+ params[7] = ":" + params[7];
+ Utils->DoOneToAllButSender(source,"NICK", params, source);
+
+ // Increment the Source Servers User Count..
+ TreeServer* SourceServer = Utils->FindServer(source);
+ if (SourceServer)
+ {
+ SourceServer->AddUserCount();
+ }
+
+ FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new));
+
+ return true;
+}
+
+/** 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.
+ */
+void TreeSocket::SendFJoins(TreeServer* Current, chanrec* c)
+{
+ std::string buffer;
+ char list[MAXBUF];
+ std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age);
+
+ size_t dlen, curlen;
+ dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
+ int numusers = 0;
+ char* ptr = list + dlen;
+
+ CUList *ulist = c->GetUsers();
+ std::string modes;
+ std::string params;
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ // The first parameter gets a : before it
+ size_t ptrlen = snprintf(ptr, MAXBUF, " %s%s,%s", !numusers ? ":" : "", c->GetAllPrefixChars(i->first), i->first->nick);
+
+ curlen += ptrlen;
+ ptr += ptrlen;
+
+ numusers++;
+
+ if (curlen > (480-NICKMAX))
+ {
+ buffer.append(list).append("\r\n");
+ dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
+ ptr = list + dlen;
+ ptrlen = 0;
+ numusers = 0;
+ }
+ }
+
+ if (numusers)
+ buffer.append(list).append("\r\n");
+
+ buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true)).append("\r\n");
+
+ int linesize = 1;
+ for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
+ {
+ int size = strlen(b->data) + 2;
+ int currsize = linesize + size;
+ if (currsize <= 350)
+ {
+ modes.append("b");
+ params.append(" ").append(b->data);
+ linesize += size;
+ }
+ if ((params.length() >= MAXMODES) || (currsize > 350))
+ {
+ /* Wrap at MAXMODES */
+ buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
+ modes.clear();
+ params.clear();
+ linesize = 1;
+ }
+ }
+
+ /* Only send these if there are any */
+ if (!modes.empty())
+ buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
+
+ this->WriteLine(buffer);
+}
+
+/** Send G, Q, Z and E lines */
+void TreeSocket::SendXLines(TreeServer* Current)
+{
+ char data[MAXBUF];
+ std::string buffer;
+ std::string n = this->Instance->Config->ServerName;
+ const char* sn = n.c_str();
+ /* Yes, these arent too nice looking, but they get the job done */
+ for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+ for (std::vector<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++)
+ {
+ snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
+ }
+
+ if (!buffer.empty())
+ this->WriteLine(buffer);
+}
+
+/** Send channel modes and topics */
+void TreeSocket::SendChannelModes(TreeServer* Current)
+{
+ char data[MAXBUF];
+ std::deque<std::string> list;
+ std::string n = this->Instance->Config->ServerName;
+ const char* sn = n.c_str();
+ Instance->Log(DEBUG,"Sending channels and modes, %d to send", this->Instance->chanlist->size());
+ for (chan_hash::iterator c = this->Instance->chanlist->begin(); c != this->Instance->chanlist->end(); c++)
+ {
+ SendFJoins(Current, c->second);
+ if (*c->second->topic)
+ {
+ snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",sn,c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic);
+ this->WriteLine(data);
+ }
+ FOREACH_MOD_I(this->Instance,I_OnSyncChannel,OnSyncChannel(c->second,(Module*)Utils->Creator,(void*)this));
+ list.clear();
+ c->second->GetExtList(list);
+ for (unsigned int j = 0; j < list.size(); j++)
+ {
+ FOREACH_MOD_I(this->Instance,I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)Utils->Creator,(void*)this,list[j]));
+ }
+ }
+}
+
+/** send all users and their oper state/modes */
+void TreeSocket::SendUsers(TreeServer* Current)
+{
+ char data[MAXBUF];
+ std::deque<std::string> list;
+ std::string dataline;
+ for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++)
+ {
+ if (u->second->registered == REG_ALL)
+ {
+ snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),u->second->GetIPString(),u->second->fullname);
+ this->WriteLine(data);
+ if (*u->second->oper)
+ {
+ snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->nick, u->second->oper);
+ this->WriteLine(data);
+ }
+ if (*u->second->awaymsg)
+ {
+ snprintf(data,MAXBUF,":%s AWAY :%s", u->second->nick, u->second->awaymsg);
+ this->WriteLine(data);
+ }
+ }
+ }
+ for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++)
+ {
+ FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this));
+ list.clear();
+ u->second->GetExtList(list);
+ for (unsigned int j = 0; j < list.size(); j++)
+ {
+ FOREACH_MOD_I(this->Instance,I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)Utils->Creator,(void*)this,list[j]));
+ }
+ }
+}
+
+/** 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
+ * users require their servers to exist, and channels require their
+ * users to exist. You get the idea.
+ */
+void TreeSocket::DoBurst(TreeServer* s)
+{
+ std::string name = s->GetName();
+ std::string burst = "BURST "+ConvToStr(Instance->Time(true));
+ std::string endburst = "ENDBURST";
+ this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s).", name.c_str(), this->GetTheirChallenge().empty() ? "plaintext password" : "SHA256-HMAC challenge-response");
+ this->WriteLine(burst);
+ /* send our version string */
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString());
+ /* Send server tree */
+ this->SendServers(Utils->TreeRoot,s,1);
+ /* Send users and their oper status */
+ this->SendUsers(s);
+ /* Send everything else (channel modes, xlines etc) */
+ this->SendChannelModes(s);
+ this->SendXLines(s);
+ FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)Utils->Creator,(void*)this));
+ this->WriteLine(endburst);
+ this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2.");
+}
+
+/** This function is called when we receive data from a remote
+ * server. We buffer the data in a std::string (it doesnt stay
+ * there for long), reading using InspSocket::Read() which can
+ * read up to 16 kilobytes in one operation.
+ *
+ * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
+ * THE SOCKET OBJECT FOR US.
+ */
+bool TreeSocket::OnDataReady()
+{
+ char* data = this->Read();
+ /* Check that the data read is a valid pointer and it has some content */
+ if (data && *data)
+ {
+ this->in_buffer.append(data);
+ /* While there is at least one new line in the buffer,
+ * do something useful (we hope!) with it.
+ */
+ while (in_buffer.find("\n") != std::string::npos)
+ {
+ std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1);
+ in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n"));
+ /* Use rfind here not find, as theres more
+ * chance of the \r being near the end of the
+ * string, not the start.
+ */
+ if (ret.find("\r") != std::string::npos)
+ ret = in_buffer.substr(0,in_buffer.find("\r")-1);
+ /* Process this one, abort if it
+ * didnt return true.
+ */
+ if (!this->ProcessLine(ret))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ /* EAGAIN returns an empty but non-NULL string, so this
+ * evaluates to TRUE for EAGAIN but to FALSE for EOF.
+ */
+ return (data && !*data);
+}
+
diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp
index d383e2394..f518151e9 100644
--- a/src/modules/m_spanningtree/treesocket2.cpp
+++ b/src/modules/m_spanningtree/treesocket2.cpp
@@ -1 +1,1554 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "socketengine.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/resolvers.h" #include "m_spanningtree/handshaketimer.h" /* $ModDep: m_spanningtree/timesynctimer.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 */ static std::map<std::string, std::string> warned; /* Server names that have had protocol violation warnings displayed for them */ int TreeSocket::WriteLine(std::string line) { Instance->Log(DEBUG, "S[%d] -> %s", this->GetFd(), line.c_str()); line.append("\r\n"); return this->Write(line); } /* Handle ERROR command */ bool TreeSocket::Error(std::deque<std::string> &params) { if (params.size() < 1) return false; this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str()); /* we will return false to cause the socket to close. */ return false; } bool TreeSocket::Modules(const std::string &prefix, std::deque<std::string> &params) { if (params.empty()) return true; if (!this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) { /* Pass it on, not for us */ Utils->DoOneToOne(prefix, "MODULES", params, params[0]); return true; } char strbuf[MAXBUF]; std::deque<std::string> par; par.push_back(prefix); par.push_back(""); userrec* source = this->Instance->FindNick(prefix); if (!source) return true; for (unsigned int i = 0; i < Instance->Config->module_names.size(); i++) { Version V = Instance->modules[i]->GetVersion(); char modulename[MAXBUF]; char flagstate[MAXBUF]; *flagstate = 0; if (V.Flags & VF_STATIC) strlcat(flagstate,", static",MAXBUF); if (V.Flags & VF_VENDOR) strlcat(flagstate,", vendor",MAXBUF); if (V.Flags & VF_COMMON) strlcat(flagstate,", common",MAXBUF); if (V.Flags & VF_SERVICEPROVIDER) strlcat(flagstate,", service provider",MAXBUF); if (!flagstate[0]) strcpy(flagstate," <no flags>"); strlcpy(modulename,Instance->Config->module_names[i].c_str(),256); if (*source->oper) { snprintf(strbuf, MAXBUF, "::%s 900 %s :0x%08lx %d.%d.%d.%d %s (%s)",Instance->Config->ServerName,source->nick,(long unsigned int)Instance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2); } else { snprintf(strbuf, MAXBUF, "::%s 900 %s :%s",Instance->Config->ServerName,source->nick,ServerConfig::CleanFilename(modulename)); } par[1] = strbuf; Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server); } snprintf(strbuf, MAXBUF, "::%s 901 %s :End of MODULES list", Instance->Config->ServerName, source->nick); par[1] = strbuf; Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server); return true; } /** remote MOTD. leet, huh? */ bool TreeSocket::Motd(const std::string &prefix, std::deque<std::string> &params) { if (params.size() > 0) { if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) { /* It's for our server */ string_list results; userrec* source = this->Instance->FindNick(prefix); if (source) { std::deque<std::string> par; par.push_back(prefix); par.push_back(""); if (!Instance->Config->MOTD.size()) { par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing."; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); return true; } par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day"; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++) { par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i]; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); } par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" End of message of the day."; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); } } else { /* Pass it on */ userrec* source = this->Instance->FindNick(prefix); if (source) Utils->DoOneToOne(prefix, "MOTD", params, params[0]); } } return true; } /** remote ADMIN. leet, huh? */ bool TreeSocket::Admin(const std::string &prefix, std::deque<std::string> &params) { if (params.size() > 0) { if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) { /* It's for our server */ string_list results; userrec* source = this->Instance->FindNick(prefix); if (source) { std::deque<std::string> par; par.push_back(prefix); par.push_back(""); par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name - "+Instance->Config->AdminName; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail - "+Instance->Config->AdminEmail; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); } } else { /* Pass it on */ userrec* source = this->Instance->FindNick(prefix); if (source) Utils->DoOneToOne(prefix, "ADMIN", params, params[0]); } } return true; } bool TreeSocket::Stats(const std::string &prefix, std::deque<std::string> &params) { /* Get the reply to a STATS query if it matches this servername, * and send it back as a load of PUSH queries */ if (params.size() > 1) { if (this->Instance->MatchText(this->Instance->Config->ServerName, params[1])) { /* It's for our server */ string_list results; userrec* source = this->Instance->FindNick(prefix); if (source) { std::deque<std::string> par; par.push_back(prefix); par.push_back(""); DoStats(this->Instance, *(params[0].c_str()), source, results); for (size_t i = 0; i < results.size(); i++) { par[1] = "::" + results[i]; Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); } } } else { /* Pass it on */ userrec* source = this->Instance->FindNick(prefix); if (source) Utils->DoOneToOne(prefix, "STATS", params, params[1]); } } return true; } /** Because the core won't let users or even SERVERS set +o, * we use the OPERTYPE command to do this. */ bool TreeSocket::OperType(const std::string &prefix, std::deque<std::string> &params) { if (params.size() != 1) return true; std::string opertype = params[0]; userrec* u = this->Instance->FindNick(prefix); if (u) { u->modes[UM_OPERATOR] = 1; this->Instance->all_opers.push_back(u); strlcpy(u->oper,opertype.c_str(),NICKMAX-1); Utils->DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server); this->Instance->SNO->WriteToSnoMask('o',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server, u->nick,u->ident,u->host,irc::Spacify(opertype.c_str())); } return true; } /** Because Andy insists that services-compatible servers must * implement SVSNICK and SVSJOIN, that's exactly what we do :p */ bool TreeSocket::ForceNick(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 3) return true; userrec* u = this->Instance->FindNick(params[0]); if (u) { Utils->DoOneToAllButSender(prefix,"SVSNICK",params,prefix); if (IS_LOCAL(u)) { std::deque<std::string> par; par.push_back(params[1]); if (!u->ForceNickChange(params[1].c_str())) { userrec::QuitUser(this->Instance, u, "Nickname collision"); return true; } u->age = atoi(params[2].c_str()); } } return true; } bool TreeSocket::OperQuit(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; userrec* u = this->Instance->FindNick(prefix); if (u) { u->SetOperQuit(params[0]); params[0] = ":" + params[0]; Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix); } return true; } bool TreeSocket::ServiceJoin(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 2) return true; userrec* u = this->Instance->FindNick(params[0]); if (u) { /* only join if it's local, otherwise just pass it on! */ if (IS_LOCAL(u)) chanrec::JoinUser(this->Instance, u, params[1].c_str(), false, "", Instance->Time()); Utils->DoOneToAllButSender(prefix,"SVSJOIN",params,prefix); } return true; } bool TreeSocket::RemoteRehash(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return false; std::string servermask = params[0]; if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask)) { this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated by \002"+prefix+"\002."); this->Instance->RehashServer(); Utils->ReadConfiguration(false); InitializeDisabledCommands(Instance->Config->DisabledCommands, Instance); } Utils->DoOneToAllButSender(prefix,"REHASH",params,prefix); return true; } bool TreeSocket::RemoteKill(const std::string &prefix, std::deque<std::string> &params) { if (params.size() != 2) return true; userrec* who = this->Instance->FindNick(params[0]); if (who) { /* Prepend kill source, if we don't have one */ if (*(params[1].c_str()) != '[') { params[1] = "[" + prefix + "] Killed (" + params[1] +")"; } std::string reason = params[1]; params[1] = ":" + params[1]; Utils->DoOneToAllButSender(prefix,"KILL",params,prefix); // NOTE: This is safe with kill hiding on, as RemoteKill is only reached if we have a server prefix. // in short this is not executed for USERS. who->Write(":%s KILL %s :%s (%s)", prefix.c_str(), who->nick, prefix.c_str(), reason.c_str()); userrec::QuitUser(this->Instance,who,reason); } return true; } bool TreeSocket::LocalPong(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; if (params.size() == 1) { TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) { ServerSource->SetPingFlag(); ServerSource->rtt = Instance->Time() - ServerSource->LastPing; } } else { std::string forwardto = params[1]; if (forwardto == this->Instance->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. */ userrec* u = this->Instance->FindNick(prefix); if (u) { u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str()); } } else { // not for us, pass it on :) Utils->DoOneToOne(prefix,"PONG",params,forwardto); } } return true; } bool TreeSocket::MetaData(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 2) return true; else if (params.size() < 3) params.push_back(""); TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) { Utils->SetRemoteBursting(ServerSource, false); if (params[0] == "*") { FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_OTHER,NULL,params[1],params[2])); } else if (*(params[0].c_str()) == '#') { chanrec* c = this->Instance->FindChan(params[0]); if (c) { FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2])); } } else if (*(params[0].c_str()) != '#') { userrec* u = this->Instance->FindNick(params[0]); if (u) { FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2])); } } } params[2] = ":" + params[2]; Utils->DoOneToAllButSender(prefix,"METADATA",params,prefix); return true; } bool TreeSocket::ServerVersion(const std::string &prefix, std::deque<std::string> &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; } bool TreeSocket::ChangeHost(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; userrec* u = this->Instance->FindNick(prefix); if (u) { u->ChangeDisplayedHost(params[0].c_str()); Utils->DoOneToAllButSender(prefix,"FHOST",params,u->server); } return true; } bool TreeSocket::AddLine(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 6) return true; bool propogate = false; if (!this->bursting) Utils->lines_to_apply = 0; switch (*(params[0].c_str())) { case 'Z': propogate = Instance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); Instance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); if (propogate) Utils->lines_to_apply |= APPLY_ZLINES; break; case 'Q': propogate = Instance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); Instance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); if (propogate) Utils->lines_to_apply |= APPLY_QLINES; break; case 'E': propogate = Instance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); Instance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); break; case 'G': propogate = Instance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); Instance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); if (propogate) Utils->lines_to_apply |= APPLY_GLINES; break; case 'K': propogate = Instance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); if (propogate) Utils->lines_to_apply |= APPLY_KLINES; break; default: /* Just in case... */ this->Instance->SNO->WriteToSnoMask('x',"\2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!"); propogate = false; break; } /* Send it on its way */ if (propogate) { if (atoi(params[4].c_str())) { time_t c_requires_crap = ConvToInt(params[4]) + Instance->Time(); this->Instance->SNO->WriteToSnoMask('x',"%s Added %cLINE on %s to expire on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),Instance->TimeString(c_requires_crap).c_str(),params[5].c_str()); } else { this->Instance->SNO->WriteToSnoMask('x',"%s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str()); } params[5] = ":" + params[5]; Utils->DoOneToAllButSender(prefix,"ADDLINE",params,prefix); } if (!this->bursting) { Instance->XLines->apply_lines(Utils->lines_to_apply); Utils->lines_to_apply = 0; } return true; } bool TreeSocket::ChangeName(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; userrec* u = this->Instance->FindNick(prefix); if (u) { u->ChangeName(params[0].c_str()); params[0] = ":" + params[0]; Utils->DoOneToAllButSender(prefix,"FNAME",params,u->server); } return true; } bool TreeSocket::Whois(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; userrec* u = this->Instance->FindNick(prefix); if (u) { // an incoming request if (params.size() == 1) { userrec* x = this->Instance->FindNick(params[0]); if ((x) && (IS_LOCAL(x))) { userrec* x = this->Instance->FindNick(params[0]); char signon[MAXBUF]; char idle[MAXBUF]; snprintf(signon, MAXBUF, "%lu", (unsigned long)x->signon); snprintf(idle, MAXBUF, "%lu", (unsigned long)abs((x->idle_lastmsg) - Instance->Time(true))); std::deque<std::string> par; par.push_back(prefix); par.push_back(signon); par.push_back(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]; userrec* who_to_send_to = this->Instance->FindNick(who_did_the_whois); if ((who_to_send_to) && (IS_LOCAL(who_to_send_to))) { // 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))) { do_whois(this->Instance, 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); } } } return true; } bool TreeSocket::Push(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 2) return true; userrec* u = this->Instance->FindNick(params[0]); if (!u) return true; if (IS_LOCAL(u)) { u->Write(params[1]); } else { // continue the raw onwards params[1] = ":" + params[1]; Utils->DoOneToOne(prefix,"PUSH",params,u->server); } return true; } bool TreeSocket::HandleSetTime(const std::string &prefix, std::deque<std::string> &params) { if (!params.size() || !Utils->EnableTimeSync) return true; bool force = false; if ((params.size() == 2) && (params[1] == "FORCE")) force = true; time_t them = atoi(params[0].c_str()); time_t us = Instance->Time(false); time_t diff = them - us; Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix); if (force || (them != us)) { time_t old = Instance->SetTimeDelta(diff); Instance->Log(DEBUG, "TS (diff %d) from %s applied (old delta was %d)", diff, prefix.c_str(), old); } return true; } bool TreeSocket::Time(const std::string &prefix, std::deque<std::string> &params) { // :source.server TIME remote.server sendernick // :remote.server TIME source.server sendernick TS if (params.size() == 2) { // someone querying our time? if (this->Instance->Config->ServerName == params[0]) { userrec* u = this->Instance->FindNick(params[1]); if (u) { params.push_back(ConvToStr(Instance->Time(false))); params[0] = prefix; Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]); } } else { // not us, pass it on userrec* u = this->Instance->FindNick(params[1]); if (u) Utils->DoOneToOne(prefix,"TIME",params,params[0]); } } else if (params.size() == 3) { // a response to a previous TIME userrec* u = this->Instance->FindNick(params[1]); if ((u) && (IS_LOCAL(u))) { time_t rawtime = atol(params[2].c_str()); struct tm * timeinfo; timeinfo = localtime(&rawtime); char tms[26]; snprintf(tms,26,"%s",asctime(timeinfo)); tms[24] = 0; u->WriteServ("391 %s %s :%s",u->nick,prefix.c_str(),tms); } else { if (u) Utils->DoOneToOne(prefix,"TIME",params,u->server); } } return true; } bool TreeSocket::LocalPing(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; if (params.size() == 1) { std::string stufftobounce = params[0]; this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" PONG "+stufftobounce); return true; } else { std::string forwardto = params[1]; if (forwardto == this->Instance->Config->ServerName) { // this is a ping for us, send back PONG to the requesting server params[1] = params[0]; params[0] = forwardto; Utils->DoOneToOne(forwardto,"PONG",params,params[1]); } else { // not for us, pass it on :) Utils->DoOneToOne(prefix,"PING",params,forwardto); } return true; } } /** TODO: This creates a total mess of output and needs to really use irc::modestacker. */ bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 1) return true; chanrec* c = Instance->FindChan(params[0]); if (c) { for (char modeletter = 'A'; modeletter <= 'z'; modeletter++) { ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL); if (mh) mh->RemoveMode(c); } } return true; } bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> &params) { if (params.size() < 4) return false; std::string servername = params[0]; std::string password = params[1]; // hopcount is not used for a remote server, we calculate this ourselves std::string description = params[3]; TreeServer* ParentOfThis = Utils->FindServer(prefix); if (!ParentOfThis) { this->SendError("Protocol error - Introduced remote server from unknown server "+prefix); return false; } TreeServer* CheckDupe = Utils->FindServer(servername); if (CheckDupe) { this->SendError("Server "+servername+" already exists!"); this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, already exists. Closing link with " + prefix); return false; } Link* lnk = Utils->FindLink(servername); TreeServer* Node = new TreeServer(this->Utils,this->Instance,servername,description,ParentOfThis,NULL, lnk ? lnk->Hidden : false); ParentOfThis->AddChild(Node); params[3] = ":" + params[3]; Utils->SetRemoteBursting(Node, true); Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix); this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")"); return true; } bool TreeSocket::ComparePass(const std::string &ours, const std::string &theirs) { if ((!strncmp(ours.c_str(), "HMAC-SHA256:", 12)) || (!strncmp(theirs.c_str(), "HMAC-SHA256:", 12))) { /* One or both of us specified hmac sha256, but we don't have sha256 module loaded! * We can't allow this password as valid. */ if (!Instance->FindModule("m_sha256.so") || !Utils->ChallengeResponse) return false; else /* Straight string compare of hashes */ return ours == theirs; } else /* Straight string compare of plaintext */ return ours == theirs; } bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> &params) { if (params.size() < 4) return false; irc::string servername = params[0].c_str(); std::string sname = params[0]; std::string password = params[1]; std::string description = params[3]; int hops = atoi(params[2].c_str()); this->InboundServerName = sname; this->InboundDescription = description; if (!sentcapab) this->SendCapabilities(); if (hops) { this->SendError("Server too far away for authentication"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); return false; } for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) { if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password)) || (x->RecvPass == password && (this->GetTheirChallenge().empty())))) { TreeServer* CheckDupe = Utils->FindServer(sname); if (CheckDupe) { this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); return false; } // Begin the sync here. this kickstarts the // other side, waiting in WAIT_AUTH_2 state, // into starting their burst, as it shows // that we're happy. this->LinkState = CONNECTED; // we should add the details of this server now // to the servers tree, as a child of the root // node. TreeServer* Node = new TreeServer(this->Utils,this->Instance,sname,description,Utils->TreeRoot,this,x->Hidden); Utils->TreeRoot->AddChild(Node); params[3] = ":" + params[3]; Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,sname); this->bursting = true; this->DoBurst(Node); return true; } } this->SendError("Invalid credentials"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials"); return false; } bool TreeSocket::Inbound_Server(std::deque<std::string> &params) { if (params.size() < 4) return false; irc::string servername = params[0].c_str(); std::string sname = params[0]; std::string password = params[1]; std::string description = params[3]; int hops = atoi(params[2].c_str()); this->InboundServerName = sname; this->InboundDescription = description; if (!sentcapab) this->SendCapabilities(); if (hops) { this->SendError("Server too far away for authentication"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); return false; } for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) { if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password) || x->RecvPass == password && (this->GetTheirChallenge().empty())))) { /* First check for instances of the server that are waiting between the inbound and outbound SERVER command */ TreeSocket* CheckDupeSocket = Utils->FindBurstingServer(sname); if (CheckDupeSocket) { /* If we find one, we abort the link to prevent a race condition */ this->SendError("Negotiation collision"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists in a negotiating state."); CheckDupeSocket->SendError("Negotiation collision"); Instance->SE->DelFd(CheckDupeSocket); CheckDupeSocket->Close(); delete CheckDupeSocket; return false; } /* Now check for fully initialized instances of the server */ TreeServer* CheckDupe = Utils->FindServer(sname); if (CheckDupe) { this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); return false; } this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")"); if (this->Hook) { std::string name = InspSocketNameRequest((Module*)Utils->Creator, this->Hook).Send(); this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2"); } Utils->AddBurstingServer(sname,this); // this is good. Send our details: Our server name and description and hopcount of 0, // along with the sendpass from this block. this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc); // move to the next state, we are now waiting for THEM. this->LinkState = WAIT_AUTH_2; return true; } } this->SendError("Invalid credentials"); this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials"); return false; } void TreeSocket::Split(const std::string &line, std::deque<std::string> &n) { n.clear(); irc::tokenstream tokens(line); std::string param; while (tokens.GetToken(param)) { if (!param.empty()) n.push_back(param); } return; } bool TreeSocket::ProcessLine(std::string &line) { std::deque<std::string> params; irc::string command; std::string prefix; line = line.substr(0, line.find_first_of("\r\n")); if (line.empty()) return true; Instance->Log(DEBUG, "S[%d] <- %s", this->GetFd(), line.c_str()); this->Split(line.c_str(),params); if (params.empty()) return true; if ((params[0][0] == ':') && (params.size() > 1)) { prefix = params[0].substr(1); params.pop_front(); } command = params[0].c_str(); params.pop_front(); switch (this->LinkState) { TreeServer* Node; case WAIT_AUTH_1: // Waiting for SERVER command from remote server. Server initiating // the connection sends the first SERVER command, listening server // replies with theirs if its happy, then if the initiator is happy, // it starts to send its net sync, which starts the merge, otherwise // it sends an ERROR. if (command == "PASS") { /* Silently ignored */ } else if (command == "SERVER") { return this->Inbound_Server(params); } else if (command == "ERROR") { return this->Error(params); } else if (command == "USER") { this->SendError("Client connections to this port are prohibited."); return false; } else if (command == "CAPAB") { return this->Capab(params); } else if ((command == "U") || (command == "S")) { this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!"); return false; } else { irc::string error = "Invalid command in negotiation phase: " + command; this->SendError(assign(error)); return false; } break; case WAIT_AUTH_2: // Waiting for start of other side's netmerge to say they liked our // password. if (command == "SERVER") { // cant do this, they sent it to us in the WAIT_AUTH_1 state! // silently ignore. return true; } else if ((command == "U") || (command == "S")) { this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!"); return false; } else if (command == "BURST") { if (params.size() && Utils->EnableTimeSync) { bool we_have_delta = (Instance->Time(false) != Instance->Time(true)); time_t them = atoi(params[0].c_str()); time_t delta = them - Instance->Time(false); if ((delta < -300) || (delta > 300)) { Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta)); SendError("Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!"); return false; } else if ((delta < -30) || (delta > 30)) { Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs(delta)); } if (!Utils->MasterTime && !we_have_delta) { this->Instance->SetTimeDelta(delta); // Send this new timestamp to any other servers Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params); } } this->LinkState = CONNECTED; Link* lnk = Utils->FindLink(InboundServerName); Node = new TreeServer(this->Utils,this->Instance, InboundServerName, InboundDescription, Utils->TreeRoot, this, lnk ? lnk->Hidden : false); Utils->DelBurstingServer(this); Utils->TreeRoot->AddChild(Node); params.clear(); params.push_back(InboundServerName); params.push_back("*"); params.push_back("1"); params.push_back(":"+InboundDescription); Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,InboundServerName); this->bursting = true; this->DoBurst(Node); } else if (command == "ERROR") { return this->Error(params); } else if (command == "CAPAB") { return this->Capab(params); } break; case LISTENER: this->SendError("Internal error -- listening socket accepted its own descriptor!!!"); return false; break; case CONNECTING: if (command == "SERVER") { // another server we connected to, which was in WAIT_AUTH_1 state, // has just sent us their credentials. If we get this far, theyre // happy with OUR credentials, and they are now in WAIT_AUTH_2 state. // if we're happy with this, we should send our netburst which // kickstarts the merge. return this->Outbound_Reply_Server(params); } else if (command == "ERROR") { return this->Error(params); } else if (command == "CAPAB") { return this->Capab(params); } break; case CONNECTED: // This is the 'authenticated' state, when all passwords // have been exchanged and anything past this point is taken // as gospel. if (!prefix.empty()) { std::string direction = prefix; userrec* t = this->Instance->FindNick(prefix); if (t) { direction = t->server; } TreeServer* route_back_again = Utils->BestRouteTo(direction); if ((!route_back_again) || (route_back_again->GetSocket() != this)) { if (route_back_again) Instance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str()); return true; } /* Fix by brain: * When there is activity on the socket, reset the ping counter so * that we're not wasting bandwidth pinging an active server. */ route_back_again->SetNextPingTime(time(NULL) + 60); route_back_again->SetPingFlag(); } else { prefix = this->GetName(); } if ((command == "MODE") && (params.size() >= 2)) { chanrec* channel = Instance->FindChan(params[0]); if (channel) { userrec* x = Instance->FindNick(prefix); if (x) { if (warned.find(x->server) == warned.end()) { Instance->Log(DEFAULT,"WARNING: I revceived modes '%s' from another server '%s'. This is not compliant with InspIRCd. Please check that server for bugs.", params[1].c_str(), x->server); Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending nonstandard modes: '%s MODE %s' where FMODE should be used, and may cause desyncs.", x->server, x->nick, params[1].c_str()); warned[x->server] = x->nick; } } } } if (command == "SVSMODE") { /* Services expects us to implement * SVSMODE. In inspircd its the same as * MODE anyway. */ command = "MODE"; } std::string target; /* Yes, know, this is a mess. Its reasonably fast though as we're * working with std::string here. */ if ((command == "NICK") && (params.size() >= 8)) { return this->IntroduceClient(prefix,params); } else if (command == "FJOIN") { TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) Utils->SetRemoteBursting(ServerSource, false); return this->ForceJoin(prefix,params); } else if (command == "STATS") { return this->Stats(prefix, params); } else if (command == "MOTD") { return this->Motd(prefix, params); } else if (command == "KILL" && Utils->IsServer(prefix)) { return this->RemoteKill(prefix,params); } else if (command == "MODULES") { return this->Modules(prefix, params); } else if (command == "ADMIN") { return this->Admin(prefix, params); } else if (command == "SERVER") { return this->RemoteServer(prefix,params); } else if (command == "ERROR") { return this->Error(params); } else if (command == "OPERTYPE") { return this->OperType(prefix,params); } else if (command == "FMODE") { TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) Utils->SetRemoteBursting(ServerSource, false); return this->ForceMode(prefix,params); } else if (command == "FTOPIC") { return this->ForceTopic(prefix,params); } else if (command == "REHASH") { return this->RemoteRehash(prefix,params); } else if (command == "METADATA") { return this->MetaData(prefix,params); } else if (command == "REMSTATUS") { return this->RemoveStatus(prefix,params); } else if (command == "PING") { if (prefix.empty()) prefix = this->GetName(); /* * We just got a ping from a server that's bursting. * This can't be right, so set them to not bursting, and * apply their lines. */ TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) Utils->SetRemoteBursting(ServerSource, false); if (this->bursting) { this->bursting = false; Instance->XLines->apply_lines(Utils->lines_to_apply); Utils->lines_to_apply = 0; } return this->LocalPing(prefix,params); } else if (command == "PONG") { if (prefix.empty()) prefix = this->GetName(); /* * We just got a pong from a server that's bursting. * This can't be right, so set them to not bursting, and * apply their lines. */ TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) Utils->SetRemoteBursting(ServerSource, false); if (this->bursting) { this->bursting = false; Instance->XLines->apply_lines(Utils->lines_to_apply); Utils->lines_to_apply = 0; } return this->LocalPong(prefix,params); } else if (command == "VERSION") { return this->ServerVersion(prefix,params); } else if (command == "FHOST") { return this->ChangeHost(prefix,params); } else if (command == "FNAME") { return this->ChangeName(prefix,params); } else if (command == "ADDLINE") { TreeServer* ServerSource = Utils->FindServer(prefix); if (ServerSource) Utils->SetRemoteBursting(ServerSource, false); return this->AddLine(prefix,params); } else if (command == "SVSNICK") { if (prefix.empty()) { prefix = this->GetName(); } return this->ForceNick(prefix,params); } else if (command == "OPERQUIT") { return this->OperQuit(prefix,params); } else if (command == "IDLE") { return this->Whois(prefix,params); } else if (command == "PUSH") { return this->Push(prefix,params); } else if (command == "TIMESET") { return this->HandleSetTime(prefix, params); } else if (command == "TIME") { return this->Time(prefix,params); } else if ((command == "KICK") && (Utils->IsServer(prefix))) { std::string sourceserv = this->myhost; if (params.size() == 3) { userrec* user = this->Instance->FindNick(params[1]); chanrec* chan = this->Instance->FindChan(params[0]); if (user && chan) { if (!chan->ServerKickUser(user, params[2].c_str(), false)) /* Yikes, the channels gone! */ delete chan; } } if (!this->InboundServerName.empty()) { sourceserv = this->InboundServerName; } return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); } else if (command == "SVSJOIN") { if (prefix.empty()) { prefix = this->GetName(); } return this->ServiceJoin(prefix,params); } else if (command == "SQUIT") { if (params.size() == 2) { this->Squit(Utils->FindServer(params[0]),params[1]); } return true; } else if (command == "OPERNOTICE") { std::string sourceserv = this->myhost; if (!this->InboundServerName.empty()) sourceserv = this->InboundServerName; if (params.size() >= 1) Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]); return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); } else if (command == "MODENOTICE") { std::string sourceserv = this->myhost; if (!this->InboundServerName.empty()) sourceserv = this->InboundServerName; if (params.size() >= 2) { Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str()); } return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); } else if (command == "SNONOTICE") { std::string sourceserv = this->myhost; if (!this->InboundServerName.empty()) sourceserv = this->InboundServerName; if (params.size() >= 2) { Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]); } return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); } else if (command == "ENDBURST") { this->bursting = false; Instance->XLines->apply_lines(Utils->lines_to_apply); Utils->lines_to_apply = 0; std::string sourceserv = this->myhost; if (!this->InboundServerName.empty()) sourceserv = this->InboundServerName; this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str()); Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server"); rmode.Send(Instance); return true; } else { // not a special inter-server command. // Emulate the actual user doing the command, // this saves us having a huge ugly parser. userrec* who = this->Instance->FindNick(prefix); std::string sourceserv = this->myhost; if (!this->InboundServerName.empty()) { sourceserv = this->InboundServerName; } if ((!who) && (command == "MODE")) { if (Utils->IsServer(prefix)) { const char* modelist[127]; for (size_t i = 0; i < params.size(); i++) modelist[i] = params[i].c_str(); userrec* fake = new userrec(Instance); fake->SetFd(FD_MAGIC_NUMBER); this->Instance->SendMode(modelist, params.size(), fake); delete fake; /* Hot potato! pass it on! */ return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); } } if (who) { if ((command == "NICK") && (params.size() > 0)) { /* On nick messages, check that the nick doesnt * already exist here. If it does, kill their copy, * and our copy. */ userrec* x = this->Instance->FindNick(params[0]); if ((x) && (x != who)) { std::deque<std::string> p; p.push_back(params[0]); p.push_back("Nickname collision ("+prefix+" -> "+params[0]+")"); Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p); p.clear(); p.push_back(prefix); p.push_back("Nickname collision"); Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p); userrec::QuitUser(this->Instance,x,"Nickname collision ("+prefix+" -> "+params[0]+")"); userrec* y = this->Instance->FindNick(prefix); if (y) { userrec::QuitUser(this->Instance,y,"Nickname collision"); } return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); } } // its a user target = who->server; const char* strparams[127]; for (unsigned int q = 0; q < params.size(); q++) { strparams[q] = params[q].c_str(); } switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who)) { case CMD_INVALID: this->SendError("Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules"); return false; break; case CMD_FAILURE: return true; break; default: /* CMD_SUCCESS and CMD_USER_DELETED fall through here */ break; } } else { // its not a user. Its either a server, or somethings screwed up. if (Utils->IsServer(prefix)) target = this->Instance->Config->ServerName; else return true; } return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); } return true; break; } return true; } std::string TreeSocket::GetName() { std::string sourceserv = this->myhost; if (!this->InboundServerName.empty()) { sourceserv = this->InboundServerName; } return sourceserv; } void TreeSocket::OnTimeout() { if (this->LinkState == CONNECTING) { this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out."); Link* MyLink = Utils->FindLink(myhost); if (MyLink) Utils->DoFailOver(MyLink); } } void TreeSocket::OnClose() { // Connection closed. // If the connection is fully up (state CONNECTED) // then propogate a netsplit to all peers. std::string quitserver = this->myhost; if (!this->InboundServerName.empty()) { quitserver = this->InboundServerName; } TreeServer* s = Utils->FindServer(quitserver); if (s) { Squit(s,"Remote host closed the connection"); } if (!quitserver.empty()) { this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str()); time_t server_uptime = Instance->Time() - this->age; if (server_uptime) Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str()); } } int TreeSocket::OnIncomingConnection(int newsock, char* ip) { /* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port, * or discovering if this port is the server port, we don't allow connections from any * IPs for which we don't have a link block. */ bool found = false; found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end()); if (!found) { for (vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++) if (irc::sockets::MatchCIDR(ip, (*i).c_str())) found = true; if (!found) { this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip); close(newsock); return false; } } TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook); s = s; /* Whinge whinge whinge, thats all GCC ever does. */ return true; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 */
+
+static std::map<std::string, std::string> warned; /* Server names that have had protocol violation warnings displayed for them */
+
+int TreeSocket::WriteLine(std::string line)
+{
+ Instance->Log(DEBUG, "S[%d] -> %s", this->GetFd(), line.c_str());
+ line.append("\r\n");
+ return this->Write(line);
+}
+
+
+/* Handle ERROR command */
+bool TreeSocket::Error(std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return false;
+ this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str());
+ /* we will return false to cause the socket to close. */
+ return false;
+}
+
+bool TreeSocket::Modules(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.empty())
+ return true;
+
+ if (!this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
+ {
+ /* Pass it on, not for us */
+ Utils->DoOneToOne(prefix, "MODULES", params, params[0]);
+ return true;
+ }
+
+ char strbuf[MAXBUF];
+ std::deque<std::string> par;
+ par.push_back(prefix);
+ par.push_back("");
+
+ userrec* source = this->Instance->FindNick(prefix);
+ if (!source)
+ return true;
+
+ for (unsigned int i = 0; i < Instance->Config->module_names.size(); i++)
+ {
+ Version V = Instance->modules[i]->GetVersion();
+ char modulename[MAXBUF];
+ char flagstate[MAXBUF];
+ *flagstate = 0;
+ if (V.Flags & VF_STATIC)
+ strlcat(flagstate,", static",MAXBUF);
+ if (V.Flags & VF_VENDOR)
+ strlcat(flagstate,", vendor",MAXBUF);
+ if (V.Flags & VF_COMMON)
+ strlcat(flagstate,", common",MAXBUF);
+ if (V.Flags & VF_SERVICEPROVIDER)
+ strlcat(flagstate,", service provider",MAXBUF);
+ if (!flagstate[0])
+ strcpy(flagstate," <no flags>");
+ strlcpy(modulename,Instance->Config->module_names[i].c_str(),256);
+ if (*source->oper)
+ {
+ snprintf(strbuf, MAXBUF, "::%s 900 %s :0x%08lx %d.%d.%d.%d %s (%s)",Instance->Config->ServerName,source->nick,(long unsigned int)Instance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2);
+ }
+ else
+ {
+ snprintf(strbuf, MAXBUF, "::%s 900 %s :%s",Instance->Config->ServerName,source->nick,ServerConfig::CleanFilename(modulename));
+ }
+ par[1] = strbuf;
+ Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server);
+ }
+ snprintf(strbuf, MAXBUF, "::%s 901 %s :End of MODULES list", Instance->Config->ServerName, source->nick);
+ par[1] = strbuf;
+ Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server);
+ return true;
+}
+
+/** remote MOTD. leet, huh? */
+bool TreeSocket::Motd(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() > 0)
+ {
+ if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
+ {
+ /* It's for our server */
+ string_list results;
+ userrec* source = this->Instance->FindNick(prefix);
+
+ if (source)
+ {
+ std::deque<std::string> par;
+ par.push_back(prefix);
+ par.push_back("");
+
+ if (!Instance->Config->MOTD.size())
+ {
+ par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing.";
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ return true;
+ }
+
+ par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day";
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+
+ for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++)
+ {
+ par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i];
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ }
+
+ par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" End of message of the day.";
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ }
+ }
+ else
+ {
+ /* Pass it on */
+ userrec* source = this->Instance->FindNick(prefix);
+ if (source)
+ Utils->DoOneToOne(prefix, "MOTD", params, params[0]);
+ }
+ }
+ return true;
+}
+
+/** remote ADMIN. leet, huh? */
+bool TreeSocket::Admin(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() > 0)
+ {
+ if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
+ {
+ /* It's for our server */
+ string_list results;
+ userrec* source = this->Instance->FindNick(prefix);
+ if (source)
+ {
+ std::deque<std::string> par;
+ par.push_back(prefix);
+ par.push_back("");
+ par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName;
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name - "+Instance->Config->AdminName;
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick;
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail - "+Instance->Config->AdminEmail;
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ }
+ }
+ else
+ {
+ /* Pass it on */
+ userrec* source = this->Instance->FindNick(prefix);
+ if (source)
+ Utils->DoOneToOne(prefix, "ADMIN", params, params[0]);
+ }
+ }
+ return true;
+}
+
+bool TreeSocket::Stats(const std::string &prefix, std::deque<std::string> &params)
+{
+ /* Get the reply to a STATS query if it matches this servername,
+ * and send it back as a load of PUSH queries
+ */
+ if (params.size() > 1)
+ {
+ if (this->Instance->MatchText(this->Instance->Config->ServerName, params[1]))
+ {
+ /* It's for our server */
+ string_list results;
+ userrec* source = this->Instance->FindNick(prefix);
+ if (source)
+ {
+ std::deque<std::string> par;
+ par.push_back(prefix);
+ par.push_back("");
+ DoStats(this->Instance, *(params[0].c_str()), source, results);
+ for (size_t i = 0; i < results.size(); i++)
+ {
+ par[1] = "::" + results[i];
+ Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
+ }
+ }
+ }
+ else
+ {
+ /* Pass it on */
+ userrec* source = this->Instance->FindNick(prefix);
+ if (source)
+ Utils->DoOneToOne(prefix, "STATS", params, params[1]);
+ }
+ }
+ return true;
+}
+
+
+/** Because the core won't let users or even SERVERS set +o,
+ * we use the OPERTYPE command to do this.
+ */
+bool TreeSocket::OperType(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() != 1)
+ return true;
+ std::string opertype = params[0];
+ userrec* u = this->Instance->FindNick(prefix);
+ if (u)
+ {
+ u->modes[UM_OPERATOR] = 1;
+ this->Instance->all_opers.push_back(u);
+ strlcpy(u->oper,opertype.c_str(),NICKMAX-1);
+ Utils->DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
+ this->Instance->SNO->WriteToSnoMask('o',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server, u->nick,u->ident,u->host,irc::Spacify(opertype.c_str()));
+ }
+ return true;
+}
+
+/** Because Andy insists that services-compatible servers must
+ * implement SVSNICK and SVSJOIN, that's exactly what we do :p
+ */
+bool TreeSocket::ForceNick(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 3)
+ return true;
+
+ userrec* u = this->Instance->FindNick(params[0]);
+
+ if (u)
+ {
+ Utils->DoOneToAllButSender(prefix,"SVSNICK",params,prefix);
+ if (IS_LOCAL(u))
+ {
+ std::deque<std::string> par;
+ par.push_back(params[1]);
+ if (!u->ForceNickChange(params[1].c_str()))
+ {
+ userrec::QuitUser(this->Instance, u, "Nickname collision");
+ return true;
+ }
+ u->age = atoi(params[2].c_str());
+ }
+ }
+ return true;
+}
+
+bool TreeSocket::OperQuit(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+
+ userrec* u = this->Instance->FindNick(prefix);
+
+ if (u)
+ {
+ u->SetOperQuit(params[0]);
+ params[0] = ":" + params[0];
+ Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix);
+ }
+ return true;
+}
+
+bool TreeSocket::ServiceJoin(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 2)
+ return true;
+
+ userrec* u = this->Instance->FindNick(params[0]);
+
+ if (u)
+ {
+ /* only join if it's local, otherwise just pass it on! */
+ if (IS_LOCAL(u))
+ chanrec::JoinUser(this->Instance, u, params[1].c_str(), false, "", Instance->Time());
+ Utils->DoOneToAllButSender(prefix,"SVSJOIN",params,prefix);
+ }
+ return true;
+}
+
+bool TreeSocket::RemoteRehash(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return false;
+
+ std::string servermask = params[0];
+
+ if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask))
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated by \002"+prefix+"\002.");
+ this->Instance->RehashServer();
+ Utils->ReadConfiguration(false);
+ InitializeDisabledCommands(Instance->Config->DisabledCommands, Instance);
+ }
+ Utils->DoOneToAllButSender(prefix,"REHASH",params,prefix);
+ return true;
+}
+
+bool TreeSocket::RemoteKill(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() != 2)
+ return true;
+
+ userrec* who = this->Instance->FindNick(params[0]);
+
+ if (who)
+ {
+ /* Prepend kill source, if we don't have one */
+ if (*(params[1].c_str()) != '[')
+ {
+ params[1] = "[" + prefix + "] Killed (" + params[1] +")";
+ }
+ std::string reason = params[1];
+ params[1] = ":" + params[1];
+ Utils->DoOneToAllButSender(prefix,"KILL",params,prefix);
+ // NOTE: This is safe with kill hiding on, as RemoteKill is only reached if we have a server prefix.
+ // in short this is not executed for USERS.
+ who->Write(":%s KILL %s :%s (%s)", prefix.c_str(), who->nick, prefix.c_str(), reason.c_str());
+ userrec::QuitUser(this->Instance,who,reason);
+ }
+ return true;
+}
+
+bool TreeSocket::LocalPong(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+
+ if (params.size() == 1)
+ {
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ {
+ ServerSource->SetPingFlag();
+ ServerSource->rtt = Instance->Time() - ServerSource->LastPing;
+ }
+ }
+ else
+ {
+ std::string forwardto = params[1];
+ if (forwardto == this->Instance->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.
+ */
+ userrec* u = this->Instance->FindNick(prefix);
+ if (u)
+ {
+ u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
+ }
+ }
+ else
+ {
+ // not for us, pass it on :)
+ Utils->DoOneToOne(prefix,"PONG",params,forwardto);
+ }
+ }
+
+ return true;
+}
+
+bool TreeSocket::MetaData(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 2)
+ return true;
+ else if (params.size() < 3)
+ params.push_back("");
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ {
+ Utils->SetRemoteBursting(ServerSource, false);
+
+ if (params[0] == "*")
+ {
+ FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_OTHER,NULL,params[1],params[2]));
+ }
+ else if (*(params[0].c_str()) == '#')
+ {
+ chanrec* c = this->Instance->FindChan(params[0]);
+ if (c)
+ {
+ FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]));
+ }
+ }
+ else if (*(params[0].c_str()) != '#')
+ {
+ userrec* u = this->Instance->FindNick(params[0]);
+ if (u)
+ {
+ FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2]));
+ }
+ }
+ }
+
+ params[2] = ":" + params[2];
+ Utils->DoOneToAllButSender(prefix,"METADATA",params,prefix);
+ return true;
+}
+
+bool TreeSocket::ServerVersion(const std::string &prefix, std::deque<std::string> &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;
+}
+
+bool TreeSocket::ChangeHost(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+ userrec* u = this->Instance->FindNick(prefix);
+
+ if (u)
+ {
+ u->ChangeDisplayedHost(params[0].c_str());
+ Utils->DoOneToAllButSender(prefix,"FHOST",params,u->server);
+ }
+ return true;
+}
+
+bool TreeSocket::AddLine(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 6)
+ return true;
+ bool propogate = false;
+ if (!this->bursting)
+ Utils->lines_to_apply = 0;
+ switch (*(params[0].c_str()))
+ {
+ case 'Z':
+ propogate = Instance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
+ Instance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
+ if (propogate)
+ Utils->lines_to_apply |= APPLY_ZLINES;
+ break;
+ case 'Q':
+ propogate = Instance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
+ Instance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
+ if (propogate)
+ Utils->lines_to_apply |= APPLY_QLINES;
+ break;
+ case 'E':
+ propogate = Instance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
+ Instance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
+ break;
+ case 'G':
+ propogate = Instance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
+ Instance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
+ if (propogate)
+ Utils->lines_to_apply |= APPLY_GLINES;
+ break;
+ case 'K':
+ propogate = Instance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
+ if (propogate)
+ Utils->lines_to_apply |= APPLY_KLINES;
+ break;
+ default:
+ /* Just in case... */
+ this->Instance->SNO->WriteToSnoMask('x',"\2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!");
+ propogate = false;
+ break;
+ }
+ /* Send it on its way */
+ if (propogate)
+ {
+ if (atoi(params[4].c_str()))
+ {
+ time_t c_requires_crap = ConvToInt(params[4]) + Instance->Time();
+ this->Instance->SNO->WriteToSnoMask('x',"%s Added %cLINE on %s to expire on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),Instance->TimeString(c_requires_crap).c_str(),params[5].c_str());
+ }
+ else
+ {
+ this->Instance->SNO->WriteToSnoMask('x',"%s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str());
+ }
+ params[5] = ":" + params[5];
+ Utils->DoOneToAllButSender(prefix,"ADDLINE",params,prefix);
+ }
+ if (!this->bursting)
+ {
+ Instance->XLines->apply_lines(Utils->lines_to_apply);
+ Utils->lines_to_apply = 0;
+ }
+ return true;
+}
+
+bool TreeSocket::ChangeName(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+ userrec* u = this->Instance->FindNick(prefix);
+ if (u)
+ {
+ u->ChangeName(params[0].c_str());
+ params[0] = ":" + params[0];
+ Utils->DoOneToAllButSender(prefix,"FNAME",params,u->server);
+ }
+ return true;
+}
+
+bool TreeSocket::Whois(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+ userrec* u = this->Instance->FindNick(prefix);
+ if (u)
+ {
+ // an incoming request
+ if (params.size() == 1)
+ {
+ userrec* x = this->Instance->FindNick(params[0]);
+ if ((x) && (IS_LOCAL(x)))
+ {
+ userrec* x = this->Instance->FindNick(params[0]);
+ char signon[MAXBUF];
+ char idle[MAXBUF];
+ snprintf(signon, MAXBUF, "%lu", (unsigned long)x->signon);
+ snprintf(idle, MAXBUF, "%lu", (unsigned long)abs((x->idle_lastmsg) - Instance->Time(true)));
+ std::deque<std::string> par;
+ par.push_back(prefix);
+ par.push_back(signon);
+ par.push_back(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];
+ userrec* who_to_send_to = this->Instance->FindNick(who_did_the_whois);
+ if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
+ {
+ // 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)))
+ {
+ do_whois(this->Instance, 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);
+ }
+ }
+ }
+ return true;
+}
+
+bool TreeSocket::Push(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 2)
+ return true;
+ userrec* u = this->Instance->FindNick(params[0]);
+ if (!u)
+ return true;
+ if (IS_LOCAL(u))
+ {
+ u->Write(params[1]);
+ }
+ else
+ {
+ // continue the raw onwards
+ params[1] = ":" + params[1];
+ Utils->DoOneToOne(prefix,"PUSH",params,u->server);
+ }
+ return true;
+}
+
+bool TreeSocket::HandleSetTime(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (!params.size() || !Utils->EnableTimeSync)
+ return true;
+
+ bool force = false;
+
+ if ((params.size() == 2) && (params[1] == "FORCE"))
+ force = true;
+
+ time_t them = atoi(params[0].c_str());
+ time_t us = Instance->Time(false);
+
+ time_t diff = them - us;
+
+ Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
+
+ if (force || (them != us))
+ {
+ time_t old = Instance->SetTimeDelta(diff);
+ Instance->Log(DEBUG, "TS (diff %d) from %s applied (old delta was %d)", diff, prefix.c_str(), old);
+ }
+
+ return true;
+}
+
+bool TreeSocket::Time(const std::string &prefix, std::deque<std::string> &params)
+{
+ // :source.server TIME remote.server sendernick
+ // :remote.server TIME source.server sendernick TS
+ if (params.size() == 2)
+ {
+ // someone querying our time?
+ if (this->Instance->Config->ServerName == params[0])
+ {
+ userrec* u = this->Instance->FindNick(params[1]);
+ if (u)
+ {
+ params.push_back(ConvToStr(Instance->Time(false)));
+ params[0] = prefix;
+ Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]);
+ }
+ }
+ else
+ {
+ // not us, pass it on
+ userrec* u = this->Instance->FindNick(params[1]);
+ if (u)
+ Utils->DoOneToOne(prefix,"TIME",params,params[0]);
+ }
+ }
+ else if (params.size() == 3)
+ {
+ // a response to a previous TIME
+ userrec* u = this->Instance->FindNick(params[1]);
+ if ((u) && (IS_LOCAL(u)))
+ {
+ time_t rawtime = atol(params[2].c_str());
+ struct tm * timeinfo;
+ timeinfo = localtime(&rawtime);
+ char tms[26];
+ snprintf(tms,26,"%s",asctime(timeinfo));
+ tms[24] = 0;
+ u->WriteServ("391 %s %s :%s",u->nick,prefix.c_str(),tms);
+ }
+ else
+ {
+ if (u)
+ Utils->DoOneToOne(prefix,"TIME",params,u->server);
+ }
+ }
+ return true;
+}
+
+bool TreeSocket::LocalPing(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+ if (params.size() == 1)
+ {
+ std::string stufftobounce = params[0];
+ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" PONG "+stufftobounce);
+ return true;
+ }
+ else
+ {
+ std::string forwardto = params[1];
+ if (forwardto == this->Instance->Config->ServerName)
+ {
+ // this is a ping for us, send back PONG to the requesting server
+ params[1] = params[0];
+ params[0] = forwardto;
+ Utils->DoOneToOne(forwardto,"PONG",params,params[1]);
+ }
+ else
+ {
+ // not for us, pass it on :)
+ Utils->DoOneToOne(prefix,"PING",params,forwardto);
+ }
+ return true;
+ }
+}
+
+/** TODO: This creates a total mess of output and needs to really use irc::modestacker.
+ */
+bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ return true;
+ chanrec* c = Instance->FindChan(params[0]);
+ if (c)
+ {
+ for (char modeletter = 'A'; modeletter <= 'z'; modeletter++)
+ {
+ ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
+ if (mh)
+ mh->RemoveMode(c);
+ }
+ }
+ return true;
+}
+
+bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> &params)
+{
+ if (params.size() < 4)
+ return false;
+ std::string servername = params[0];
+ std::string password = params[1];
+ // hopcount is not used for a remote server, we calculate this ourselves
+ std::string description = params[3];
+ TreeServer* ParentOfThis = Utils->FindServer(prefix);
+ if (!ParentOfThis)
+ {
+ this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
+ return false;
+ }
+ TreeServer* CheckDupe = Utils->FindServer(servername);
+ if (CheckDupe)
+ {
+ this->SendError("Server "+servername+" already exists!");
+ this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, already exists. Closing link with " + prefix);
+ return false;
+ }
+ Link* lnk = Utils->FindLink(servername);
+ TreeServer* Node = new TreeServer(this->Utils,this->Instance,servername,description,ParentOfThis,NULL, lnk ? lnk->Hidden : false);
+ ParentOfThis->AddChild(Node);
+ params[3] = ":" + params[3];
+ Utils->SetRemoteBursting(Node, true);
+ Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
+ this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
+ return true;
+}
+
+bool TreeSocket::ComparePass(const std::string &ours, const std::string &theirs)
+{
+ if ((!strncmp(ours.c_str(), "HMAC-SHA256:", 12)) || (!strncmp(theirs.c_str(), "HMAC-SHA256:", 12)))
+ {
+ /* One or both of us specified hmac sha256, but we don't have sha256 module loaded!
+ * We can't allow this password as valid.
+ */
+ if (!Instance->FindModule("m_sha256.so") || !Utils->ChallengeResponse)
+ return false;
+ else
+ /* Straight string compare of hashes */
+ return ours == theirs;
+ }
+ else
+ /* Straight string compare of plaintext */
+ return ours == theirs;
+}
+
+bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> &params)
+{
+ if (params.size() < 4)
+ return false;
+
+ irc::string servername = params[0].c_str();
+ std::string sname = params[0];
+ std::string password = params[1];
+ std::string description = params[3];
+ int hops = atoi(params[2].c_str());
+
+ this->InboundServerName = sname;
+ this->InboundDescription = description;
+
+ if (!sentcapab)
+ this->SendCapabilities();
+
+ if (hops)
+ {
+ this->SendError("Server too far away for authentication");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
+ return false;
+ }
+
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password)) || (x->RecvPass == password && (this->GetTheirChallenge().empty()))))
+ {
+ TreeServer* CheckDupe = Utils->FindServer(sname);
+ if (CheckDupe)
+ {
+ this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
+ return false;
+ }
+ // Begin the sync here. this kickstarts the
+ // other side, waiting in WAIT_AUTH_2 state,
+ // into starting their burst, as it shows
+ // that we're happy.
+ this->LinkState = CONNECTED;
+ // we should add the details of this server now
+ // to the servers tree, as a child of the root
+ // node.
+ TreeServer* Node = new TreeServer(this->Utils,this->Instance,sname,description,Utils->TreeRoot,this,x->Hidden);
+ Utils->TreeRoot->AddChild(Node);
+ params[3] = ":" + params[3];
+ Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,sname);
+ this->bursting = true;
+ this->DoBurst(Node);
+ return true;
+ }
+ }
+ this->SendError("Invalid credentials");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
+ return false;
+}
+
+bool TreeSocket::Inbound_Server(std::deque<std::string> &params)
+{
+ if (params.size() < 4)
+ return false;
+ irc::string servername = params[0].c_str();
+ std::string sname = params[0];
+ std::string password = params[1];
+ std::string description = params[3];
+ int hops = atoi(params[2].c_str());
+
+ this->InboundServerName = sname;
+ this->InboundDescription = description;
+
+ if (!sentcapab)
+ this->SendCapabilities();
+
+ if (hops)
+ {
+ this->SendError("Server too far away for authentication");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
+ return false;
+ }
+
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password) || x->RecvPass == password && (this->GetTheirChallenge().empty()))))
+ {
+ /* First check for instances of the server that are waiting between the inbound and outbound SERVER command */
+ TreeSocket* CheckDupeSocket = Utils->FindBurstingServer(sname);
+ if (CheckDupeSocket)
+ {
+ /* If we find one, we abort the link to prevent a race condition */
+ this->SendError("Negotiation collision");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists in a negotiating state.");
+ CheckDupeSocket->SendError("Negotiation collision");
+ Instance->SE->DelFd(CheckDupeSocket);
+ CheckDupeSocket->Close();
+ delete CheckDupeSocket;
+ return false;
+ }
+ /* Now check for fully initialized instances of the server */
+ TreeServer* CheckDupe = Utils->FindServer(sname);
+ if (CheckDupe)
+ {
+ this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
+ return false;
+ }
+ this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")");
+ if (this->Hook)
+ {
+ std::string name = InspSocketNameRequest((Module*)Utils->Creator, this->Hook).Send();
+ this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2");
+ }
+
+ Utils->AddBurstingServer(sname,this);
+
+ // this is good. Send our details: Our server name and description and hopcount of 0,
+ // along with the sendpass from this block.
+ this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc);
+ // move to the next state, we are now waiting for THEM.
+ this->LinkState = WAIT_AUTH_2;
+ return true;
+ }
+ }
+ this->SendError("Invalid credentials");
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
+ return false;
+}
+
+void TreeSocket::Split(const std::string &line, std::deque<std::string> &n)
+{
+ n.clear();
+ irc::tokenstream tokens(line);
+ std::string param;
+ while (tokens.GetToken(param))
+ {
+ if (!param.empty())
+ n.push_back(param);
+ }
+ return;
+}
+
+bool TreeSocket::ProcessLine(std::string &line)
+{
+ std::deque<std::string> params;
+ irc::string command;
+ std::string prefix;
+
+ line = line.substr(0, line.find_first_of("\r\n"));
+
+ if (line.empty())
+ return true;
+
+ Instance->Log(DEBUG, "S[%d] <- %s", this->GetFd(), line.c_str());
+
+ this->Split(line.c_str(),params);
+
+ if (params.empty())
+ return true;
+
+ if ((params[0][0] == ':') && (params.size() > 1))
+ {
+ prefix = params[0].substr(1);
+ params.pop_front();
+ }
+ command = params[0].c_str();
+ params.pop_front();
+ switch (this->LinkState)
+ {
+ TreeServer* Node;
+
+ case WAIT_AUTH_1:
+ // Waiting for SERVER command from remote server. Server initiating
+ // the connection sends the first SERVER command, listening server
+ // replies with theirs if its happy, then if the initiator is happy,
+ // it starts to send its net sync, which starts the merge, otherwise
+ // it sends an ERROR.
+ if (command == "PASS")
+ {
+ /* Silently ignored */
+ }
+ else if (command == "SERVER")
+ {
+ return this->Inbound_Server(params);
+ }
+ else if (command == "ERROR")
+ {
+ return this->Error(params);
+ }
+ else if (command == "USER")
+ {
+ this->SendError("Client connections to this port are prohibited.");
+ return false;
+ }
+ else if (command == "CAPAB")
+ {
+ return this->Capab(params);
+ }
+ else if ((command == "U") || (command == "S"))
+ {
+ this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
+ return false;
+ }
+ else
+ {
+ irc::string error = "Invalid command in negotiation phase: " + command;
+ this->SendError(assign(error));
+ return false;
+ }
+ break;
+ case WAIT_AUTH_2:
+ // Waiting for start of other side's netmerge to say they liked our
+ // password.
+ if (command == "SERVER")
+ {
+ // cant do this, they sent it to us in the WAIT_AUTH_1 state!
+ // silently ignore.
+ return true;
+ }
+ else if ((command == "U") || (command == "S"))
+ {
+ this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
+ return false;
+ }
+ else if (command == "BURST")
+ {
+ if (params.size() && Utils->EnableTimeSync)
+ {
+ bool we_have_delta = (Instance->Time(false) != Instance->Time(true));
+ time_t them = atoi(params[0].c_str());
+ time_t delta = them - Instance->Time(false);
+ if ((delta < -300) || (delta > 300))
+ {
+ Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta));
+ SendError("Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
+ return false;
+ }
+ else if ((delta < -30) || (delta > 30))
+ {
+ Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs(delta));
+ }
+
+ if (!Utils->MasterTime && !we_have_delta)
+ {
+ this->Instance->SetTimeDelta(delta);
+ // Send this new timestamp to any other servers
+ Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
+ }
+ }
+ this->LinkState = CONNECTED;
+ Link* lnk = Utils->FindLink(InboundServerName);
+ Node = new TreeServer(this->Utils,this->Instance, InboundServerName, InboundDescription, Utils->TreeRoot, this, lnk ? lnk->Hidden : false);
+ Utils->DelBurstingServer(this);
+ Utils->TreeRoot->AddChild(Node);
+ params.clear();
+ params.push_back(InboundServerName);
+ params.push_back("*");
+ params.push_back("1");
+ params.push_back(":"+InboundDescription);
+ Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,InboundServerName);
+ this->bursting = true;
+ this->DoBurst(Node);
+ }
+ else if (command == "ERROR")
+ {
+ return this->Error(params);
+ }
+ else if (command == "CAPAB")
+ {
+ return this->Capab(params);
+ }
+
+ break;
+ case LISTENER:
+ this->SendError("Internal error -- listening socket accepted its own descriptor!!!");
+ return false;
+ break;
+ case CONNECTING:
+ if (command == "SERVER")
+ {
+ // another server we connected to, which was in WAIT_AUTH_1 state,
+ // has just sent us their credentials. If we get this far, theyre
+ // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
+ // if we're happy with this, we should send our netburst which
+ // kickstarts the merge.
+ return this->Outbound_Reply_Server(params);
+ }
+ else if (command == "ERROR")
+ {
+ return this->Error(params);
+ }
+ else if (command == "CAPAB")
+ {
+ return this->Capab(params);
+ }
+ break;
+ case CONNECTED:
+ // This is the 'authenticated' state, when all passwords
+ // have been exchanged and anything past this point is taken
+ // as gospel.
+
+ if (!prefix.empty())
+ {
+ std::string direction = prefix;
+ userrec* t = this->Instance->FindNick(prefix);
+ if (t)
+ {
+ direction = t->server;
+ }
+ TreeServer* route_back_again = Utils->BestRouteTo(direction);
+ if ((!route_back_again) || (route_back_again->GetSocket() != this))
+ {
+ if (route_back_again)
+ Instance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
+ return true;
+ }
+ /* Fix by brain:
+ * When there is activity on the socket, reset the ping counter so
+ * that we're not wasting bandwidth pinging an active server.
+ */
+ route_back_again->SetNextPingTime(time(NULL) + 60);
+ route_back_again->SetPingFlag();
+ }
+ else
+ {
+ prefix = this->GetName();
+ }
+
+ if ((command == "MODE") && (params.size() >= 2))
+ {
+ chanrec* channel = Instance->FindChan(params[0]);
+ if (channel)
+ {
+ userrec* x = Instance->FindNick(prefix);
+ if (x)
+ {
+ if (warned.find(x->server) == warned.end())
+ {
+ Instance->Log(DEFAULT,"WARNING: I revceived modes '%s' from another server '%s'. This is not compliant with InspIRCd. Please check that server for bugs.", params[1].c_str(), x->server);
+ Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending nonstandard modes: '%s MODE %s' where FMODE should be used, and may cause desyncs.", x->server, x->nick, params[1].c_str());
+ warned[x->server] = x->nick;
+ }
+ }
+ }
+ }
+
+ if (command == "SVSMODE")
+ {
+ /* Services expects us to implement
+ * SVSMODE. In inspircd its the same as
+ * MODE anyway.
+ */
+ command = "MODE";
+ }
+ std::string target;
+ /* Yes, know, this is a mess. Its reasonably fast though as we're
+ * working with std::string here.
+ */
+ if ((command == "NICK") && (params.size() >= 8))
+ {
+ return this->IntroduceClient(prefix,params);
+ }
+ else if (command == "FJOIN")
+ {
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ Utils->SetRemoteBursting(ServerSource, false);
+ return this->ForceJoin(prefix,params);
+ }
+ else if (command == "STATS")
+ {
+ return this->Stats(prefix, params);
+ }
+ else if (command == "MOTD")
+ {
+ return this->Motd(prefix, params);
+ }
+ else if (command == "KILL" && Utils->IsServer(prefix))
+ {
+ return this->RemoteKill(prefix,params);
+ }
+ else if (command == "MODULES")
+ {
+ return this->Modules(prefix, params);
+ }
+ else if (command == "ADMIN")
+ {
+ return this->Admin(prefix, params);
+ }
+ else if (command == "SERVER")
+ {
+ return this->RemoteServer(prefix,params);
+ }
+ else if (command == "ERROR")
+ {
+ return this->Error(params);
+ }
+ else if (command == "OPERTYPE")
+ {
+ return this->OperType(prefix,params);
+ }
+ else if (command == "FMODE")
+ {
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ Utils->SetRemoteBursting(ServerSource, false);
+ return this->ForceMode(prefix,params);
+ }
+ else if (command == "FTOPIC")
+ {
+ return this->ForceTopic(prefix,params);
+ }
+ else if (command == "REHASH")
+ {
+ return this->RemoteRehash(prefix,params);
+ }
+ else if (command == "METADATA")
+ {
+ return this->MetaData(prefix,params);
+ }
+ else if (command == "REMSTATUS")
+ {
+ return this->RemoveStatus(prefix,params);
+ }
+ else if (command == "PING")
+ {
+ if (prefix.empty())
+ prefix = this->GetName();
+ /*
+ * We just got a ping from a server that's bursting.
+ * This can't be right, so set them to not bursting, and
+ * apply their lines.
+ */
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ Utils->SetRemoteBursting(ServerSource, false);
+
+ if (this->bursting)
+ {
+ this->bursting = false;
+ Instance->XLines->apply_lines(Utils->lines_to_apply);
+ Utils->lines_to_apply = 0;
+ }
+
+ return this->LocalPing(prefix,params);
+ }
+ else if (command == "PONG")
+ {
+ if (prefix.empty())
+ prefix = this->GetName();
+ /*
+ * We just got a pong from a server that's bursting.
+ * This can't be right, so set them to not bursting, and
+ * apply their lines.
+ */
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ Utils->SetRemoteBursting(ServerSource, false);
+
+ if (this->bursting)
+ {
+ this->bursting = false;
+ Instance->XLines->apply_lines(Utils->lines_to_apply);
+ Utils->lines_to_apply = 0;
+ }
+
+ return this->LocalPong(prefix,params);
+ }
+ else if (command == "VERSION")
+ {
+ return this->ServerVersion(prefix,params);
+ }
+ else if (command == "FHOST")
+ {
+ return this->ChangeHost(prefix,params);
+ }
+ else if (command == "FNAME")
+ {
+ return this->ChangeName(prefix,params);
+ }
+ else if (command == "ADDLINE")
+ {
+ TreeServer* ServerSource = Utils->FindServer(prefix);
+ if (ServerSource)
+ Utils->SetRemoteBursting(ServerSource, false);
+ return this->AddLine(prefix,params);
+ }
+ else if (command == "SVSNICK")
+ {
+ if (prefix.empty())
+ {
+ prefix = this->GetName();
+ }
+ return this->ForceNick(prefix,params);
+ }
+ else if (command == "OPERQUIT")
+ {
+ return this->OperQuit(prefix,params);
+ }
+ else if (command == "IDLE")
+ {
+ return this->Whois(prefix,params);
+ }
+ else if (command == "PUSH")
+ {
+ return this->Push(prefix,params);
+ }
+ else if (command == "TIMESET")
+ {
+ return this->HandleSetTime(prefix, params);
+ }
+ else if (command == "TIME")
+ {
+ return this->Time(prefix,params);
+ }
+ else if ((command == "KICK") && (Utils->IsServer(prefix)))
+ {
+ std::string sourceserv = this->myhost;
+ if (params.size() == 3)
+ {
+ userrec* user = this->Instance->FindNick(params[1]);
+ chanrec* chan = this->Instance->FindChan(params[0]);
+ if (user && chan)
+ {
+ if (!chan->ServerKickUser(user, params[2].c_str(), false))
+ /* Yikes, the channels gone! */
+ delete chan;
+ }
+ }
+ if (!this->InboundServerName.empty())
+ {
+ sourceserv = this->InboundServerName;
+ }
+ return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
+ }
+ else if (command == "SVSJOIN")
+ {
+ if (prefix.empty())
+ {
+ prefix = this->GetName();
+ }
+ return this->ServiceJoin(prefix,params);
+ }
+ else if (command == "SQUIT")
+ {
+ if (params.size() == 2)
+ {
+ this->Squit(Utils->FindServer(params[0]),params[1]);
+ }
+ return true;
+ }
+ else if (command == "OPERNOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (!this->InboundServerName.empty())
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 1)
+ Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]);
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "MODENOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (!this->InboundServerName.empty())
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 2)
+ {
+ Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str());
+ }
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "SNONOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (!this->InboundServerName.empty())
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 2)
+ {
+ Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]);
+ }
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "ENDBURST")
+ {
+ this->bursting = false;
+ Instance->XLines->apply_lines(Utils->lines_to_apply);
+ Utils->lines_to_apply = 0;
+ std::string sourceserv = this->myhost;
+ if (!this->InboundServerName.empty())
+ sourceserv = this->InboundServerName;
+ this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str());
+
+ Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server");
+ rmode.Send(Instance);
+
+ return true;
+ }
+ else
+ {
+ // not a special inter-server command.
+ // Emulate the actual user doing the command,
+ // this saves us having a huge ugly parser.
+ userrec* who = this->Instance->FindNick(prefix);
+ std::string sourceserv = this->myhost;
+ if (!this->InboundServerName.empty())
+ {
+ sourceserv = this->InboundServerName;
+ }
+ if ((!who) && (command == "MODE"))
+ {
+ if (Utils->IsServer(prefix))
+ {
+ const char* modelist[127];
+ for (size_t i = 0; i < params.size(); i++)
+ modelist[i] = params[i].c_str();
+ userrec* fake = new userrec(Instance);
+ fake->SetFd(FD_MAGIC_NUMBER);
+ this->Instance->SendMode(modelist, params.size(), fake);
+
+ delete fake;
+ /* Hot potato! pass it on! */
+ return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
+ }
+ }
+ if (who)
+ {
+ if ((command == "NICK") && (params.size() > 0))
+ {
+ /* On nick messages, check that the nick doesnt
+ * already exist here. If it does, kill their copy,
+ * and our copy.
+ */
+ userrec* x = this->Instance->FindNick(params[0]);
+ if ((x) && (x != who))
+ {
+ std::deque<std::string> p;
+ p.push_back(params[0]);
+ p.push_back("Nickname collision ("+prefix+" -> "+params[0]+")");
+ Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p);
+ p.clear();
+ p.push_back(prefix);
+ p.push_back("Nickname collision");
+ Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p);
+ userrec::QuitUser(this->Instance,x,"Nickname collision ("+prefix+" -> "+params[0]+")");
+ userrec* y = this->Instance->FindNick(prefix);
+ if (y)
+ {
+ userrec::QuitUser(this->Instance,y,"Nickname collision");
+ }
+ return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
+ }
+ }
+ // its a user
+ target = who->server;
+ const char* strparams[127];
+ for (unsigned int q = 0; q < params.size(); q++)
+ {
+ strparams[q] = params[q].c_str();
+ }
+ switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
+ {
+ case CMD_INVALID:
+ this->SendError("Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules");
+ return false;
+ break;
+ case CMD_FAILURE:
+ return true;
+ break;
+ default:
+ /* CMD_SUCCESS and CMD_USER_DELETED fall through here */
+ break;
+ }
+ }
+ else
+ {
+ // its not a user. Its either a server, or somethings screwed up.
+ if (Utils->IsServer(prefix))
+ target = this->Instance->Config->ServerName;
+ else
+ return true;
+ }
+ return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
+
+ }
+ return true;
+ break;
+ }
+ return true;
+}
+
+std::string TreeSocket::GetName()
+{
+ std::string sourceserv = this->myhost;
+ if (!this->InboundServerName.empty())
+ {
+ sourceserv = this->InboundServerName;
+ }
+ return sourceserv;
+}
+
+void TreeSocket::OnTimeout()
+{
+ if (this->LinkState == CONNECTING)
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out.");
+ Link* MyLink = Utils->FindLink(myhost);
+ if (MyLink)
+ Utils->DoFailOver(MyLink);
+ }
+}
+
+void TreeSocket::OnClose()
+{
+ // Connection closed.
+ // If the connection is fully up (state CONNECTED)
+ // then propogate a netsplit to all peers.
+ std::string quitserver = this->myhost;
+ if (!this->InboundServerName.empty())
+ {
+ quitserver = this->InboundServerName;
+ }
+ TreeServer* s = Utils->FindServer(quitserver);
+ if (s)
+ {
+ Squit(s,"Remote host closed the connection");
+ }
+
+ if (!quitserver.empty())
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str());
+ time_t server_uptime = Instance->Time() - this->age;
+ if (server_uptime)
+ Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str());
+ }
+}
+
+int TreeSocket::OnIncomingConnection(int newsock, char* ip)
+{
+ /* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port,
+ * or discovering if this port is the server port, we don't allow connections from any
+ * IPs for which we don't have a link block.
+ */
+ bool found = false;
+
+ found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end());
+ if (!found)
+ {
+ for (vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
+ if (irc::sockets::MatchCIDR(ip, (*i).c_str()))
+ found = true;
+
+ if (!found)
+ {
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip);
+ close(newsock);
+ return false;
+ }
+ }
+
+ TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook);
+ s = s; /* Whinge whinge whinge, thats all GCC ever does. */
+ return true;
+}
diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp
index 4d0256fa2..9675a6ac8 100644
--- a/src/modules/m_spanningtree/utils.cpp
+++ b/src/modules/m_spanningtree/utils.cpp
@@ -1 +1,649 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" #include "wildcard.h" #include "xline.h" #include "transport.h" #include "socketengine.h" #include "m_spanningtree/main.h" #include "m_spanningtree/utils.h" #include "m_spanningtree/treeserver.h" #include "m_spanningtree/link.h" #include "m_spanningtree/treesocket.h" #include "m_spanningtree/resolvers.h" /* $ModDep: m_spanningtree/timesynctimer.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 */ /** 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) { server_hash::iterator iter = serverlist.find(ServerName.c_str()); if (iter != serverlist.end()) { return iter->second; } else { return NULL; } } TreeServer* SpanningTreeUtilities::FindRemoteBurstServer(TreeServer* Server) { server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str()); if (iter != RemoteServersBursting.end()) return iter->second; else return NULL; } TreeSocket* SpanningTreeUtilities::FindBurstingServer(const std::string &ServerName) { std::map<irc::string,TreeSocket*>::iterator iter; iter = burstingserverlist.find(ServerName.c_str()); if (iter != burstingserverlist.end()) { return iter->second; } else { return NULL; } } void SpanningTreeUtilities::SetRemoteBursting(TreeServer* Server, bool bursting) { server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str()); if (bursting) { if (iter == RemoteServersBursting.end()) RemoteServersBursting.insert(make_pair(Server->GetName(), Server)); else return; } else { if (iter != RemoteServersBursting.end()) RemoteServersBursting.erase(iter); else return; } ServerInstance->Log(DEBUG,"Server %s is %sbursting nicknames", Server->GetName().c_str(), bursting ? "" : "no longer "); } void SpanningTreeUtilities::AddBurstingServer(const std::string &ServerName, TreeSocket* s) { std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.find(ServerName.c_str()); if (iter == burstingserverlist.end()) burstingserverlist[ServerName.c_str()] = s; } void SpanningTreeUtilities::DelBurstingServer(TreeSocket* s) { for (std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.begin(); iter != burstingserverlist.end(); iter++) { if (iter->second == s) { burstingserverlist.erase(iter); return; } } } /** Returns the locally connected server we must route a * message through to reach server 'ServerName'. This * only applies to one-to-one and not one-to-many routing. * See the comments for the constructor of TreeServer * for more details. */ TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName) { if (ServerName.c_str() == TreeRoot->GetName()) return NULL; TreeServer* Found = FindServer(ServerName); if (Found) { return Found->GetRoute(); } else { return NULL; } } /** Find the first server matching a given glob mask. * Theres no find-using-glob method of hash_map [awwww :-(] * so instead, we iterate over the list using an iterator * and match each one until we get a hit. Yes its slow, * deal with it. */ TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName) { for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++) { if (match(i->first.c_str(),ServerName.c_str())) return i->second; } return NULL; } /* A convenient wrapper that returns true if a server exists */ bool SpanningTreeUtilities::IsServer(const std::string &ServerName) { return (FindServer(ServerName) != NULL); } SpanningTreeUtilities::SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* C) : ServerInstance(Instance), Creator(C) { Bindings.clear(); lines_to_apply = 0; this->TreeRoot = new TreeServer(this, ServerInstance, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc); modulelist* ml = ServerInstance->FindInterface("InspSocketHook"); /* Did we find any modules? */ if (ml) { /* Yes, enumerate them all to find out the hook name */ for (modulelist::iterator m = ml->begin(); m != ml->end(); m++) { /* Make a request to it for its name, its implementing * InspSocketHook so we know its safe to do this */ std::string name = InspSocketNameRequest((Module*)Creator, *m).Send(); /* Build a map of them */ hooks[name.c_str()] = *m; hooknames.push_back(name); } } this->ReadConfiguration(true); } SpanningTreeUtilities::~SpanningTreeUtilities() { for (unsigned int i = 0; i < Bindings.size(); i++) { ServerInstance->SE->DelFd(Bindings[i]); Bindings[i]->Close(); DELETE(Bindings[i]); } while (TreeRoot->ChildCount()) { TreeServer* child_server = TreeRoot->GetChild(0); if (child_server) { TreeSocket* sock = child_server->GetSocket(); ServerInstance->SE->DelFd(sock); sock->Close(); DELETE(sock); } } 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(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list) { CUList *ulist; switch (status) { case '@': ulist = c->GetOppedUsers(); break; case '%': ulist = c->GetHalfoppedUsers(); break; case '+': ulist = c->GetVoicedUsers(); break; default: ulist = c->GetUsers(); break; } for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if ((i->first->GetFd() < 0) && (exempt_list.find(i->first) == exempt_list.end())) { TreeServer* best = this->BestRouteTo(i->first->server); if (best) AddThisServer(best,list); } } return; } bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> &params) { char pfx = 0; TreeServer* omitroute = this->BestRouteTo(omit); if ((command == "NOTICE") || (command == "PRIVMSG")) { if (params.size() >= 2) { /* Prefixes */ if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+')) { pfx = params[0][0]; params[0] = params[0].substr(1, params[0].length()-1); } if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$')) { // special routing for private messages/notices userrec* d = ServerInstance->FindNick(params[0]); if (d) { std::deque<std::string> par; par.push_back(params[0]); par.push_back(":"+params[1]); this->DoOneToOne(prefix,command.c_str(),par,d->server); return true; } } else if (*(params[0].c_str()) == '$') { std::deque<std::string> par; par.push_back(params[0]); par.push_back(":"+params[1]); this->DoOneToAllButSender(prefix,command.c_str(),par,omitroute->GetName()); return true; } else { chanrec* c = ServerInstance->FindChan(params[0]); userrec* u = ServerInstance->FindNick(prefix); if (c && u) { CUList elist; TreeServerList list; FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist)); GetListOfServersForChannel(c,list,pfx,elist); for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { TreeSocket* Sock = i->second->GetSocket(); if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second)) { Sock->WriteLine(data); } } return true; } } } } unsigned int items =this->TreeRoot->ChildCount(); for (unsigned int x = 0; x < items; x++) { TreeServer* Route = this->TreeRoot->GetChild(x); if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route)) { TreeSocket* Sock = Route->GetSocket(); if (Sock) Sock->WriteLine(data); } } return true; } bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string omit) { TreeServer* omitroute = this->BestRouteTo(omit); std::string FullLine = ":" + prefix + " " + command; unsigned int words = params.size(); for (unsigned int x = 0; x < words; x++) { FullLine = FullLine + " " + params[x]; } unsigned int items = this->TreeRoot->ChildCount(); for (unsigned int x = 0; x < items; x++) { TreeServer* Route = this->TreeRoot->GetChild(x); // Send the line IF: // The route has a socket (its a direct connection) // The route isnt the one to be omitted // The route isnt the path to the one to be omitted if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route)) { TreeSocket* Sock = Route->GetSocket(); if (Sock) Sock->WriteLine(FullLine); } } return true; } bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> &params) { std::string FullLine = ":" + prefix + " " + command; unsigned int words = params.size(); for (unsigned int x = 0; x < words; x++) { FullLine = FullLine + " " + params[x]; } unsigned int items = this->TreeRoot->ChildCount(); for (unsigned int x = 0; x < items; x++) { TreeServer* Route = this->TreeRoot->GetChild(x); if (Route && Route->GetSocket()) { TreeSocket* Sock = Route->GetSocket(); if (Sock) Sock->WriteLine(FullLine); } } return true; } bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, std::deque<std::string> &params) { std::string spfx = prefix; std::string scmd = command; return this->DoOneToMany(spfx, scmd, params); } bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> &params, std::string omit) { std::string spfx = prefix; std::string scmd = command; return this->DoOneToAllButSender(spfx, scmd, params, omit); } bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string target) { 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; } } void SpanningTreeUtilities::RefreshIPCache() { ValidIPs.clear(); for (std::vector<Link>::iterator L = LinkBlocks.begin(); L != LinkBlocks.end(); L++) { if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port)) { ValidIPs.push_back(L->IPAddr); if (L->AllowMask.length()) ValidIPs.push_back(L->AllowMask); /* Needs resolving */ bool ipvalid = true; QueryType start_type = DNS_QUERY_A; #ifdef IPV6 start_type = DNS_QUERY_AAAA; if (strchr(L->IPAddr.c_str(),':')) { in6_addr n; if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1) ipvalid = false; } else #endif { in_addr n; if (inet_aton(L->IPAddr.c_str(),&n) < 1) ipvalid = false; } if (!ipvalid) { try { bool cached; SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L->IPAddr, *L, cached, start_type); ServerInstance->AddResolver(sr, cached); } catch (...) { } } } } } void SpanningTreeUtilities::ReadConfiguration(bool rebind) { ConfigReader* Conf = new ConfigReader(ServerInstance); if (rebind) { for (int j = 0; j < Conf->Enumerate("bind"); j++) { std::string Type = Conf->ReadValue("bind","type",j); std::string IP = Conf->ReadValue("bind","address",j); std::string Port = Conf->ReadValue("bind","port",j); std::string transport = Conf->ReadValue("bind","transport",j); if (Type == "servers") { irc::portparser portrange(Port, false); int portno = -1; while ((portno = portrange.GetToken())) { if (IP == "*") IP.clear(); if ((!transport.empty()) && (hooks.find(transport.c_str()) == hooks.end())) { ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for port %s:%s - maybe you forgot to load it BEFORE m_spanningtree in your config file? - Skipping this port binding", transport.c_str(), IP.c_str(), Port.c_str()); break; } TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10, transport.empty() ? NULL : hooks[transport.c_str()]); if (listener->GetState() == I_LISTENING) { ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno); Bindings.push_back(listener); } else { ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port: %s:%d: %s",IP.c_str(), portno, strerror(errno)); listener->Close(); DELETE(listener); } } } } } FlatLinks = Conf->ReadFlag("options","flatlinks",0); HideULines = Conf->ReadFlag("options","hideulines",0); AnnounceTSChange = Conf->ReadFlag("options","announcets",0); EnableTimeSync = Conf->ReadFlag("timesync","enable",0); MasterTime = Conf->ReadFlag("timesync", "master", 0); ChallengeResponse = !Conf->ReadFlag("options", "disablehmac", 0); quiet_bursts = Conf->ReadFlag("options", "quietbursts", 0); PingWarnTime = Conf->ReadInteger("options", "pingwarning", 0, true); if (PingWarnTime < 0 || PingWarnTime > 59) PingWarnTime = 0; LinkBlocks.clear(); ValidIPs.clear(); for (int j = 0; j < Conf->Enumerate("link"); j++) { Link L; std::string Allow = Conf->ReadValue("link", "allowmask", j); L.Name = (Conf->ReadValue("link", "name", j)).c_str(); L.AllowMask = Allow; L.IPAddr = Conf->ReadValue("link", "ipaddr", j); L.FailOver = Conf->ReadValue("link", "failover", j).c_str(); L.Port = Conf->ReadInteger("link", "port", j, true); L.SendPass = Conf->ReadValue("link", "sendpass", j); L.RecvPass = Conf->ReadValue("link", "recvpass", j); L.AutoConnect = Conf->ReadInteger("link", "autoconnect", j, true); L.HiddenFromStats = Conf->ReadFlag("link", "statshidden", j); L.Timeout = Conf->ReadInteger("link", "timeout", j, true); L.Hook = Conf->ReadValue("link", "transport", j); L.Bind = Conf->ReadValue("link", "bind", j); L.Hidden = Conf->ReadFlag("link", "hidden", j); if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) == hooks.end())) { ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for link '%s' - maybe you forgot to load it BEFORE m_spanningtree in your config file? Skipping <link> tag completely.", L.Hook.c_str(), L.Name.c_str()); continue; } L.NextConnectTime = time(NULL) + L.AutoConnect; /* Bugfix by brain, do not allow people to enter bad configurations */ if (L.Name != ServerInstance->Config->ServerName) { if ((!L.IPAddr.empty()) && (!L.RecvPass.empty()) && (!L.SendPass.empty()) && (!L.Name.empty()) && (L.Port)) { ValidIPs.push_back(L.IPAddr); if (Allow.length()) ValidIPs.push_back(Allow); /* Needs resolving */ bool ipvalid = true; QueryType start_type = DNS_QUERY_A; #ifdef IPV6 start_type = DNS_QUERY_AAAA; if (strchr(L.IPAddr.c_str(),':')) { in6_addr n; if (inet_pton(AF_INET6, L.IPAddr.c_str(), &n) < 1) ipvalid = false; } else { in_addr n; if (inet_aton(L.IPAddr.c_str(),&n) < 1) ipvalid = false; } #else in_addr n; if (inet_aton(L.IPAddr.c_str(),&n) < 1) ipvalid = false; #endif if (!ipvalid) { try { bool cached; SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached, start_type); ServerInstance->AddResolver(sr, cached); } catch (...) { } } LinkBlocks.push_back(L); } else { if (L.IPAddr.empty()) { ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str()); } else if (L.RecvPass.empty()) { ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str()); } else if (L.SendPass.empty()) { ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str()); } else if (L.Name.empty()) { ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!"); } else if (!L.Port) { ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str()); } } } else { ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str()); } } DELETE(Conf); } void SpanningTreeUtilities::DoFailOver(Link* x) { if (x->FailOver.length()) { if (x->FailOver == x->Name) { ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str()); return; } Link* TryThisOne = this->FindLink(x->FailOver.c_str()); if (TryThisOne) { ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str()); Creator->ConnectServer(TryThisOne); } else { ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str()); } } } Link* SpanningTreeUtilities::FindLink(const std::string& name) { for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++) { if (ServerInstance->MatchText(x->Name.c_str(), name.c_str())) { return &(*x); } } return NULL; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.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 */
+
+/** 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)
+{
+ server_hash::iterator iter = serverlist.find(ServerName.c_str());
+ if (iter != serverlist.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+TreeServer* SpanningTreeUtilities::FindRemoteBurstServer(TreeServer* Server)
+{
+ server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str());
+ if (iter != RemoteServersBursting.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+TreeSocket* SpanningTreeUtilities::FindBurstingServer(const std::string &ServerName)
+{
+ std::map<irc::string,TreeSocket*>::iterator iter;
+ iter = burstingserverlist.find(ServerName.c_str());
+ if (iter != burstingserverlist.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void SpanningTreeUtilities::SetRemoteBursting(TreeServer* Server, bool bursting)
+{
+ server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str());
+ if (bursting)
+ {
+ if (iter == RemoteServersBursting.end())
+ RemoteServersBursting.insert(make_pair(Server->GetName(), Server));
+ else return;
+ }
+ else
+ {
+ if (iter != RemoteServersBursting.end())
+ RemoteServersBursting.erase(iter);
+ else return;
+ }
+ ServerInstance->Log(DEBUG,"Server %s is %sbursting nicknames", Server->GetName().c_str(), bursting ? "" : "no longer ");
+}
+
+void SpanningTreeUtilities::AddBurstingServer(const std::string &ServerName, TreeSocket* s)
+{
+ std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.find(ServerName.c_str());
+ if (iter == burstingserverlist.end())
+ burstingserverlist[ServerName.c_str()] = s;
+}
+
+void SpanningTreeUtilities::DelBurstingServer(TreeSocket* s)
+{
+ for (std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.begin(); iter != burstingserverlist.end(); iter++)
+ {
+ if (iter->second == s)
+ {
+ burstingserverlist.erase(iter);
+ return;
+ }
+ }
+}
+
+/** Returns the locally connected server we must route a
+ * message through to reach server 'ServerName'. This
+ * only applies to one-to-one and not one-to-many routing.
+ * See the comments for the constructor of TreeServer
+ * for more details.
+ */
+TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
+{
+ if (ServerName.c_str() == TreeRoot->GetName())
+ return NULL;
+ TreeServer* Found = FindServer(ServerName);
+ if (Found)
+ {
+ return Found->GetRoute();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/** Find the first server matching a given glob mask.
+ * Theres no find-using-glob method of hash_map [awwww :-(]
+ * so instead, we iterate over the list using an iterator
+ * and match each one until we get a hit. Yes its slow,
+ * deal with it.
+ */
+TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
+{
+ for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
+ {
+ if (match(i->first.c_str(),ServerName.c_str()))
+ return i->second;
+ }
+ return NULL;
+}
+
+/* A convenient wrapper that returns true if a server exists */
+bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
+{
+ return (FindServer(ServerName) != NULL);
+}
+
+SpanningTreeUtilities::SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* C) : ServerInstance(Instance), Creator(C)
+{
+ Bindings.clear();
+
+ lines_to_apply = 0;
+
+ this->TreeRoot = new TreeServer(this, ServerInstance, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
+
+ modulelist* ml = ServerInstance->FindInterface("InspSocketHook");
+
+ /* Did we find any modules? */
+ if (ml)
+ {
+ /* Yes, enumerate them all to find out the hook name */
+ for (modulelist::iterator m = ml->begin(); m != ml->end(); m++)
+ {
+ /* Make a request to it for its name, its implementing
+ * InspSocketHook so we know its safe to do this
+ */
+ std::string name = InspSocketNameRequest((Module*)Creator, *m).Send();
+ /* Build a map of them */
+ hooks[name.c_str()] = *m;
+ hooknames.push_back(name);
+ }
+ }
+
+ this->ReadConfiguration(true);
+}
+
+SpanningTreeUtilities::~SpanningTreeUtilities()
+{
+ for (unsigned int i = 0; i < Bindings.size(); i++)
+ {
+ ServerInstance->SE->DelFd(Bindings[i]);
+ Bindings[i]->Close();
+ DELETE(Bindings[i]);
+ }
+ while (TreeRoot->ChildCount())
+ {
+ TreeServer* child_server = TreeRoot->GetChild(0);
+ if (child_server)
+ {
+ TreeSocket* sock = child_server->GetSocket();
+ ServerInstance->SE->DelFd(sock);
+ sock->Close();
+ DELETE(sock);
+ }
+ }
+ 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(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list)
+{
+ CUList *ulist;
+ switch (status)
+ {
+ case '@':
+ ulist = c->GetOppedUsers();
+ break;
+ case '%':
+ ulist = c->GetHalfoppedUsers();
+ break;
+ case '+':
+ ulist = c->GetVoicedUsers();
+ break;
+ default:
+ ulist = c->GetUsers();
+ break;
+ }
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if ((i->first->GetFd() < 0) && (exempt_list.find(i->first) == exempt_list.end()))
+ {
+ TreeServer* best = this->BestRouteTo(i->first->server);
+ if (best)
+ AddThisServer(best,list);
+ }
+ }
+ return;
+}
+
+bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> &params)
+{
+ char pfx = 0;
+ TreeServer* omitroute = this->BestRouteTo(omit);
+ if ((command == "NOTICE") || (command == "PRIVMSG"))
+ {
+ if (params.size() >= 2)
+ {
+ /* Prefixes */
+ if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+'))
+ {
+ pfx = params[0][0];
+ params[0] = params[0].substr(1, params[0].length()-1);
+ }
+ if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$'))
+ {
+ // special routing for private messages/notices
+ userrec* d = ServerInstance->FindNick(params[0]);
+ if (d)
+ {
+ std::deque<std::string> par;
+ par.push_back(params[0]);
+ par.push_back(":"+params[1]);
+ this->DoOneToOne(prefix,command.c_str(),par,d->server);
+ return true;
+ }
+ }
+ else if (*(params[0].c_str()) == '$')
+ {
+ std::deque<std::string> par;
+ par.push_back(params[0]);
+ par.push_back(":"+params[1]);
+ this->DoOneToAllButSender(prefix,command.c_str(),par,omitroute->GetName());
+ return true;
+ }
+ else
+ {
+ chanrec* c = ServerInstance->FindChan(params[0]);
+ userrec* u = ServerInstance->FindNick(prefix);
+ if (c && u)
+ {
+ CUList elist;
+ TreeServerList list;
+ FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist));
+ GetListOfServersForChannel(c,list,pfx,elist);
+
+ for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
+ {
+ TreeSocket* Sock = i->second->GetSocket();
+ if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second))
+ {
+ Sock->WriteLine(data);
+ }
+ }
+ return true;
+ }
+ }
+ }
+ }
+ unsigned int items =this->TreeRoot->ChildCount();
+ for (unsigned int x = 0; x < items; x++)
+ {
+ TreeServer* Route = this->TreeRoot->GetChild(x);
+ if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
+ {
+ TreeSocket* Sock = Route->GetSocket();
+ if (Sock)
+ Sock->WriteLine(data);
+ }
+ }
+ return true;
+}
+
+bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string omit)
+{
+ TreeServer* omitroute = this->BestRouteTo(omit);
+ std::string FullLine = ":" + prefix + " " + command;
+ unsigned int words = params.size();
+ for (unsigned int x = 0; x < words; x++)
+ {
+ FullLine = FullLine + " " + params[x];
+ }
+ unsigned int items = this->TreeRoot->ChildCount();
+ for (unsigned int x = 0; x < items; x++)
+ {
+ TreeServer* Route = this->TreeRoot->GetChild(x);
+ // Send the line IF:
+ // The route has a socket (its a direct connection)
+ // The route isnt the one to be omitted
+ // The route isnt the path to the one to be omitted
+ if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
+ {
+ TreeSocket* Sock = Route->GetSocket();
+ if (Sock)
+ Sock->WriteLine(FullLine);
+ }
+ }
+ return true;
+}
+
+bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> &params)
+{
+ std::string FullLine = ":" + prefix + " " + command;
+ unsigned int words = params.size();
+ for (unsigned int x = 0; x < words; x++)
+ {
+ FullLine = FullLine + " " + params[x];
+ }
+ unsigned int items = this->TreeRoot->ChildCount();
+ for (unsigned int x = 0; x < items; x++)
+ {
+ TreeServer* Route = this->TreeRoot->GetChild(x);
+ if (Route && Route->GetSocket())
+ {
+ TreeSocket* Sock = Route->GetSocket();
+ if (Sock)
+ Sock->WriteLine(FullLine);
+ }
+ }
+ return true;
+}
+
+bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, std::deque<std::string> &params)
+{
+ std::string spfx = prefix;
+ std::string scmd = command;
+ return this->DoOneToMany(spfx, scmd, params);
+}
+
+bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> &params, std::string omit)
+{
+ std::string spfx = prefix;
+ std::string scmd = command;
+ return this->DoOneToAllButSender(spfx, scmd, params, omit);
+}
+
+bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string target)
+{
+ 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;
+ }
+}
+
+void SpanningTreeUtilities::RefreshIPCache()
+{
+ ValidIPs.clear();
+ for (std::vector<Link>::iterator L = LinkBlocks.begin(); L != LinkBlocks.end(); L++)
+ {
+ if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port))
+ {
+ ValidIPs.push_back(L->IPAddr);
+
+ if (L->AllowMask.length())
+ ValidIPs.push_back(L->AllowMask);
+
+ /* Needs resolving */
+ bool ipvalid = true;
+ QueryType start_type = DNS_QUERY_A;
+#ifdef IPV6
+ start_type = DNS_QUERY_AAAA;
+ if (strchr(L->IPAddr.c_str(),':'))
+ {
+ in6_addr n;
+ if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1)
+ ipvalid = false;
+ }
+ else
+#endif
+ {
+ in_addr n;
+ if (inet_aton(L->IPAddr.c_str(),&n) < 1)
+ ipvalid = false;
+ }
+ if (!ipvalid)
+ {
+ try
+ {
+ bool cached;
+ SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L->IPAddr, *L, cached, start_type);
+ ServerInstance->AddResolver(sr, cached);
+ }
+ catch (...)
+ {
+ }
+ }
+ }
+ }
+}
+
+void SpanningTreeUtilities::ReadConfiguration(bool rebind)
+{
+ ConfigReader* Conf = new ConfigReader(ServerInstance);
+ if (rebind)
+ {
+ for (int j = 0; j < Conf->Enumerate("bind"); j++)
+ {
+ std::string Type = Conf->ReadValue("bind","type",j);
+ std::string IP = Conf->ReadValue("bind","address",j);
+ std::string Port = Conf->ReadValue("bind","port",j);
+ std::string transport = Conf->ReadValue("bind","transport",j);
+ if (Type == "servers")
+ {
+ irc::portparser portrange(Port, false);
+ int portno = -1;
+ while ((portno = portrange.GetToken()))
+ {
+ if (IP == "*")
+ IP.clear();
+
+ if ((!transport.empty()) && (hooks.find(transport.c_str()) == hooks.end()))
+ {
+ ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for port %s:%s - maybe you forgot to load it BEFORE m_spanningtree in your config file? - Skipping this port binding", transport.c_str(), IP.c_str(), Port.c_str());
+ break;
+ }
+
+ TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10, transport.empty() ? NULL : hooks[transport.c_str()]);
+ if (listener->GetState() == I_LISTENING)
+ {
+ ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno);
+ Bindings.push_back(listener);
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port: %s:%d: %s",IP.c_str(), portno, strerror(errno));
+ listener->Close();
+ DELETE(listener);
+ }
+ }
+ }
+ }
+ }
+ FlatLinks = Conf->ReadFlag("options","flatlinks",0);
+ HideULines = Conf->ReadFlag("options","hideulines",0);
+ AnnounceTSChange = Conf->ReadFlag("options","announcets",0);
+ EnableTimeSync = Conf->ReadFlag("timesync","enable",0);
+ MasterTime = Conf->ReadFlag("timesync", "master", 0);
+ ChallengeResponse = !Conf->ReadFlag("options", "disablehmac", 0);
+ quiet_bursts = Conf->ReadFlag("options", "quietbursts", 0);
+ PingWarnTime = Conf->ReadInteger("options", "pingwarning", 0, true);
+
+ if (PingWarnTime < 0 || PingWarnTime > 59)
+ PingWarnTime = 0;
+
+ LinkBlocks.clear();
+ ValidIPs.clear();
+ for (int j = 0; j < Conf->Enumerate("link"); j++)
+ {
+ Link L;
+ std::string Allow = Conf->ReadValue("link", "allowmask", j);
+ L.Name = (Conf->ReadValue("link", "name", j)).c_str();
+ L.AllowMask = Allow;
+ L.IPAddr = Conf->ReadValue("link", "ipaddr", j);
+ L.FailOver = Conf->ReadValue("link", "failover", j).c_str();
+ L.Port = Conf->ReadInteger("link", "port", j, true);
+ L.SendPass = Conf->ReadValue("link", "sendpass", j);
+ L.RecvPass = Conf->ReadValue("link", "recvpass", j);
+ L.AutoConnect = Conf->ReadInteger("link", "autoconnect", j, true);
+ L.HiddenFromStats = Conf->ReadFlag("link", "statshidden", j);
+ L.Timeout = Conf->ReadInteger("link", "timeout", j, true);
+ L.Hook = Conf->ReadValue("link", "transport", j);
+ L.Bind = Conf->ReadValue("link", "bind", j);
+ L.Hidden = Conf->ReadFlag("link", "hidden", j);
+
+ if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) == hooks.end()))
+ {
+ ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for link '%s' - maybe you forgot to load it BEFORE m_spanningtree in your config file? Skipping <link> tag completely.",
+ L.Hook.c_str(), L.Name.c_str());
+ continue;
+
+ }
+
+ L.NextConnectTime = time(NULL) + L.AutoConnect;
+ /* Bugfix by brain, do not allow people to enter bad configurations */
+ if (L.Name != ServerInstance->Config->ServerName)
+ {
+ if ((!L.IPAddr.empty()) && (!L.RecvPass.empty()) && (!L.SendPass.empty()) && (!L.Name.empty()) && (L.Port))
+ {
+ ValidIPs.push_back(L.IPAddr);
+
+ if (Allow.length())
+ ValidIPs.push_back(Allow);
+
+ /* Needs resolving */
+ bool ipvalid = true;
+ QueryType start_type = DNS_QUERY_A;
+#ifdef IPV6
+ start_type = DNS_QUERY_AAAA;
+ if (strchr(L.IPAddr.c_str(),':'))
+ {
+ in6_addr n;
+ if (inet_pton(AF_INET6, L.IPAddr.c_str(), &n) < 1)
+ ipvalid = false;
+ }
+ else
+ {
+ in_addr n;
+ if (inet_aton(L.IPAddr.c_str(),&n) < 1)
+ ipvalid = false;
+ }
+#else
+ in_addr n;
+ if (inet_aton(L.IPAddr.c_str(),&n) < 1)
+ ipvalid = false;
+#endif
+
+ if (!ipvalid)
+ {
+ try
+ {
+ bool cached;
+ SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached, start_type);
+ ServerInstance->AddResolver(sr, cached);
+ }
+ catch (...)
+ {
+ }
+ }
+
+ LinkBlocks.push_back(L);
+ }
+ else
+ {
+ if (L.IPAddr.empty())
+ {
+ ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str());
+ }
+ else if (L.RecvPass.empty())
+ {
+ ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str());
+ }
+ else if (L.SendPass.empty())
+ {
+ ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str());
+ }
+ else if (L.Name.empty())
+ {
+ ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!");
+ }
+ else if (!L.Port)
+ {
+ ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str());
+ }
+ }
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str());
+ }
+ }
+ DELETE(Conf);
+}
+
+void SpanningTreeUtilities::DoFailOver(Link* x)
+{
+ if (x->FailOver.length())
+ {
+ if (x->FailOver == x->Name)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str());
+ return;
+ }
+ Link* TryThisOne = this->FindLink(x->FailOver.c_str());
+ if (TryThisOne)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str());
+ Creator->ConnectServer(TryThisOne);
+ }
+ else
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str());
+ }
+ }
+}
+
+Link* SpanningTreeUtilities::FindLink(const std::string& name)
+{
+ for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
+ {
+ if (ServerInstance->MatchText(x->Name.c_str(), name.c_str()))
+ {
+ return &(*x);
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/modules/m_spanningtree/utils.h b/src/modules/m_spanningtree/utils.h
index 48146e89e..cb783a81a 100644
--- a/src/modules/m_spanningtree/utils.h
+++ b/src/modules/m_spanningtree/utils.h
@@ -1 +1,194 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __ST__UTIL__ #define __ST__UTIL__ #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" #include "inspircd.h" /* Foward declarations */ class TreeServer; class TreeSocket; class Link; class ModuleSpanningTree; /* This hash_map holds the hash equivalent of the server * tree, used for rapid linear lookups. */ #ifdef WINDOWS typedef nspace::hash_map<std::string, TreeServer*, nspace::hash_compare<string, less<string> > > server_hash; #else typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash; #endif typedef std::map<TreeServer*,TreeServer*> TreeServerList; /** A group of modules that implement InspSocketHook * that we can use to hook our server to server connections. */ typedef std::map<irc::string, Module*> hookmodules; /** Contains helper functions and variables for this module, * and keeps them out of the global namespace */ class SpanningTreeUtilities { private: /** Creator server */ InspIRCd* ServerInstance; public: /** Creator module */ ModuleSpanningTree* Creator; /** Remote servers that are currently bursting */ server_hash RemoteServersBursting; /** Flatten links and /MAP for non-opers */ bool FlatLinks; /** Hide U-Lined servers in /MAP and /LINKS */ bool HideULines; /** Announce TS changes to channels on merge */ bool AnnounceTSChange; /** Synchronize timestamps between servers */ bool EnableTimeSync; /** Make snomasks +CQ quiet during bursts and splits */ bool quiet_bursts; /** Socket bindings for listening sockets */ std::vector<TreeSocket*> Bindings; /* Number of seconds that a server can go without ping * before opers are warned of high latency. */ int PingWarnTime; /** This variable represents the root of the server tree */ TreeServer *TreeRoot; /** IPs allowed to link to us */ std::vector<std::string> ValidIPs; /** Hash of currently connected servers by name */ server_hash serverlist; /** Hash of servers currently bursting but not initialized as connected */ std::map<irc::string,TreeSocket*> burstingserverlist; /** Holds the data from the <link> tags in the conf */ std::vector<Link> LinkBlocks; /** Holds a bitmask of queued xline types waiting to be applied. * Will be a mask containing values APPLY_GLINES, APPLY_KLINES, * APPLY_QLINES and APPLY_ZLINES. */ int lines_to_apply; /** If this is true, this server is the master sync server for time * synching - e.g. it is the server with its clock correct. It will * send out the correct time at intervals. */ bool MasterTime; /** List of module pointers which can provide I/O abstraction */ hookmodules hooks; /** List of module names which can provide I/O abstraction */ std::vector<std::string> hooknames; /** 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; /** Initialise utility class */ SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* Creator); /** Destroy class and free listeners etc */ ~SpanningTreeUtilities(); /** Send a message from this server to one other local or remote */ bool DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string 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, std::deque<std::string> &params, std::string omit); /** Send a message from this server to all but one other, local or remote */ bool DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> &params, std::string omit); /** Send a message from this server to all others */ bool DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> &params); /** Send a message from this server to all others */ bool DoOneToMany(const char* prefix, const char* command, std::deque<std::string> &params); /** Send a message from this server to all others, without doing any processing on the command (e.g. send it as-is with colons and all) */ bool DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> &params); /** Read the spanningtree module's tags from the config file */ void ReadConfiguration(bool rebind); /** Add a server to the server list for GetListOfServersForChannel */ void AddThisServer(TreeServer* server, TreeServerList &list); /** Compile a list of servers which contain members of channel c */ void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list); /** Find a server by name */ TreeServer* FindServer(const std::string &ServerName); /** Find a remote bursting server by name */ TreeServer* FindRemoteBurstServer(TreeServer* Server); /** Set a remote server to bursting or not bursting */ void SetRemoteBursting(TreeServer* Server, bool bursting); /** Find a route to a server by name */ TreeServer* BestRouteTo(const std::string &ServerName); /** Find a server by glob mask */ TreeServer* FindServerMask(const std::string &ServerName); /** Returns true if this is a server name we recognise */ bool IsServer(const std::string &ServerName); /** Attempt to connect to the failover link of link x */ void DoFailOver(Link* x); /** Find a link tag from a server name */ Link* FindLink(const std::string& name); /** Refresh the IP cache used for allowing inbound connections */ void RefreshIPCache(); TreeSocket* FindBurstingServer(const std::string &ServerName); void AddBurstingServer(const std::string &ServerName, TreeSocket* s); void DelBurstingServer(TreeSocket* s); }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __ST__UTIL__
+#define __ST__UTIL__
+
+#include "configreader.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "inspircd.h"
+
+/* Foward declarations */
+class TreeServer;
+class TreeSocket;
+class Link;
+class ModuleSpanningTree;
+
+/* This hash_map holds the hash equivalent of the server
+ * tree, used for rapid linear lookups.
+ */
+#ifdef WINDOWS
+typedef nspace::hash_map<std::string, TreeServer*, nspace::hash_compare<string, less<string> > > server_hash;
+#else
+typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash;
+#endif
+
+typedef std::map<TreeServer*,TreeServer*> TreeServerList;
+
+/** A group of modules that implement InspSocketHook
+ * that we can use to hook our server to server connections.
+ */
+typedef std::map<irc::string, Module*> hookmodules;
+
+/** Contains helper functions and variables for this module,
+ * and keeps them out of the global namespace
+ */
+class SpanningTreeUtilities
+{
+ private:
+ /** Creator server
+ */
+ InspIRCd* ServerInstance;
+ public:
+ /** Creator module
+ */
+ ModuleSpanningTree* Creator;
+ /** Remote servers that are currently bursting
+ */
+ server_hash RemoteServersBursting;
+ /** Flatten links and /MAP for non-opers
+ */
+ bool FlatLinks;
+ /** Hide U-Lined servers in /MAP and /LINKS
+ */
+ bool HideULines;
+ /** Announce TS changes to channels on merge
+ */
+ bool AnnounceTSChange;
+ /** Synchronize timestamps between servers
+ */
+ bool EnableTimeSync;
+ /** Make snomasks +CQ quiet during bursts and splits
+ */
+ bool quiet_bursts;
+ /** Socket bindings for listening sockets
+ */
+ std::vector<TreeSocket*> Bindings;
+ /* Number of seconds that a server can go without ping
+ * before opers are warned of high latency.
+ */
+ int PingWarnTime;
+ /** This variable represents the root of the server tree
+ */
+ TreeServer *TreeRoot;
+ /** IPs allowed to link to us
+ */
+ std::vector<std::string> ValidIPs;
+ /** Hash of currently connected servers by name
+ */
+ server_hash serverlist;
+ /** Hash of servers currently bursting but not initialized as connected
+ */
+ std::map<irc::string,TreeSocket*> burstingserverlist;
+ /** Holds the data from the <link> tags in the conf
+ */
+ std::vector<Link> LinkBlocks;
+ /** Holds a bitmask of queued xline types waiting to be applied.
+ * Will be a mask containing values APPLY_GLINES, APPLY_KLINES,
+ * APPLY_QLINES and APPLY_ZLINES.
+ */
+ int lines_to_apply;
+
+ /** If this is true, this server is the master sync server for time
+ * synching - e.g. it is the server with its clock correct. It will
+ * send out the correct time at intervals.
+ */
+ bool MasterTime;
+
+ /** List of module pointers which can provide I/O abstraction
+ */
+ hookmodules hooks;
+
+ /** List of module names which can provide I/O abstraction
+ */
+ std::vector<std::string> hooknames;
+
+ /** 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;
+
+ /** Initialise utility class
+ */
+ SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* Creator);
+ /** Destroy class and free listeners etc
+ */
+ ~SpanningTreeUtilities();
+ /** Send a message from this server to one other local or remote
+ */
+ bool DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string 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, std::deque<std::string> &params, std::string omit);
+ /** Send a message from this server to all but one other, local or remote
+ */
+ bool DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> &params, std::string omit);
+ /** Send a message from this server to all others
+ */
+ bool DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> &params);
+ /** Send a message from this server to all others
+ */
+ bool DoOneToMany(const char* prefix, const char* command, std::deque<std::string> &params);
+ /** Send a message from this server to all others, without doing any processing on the command (e.g. send it as-is with colons and all)
+ */
+ bool DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> &params);
+ /** Read the spanningtree module's tags from the config file
+ */
+ void ReadConfiguration(bool rebind);
+ /** Add a server to the server list for GetListOfServersForChannel
+ */
+ void AddThisServer(TreeServer* server, TreeServerList &list);
+ /** Compile a list of servers which contain members of channel c
+ */
+ void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list);
+ /** Find a server by name
+ */
+ TreeServer* FindServer(const std::string &ServerName);
+ /** Find a remote bursting server by name
+ */
+ TreeServer* FindRemoteBurstServer(TreeServer* Server);
+ /** Set a remote server to bursting or not bursting
+ */
+ void SetRemoteBursting(TreeServer* Server, bool bursting);
+ /** Find a route to a server by name
+ */
+ TreeServer* BestRouteTo(const std::string &ServerName);
+ /** Find a server by glob mask
+ */
+ TreeServer* FindServerMask(const std::string &ServerName);
+ /** Returns true if this is a server name we recognise
+ */
+ bool IsServer(const std::string &ServerName);
+ /** Attempt to connect to the failover link of link x
+ */
+ void DoFailOver(Link* x);
+ /** Find a link tag from a server name
+ */
+ Link* FindLink(const std::string& name);
+ /** Refresh the IP cache used for allowing inbound connections
+ */
+ void RefreshIPCache();
+
+ TreeSocket* FindBurstingServer(const std::string &ServerName);
+
+ void AddBurstingServer(const std::string &ServerName, TreeSocket* s);
+
+ void DelBurstingServer(TreeSocket* s);
+};
+
+#endif
diff --git a/src/modules/m_spy.cpp b/src/modules/m_spy.cpp
index 11257c437..20b59977c 100644
--- a/src/modules/m_spy.cpp
+++ b/src/modules/m_spy.cpp
@@ -1 +1,163 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* NO, THIS MODULE DOES NOT SPY ON CHANNELS OR USERS. * IT JUST ALLOWS OPERS TO SEE +s CHANNELS IN LIST AND * WHOIS, WHICH IS SUPPORTED BY MOST IRCDS IN CORE. */ /* $ModDesc: Provides SPYLIST and SPYNAMES capability, allowing opers to see who's in +s channels */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" void spy_userlist(userrec *user, chanrec *c) { char list[MAXBUF]; size_t dlen, curlen; dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name); int numusers = 0; char* ptr = list + dlen; CUList *ulist= c->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", c->GetPrefixChar(i->first), i->first->nick); curlen += ptrlen; ptr += ptrlen; numusers++; if (curlen > (480-NICKMAX)) { /* list overflowed into multiple numerics */ user->WriteServ(std::string(list)); /* reset our lengths */ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name); ptr = list + dlen; ptrlen = 0; numusers = 0; } } /* if whats left in the list isnt empty, send it */ if (numusers) { user->WriteServ(std::string(list)); } user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, c->name); } /** Handle /SPYLIST */ class cmd_spylist : public command_t { public: cmd_spylist (InspIRCd* Instance) : command_t(Instance,"SPYLIST", 'o', 0) { this->source = "m_spy.so"; syntax.clear(); } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { ServerInstance->WriteOpers("*** Oper %s used SPYLIST to list +s/+p channels and keys.",user->nick); user->WriteServ("321 %s Channel :Users Name",user->nick); for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) { if (pcnt && !match(i->second->name, parameters[0])) continue; user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,i->second->GetUserCounter(),i->second->ChanModes(true),i->second->topic); } user->WriteServ("323 %s :End of channel list.",user->nick); /* Dont send out across the network */ return CMD_FAILURE; } }; /** Handle /SPYNAMES */ class cmd_spynames : public command_t { public: cmd_spynames (InspIRCd* Instance) : command_t(Instance,"SPYNAMES", 'o', 0) { this->source = "m_spy.so"; syntax = "{<channel>{,<channel>}}"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { chanrec* c = NULL; if (!pcnt) { user->WriteServ("366 %s * :End of /NAMES list.",user->nick); return CMD_FAILURE; } if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) return CMD_FAILURE; c = ServerInstance->FindChan(parameters[0]); if (c) { ServerInstance->WriteOpers("*** Oper %s used SPYNAMES to view the users on %s", user->nick, parameters[0]); spy_userlist(user,c); } else { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); } return CMD_FAILURE; } }; class ModuleSpy : public Module { cmd_spylist *mycommand; cmd_spynames *mycommand2; public: ModuleSpy(InspIRCd* Me) : Module(Me) { mycommand = new cmd_spylist(ServerInstance); mycommand2 = new cmd_spynames(ServerInstance); ServerInstance->AddCommand(mycommand); ServerInstance->AddCommand(mycommand2); } virtual ~ModuleSpy() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleSpy) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* NO, THIS MODULE DOES NOT SPY ON CHANNELS OR USERS.
+ * IT JUST ALLOWS OPERS TO SEE +s CHANNELS IN LIST AND
+ * WHOIS, WHICH IS SUPPORTED BY MOST IRCDS IN CORE.
+ */
+
+/* $ModDesc: Provides SPYLIST and SPYNAMES capability, allowing opers to see who's in +s channels */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+void spy_userlist(userrec *user, chanrec *c)
+{
+ char list[MAXBUF];
+ size_t dlen, curlen;
+
+ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
+
+ int numusers = 0;
+ char* ptr = list + dlen;
+
+ CUList *ulist= c->GetUsers();
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", c->GetPrefixChar(i->first), i->first->nick);
+
+ curlen += ptrlen;
+ ptr += ptrlen;
+
+ numusers++;
+
+ if (curlen > (480-NICKMAX))
+ {
+ /* list overflowed into multiple numerics */
+ user->WriteServ(std::string(list));
+
+ /* reset our lengths */
+ dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
+ ptr = list + dlen;
+
+ ptrlen = 0;
+ numusers = 0;
+ }
+ }
+
+ /* if whats left in the list isnt empty, send it */
+ if (numusers)
+ {
+ user->WriteServ(std::string(list));
+ }
+
+ user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, c->name);
+
+}
+
+/** Handle /SPYLIST
+ */
+class cmd_spylist : public command_t
+{
+ public:
+ cmd_spylist (InspIRCd* Instance) : command_t(Instance,"SPYLIST", 'o', 0)
+ {
+ this->source = "m_spy.so";
+ syntax.clear();
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ ServerInstance->WriteOpers("*** Oper %s used SPYLIST to list +s/+p channels and keys.",user->nick);
+ user->WriteServ("321 %s Channel :Users Name",user->nick);
+ for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+ {
+ if (pcnt && !match(i->second->name, parameters[0]))
+ continue;
+ user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,i->second->GetUserCounter(),i->second->ChanModes(true),i->second->topic);
+ }
+ user->WriteServ("323 %s :End of channel list.",user->nick);
+
+ /* Dont send out across the network */
+ return CMD_FAILURE;
+ }
+};
+
+/** Handle /SPYNAMES
+ */
+class cmd_spynames : public command_t
+{
+ public:
+ cmd_spynames (InspIRCd* Instance) : command_t(Instance,"SPYNAMES", 'o', 0)
+ {
+ this->source = "m_spy.so";
+ syntax = "{<channel>{,<channel>}}";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ chanrec* c = NULL;
+
+ if (!pcnt)
+ {
+ user->WriteServ("366 %s * :End of /NAMES list.",user->nick);
+ return CMD_FAILURE;
+ }
+
+ if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
+ return CMD_FAILURE;
+
+ c = ServerInstance->FindChan(parameters[0]);
+ if (c)
+ {
+ ServerInstance->WriteOpers("*** Oper %s used SPYNAMES to view the users on %s", user->nick, parameters[0]);
+ spy_userlist(user,c);
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleSpy : public Module
+{
+ cmd_spylist *mycommand;
+ cmd_spynames *mycommand2;
+ public:
+ ModuleSpy(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_spylist(ServerInstance);
+ mycommand2 = new cmd_spynames(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ ServerInstance->AddCommand(mycommand2);
+ }
+
+ virtual ~ModuleSpy()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSpy)
diff --git a/src/modules/m_ssl_dummy.cpp b/src/modules/m_ssl_dummy.cpp
index 3b872b81c..fb3032da2 100644
--- a/src/modules/m_ssl_dummy.cpp
+++ b/src/modules/m_ssl_dummy.cpp
@@ -1 +1,84 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" /* $ModDesc: Makes remote /whoises to SSL servers work on a non-ssl server */ class ModuleSSLDummy : public Module { char* dummy; public: ModuleSSLDummy(InspIRCd* Me) : Module(Me) { } virtual ~ModuleSSLDummy() { } virtual Version GetVersion() { return Version(1, 0, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnWhois] = 1; } // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection virtual void OnWhois(userrec* source, userrec* dest) { if(dest->GetExt("ssl", dummy)) { ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick); } } virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) { // check if the linking module wants to know about OUR metadata if(extname == "ssl") { // check if this user has an ssl field to send if(user->GetExt(extname, dummy)) { // call this function in the linking module, let it format the data how it // sees fit, and send it on its way. We dont need or want to know how. proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON"); } } } virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { // check if its our metadata key, and its associated with a user if ((target_type == TYPE_USER) && (extname == "ssl")) { userrec* dest = (userrec*)target; // if they dont already have an ssl flag, accept the remote server's if (!dest->GetExt(extname, dummy)) { dest->Extend(extname, "ON"); } } } }; MODULE_INIT(ModuleSSLDummy) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+
+/* $ModDesc: Makes remote /whoises to SSL servers work on a non-ssl server */
+
+class ModuleSSLDummy : public Module
+{
+
+ char* dummy;
+ public:
+
+ ModuleSSLDummy(InspIRCd* Me) : Module(Me)
+ {
+
+ }
+
+ virtual ~ModuleSSLDummy()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 0, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnWhois] = 1;
+ }
+
+ // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
+ virtual void OnWhois(userrec* source, userrec* dest)
+ {
+ if(dest->GetExt("ssl", dummy))
+ {
+ ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
+ }
+ }
+
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
+ {
+ // check if the linking module wants to know about OUR metadata
+ if(extname == "ssl")
+ {
+ // check if this user has an ssl field to send
+ if(user->GetExt(extname, dummy))
+ {
+ // call this function in the linking module, let it format the data how it
+ // sees fit, and send it on its way. We dont need or want to know how.
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
+ }
+ }
+ }
+
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ // check if its our metadata key, and its associated with a user
+ if ((target_type == TYPE_USER) && (extname == "ssl"))
+ {
+ userrec* dest = (userrec*)target;
+ // if they dont already have an ssl flag, accept the remote server's
+ if (!dest->GetExt(extname, dummy))
+ {
+ dest->Extend(extname, "ON");
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleSSLDummy)
diff --git a/src/modules/m_sslmodes.cpp b/src/modules/m_sslmodes.cpp
index 0e06aa314..c8eee5a03 100644
--- a/src/modules/m_sslmodes.cpp
+++ b/src/modules/m_sslmodes.cpp
@@ -1 +1,145 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for unreal-style channel mode +z */ static char* dummy; /** Handle channel mode +z */ class SSLMode : public ModeHandler { public: SSLMode(InspIRCd* Instance) : ModeHandler(Instance, 'z', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('z')) { if (IS_LOCAL(source)) { CUList* userlist = channel->GetUsers(); for(CUList::iterator i = userlist->begin(); i != userlist->end(); i++) { if(!i->first->GetExt("ssl", dummy)) { source->WriteServ("490 %s %s :all members of the channel must be connected via SSL", source->nick, channel->name); return MODEACTION_DENY; } } } channel->SetMode('z',true); return MODEACTION_ALLOW; } else { return MODEACTION_DENY; } } else { if (channel->IsModeSet('z')) { channel->SetMode('z',false); return MODEACTION_ALLOW; } return MODEACTION_DENY; } } }; class ModuleSSLModes : public Module { SSLMode* sslm; public: ModuleSSLModes(InspIRCd* Me) : Module(Me) { sslm = new SSLMode(ServerInstance); if (!ServerInstance->AddMode(sslm, 'z')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreJoin] = 1; } virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { if(chan && chan->IsModeSet('z')) { if(user->GetExt("ssl", dummy)) { // Let them in return 0; } else { // Deny user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick, cname); return 1; } } return 0; } virtual ~ModuleSSLModes() { ServerInstance->Modes->DelMode(sslm); DELETE(sslm); } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; class ModuleSSLModesFactory : public ModuleFactory { public: ModuleSSLModesFactory() { } ~ModuleSSLModesFactory() { } virtual Module* CreateModule(InspIRCd* Me) { return new ModuleSSLModes(Me); } }; extern "C" DllExport void * init_module( void ) { return new ModuleSSLModesFactory; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for unreal-style channel mode +z */
+
+static char* dummy;
+
+/** Handle channel mode +z
+ */
+class SSLMode : public ModeHandler
+{
+ public:
+ SSLMode(InspIRCd* Instance) : ModeHandler(Instance, 'z', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('z'))
+ {
+ if (IS_LOCAL(source))
+ {
+ CUList* userlist = channel->GetUsers();
+ for(CUList::iterator i = userlist->begin(); i != userlist->end(); i++)
+ {
+ if(!i->first->GetExt("ssl", dummy))
+ {
+ source->WriteServ("490 %s %s :all members of the channel must be connected via SSL", source->nick, channel->name);
+ return MODEACTION_DENY;
+ }
+ }
+ }
+ channel->SetMode('z',true);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ return MODEACTION_DENY;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('z'))
+ {
+ channel->SetMode('z',false);
+ return MODEACTION_ALLOW;
+ }
+
+ return MODEACTION_DENY;
+ }
+ }
+};
+
+class ModuleSSLModes : public Module
+{
+
+ SSLMode* sslm;
+
+ public:
+ ModuleSSLModes(InspIRCd* Me)
+ : Module(Me)
+ {
+
+
+ sslm = new SSLMode(ServerInstance);
+ if (!ServerInstance->AddMode(sslm, 'z'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreJoin] = 1;
+ }
+
+ virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
+ {
+ if(chan && chan->IsModeSet('z'))
+ {
+ if(user->GetExt("ssl", dummy))
+ {
+ // Let them in
+ return 0;
+ }
+ else
+ {
+ // Deny
+ user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick, cname);
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ virtual ~ModuleSSLModes()
+ {
+ ServerInstance->Modes->DelMode(sslm);
+ DELETE(sslm);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+};
+
+
+class ModuleSSLModesFactory : public ModuleFactory
+{
+ public:
+ ModuleSSLModesFactory()
+ {
+ }
+
+ ~ModuleSSLModesFactory()
+ {
+ }
+
+ virtual Module* CreateModule(InspIRCd* Me)
+ {
+ return new ModuleSSLModes(Me);
+ }
+
+};
+
+
+extern "C" DllExport void * init_module( void )
+{
+ return new ModuleSSLModesFactory;
+}
diff --git a/src/modules/m_stripcolor.cpp b/src/modules/m_stripcolor.cpp
index 6f1d7b130..aad253bc7 100644
--- a/src/modules/m_stripcolor.cpp
+++ b/src/modules/m_stripcolor.cpp
@@ -1 +1,185 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides channel +S mode (strip ansi colour) */ /** Handles channel mode +S */ class ChannelStripColor : public ModeHandler { public: ChannelStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_CHANNEL, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { if (adding) { if (!channel->IsModeSet('S')) { channel->SetMode('S',true); return MODEACTION_ALLOW; } } else { if (channel->IsModeSet('S')) { channel->SetMode('S',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; /** Handles user mode +S */ class UserStripColor : public ModeHandler { public: UserStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_USER, false) { } ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding) { /* Only opers can change other users modes */ if (source != dest) return MODEACTION_DENY; if (adding) { if (!dest->IsModeSet('S')) { dest->SetMode('S',true); return MODEACTION_ALLOW; } } else { if (dest->IsModeSet('S')) { dest->SetMode('S',false); return MODEACTION_ALLOW; } } return MODEACTION_DENY; } }; class ModuleStripColor : public Module { bool AllowChanOps; ChannelStripColor *csc; UserStripColor *usc; public: ModuleStripColor(InspIRCd* Me) : Module(Me) { usc = new UserStripColor(ServerInstance); csc = new ChannelStripColor(ServerInstance); if (!ServerInstance->AddMode(usc, 'S') || !ServerInstance->AddMode(csc, 'S')) throw ModuleException("Could not add new modes!"); } void Implements(char* List) { List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; } virtual ~ModuleStripColor() { ServerInstance->Modes->DelMode(usc); ServerInstance->Modes->DelMode(csc); DELETE(usc); DELETE(csc); } virtual void ReplaceLine(std::string &sentence) { /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */ int seq = 0; std::string::iterator i,safei; for (i = sentence.begin(); i != sentence.end(); ++i) { if ((*i == 3)) seq = 1; else if (seq && ( (*i >= '0') && (*i <= '9') || (*i == ',') ) ) { seq++; if ( (seq <= 4) && (*i == ',') ) seq = 1; else if (seq > 3) seq = 0; } else seq = 0; if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31))) { safei = i; --i; sentence.erase(safei); } } } virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) { if (!IS_LOCAL(user)) return 0; bool active = false; if (target_type == TYPE_USER) { userrec* t = (userrec*)dest; active = t->IsModeSet('S'); } else if (target_type == TYPE_CHANNEL) { chanrec* t = (chanrec*)dest; // check if we allow ops to bypass filtering, if we do, check if they're opped accordingly. // note: short circut logic here, don't wreck it. -- w00t if (!CHANOPS_EXEMPT(ServerInstance, 'S') || CHANOPS_EXEMPT(ServerInstance, 'S') && t->GetStatus(user) != STATUS_OP) active = t->IsModeSet('S'); } if (active) { this->ReplaceLine(text); } return 0; } virtual int OnUserPreNotice(userrec* 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() { return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleStripColor) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides channel +S mode (strip ansi colour) */
+
+/** Handles channel mode +S
+ */
+class ChannelStripColor : public ModeHandler
+{
+ public:
+ ChannelStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_CHANNEL, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ if (adding)
+ {
+ if (!channel->IsModeSet('S'))
+ {
+ channel->SetMode('S',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (channel->IsModeSet('S'))
+ {
+ channel->SetMode('S',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+/** Handles user mode +S
+ */
+class UserStripColor : public ModeHandler
+{
+ public:
+ UserStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_USER, false) { }
+
+ ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
+ {
+ /* Only opers can change other users modes */
+ if (source != dest)
+ return MODEACTION_DENY;
+
+ if (adding)
+ {
+ if (!dest->IsModeSet('S'))
+ {
+ dest->SetMode('S',true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet('S'))
+ {
+ dest->SetMode('S',false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
+ }
+};
+
+
+class ModuleStripColor : public Module
+{
+ bool AllowChanOps;
+ ChannelStripColor *csc;
+ UserStripColor *usc;
+
+ public:
+ ModuleStripColor(InspIRCd* Me) : Module(Me)
+ {
+ usc = new UserStripColor(ServerInstance);
+ csc = new ChannelStripColor(ServerInstance);
+
+ if (!ServerInstance->AddMode(usc, 'S') || !ServerInstance->AddMode(csc, 'S'))
+ throw ModuleException("Could not add new modes!");
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
+ }
+
+ virtual ~ModuleStripColor()
+ {
+ ServerInstance->Modes->DelMode(usc);
+ ServerInstance->Modes->DelMode(csc);
+ DELETE(usc);
+ DELETE(csc);
+ }
+
+ virtual void ReplaceLine(std::string &sentence)
+ {
+ /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
+ int seq = 0;
+ std::string::iterator i,safei;
+ for (i = sentence.begin(); i != sentence.end(); ++i)
+ {
+ if ((*i == 3))
+ seq = 1;
+ else if (seq && ( (*i >= '0') && (*i <= '9') || (*i == ',') ) )
+ {
+ seq++;
+ if ( (seq <= 4) && (*i == ',') )
+ seq = 1;
+ else if (seq > 3)
+ seq = 0;
+ }
+ else
+ seq = 0;
+
+ if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31)))
+ {
+ safei = i;
+ --i;
+ sentence.erase(safei);
+ }
+ }
+ }
+
+ virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ {
+ if (!IS_LOCAL(user))
+ return 0;
+
+ bool active = false;
+ if (target_type == TYPE_USER)
+ {
+ userrec* t = (userrec*)dest;
+ active = t->IsModeSet('S');
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ chanrec* t = (chanrec*)dest;
+
+ // check if we allow ops to bypass filtering, if we do, check if they're opped accordingly.
+ // note: short circut logic here, don't wreck it. -- w00t
+ if (!CHANOPS_EXEMPT(ServerInstance, 'S') || CHANOPS_EXEMPT(ServerInstance, 'S') && t->GetStatus(user) != STATUS_OP)
+ active = t->IsModeSet('S');
+ }
+
+ if (active)
+ {
+ this->ReplaceLine(text);
+ }
+
+ return 0;
+ }
+
+ virtual int OnUserPreNotice(userrec* 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()
+ {
+ return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleStripColor)
diff --git a/src/modules/m_svshold.cpp b/src/modules/m_svshold.cpp
index f220d1638..4058c04d0 100644
--- a/src/modules/m_svshold.cpp
+++ b/src/modules/m_svshold.cpp
@@ -1 +1,282 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <algorithm> #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" /* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */ /** Holds a SVSHold item */ class SVSHold : public classbase { public: std::string nickname; std::string set_by; time_t set_on; long length; std::string reason; SVSHold() { } SVSHold(const std::string &nn, const std::string &sb, const time_t so, const long ln, const std::string &rs) : nickname(nn), set_by(sb), set_on(so), length(ln), reason(rs) { } }; bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2); typedef std::vector<SVSHold*> SVSHoldlist; typedef std::map<irc::string, SVSHold*> SVSHoldMap; /* SVSHolds is declared here, as our type is right above. Don't try move it. */ SVSHoldlist SVSHolds; SVSHoldMap HoldMap; /** Handle /SVSHold */ class cmd_svshold : public command_t { public: cmd_svshold(InspIRCd* Me) : command_t(Me, "SVSHOLD", 'o', 1) { this->source = "m_svshold.so"; this->syntax = "<nickname> [<duration> :<reason>]"; } CmdResult Handle(const char** parameters, int pcnt, userrec *user) { /* syntax: svshold nickname time :reason goes here */ /* 'time' is a human-readable timestring, like 2d3h2s. */ if (!ServerInstance->ULine(user->server)) { /* don't allow SVSHOLD from non-ulined clients */ return CMD_FAILURE; } if (pcnt == 1) { SVSHoldMap::iterator n = HoldMap.find(parameters[0]); if (n != HoldMap.end()) { /* form: svshold nickname removes a hold. */ for (SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++) { if (parameters[0] == assign((*iter)->nickname)) { unsigned long remaining = 0; if ((*iter)->length) { remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time(); user->WriteServ( "386 %s %s :Removed SVSHOLD with %lu seconds left before expiry (%s)", user->nick, (*iter)->nickname.c_str(), remaining, (*iter)->reason.c_str()); } else { user->WriteServ( "386 %s %s :Removed permanent SVSHOLD (%s)", user->nick, (*iter)->nickname.c_str(), (*iter)->reason.c_str()); } SVSHolds.erase(iter); break; } } HoldMap.erase(n); delete n->second; } } else if (pcnt >= 2) { /* full form to add a SVSHold */ if (ServerInstance->IsNick(parameters[0])) { // parameters[0] = w00t // parameters[1] = 1h3m2s // parameters[2] = Registered nickname /* Already exists? */ if (HoldMap.find(parameters[0]) != HoldMap.end()) { user->WriteServ( "385 %s %s :SVSHOLD already exists", user->nick, parameters[0]); return CMD_FAILURE; } long length = ServerInstance->Duration(parameters[1]); std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied"; SVSHold* S = new SVSHold(parameters[0], user->nick, ServerInstance->Time(), length, reason); SVSHolds.push_back(S); HoldMap[parameters[0]] = S; std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp); if(length > 0) { user->WriteServ( "385 %s %s :Added %lu second SVSHOLD (%s)", user->nick, parameters[0], length, reason.c_str()); ServerInstance->WriteOpers("*** %s added %lu second SVSHOLD on %s (%s)", user->nick, length, parameters[0], reason.c_str()); } else { user->WriteServ( "385 %s %s :Added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], parameters[0], reason.c_str()); ServerInstance->WriteOpers("*** %s added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], reason.c_str()); } } else { /* as this is primarily a Services command, do not provide an error */ return CMD_FAILURE; } } return CMD_SUCCESS; } }; bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2) { return ((ban1->set_on + ban1->length) < (ban2->set_on + ban2->length)); } class ModuleSVSHold : public Module { cmd_svshold *mycommand; public: ModuleSVSHold(InspIRCd* Me) : Module(Me) { mycommand = new cmd_svshold(Me); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_OnUserPreNick] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1; } virtual int OnStats(char symbol, userrec* user, string_list &results) { ExpireBans(); if(symbol == 'S') { for(SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++) { unsigned long remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time(); results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+(*iter)->nickname.c_str()+" "+(*iter)->set_by+" "+ConvToStr((*iter)->set_on)+" "+ConvToStr((*iter)->length)+" "+ConvToStr(remaining)+" :"+(*iter)->reason); } } return 0; } virtual int OnUserPreNick(userrec *user, const std::string &newnick) { ExpireBans(); /* check SVSHolds in here, and apply as necessary. */ SVSHoldMap::iterator n = HoldMap.find(assign(newnick)); if (n != HoldMap.end()) { user->WriteServ( "432 %s %s :Reserved nickname: %s", user->nick, newnick.c_str(), n->second->reason.c_str()); return 1; } return 0; } virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { for(SVSHoldMap::iterator iter = HoldMap.begin(); iter != HoldMap.end(); iter++) { proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "SVSHold", EncodeSVSHold(iter->second)); } } virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { if((target_type == TYPE_OTHER) && (extname == "SVSHold")) { SVSHold* S = DecodeSVSHold(extdata); /* NOTE: Allocates a new SVSHold* */ if (HoldMap.find(assign(S->nickname)) == HoldMap.end()) { SVSHolds.push_back(S); HoldMap[assign(S->nickname)] = S; std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp); } else { delete S; } } } virtual ~ModuleSVSHold() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR|VF_COMMON,API_VERSION); } std::string EncodeSVSHold(const SVSHold* ban) { std::ostringstream stream; stream << ban->nickname << " " << ban->set_by << " " << ban->set_on << " " << ban->length << " :" << ban->reason; return stream.str(); } SVSHold* DecodeSVSHold(const std::string &data) { SVSHold* res = new SVSHold(); int set_on; irc::tokenstream tokens(data); tokens.GetToken(res->nickname); tokens.GetToken(res->set_by); tokens.GetToken(set_on); res->set_on = set_on; tokens.GetToken(res->length); tokens.GetToken(res->reason); return res; } void ExpireBans() { SVSHoldlist::iterator iter,safeiter; for (iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++) { /* 0 == permanent, don't mess with them! -- w00t */ if ((*iter)->length != 0) { if ((*iter)->set_on + (*iter)->length <= ServerInstance->Time()) { ServerInstance->Log(DEBUG, "m_svshold.so: hold on %s expired, removing...", (*iter)->nickname.c_str()); ServerInstance->WriteOpers("*** %li second SVSHOLD on %s (%s) set %u seconds ago expired", (*iter)->length, (*iter)->nickname.c_str(), (*iter)->reason.c_str(), ServerInstance->Time() - (*iter)->set_on); HoldMap.erase(assign((*iter)->nickname)); delete *iter; safeiter = iter; --iter; SVSHolds.erase(safeiter); } } } } }; MODULE_INIT(ModuleSVSHold) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <algorithm>
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+
+/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */
+
+/** Holds a SVSHold item
+ */
+class SVSHold : public classbase
+{
+public:
+ std::string nickname;
+ std::string set_by;
+ time_t set_on;
+ long length;
+ std::string reason;
+
+ SVSHold()
+ {
+ }
+
+ SVSHold(const std::string &nn, const std::string &sb, const time_t so, const long ln, const std::string &rs) : nickname(nn), set_by(sb), set_on(so), length(ln), reason(rs)
+ {
+ }
+};
+
+
+bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2);
+
+typedef std::vector<SVSHold*> SVSHoldlist;
+typedef std::map<irc::string, SVSHold*> SVSHoldMap;
+
+/* SVSHolds is declared here, as our type is right above. Don't try move it. */
+SVSHoldlist SVSHolds;
+SVSHoldMap HoldMap;
+
+/** Handle /SVSHold
+ */
+class cmd_svshold : public command_t
+{
+ public:
+ cmd_svshold(InspIRCd* Me) : command_t(Me, "SVSHOLD", 'o', 1)
+ {
+ this->source = "m_svshold.so";
+ this->syntax = "<nickname> [<duration> :<reason>]";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec *user)
+ {
+ /* syntax: svshold nickname time :reason goes here */
+ /* 'time' is a human-readable timestring, like 2d3h2s. */
+
+ if (!ServerInstance->ULine(user->server))
+ {
+ /* don't allow SVSHOLD from non-ulined clients */
+ return CMD_FAILURE;
+ }
+
+ if (pcnt == 1)
+ {
+ SVSHoldMap::iterator n = HoldMap.find(parameters[0]);
+ if (n != HoldMap.end())
+ {
+ /* form: svshold nickname removes a hold. */
+ for (SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++)
+ {
+ if (parameters[0] == assign((*iter)->nickname))
+ {
+ unsigned long remaining = 0;
+ if ((*iter)->length)
+ {
+ remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time();
+ user->WriteServ( "386 %s %s :Removed SVSHOLD with %lu seconds left before expiry (%s)", user->nick, (*iter)->nickname.c_str(), remaining, (*iter)->reason.c_str());
+ }
+ else
+ {
+ user->WriteServ( "386 %s %s :Removed permanent SVSHOLD (%s)", user->nick, (*iter)->nickname.c_str(), (*iter)->reason.c_str());
+ }
+ SVSHolds.erase(iter);
+ break;
+ }
+ }
+
+ HoldMap.erase(n);
+ delete n->second;
+ }
+ }
+ else if (pcnt >= 2)
+ {
+ /* full form to add a SVSHold */
+ if (ServerInstance->IsNick(parameters[0]))
+ {
+ // parameters[0] = w00t
+ // parameters[1] = 1h3m2s
+ // parameters[2] = Registered nickname
+
+ /* Already exists? */
+ if (HoldMap.find(parameters[0]) != HoldMap.end())
+ {
+ user->WriteServ( "385 %s %s :SVSHOLD already exists", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ long length = ServerInstance->Duration(parameters[1]);
+ std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied";
+
+ SVSHold* S = new SVSHold(parameters[0], user->nick, ServerInstance->Time(), length, reason);
+ SVSHolds.push_back(S);
+ HoldMap[parameters[0]] = S;
+
+ std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp);
+
+ if(length > 0)
+ {
+ user->WriteServ( "385 %s %s :Added %lu second SVSHOLD (%s)", user->nick, parameters[0], length, reason.c_str());
+ ServerInstance->WriteOpers("*** %s added %lu second SVSHOLD on %s (%s)", user->nick, length, parameters[0], reason.c_str());
+ }
+ else
+ {
+ user->WriteServ( "385 %s %s :Added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], parameters[0], reason.c_str());
+ ServerInstance->WriteOpers("*** %s added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], reason.c_str());
+ }
+ }
+ else
+ {
+ /* as this is primarily a Services command, do not provide an error */
+ return CMD_FAILURE;
+ }
+ }
+
+ return CMD_SUCCESS;
+ }
+};
+
+bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2)
+{
+ return ((ban1->set_on + ban1->length) < (ban2->set_on + ban2->length));
+}
+
+class ModuleSVSHold : public Module
+{
+ cmd_svshold *mycommand;
+
+
+ public:
+ ModuleSVSHold(InspIRCd* Me) : Module(Me)
+ {
+ mycommand = new cmd_svshold(Me);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUserPreNick] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1;
+ }
+
+ virtual int OnStats(char symbol, userrec* user, string_list &results)
+ {
+ ExpireBans();
+
+ if(symbol == 'S')
+ {
+ for(SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++)
+ {
+ unsigned long remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time();
+ results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+(*iter)->nickname.c_str()+" "+(*iter)->set_by+" "+ConvToStr((*iter)->set_on)+" "+ConvToStr((*iter)->length)+" "+ConvToStr(remaining)+" :"+(*iter)->reason);
+ }
+ }
+
+ return 0;
+ }
+
+ virtual int OnUserPreNick(userrec *user, const std::string &newnick)
+ {
+ ExpireBans();
+
+ /* check SVSHolds in here, and apply as necessary. */
+ SVSHoldMap::iterator n = HoldMap.find(assign(newnick));
+ if (n != HoldMap.end())
+ {
+ user->WriteServ( "432 %s %s :Reserved nickname: %s", user->nick, newnick.c_str(), n->second->reason.c_str());
+ return 1;
+ }
+ return 0;
+ }
+
+ virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
+ {
+ for(SVSHoldMap::iterator iter = HoldMap.begin(); iter != HoldMap.end(); iter++)
+ {
+ proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "SVSHold", EncodeSVSHold(iter->second));
+ }
+ }
+
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ if((target_type == TYPE_OTHER) && (extname == "SVSHold"))
+ {
+ SVSHold* S = DecodeSVSHold(extdata); /* NOTE: Allocates a new SVSHold* */
+ if (HoldMap.find(assign(S->nickname)) == HoldMap.end())
+ {
+ SVSHolds.push_back(S);
+ HoldMap[assign(S->nickname)] = S;
+ std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp);
+ }
+ else
+ {
+ delete S;
+ }
+ }
+ }
+
+ virtual ~ModuleSVSHold()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR|VF_COMMON,API_VERSION);
+ }
+
+ std::string EncodeSVSHold(const SVSHold* ban)
+ {
+ std::ostringstream stream;
+ stream << ban->nickname << " " << ban->set_by << " " << ban->set_on << " " << ban->length << " :" << ban->reason;
+ return stream.str();
+ }
+
+ SVSHold* DecodeSVSHold(const std::string &data)
+ {
+ SVSHold* res = new SVSHold();
+ int set_on;
+ irc::tokenstream tokens(data);
+ tokens.GetToken(res->nickname);
+ tokens.GetToken(res->set_by);
+ tokens.GetToken(set_on);
+ res->set_on = set_on;
+ tokens.GetToken(res->length);
+ tokens.GetToken(res->reason);
+ return res;
+ }
+
+ void ExpireBans()
+ {
+ SVSHoldlist::iterator iter,safeiter;
+ for (iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++)
+ {
+ /* 0 == permanent, don't mess with them! -- w00t */
+ if ((*iter)->length != 0)
+ {
+ if ((*iter)->set_on + (*iter)->length <= ServerInstance->Time())
+ {
+ ServerInstance->Log(DEBUG, "m_svshold.so: hold on %s expired, removing...", (*iter)->nickname.c_str());
+ ServerInstance->WriteOpers("*** %li second SVSHOLD on %s (%s) set %u seconds ago expired", (*iter)->length, (*iter)->nickname.c_str(), (*iter)->reason.c_str(), ServerInstance->Time() - (*iter)->set_on);
+ HoldMap.erase(assign((*iter)->nickname));
+ delete *iter;
+ safeiter = iter;
+ --iter;
+ SVSHolds.erase(safeiter);
+ }
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleSVSHold)
diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp
index 5df5fe4eb..d635654a5 100644
--- a/src/modules/m_swhois.cpp
+++ b/src/modules/m_swhois.cpp
@@ -1 +1,267 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides the SWHOIS command which allows setting of arbitary WHOIS lines */ /** Handle /SWHOIS */ class cmd_swhois : public command_t { public: cmd_swhois (InspIRCd* Instance) : command_t(Instance,"SWHOIS",'o',2) { this->source = "m_swhois.so"; syntax = "<nick> <swhois>"; } CmdResult Handle(const char** parameters, int pcnt, userrec* user) { userrec* dest = ServerInstance->FindNick(parameters[0]); if (!dest) { user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); return CMD_FAILURE; } if (!*parameters[1]) { user->WriteServ("NOTICE %s :*** SWHOIS: Whois line must be specified", user->nick); return CMD_FAILURE; } std::string line; for (int i = 1; i < pcnt; i++) { if (i != 1) line.append(" "); line.append(parameters[i]); } std::string* text; dest->GetExt("swhois", text); if (text) { // We already had it set... if (!ServerInstance->ULine(user->server)) // Ulines set SWHOISes silently ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick, dest->nick, text->c_str(), line.c_str()); dest->Shrink("swhois"); DELETE(text); } else if (!ServerInstance->ULine(user->server)) { // Ulines set SWHOISes silently ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois to '%s'", user->nick, dest->nick, line.c_str()); } text = new std::string(line); dest->Extend("swhois", text); return CMD_SUCCESS; } }; class ModuleSWhois : public Module { cmd_swhois* mycommand; ConfigReader* Conf; public: ModuleSWhois(InspIRCd* Me) : Module(Me) { Conf = new ConfigReader(ServerInstance); mycommand = new cmd_swhois(ServerInstance); ServerInstance->AddCommand(mycommand); } void OnRehash(userrec* user, const std::string &parameter) { DELETE(Conf); Conf = new ConfigReader(ServerInstance); } void Implements(char* List) { List[I_OnDecodeMetaData] = List[I_OnWhoisLine] = List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnRehash] = List[I_OnPostCommand] = 1; } // :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games. int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { /* We use this and not OnWhois because this triggers for remote, too */ if (numeric == 312) { /* Insert our numeric before 312 */ std::string* swhois; dest->GetExt("swhois", swhois); if (swhois) { ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick,dest->nick,swhois->c_str()); } } /* Dont block anything */ return 0; } // Whenever the linking module wants to send out data, but doesnt know what the data // represents (e.g. it is metadata, added to a userrec or chanrec by a module) then // this method is called. We should use the ProtoSendMetaData function after we've // corrected decided how the data should look, to send the metadata on its way if // it is ours. virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) { // check if the linking module wants to know about OUR metadata if (extname == "swhois") { // check if this user has an swhois field to send std::string* swhois; user->GetExt("swhois", swhois); if (swhois) { // call this function in the linking module, let it format the data how it // sees fit, and send it on its way. We dont need or want to know how. proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*swhois); } } } // when a user quits, tidy up their metadata virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) { std::string* swhois; user->GetExt("swhois", swhois); if (swhois) { user->Shrink("swhois"); DELETE(swhois); } } // if the module is unloaded, tidy up all our dangling metadata virtual void OnCleanup(int target_type, void* item) { if (target_type == TYPE_USER) { userrec* user = (userrec*)item; std::string* swhois; user->GetExt("swhois", swhois); if (swhois) { user->Shrink("swhois"); DELETE(swhois); } } } // 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. virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { // check if its our metadata key, and its associated with a user if ((target_type == TYPE_USER) && (extname == "swhois")) { userrec* dest = (userrec*)target; // if they dont already have an swhois field, accept the remote server's std::string* text; if (!dest->GetExt("swhois", text)) { std::string* text = new std::string(extdata); dest->Extend("swhois",text); } } } virtual void OnPostCommand(const std::string &command, const char **params, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { if ((command != "OPER") || (result != CMD_SUCCESS)) return; std::string swhois; for (int i = 0; i < Conf->Enumerate("oper"); i++) { std::string name = Conf->ReadValue("oper", "name", i); if (name == params[0]) { swhois = Conf->ReadValue("oper", "swhois", i); break; } } if (!swhois.length()) { for (int i = 0; i < Conf->Enumerate("type"); i++) { std::string type = Conf->ReadValue("type", "name", i); if (type == user->oper) { swhois = Conf->ReadValue("type", "swhois", i); break; } } } std::string *old; if (user->GetExt("swhois", old)) { user->Shrink("swhois"); DELETE(old); } if (!swhois.length()) return; std::string *text = new std::string(swhois); user->Extend("swhois", text); std::deque<std::string>* metadata = new std::deque<std::string>; metadata->push_back(user->nick); metadata->push_back("swhois"); // The metadata id metadata->push_back(*text); // The value to send Event event((char*)metadata,(Module*)this,"send_metadata"); event.Send(ServerInstance); delete metadata; } virtual ~ModuleSWhois() { DELETE(Conf); } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleSWhois) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides the SWHOIS command which allows setting of arbitary WHOIS lines */
+
+/** Handle /SWHOIS
+ */
+class cmd_swhois : public command_t
+{
+
+ public:
+ cmd_swhois (InspIRCd* Instance) : command_t(Instance,"SWHOIS",'o',2)
+ {
+ this->source = "m_swhois.so";
+ syntax = "<nick> <swhois>";
+ }
+
+ CmdResult Handle(const char** parameters, int pcnt, userrec* user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+
+ if (!dest)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+
+ if (!*parameters[1])
+ {
+ user->WriteServ("NOTICE %s :*** SWHOIS: Whois line must be specified", user->nick);
+ return CMD_FAILURE;
+ }
+
+ std::string line;
+ for (int i = 1; i < pcnt; i++)
+ {
+ if (i != 1)
+ line.append(" ");
+
+ line.append(parameters[i]);
+ }
+
+ std::string* text;
+ dest->GetExt("swhois", text);
+
+ if (text)
+ {
+ // We already had it set...
+
+ if (!ServerInstance->ULine(user->server))
+ // Ulines set SWHOISes silently
+ ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick, dest->nick, text->c_str(), line.c_str());
+
+ dest->Shrink("swhois");
+ DELETE(text);
+ }
+ else if (!ServerInstance->ULine(user->server))
+ {
+ // Ulines set SWHOISes silently
+ ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois to '%s'", user->nick, dest->nick, line.c_str());
+ }
+
+ text = new std::string(line);
+ dest->Extend("swhois", text);
+
+ return CMD_SUCCESS;
+ }
+
+};
+
+class ModuleSWhois : public Module
+{
+ cmd_swhois* mycommand;
+
+ ConfigReader* Conf;
+
+ public:
+ ModuleSWhois(InspIRCd* Me) : Module(Me)
+ {
+
+ Conf = new ConfigReader(ServerInstance);
+ mycommand = new cmd_swhois(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void OnRehash(userrec* user, const std::string &parameter)
+ {
+ DELETE(Conf);
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnDecodeMetaData] = List[I_OnWhoisLine] = List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnRehash] = List[I_OnPostCommand] = 1;
+ }
+
+ // :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
+ int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text)
+ {
+ /* We use this and not OnWhois because this triggers for remote, too */
+ if (numeric == 312)
+ {
+ /* Insert our numeric before 312 */
+ std::string* swhois;
+ dest->GetExt("swhois", swhois);
+ if (swhois)
+ {
+ ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick,dest->nick,swhois->c_str());
+ }
+ }
+ /* Dont block anything */
+ return 0;
+ }
+
+ // Whenever the linking module wants to send out data, but doesnt know what the data
+ // represents (e.g. it is metadata, added to a userrec or chanrec by a module) then
+ // this method is called. We should use the ProtoSendMetaData function after we've
+ // corrected decided how the data should look, to send the metadata on its way if
+ // it is ours.
+ virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
+ {
+ // check if the linking module wants to know about OUR metadata
+ if (extname == "swhois")
+ {
+ // check if this user has an swhois field to send
+ std::string* swhois;
+ user->GetExt("swhois", swhois);
+ if (swhois)
+ {
+ // call this function in the linking module, let it format the data how it
+ // sees fit, and send it on its way. We dont need or want to know how.
+ proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*swhois);
+ }
+ }
+ }
+
+ // when a user quits, tidy up their metadata
+ virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
+ {
+ std::string* swhois;
+ user->GetExt("swhois", swhois);
+ if (swhois)
+ {
+ user->Shrink("swhois");
+ DELETE(swhois);
+ }
+ }
+
+ // if the module is unloaded, tidy up all our dangling metadata
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* user = (userrec*)item;
+ std::string* swhois;
+ user->GetExt("swhois", swhois);
+ if (swhois)
+ {
+ user->Shrink("swhois");
+ DELETE(swhois);
+ }
+ }
+ }
+
+ // 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.
+ virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ // check if its our metadata key, and its associated with a user
+ if ((target_type == TYPE_USER) && (extname == "swhois"))
+ {
+ userrec* dest = (userrec*)target;
+ // if they dont already have an swhois field, accept the remote server's
+ std::string* text;
+ if (!dest->GetExt("swhois", text))
+ {
+ std::string* text = new std::string(extdata);
+ dest->Extend("swhois",text);
+ }
+ }
+ }
+
+ virtual void OnPostCommand(const std::string &command, const char **params, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
+ {
+ if ((command != "OPER") || (result != CMD_SUCCESS))
+ return;
+
+ std::string swhois;
+
+ for (int i = 0; i < Conf->Enumerate("oper"); i++)
+ {
+ std::string name = Conf->ReadValue("oper", "name", i);
+
+ if (name == params[0])
+ {
+ swhois = Conf->ReadValue("oper", "swhois", i);
+ break;
+ }
+ }
+
+ if (!swhois.length())
+ {
+ for (int i = 0; i < Conf->Enumerate("type"); i++)
+ {
+ std::string type = Conf->ReadValue("type", "name", i);
+
+ if (type == user->oper)
+ {
+ swhois = Conf->ReadValue("type", "swhois", i);
+ break;
+ }
+ }
+ }
+
+ std::string *old;
+ if (user->GetExt("swhois", old))
+ {
+ user->Shrink("swhois");
+ DELETE(old);
+ }
+
+ if (!swhois.length())
+ return;
+
+ std::string *text = new std::string(swhois);
+ user->Extend("swhois", text);
+ std::deque<std::string>* metadata = new std::deque<std::string>;
+ metadata->push_back(user->nick);
+ metadata->push_back("swhois"); // The metadata id
+ metadata->push_back(*text); // The value to send
+ Event event((char*)metadata,(Module*)this,"send_metadata");
+ event.Send(ServerInstance);
+ delete metadata;
+ }
+
+ virtual ~ModuleSWhois()
+ {
+ DELETE(Conf);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleSWhois)
diff --git a/src/modules/m_taxonomy.cpp b/src/modules/m_taxonomy.cpp
index edae9ccf6..79dc8e23f 100644
--- a/src/modules/m_taxonomy.cpp
+++ b/src/modules/m_taxonomy.cpp
@@ -1 +1,99 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides the /TAXONOMY command, used to view all metadata attached to a user */ /** Handle /WOOT */ class cmd_taxonomy : public command_t { Module* Creator; bool& claimed; public: /* Command 'taxonomy', takes no parameters and needs no special modes */ cmd_taxonomy (InspIRCd* Instance, Module* maker, bool &claim) : command_t(Instance,"TAXONOMY", 'o', 1), Creator(maker), claimed(claim) { this->source = "m_taxonomy.so"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* dest = ServerInstance->FindNick(parameters[0]); if (dest) { std::deque<std::string> list; list.clear(); user->GetExtList(list); user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY ITEMS " + std::string(dest->nick) + " " +ConvToStr(list.size())); for (unsigned int j = 0; j < list.size(); j++) { claimed = false; FOREACH_MOD(I_OnSyncUserMetaData, OnSyncUserMetaData(user, Creator, dest, list[j], true)); if (!claimed) { user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY METADATA " + list[j] + " = <unknown>"); } } user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY END"); } return CMD_FAILURE; } }; class ModuleTaxonomy : public Module { cmd_taxonomy* newcommand; bool claimed; public: ModuleTaxonomy(InspIRCd* Me) : Module(Me) { // Create a new command newcommand = new cmd_taxonomy(ServerInstance, this, claimed); ServerInstance->AddCommand(newcommand); } void Implements(char* List) { List[I_ProtoSendMetaData] = 1; } void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { if (target_type == TYPE_USER) { userrec* spool = (userrec*)opaque; std::string taxstr = "304 " + std::string(spool->nick) + ":TAXONOMY METADATA "+extname+" = "+extdata; spool->WriteServ(taxstr); claimed = true; } } virtual ~ModuleTaxonomy() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleTaxonomy) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides the /TAXONOMY command, used to view all metadata attached to a user */
+
+/** Handle /WOOT
+ */
+class cmd_taxonomy : public command_t
+{
+ Module* Creator;
+ bool& claimed;
+ public:
+ /* Command 'taxonomy', takes no parameters and needs no special modes */
+ cmd_taxonomy (InspIRCd* Instance, Module* maker, bool &claim) : command_t(Instance,"TAXONOMY", 'o', 1), Creator(maker), claimed(claim)
+ {
+ this->source = "m_taxonomy.so";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* dest = ServerInstance->FindNick(parameters[0]);
+ if (dest)
+ {
+ std::deque<std::string> list;
+ list.clear();
+ user->GetExtList(list);
+ user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY ITEMS " + std::string(dest->nick) + " " +ConvToStr(list.size()));
+ for (unsigned int j = 0; j < list.size(); j++)
+ {
+ claimed = false;
+ FOREACH_MOD(I_OnSyncUserMetaData, OnSyncUserMetaData(user, Creator, dest, list[j], true));
+ if (!claimed)
+ {
+ user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY METADATA " + list[j] + " = <unknown>");
+ }
+ }
+ user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY END");
+ }
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleTaxonomy : public Module
+{
+ cmd_taxonomy* newcommand;
+ bool claimed;
+ public:
+ ModuleTaxonomy(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ // Create a new command
+ newcommand = new cmd_taxonomy(ServerInstance, this, claimed);
+ ServerInstance->AddCommand(newcommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_ProtoSendMetaData] = 1;
+ }
+
+ void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
+ {
+ if (target_type == TYPE_USER)
+ {
+ userrec* spool = (userrec*)opaque;
+ std::string taxstr = "304 " + std::string(spool->nick) + ":TAXONOMY METADATA "+extname+" = "+extdata;
+ spool->WriteServ(taxstr);
+ claimed = true;
+ }
+ }
+
+ virtual ~ModuleTaxonomy()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleTaxonomy)
+
diff --git a/src/modules/m_testcommand.cpp b/src/modules/m_testcommand.cpp
index 0733fd0f0..6ec197eb6 100644
--- a/src/modules/m_testcommand.cpp
+++ b/src/modules/m_testcommand.cpp
@@ -1 +1,67 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides a pointless /dalinfo command, demo module */ /** Handle /DALINFO */ class cmd_dalinfo : public command_t { public: /* Command 'dalinfo', takes no parameters and needs no special modes */ cmd_dalinfo (InspIRCd* Instance) : command_t(Instance,"DALINFO", 0, 0) { this->source = "m_testcommand.so"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("NOTICE %s :*** DALNet had nothing to do with it.", user->nick); return CMD_FAILURE; } }; class ModuleTestCommand : public Module { cmd_dalinfo* newcommand; public: ModuleTestCommand(InspIRCd* Me) : Module(Me) { // Create a new command newcommand = new cmd_dalinfo(ServerInstance); ServerInstance->AddCommand(newcommand); } void Implements(char* List) { } virtual ~ModuleTestCommand() { delete newcommand; } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleTestCommand) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides a pointless /dalinfo command, demo module */
+
+/** Handle /DALINFO
+ */
+class cmd_dalinfo : public command_t
+{
+ public:
+ /* Command 'dalinfo', takes no parameters and needs no special modes */
+ cmd_dalinfo (InspIRCd* Instance) : command_t(Instance,"DALINFO", 0, 0)
+ {
+ this->source = "m_testcommand.so";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ user->WriteServ("NOTICE %s :*** DALNet had nothing to do with it.", user->nick);
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleTestCommand : public Module
+{
+ cmd_dalinfo* newcommand;
+ public:
+ ModuleTestCommand(InspIRCd* Me)
+ : Module(Me)
+ {
+ // Create a new command
+ newcommand = new cmd_dalinfo(ServerInstance);
+ ServerInstance->AddCommand(newcommand);
+ }
+
+ void Implements(char* List)
+ {
+ }
+
+ virtual ~ModuleTestCommand()
+ {
+ delete newcommand;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleTestCommand)
+
diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp
index f705a1f95..ae3da7549 100644
--- a/src/modules/m_timedbans.cpp
+++ b/src/modules/m_timedbans.cpp
@@ -1 +1,204 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Adds timed bans */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" #include "configreader.h" /** Holds a timed ban */ class TimedBan : public classbase { public: std::string channel; std::string mask; time_t expire; }; typedef std::vector<TimedBan> timedbans; timedbans TimedBanList; /** Handle /TBAN */ class cmd_tban : public command_t { public: cmd_tban (InspIRCd* Instance) : command_t(Instance,"TBAN", 0, 3) { this->source = "m_timedbans.so"; syntax = "<channel> <duration> <banmask>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { chanrec* channel = ServerInstance->FindChan(parameters[0]); if (channel) { int cm = channel->GetStatus(user); if ((cm == STATUS_HOP) || (cm == STATUS_OP)) { if (!ServerInstance->IsValidMask(parameters[2])) { user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban mask"); return CMD_FAILURE; } for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) { if (!strcasecmp(i->data,parameters[2])) { user->WriteServ("NOTICE "+std::string(user->nick)+" :The ban "+std::string(parameters[2])+" is already on the banlist of "+std::string(parameters[0])); return CMD_FAILURE; } } TimedBan T; std::string channelname = parameters[0]; long duration = ServerInstance->Duration(parameters[1]); unsigned long expire = duration + time(NULL); if (duration < 1) { user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban time"); return CMD_FAILURE; } std::string mask = parameters[2]; const char *setban[32]; setban[0] = parameters[0]; setban[1] = "+b"; setban[2] = parameters[2]; // use CallCommandHandler to make it so that the user sets the mode // themselves ServerInstance->CallCommandHandler("MODE",setban,3,user); /* Check if the ban was actually added (e.g. banlist was NOT full) */ bool was_added = false; for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) if (!strcasecmp(i->data,mask.c_str())) was_added = true; if (was_added) { T.channel = channelname; T.mask = mask; T.expire = expire; TimedBanList.push_back(T); channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s added a timed ban on %s lasting for %ld seconds.", channel->name, user->nick, mask.c_str(), duration); return CMD_SUCCESS; } return CMD_FAILURE; } else user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, channel->name); return CMD_FAILURE; } user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]); return CMD_FAILURE; } }; class ModuleTimedBans : public Module { cmd_tban* mycommand; public: ModuleTimedBans(InspIRCd* Me) : Module(Me) { mycommand = new cmd_tban(ServerInstance); ServerInstance->AddCommand(mycommand); TimedBanList.clear(); } virtual ~ModuleTimedBans() { TimedBanList.clear(); } void Implements(char* List) { List[I_OnDelBan] = List[I_OnBackgroundTimer] = 1; } virtual int OnDelBan(userrec* source, chanrec* chan, const std::string &banmask) { irc::string listitem = banmask.c_str(); irc::string thischan = chan->name; 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 0; } virtual void OnBackgroundTimer(time_t curtime) { bool again = true; while (again) { again = false; for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++) { if (curtime > i->expire) { chanrec* cr = ServerInstance->FindChan(i->channel); again = true; if (cr) { cr->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :Timed ban on %s expired.", cr->name, i->mask.c_str()); const char *setban[3]; setban[0] = i->channel.c_str(); setban[1] = "-b"; setban[2] = i->mask.c_str(); // kludge alert! // ::SendMode expects a userrec* to send the numeric replies // back to, so we create it a fake user that isnt in the user // hash and set its descriptor to FD_MAGIC_NUMBER so the data // falls into the abyss :p userrec* temp = new userrec(ServerInstance); temp->SetFd(FD_MAGIC_NUMBER); /* FIX: Send mode remotely*/ std::deque<std::string> n; n.push_back(setban[0]); n.push_back("-b"); n.push_back(setban[2]); ServerInstance->SendMode(setban,3,temp); Event rmode((char *)&n, NULL, "send_mode"); rmode.Send(ServerInstance); DELETE(temp); } else { /* Where the hell did our channel go?! */ TimedBanList.erase(i); } // we used to delete the item here, but we dont need to as the servermode above does it for us, break; } } } } virtual Version GetVersion() { return Version(1,1,0,0,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleTimedBans) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Adds timed bans */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+#include "configreader.h"
+
+/** Holds a timed ban
+ */
+class TimedBan : public classbase
+{
+ public:
+ std::string channel;
+ std::string mask;
+ time_t expire;
+};
+
+typedef std::vector<TimedBan> timedbans;
+timedbans TimedBanList;
+
+/** Handle /TBAN
+ */
+class cmd_tban : public command_t
+{
+ public:
+ cmd_tban (InspIRCd* Instance) : command_t(Instance,"TBAN", 0, 3)
+ {
+ this->source = "m_timedbans.so";
+ syntax = "<channel> <duration> <banmask>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ chanrec* channel = ServerInstance->FindChan(parameters[0]);
+ if (channel)
+ {
+ int cm = channel->GetStatus(user);
+ if ((cm == STATUS_HOP) || (cm == STATUS_OP))
+ {
+ if (!ServerInstance->IsValidMask(parameters[2]))
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban mask");
+ return CMD_FAILURE;
+ }
+ for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
+ {
+ if (!strcasecmp(i->data,parameters[2]))
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :The ban "+std::string(parameters[2])+" is already on the banlist of "+std::string(parameters[0]));
+ return CMD_FAILURE;
+ }
+ }
+ TimedBan T;
+ std::string channelname = parameters[0];
+ long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long expire = duration + time(NULL);
+ if (duration < 1)
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban time");
+ return CMD_FAILURE;
+ }
+ std::string mask = parameters[2];
+ const char *setban[32];
+ setban[0] = parameters[0];
+ setban[1] = "+b";
+ setban[2] = parameters[2];
+ // use CallCommandHandler to make it so that the user sets the mode
+ // themselves
+ ServerInstance->CallCommandHandler("MODE",setban,3,user);
+ /* Check if the ban was actually added (e.g. banlist was NOT full) */
+ bool was_added = false;
+ for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
+ if (!strcasecmp(i->data,mask.c_str()))
+ was_added = true;
+ if (was_added)
+ {
+ T.channel = channelname;
+ T.mask = mask;
+ T.expire = expire;
+ TimedBanList.push_back(T);
+ channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s added a timed ban on %s lasting for %ld seconds.", channel->name, user->nick, mask.c_str(), duration);
+ return CMD_SUCCESS;
+ }
+ return CMD_FAILURE;
+ }
+ else user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, channel->name);
+ return CMD_FAILURE;
+ }
+ user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]);
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleTimedBans : public Module
+{
+ cmd_tban* mycommand;
+ public:
+ ModuleTimedBans(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_tban(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ TimedBanList.clear();
+ }
+
+ virtual ~ModuleTimedBans()
+ {
+ TimedBanList.clear();
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnDelBan] = List[I_OnBackgroundTimer] = 1;
+ }
+
+ virtual int OnDelBan(userrec* source, chanrec* chan, const std::string &banmask)
+ {
+ irc::string listitem = banmask.c_str();
+ irc::string thischan = chan->name;
+ 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 0;
+ }
+
+ virtual void OnBackgroundTimer(time_t curtime)
+ {
+ bool again = true;
+ while (again)
+ {
+ again = false;
+ for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++)
+ {
+ if (curtime > i->expire)
+ {
+ chanrec* cr = ServerInstance->FindChan(i->channel);
+ again = true;
+ if (cr)
+ {
+ cr->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :Timed ban on %s expired.", cr->name, i->mask.c_str());
+ const char *setban[3];
+ setban[0] = i->channel.c_str();
+ setban[1] = "-b";
+ setban[2] = i->mask.c_str();
+ // kludge alert!
+ // ::SendMode expects a userrec* to send the numeric replies
+ // back to, so we create it a fake user that isnt in the user
+ // hash and set its descriptor to FD_MAGIC_NUMBER so the data
+ // falls into the abyss :p
+ userrec* temp = new userrec(ServerInstance);
+ temp->SetFd(FD_MAGIC_NUMBER);
+ /* FIX: Send mode remotely*/
+ std::deque<std::string> n;
+ n.push_back(setban[0]);
+ n.push_back("-b");
+ n.push_back(setban[2]);
+ ServerInstance->SendMode(setban,3,temp);
+ Event rmode((char *)&n, NULL, "send_mode");
+ rmode.Send(ServerInstance);
+ DELETE(temp);
+ }
+ else
+ {
+ /* Where the hell did our channel go?! */
+ TimedBanList.erase(i);
+ }
+ // we used to delete the item here, but we dont need to as the servermode above does it for us,
+ break;
+ }
+ }
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,0,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleTimedBans)
+
diff --git a/src/modules/m_tline.cpp b/src/modules/m_tline.cpp
index dd13a965c..834cb7f4c 100644
--- a/src/modules/m_tline.cpp
+++ b/src/modules/m_tline.cpp
@@ -1 +1,95 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "wildcard.h" /* $ModDesc: Provides /tline command used to test who a mask matches */ /** Handle /TLINE */ class cmd_tline : public command_t { public: cmd_tline (InspIRCd* Instance) : command_t(Instance,"TLINE", 'o', 1) { this->source = "m_tline.so"; this->syntax = "<mask>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { float n_counted = 0; float n_matched = 0; float n_match_host = 0; float n_match_ip = 0; for (user_hash::const_iterator u = ServerInstance->clientlist->begin(); u != ServerInstance->clientlist->end(); u++) { n_counted++; if (match(u->second->GetFullRealHost(),parameters[0])) { n_matched++; n_match_host++; } else { char host[MAXBUF]; snprintf(host, MAXBUF, "%s@%s", u->second->ident, u->second->GetIPString()); if (match(host, parameters[0], true)) { n_matched++; n_match_ip++; } } } 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, n_counted, parameters[0], n_matched, (n_matched/n_counted)*100, 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, n_counted, parameters[0]); return CMD_LOCALONLY; } }; class ModuleTLine : public Module { cmd_tline* newcommand; public: ModuleTLine(InspIRCd* Me) : Module(Me) { newcommand = new cmd_tline(ServerInstance); ServerInstance->AddCommand(newcommand); } void Implements(char* List) { } virtual ~ModuleTLine() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleTLine) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "wildcard.h"
+
+/* $ModDesc: Provides /tline command used to test who a mask matches */
+
+/** Handle /TLINE
+ */
+class cmd_tline : public command_t
+{
+ public:
+ cmd_tline (InspIRCd* Instance) : command_t(Instance,"TLINE", 'o', 1)
+ {
+ this->source = "m_tline.so";
+ this->syntax = "<mask>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ float n_counted = 0;
+ float n_matched = 0;
+ float n_match_host = 0;
+ float n_match_ip = 0;
+
+ for (user_hash::const_iterator u = ServerInstance->clientlist->begin(); u != ServerInstance->clientlist->end(); u++)
+ {
+ n_counted++;
+ if (match(u->second->GetFullRealHost(),parameters[0]))
+ {
+ n_matched++;
+ n_match_host++;
+ }
+ else
+ {
+ char host[MAXBUF];
+ snprintf(host, MAXBUF, "%s@%s", u->second->ident, u->second->GetIPString());
+ if (match(host, parameters[0], true))
+ {
+ n_matched++;
+ n_match_ip++;
+ }
+ }
+ }
+ 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, n_counted, parameters[0], n_matched, (n_matched/n_counted)*100, 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, n_counted, parameters[0]);
+
+ return CMD_LOCALONLY;
+ }
+};
+
+class ModuleTLine : public Module
+{
+ cmd_tline* newcommand;
+ public:
+ ModuleTLine(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ newcommand = new cmd_tline(ServerInstance);
+ ServerInstance->AddCommand(newcommand);
+ }
+
+ void Implements(char* List)
+ {
+ }
+
+ virtual ~ModuleTLine()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleTLine)
+
diff --git a/src/modules/m_uhnames.cpp b/src/modules/m_uhnames.cpp
index 61b58f302..6794f4643 100644
--- a/src/modules/m_uhnames.cpp
+++ b/src/modules/m_uhnames.cpp
@@ -1 +1,98 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" static const char* dummy = "ON"; /* $ModDesc: Provides aliases of commands. */ class ModuleUHNames : public Module { CUList nl; public: ModuleUHNames(InspIRCd* Me) : Module(Me) { } void Implements(char* List) { List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1; } virtual ~ModuleUHNames() { } void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { if ((displayable) && (extname == "UHNAMES")) proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled"); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } virtual void On005Numeric(std::string &output) { output.append(" UHNAMES"); } Priority Prioritize() { return (Priority)ServerInstance->PriorityBefore("m_namesx.so"); } virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { irc::string c = command.c_str(); /* We don't actually create a proper command handler class for PROTOCTL, * because other modules might want to have PROTOCTL hooks too. * Therefore, we just hook its as an unvalidated command therefore we * can capture it even if it doesnt exist! :-) */ if (c == "PROTOCTL") { if ((pcnt) && (!strcasecmp(parameters[0],"UHNAMES"))) { user->Extend("UHNAMES",dummy); return 1; } } return 0; } /* IMPORTANT: This must be prioritized above NAMESX! */ virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist) { if (user->GetExt("UHNAMES")) { if (!ulist) ulist = Ptr->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) i->second = i->first->GetFullHost(); } return 0; } }; MODULE_INIT(ModuleUHNames) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+static const char* dummy = "ON";
+
+/* $ModDesc: Provides aliases of commands. */
+
+class ModuleUHNames : public Module
+{
+ CUList nl;
+ public:
+
+ ModuleUHNames(InspIRCd* Me)
+ : Module(Me)
+ {
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1;
+ }
+
+ virtual ~ModuleUHNames()
+ {
+ }
+
+ void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable)
+ {
+ if ((displayable) && (extname == "UHNAMES"))
+ proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled");
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output.append(" UHNAMES");
+ }
+
+ Priority Prioritize()
+ {
+ return (Priority)ServerInstance->PriorityBefore("m_namesx.so");
+ }
+
+ virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
+ {
+ irc::string c = command.c_str();
+ /* We don't actually create a proper command handler class for PROTOCTL,
+ * because other modules might want to have PROTOCTL hooks too.
+ * Therefore, we just hook its as an unvalidated command therefore we
+ * can capture it even if it doesnt exist! :-)
+ */
+ if (c == "PROTOCTL")
+ {
+ if ((pcnt) && (!strcasecmp(parameters[0],"UHNAMES")))
+ {
+ user->Extend("UHNAMES",dummy);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* IMPORTANT: This must be prioritized above NAMESX! */
+ virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist)
+ {
+ if (user->GetExt("UHNAMES"))
+ {
+ if (!ulist)
+ ulist = Ptr->GetUsers();
+
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ i->second = i->first->GetFullHost();
+ }
+ return 0;
+ }
+};
+
+MODULE_INIT(ModuleUHNames)
+
diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp
index bcf18b308..ab748663c 100644
--- a/src/modules/m_uninvite.cpp
+++ b/src/modules/m_uninvite.cpp
@@ -1 +1,107 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "configreader.h" /** Handle /UNINVITE */ class cmd_uninvite : public command_t { public: cmd_uninvite (InspIRCd* Instance) : command_t(Instance,"UNINVITE", 0, 2) { this->source = "m_uninvite.so"; syntax = "<nick> <channel>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { userrec* u = ServerInstance->FindNick(parameters[0]); chanrec* c = ServerInstance->FindChan(parameters[1]); if ((!c) || (!u)) { if (!c) { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]); } else { user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); } return CMD_FAILURE; } if (c->modes[CM_INVITEONLY]) { if (c->GetStatus(user) < STATUS_HOP) { user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name); return CMD_FAILURE; } } irc::string xname(c->name); if (!u->IsInvited(xname)) { user->WriteServ("491 %s %s %s :Is not invited to channel %s",user->nick,u->nick,c->name,c->name); return CMD_FAILURE; } if (!c->HasUser(user)) { user->WriteServ("492 %s %s :You're not on that channel!",user->nick, c->name); return CMD_FAILURE; } u->RemoveInvite(xname); user->WriteServ("494 %s %s %s :Uninvited",user->nick,c->name,u->nick); u->WriteServ("493 %s :You were uninvited from %s by %s",u->nick,c->name,user->nick); c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s uninvited %s.", c->name, user->nick, u->nick); return CMD_SUCCESS; } }; class ModuleUninvite : public Module { cmd_uninvite *mycommand; public: ModuleUninvite(InspIRCd* Me) : Module(Me) { mycommand = new cmd_uninvite(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleUninvite() { } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } }; MODULE_INIT(ModuleUninvite) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "configreader.h"
+
+/** Handle /UNINVITE
+ */
+class cmd_uninvite : public command_t
+{
+ public:
+ cmd_uninvite (InspIRCd* Instance) : command_t(Instance,"UNINVITE", 0, 2)
+ {
+ this->source = "m_uninvite.so";
+ syntax = "<nick> <channel>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ userrec* u = ServerInstance->FindNick(parameters[0]);
+ chanrec* c = ServerInstance->FindChan(parameters[1]);
+
+ if ((!c) || (!u))
+ {
+ if (!c)
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
+ }
+ else
+ {
+ user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
+ }
+
+ return CMD_FAILURE;
+ }
+
+ if (c->modes[CM_INVITEONLY])
+ {
+ if (c->GetStatus(user) < STATUS_HOP)
+ {
+ user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+ }
+
+ irc::string xname(c->name);
+
+ if (!u->IsInvited(xname))
+ {
+ user->WriteServ("491 %s %s %s :Is not invited to channel %s",user->nick,u->nick,c->name,c->name);
+ return CMD_FAILURE;
+ }
+ if (!c->HasUser(user))
+ {
+ user->WriteServ("492 %s %s :You're not on that channel!",user->nick, c->name);
+ return CMD_FAILURE;
+ }
+
+ u->RemoveInvite(xname);
+ user->WriteServ("494 %s %s %s :Uninvited",user->nick,c->name,u->nick);
+ u->WriteServ("493 %s :You were uninvited from %s by %s",u->nick,c->name,user->nick);
+ c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s uninvited %s.", c->name, user->nick, u->nick);
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleUninvite : public Module
+{
+ cmd_uninvite *mycommand;
+
+ public:
+
+ ModuleUninvite(InspIRCd* Me) : Module(Me)
+ {
+
+ mycommand = new cmd_uninvite(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleUninvite()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+MODULE_INIT(ModuleUninvite)
+
diff --git a/src/modules/m_userip.cpp b/src/modules/m_userip.cpp
index 979ee6112..296d52300 100644
--- a/src/modules/m_userip.cpp
+++ b/src/modules/m_userip.cpp
@@ -1 +1,86 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides support for USERIP command */ /** Handle /USERIP */ class cmd_userip : public command_t { public: cmd_userip (InspIRCd* Instance) : command_t(Instance,"USERIP", 'o', 1) { this->source = "m_userip.so"; syntax = "<nick>{,<nick>}"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { std::string retbuf = std::string("340 ") + user->nick + " :"; for (int i = 0; i < pcnt; i++) { userrec *u = ServerInstance->FindNick(parameters[i]); if ((u) && (u->registered == REG_ALL)) { retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=+" + u->ident + "@" + u->GetIPString() + " "; } } user->WriteServ(retbuf); /* Dont send to the network */ return CMD_FAILURE; } }; class ModuleUserIP : public Module { cmd_userip* mycommand; public: ModuleUserIP(InspIRCd* Me) : Module(Me) { mycommand = new cmd_userip(ServerInstance); ServerInstance->AddCommand(mycommand); } void Implements(char* List) { List[I_On005Numeric] = 1; } virtual void On005Numeric(std::string &output) { output = output + std::string(" USERIP"); } virtual ~ModuleUserIP() { } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleUserIP) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides support for USERIP command */
+
+/** Handle /USERIP
+ */
+class cmd_userip : public command_t
+{
+ public:
+ cmd_userip (InspIRCd* Instance) : command_t(Instance,"USERIP", 'o', 1)
+ {
+ this->source = "m_userip.so";
+ syntax = "<nick>{,<nick>}";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ std::string retbuf = std::string("340 ") + user->nick + " :";
+
+ for (int i = 0; i < pcnt; i++)
+ {
+ userrec *u = ServerInstance->FindNick(parameters[i]);
+ if ((u) && (u->registered == REG_ALL))
+ {
+ retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=+" + u->ident + "@" + u->GetIPString() + " ";
+ }
+ }
+
+ user->WriteServ(retbuf);
+
+ /* Dont send to the network */
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleUserIP : public Module
+{
+ cmd_userip* mycommand;
+ public:
+ ModuleUserIP(InspIRCd* Me)
+ : Module(Me)
+ {
+
+ mycommand = new cmd_userip(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_On005Numeric] = 1;
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ output = output + std::string(" USERIP");
+ }
+
+ virtual ~ModuleUserIP()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleUserIP)
+
diff --git a/src/modules/m_vhost.cpp b/src/modules/m_vhost.cpp
index 10fc76f05..c77a3f85f 100644
--- a/src/modules/m_vhost.cpp
+++ b/src/modules/m_vhost.cpp
@@ -1 +1,95 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" /* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */ static ConfigReader* Conf; /** Handle /VHOST */ class cmd_vhost : public command_t { public: cmd_vhost (InspIRCd* Instance) : command_t(Instance,"VHOST", 0, 2) { this->source = "m_vhost.so"; syntax = "<username> <password>"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { for (int index = 0; index < Conf->Enumerate("vhost"); index++) { std::string mask = Conf->ReadValue("vhost","host",index); std::string username = Conf->ReadValue("vhost","user",index); std::string pass = Conf->ReadValue("vhost","pass",index); if ((!strcmp(parameters[0],username.c_str())) && (!strcmp(parameters[1],pass.c_str()))) { if (!mask.empty()) { user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your VHost: " + mask); user->ChangeDisplayedHost(mask.c_str()); return CMD_FAILURE; } } } user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid username or password."); return CMD_FAILURE; } }; class ModuleVHost : public Module { private: cmd_vhost* mycommand; public: ModuleVHost(InspIRCd* Me) : Module(Me) { Conf = new ConfigReader(ServerInstance); mycommand = new cmd_vhost(ServerInstance); ServerInstance->AddCommand(mycommand); } virtual ~ModuleVHost() { DELETE(Conf); } void Implements(char* List) { List[I_OnRehash] = 1; } virtual void OnRehash(userrec* user, const std::string &parameter) { DELETE(Conf); Conf = new ConfigReader(ServerInstance); } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(ModuleVHost) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+
+/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */
+
+static ConfigReader* Conf;
+
+/** Handle /VHOST
+ */
+class cmd_vhost : public command_t
+{
+ public:
+ cmd_vhost (InspIRCd* Instance) : command_t(Instance,"VHOST", 0, 2)
+ {
+ this->source = "m_vhost.so";
+ syntax = "<username> <password>";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ for (int index = 0; index < Conf->Enumerate("vhost"); index++)
+ {
+ std::string mask = Conf->ReadValue("vhost","host",index);
+ std::string username = Conf->ReadValue("vhost","user",index);
+ std::string pass = Conf->ReadValue("vhost","pass",index);
+ if ((!strcmp(parameters[0],username.c_str())) && (!strcmp(parameters[1],pass.c_str())))
+ {
+ if (!mask.empty())
+ {
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your VHost: " + mask);
+ user->ChangeDisplayedHost(mask.c_str());
+ return CMD_FAILURE;
+ }
+ }
+ }
+ user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid username or password.");
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleVHost : public Module
+{
+ private:
+
+ cmd_vhost* mycommand;
+
+ public:
+ ModuleVHost(InspIRCd* Me) : Module(Me)
+ {
+
+ Conf = new ConfigReader(ServerInstance);
+ mycommand = new cmd_vhost(ServerInstance);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual ~ModuleVHost()
+ {
+ DELETE(Conf);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = 1;
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ DELETE(Conf);
+ Conf = new ConfigReader(ServerInstance);
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+
+};
+
+MODULE_INIT(ModuleVHost)
+
diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp
index 2f847661e..552e3317a 100644
--- a/src/modules/m_watch.cpp
+++ b/src/modules/m_watch.cpp
@@ -1 +1,472 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" /* $ModDesc: Provides support for the /WATCH command */ /* 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 userrec 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 */ #ifdef WINDOWS typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash_compare<irc::string, less<irc::string> > > watchentries; #else typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash<irc::string> > watchentries; #endif typedef std::map<irc::string, std::string> watchlist; /* 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; /** Handle /WATCH */ class cmd_watch : public command_t { unsigned int& MAX_WATCH; public: CmdResult remove_watch(userrec* user, const char* nick) { // removing an item from the list if (!ServerInstance->IsNick(nick)) { user->WriteServ("942 %s %s :Invalid nickname", user->nick, nick); return CMD_FAILURE; } watchlist* wl; if (user->GetExt("watchlist", wl)) { /* Yup, is on my list */ watchlist::iterator n = wl->find(nick); if (n != wl->end()) { if (!n->second.empty()) user->WriteServ("602 %s %s %s :stopped watching", user->nick, n->first.c_str(), n->second.c_str()); else user->WriteServ("602 %s %s * * 0 :stopped watching", user->nick, nick); wl->erase(n); } if (!wl->size()) { user->Shrink("watchlist"); delete wl; } 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<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user); if (n != x->second.end()) /* I'm no longer watching you... */ x->second.erase(n); if (!x->second.size()) whos_watching_me->erase(nick); } } /* This might seem confusing, but we return CMD_FAILURE * to indicate that this message shouldnt be routed across * the network to other linked servers. */ return CMD_FAILURE; } CmdResult add_watch(userrec* user, const char* nick) { if (!ServerInstance->IsNick(nick)) { user->WriteServ("942 %s %s :Invalid nickname",user->nick,nick); return CMD_FAILURE; } watchlist* wl; if (!user->GetExt("watchlist", wl)) { wl = new watchlist(); user->Extend("watchlist", wl); } if (wl->size() == MAX_WATCH) { user->WriteServ("512 %s %s :Too many WATCH entries", user->nick, nick); return CMD_FAILURE; } watchlist::iterator n = wl->find(nick); if (n == wl->end()) { /* Don't already have the user on my watch list, proceed */ watchentries::iterator x = whos_watching_me->find(nick); if (x != whos_watching_me->end()) { /* People are watching this user, add myself */ x->second.push_back(user); } else { std::deque<userrec*> newlist; newlist.push_back(user); (*(whos_watching_me))[nick] = newlist; } userrec* target = ServerInstance->FindNick(nick); if (target) { if (target->Visibility && !target->Visibility->VisibleTo(user)) { (*wl)[nick] = ""; user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick); return CMD_FAILURE; } (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age)); user->WriteServ("604 %s %s %s :is online",user->nick, nick, (*wl)[nick].c_str()); } else { (*wl)[nick] = ""; user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick); } } return CMD_FAILURE; } cmd_watch (InspIRCd* Instance, unsigned int &maxwatch) : command_t(Instance,"WATCH",0,0), MAX_WATCH(maxwatch) { this->source = "m_watch.so"; syntax = "[C|L|S]|[+|-<nick>]"; } CmdResult Handle (const char** parameters, int pcnt, userrec *user) { if (!pcnt) { watchlist* wl; if (user->GetExt("watchlist", wl)) { for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) { if (!q->second.empty()) user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str()); } } user->WriteServ("607 %s :End of WATCH list",user->nick); } else if (pcnt > 0) { for (int x = 0; x < pcnt; x++) { const char *nick = parameters[x]; if (!strcasecmp(nick,"C")) { // watch clear watchlist* wl; if (user->GetExt("watchlist", wl)) { for (watchlist::iterator i = wl->begin(); i != wl->end(); i++) { watchentries::iterator x = whos_watching_me->find(i->first); if (x != whos_watching_me->end()) { /* People are watching this user, am i one of them? */ std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user); if (n != x->second.end()) /* I'm no longer watching you... */ x->second.erase(n); if (!x->second.size()) whos_watching_me->erase(user->nick); } } delete wl; user->Shrink("watchlist"); } } else if (!strcasecmp(nick,"L")) { watchlist* wl; if (user->GetExt("watchlist", wl)) { for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) { if (!q->second.empty()) user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str()); else user->WriteServ("605 %s %s * * 0 :is offline", user->nick, q->first.c_str()); } } user->WriteServ("607 %s :End of WATCH list",user->nick); } else if (!strcasecmp(nick,"S")) { watchlist* wl; int you_have = 0; int youre_on = 0; std::string list; if (user->GetExt("watchlist", wl)) { for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) list.append(q->first.c_str()).append(" "); you_have = wl->size(); } watchentries::iterator x = whos_watching_me->find(user->nick); if (x != whos_watching_me->end()) youre_on = x->second.size(); user->WriteServ("603 %s :You have %d and are on %d WATCH entries", user->nick, you_have, youre_on); user->WriteServ("606 %s :%s",user->nick, list.c_str()); user->WriteServ("607 %s :End of WATCH S",user->nick); } else if (nick[0] == '-') { nick++; remove_watch(user, nick); } else if (nick[0] == '+') { nick++; add_watch(user, nick); } } } /* So that spanningtree doesnt pass the WATCH commands to the network! */ return CMD_FAILURE; } }; class Modulewatch : public Module { cmd_watch* mycommand; unsigned int maxwatch; public: Modulewatch(InspIRCd* Me) : Module(Me), maxwatch(32) { OnRehash(NULL, ""); whos_watching_me = new watchentries(); mycommand = new cmd_watch(ServerInstance, maxwatch); ServerInstance->AddCommand(mycommand); } virtual void OnRehash(userrec* user, const std::string &parameter) { ConfigReader Conf(ServerInstance); maxwatch = Conf.ReadInteger("watch", "maxentries", 0, true); if (!maxwatch) maxwatch = 32; } void Implements(char* List) { List[I_OnRehash] = List[I_OnGarbageCollect] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_OnPostConnect] = List[I_OnUserPostNick] = List[I_On005Numeric] = 1; } virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) { watchentries::iterator x = whos_watching_me->find(user->nick); if (x != whos_watching_me->end()) { for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++) { if (!user->Visibility || user->Visibility->VisibleTo(user)) (*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick ,user->nick, user->ident, user->dhost, ServerInstance->Time()); watchlist* wl; if ((*n)->GetExt("watchlist", wl)) /* We were on somebody's notify list, set ourselves offline */ (*wl)[user->nick] = ""; } } /* Now im quitting, if i have a notify list, im no longer watching anyone */ watchlist* wl; if (user->GetExt("watchlist", 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 x = whos_watching_me->find(i->first); if (x != whos_watching_me->end()) { /* People are watching this user, am i one of them? */ std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user); if (n != x->second.end()) /* I'm no longer watching you... */ x->second.erase(n); if (!x->second.size()) whos_watching_me->erase(user->nick); } } /* User's quitting, we're done with this. */ delete wl; } } virtual void OnGarbageCollect() { 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; } virtual void OnCleanup(int target_type, void* item) { if (target_type == TYPE_USER) { watchlist* wl; userrec* user = (userrec*)item; if (user->GetExt("watchlist", wl)) { user->Shrink("watchlist"); delete wl; } } } virtual void OnPostConnect(userrec* user) { watchentries::iterator x = whos_watching_me->find(user->nick); if (x != whos_watching_me->end()) { for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++) { if (!user->Visibility || user->Visibility->VisibleTo(user)) (*n)->WriteServ("600 %s %s %s %s %lu :arrived online", (*n)->nick, user->nick, user->ident, user->dhost, user->age); watchlist* wl; if ((*n)->GetExt("watchlist", wl)) /* We were on somebody's notify list, set ourselves online */ (*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age)); } } } virtual void OnUserPostNick(userrec* user, const std::string &oldnick) { watchentries::iterator new_online = whos_watching_me->find(user->nick); watchentries::iterator new_offline = whos_watching_me->find(assign(oldnick)); if (new_online != whos_watching_me->end()) { for (std::deque<userrec*>::iterator n = new_online->second.begin(); n != new_online->second.end(); n++) { watchlist* wl; if ((*n)->GetExt("watchlist", wl)) { (*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age)); if (!user->Visibility || user->Visibility->VisibleTo(user)) (*n)->WriteServ("600 %s %s %s :arrived online", (*n)->nick, user->nick, (*wl)[user->nick].c_str()); } } } if (new_offline != whos_watching_me->end()) { for (std::deque<userrec*>::iterator n = new_offline->second.begin(); n != new_offline->second.end(); n++) { watchlist* wl; if ((*n)->GetExt("watchlist", wl)) { if (!user->Visibility || user->Visibility->VisibleTo(user)) (*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick, oldnick.c_str(), user->ident, user->dhost, user->age); (*wl)[oldnick.c_str()] = ""; } } } } virtual void On005Numeric(std::string &output) { // we don't really have a limit... output = output + " WATCH=" + ConvToStr(maxwatch); } virtual ~Modulewatch() { delete whos_watching_me; } virtual Version GetVersion() { return Version(1,1,0,1,VF_VENDOR,API_VERSION); } }; MODULE_INIT(Modulewatch) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+
+/* $ModDesc: Provides support for the /WATCH command */
+
+/* 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 userrec 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
+ */
+#ifdef WINDOWS
+typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash_compare<irc::string, less<irc::string> > > watchentries;
+#else
+typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash<irc::string> > watchentries;
+#endif
+typedef std::map<irc::string, std::string> watchlist;
+
+/* 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;
+
+/** Handle /WATCH
+ */
+class cmd_watch : public command_t
+{
+ unsigned int& MAX_WATCH;
+ public:
+ CmdResult remove_watch(userrec* user, const char* nick)
+ {
+ // removing an item from the list
+ if (!ServerInstance->IsNick(nick))
+ {
+ user->WriteServ("942 %s %s :Invalid nickname", user->nick, nick);
+ return CMD_FAILURE;
+ }
+
+ watchlist* wl;
+ if (user->GetExt("watchlist", wl))
+ {
+ /* Yup, is on my list */
+ watchlist::iterator n = wl->find(nick);
+ if (n != wl->end())
+ {
+ if (!n->second.empty())
+ user->WriteServ("602 %s %s %s :stopped watching", user->nick, n->first.c_str(), n->second.c_str());
+ else
+ user->WriteServ("602 %s %s * * 0 :stopped watching", user->nick, nick);
+
+ wl->erase(n);
+ }
+
+ if (!wl->size())
+ {
+ user->Shrink("watchlist");
+ delete wl;
+ }
+
+ 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<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user);
+ if (n != x->second.end())
+ /* I'm no longer watching you... */
+ x->second.erase(n);
+
+ if (!x->second.size())
+ whos_watching_me->erase(nick);
+ }
+ }
+
+ /* This might seem confusing, but we return CMD_FAILURE
+ * to indicate that this message shouldnt be routed across
+ * the network to other linked servers.
+ */
+ return CMD_FAILURE;
+ }
+
+ CmdResult add_watch(userrec* user, const char* nick)
+ {
+ if (!ServerInstance->IsNick(nick))
+ {
+ user->WriteServ("942 %s %s :Invalid nickname",user->nick,nick);
+ return CMD_FAILURE;
+ }
+
+ watchlist* wl;
+ if (!user->GetExt("watchlist", wl))
+ {
+ wl = new watchlist();
+ user->Extend("watchlist", wl);
+ }
+
+ if (wl->size() == MAX_WATCH)
+ {
+ user->WriteServ("512 %s %s :Too many WATCH entries", user->nick, nick);
+ return CMD_FAILURE;
+ }
+
+ watchlist::iterator n = wl->find(nick);
+ if (n == wl->end())
+ {
+ /* Don't already have the user on my watch list, proceed */
+ watchentries::iterator x = whos_watching_me->find(nick);
+ if (x != whos_watching_me->end())
+ {
+ /* People are watching this user, add myself */
+ x->second.push_back(user);
+ }
+ else
+ {
+ std::deque<userrec*> newlist;
+ newlist.push_back(user);
+ (*(whos_watching_me))[nick] = newlist;
+ }
+
+ userrec* target = ServerInstance->FindNick(nick);
+ if (target)
+ {
+ if (target->Visibility && !target->Visibility->VisibleTo(user))
+ {
+ (*wl)[nick] = "";
+ user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick);
+ return CMD_FAILURE;
+ }
+
+ (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age));
+ user->WriteServ("604 %s %s %s :is online",user->nick, nick, (*wl)[nick].c_str());
+ }
+ else
+ {
+ (*wl)[nick] = "";
+ user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick);
+ }
+ }
+
+ return CMD_FAILURE;
+ }
+
+ cmd_watch (InspIRCd* Instance, unsigned int &maxwatch) : command_t(Instance,"WATCH",0,0), MAX_WATCH(maxwatch)
+ {
+ this->source = "m_watch.so";
+ syntax = "[C|L|S]|[+|-<nick>]";
+ }
+
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
+ {
+ if (!pcnt)
+ {
+ watchlist* wl;
+ if (user->GetExt("watchlist", wl))
+ {
+ for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
+ {
+ if (!q->second.empty())
+ user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str());
+ }
+ }
+ user->WriteServ("607 %s :End of WATCH list",user->nick);
+ }
+ else if (pcnt > 0)
+ {
+ for (int x = 0; x < pcnt; x++)
+ {
+ const char *nick = parameters[x];
+ if (!strcasecmp(nick,"C"))
+ {
+ // watch clear
+ watchlist* wl;
+ if (user->GetExt("watchlist", wl))
+ {
+ for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
+ {
+ watchentries::iterator x = whos_watching_me->find(i->first);
+ if (x != whos_watching_me->end())
+ {
+ /* People are watching this user, am i one of them? */
+ std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user);
+ if (n != x->second.end())
+ /* I'm no longer watching you... */
+ x->second.erase(n);
+
+ if (!x->second.size())
+ whos_watching_me->erase(user->nick);
+ }
+ }
+
+ delete wl;
+ user->Shrink("watchlist");
+ }
+ }
+ else if (!strcasecmp(nick,"L"))
+ {
+ watchlist* wl;
+ if (user->GetExt("watchlist", wl))
+ {
+ for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
+ {
+ if (!q->second.empty())
+ user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str());
+ else
+ user->WriteServ("605 %s %s * * 0 :is offline", user->nick, q->first.c_str());
+ }
+ }
+ user->WriteServ("607 %s :End of WATCH list",user->nick);
+ }
+ else if (!strcasecmp(nick,"S"))
+ {
+ watchlist* wl;
+ int you_have = 0;
+ int youre_on = 0;
+ std::string list;
+
+ if (user->GetExt("watchlist", wl))
+ {
+ for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
+ list.append(q->first.c_str()).append(" ");
+ you_have = wl->size();
+ }
+
+ watchentries::iterator x = whos_watching_me->find(user->nick);
+ if (x != whos_watching_me->end())
+ youre_on = x->second.size();
+
+ user->WriteServ("603 %s :You have %d and are on %d WATCH entries", user->nick, you_have, youre_on);
+ user->WriteServ("606 %s :%s",user->nick, list.c_str());
+ user->WriteServ("607 %s :End of WATCH S",user->nick);
+ }
+ else if (nick[0] == '-')
+ {
+ nick++;
+ remove_watch(user, nick);
+ }
+ else if (nick[0] == '+')
+ {
+ nick++;
+ add_watch(user, nick);
+ }
+ }
+ }
+ /* So that spanningtree doesnt pass the WATCH commands to the network! */
+ return CMD_FAILURE;
+ }
+};
+
+class Modulewatch : public Module
+{
+ cmd_watch* mycommand;
+ unsigned int maxwatch;
+ public:
+
+ Modulewatch(InspIRCd* Me)
+ : Module(Me), maxwatch(32)
+ {
+ OnRehash(NULL, "");
+ whos_watching_me = new watchentries();
+ mycommand = new cmd_watch(ServerInstance, maxwatch);
+ ServerInstance->AddCommand(mycommand);
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &parameter)
+ {
+ ConfigReader Conf(ServerInstance);
+ maxwatch = Conf.ReadInteger("watch", "maxentries", 0, true);
+ if (!maxwatch)
+ maxwatch = 32;
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnRehash] = List[I_OnGarbageCollect] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_OnPostConnect] = List[I_OnUserPostNick] = List[I_On005Numeric] = 1;
+ }
+
+ virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
+ {
+ watchentries::iterator x = whos_watching_me->find(user->nick);
+ if (x != whos_watching_me->end())
+ {
+ for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++)
+ {
+ if (!user->Visibility || user->Visibility->VisibleTo(user))
+ (*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick ,user->nick, user->ident, user->dhost, ServerInstance->Time());
+
+ watchlist* wl;
+ if ((*n)->GetExt("watchlist", wl))
+ /* We were on somebody's notify list, set ourselves offline */
+ (*wl)[user->nick] = "";
+ }
+ }
+
+ /* Now im quitting, if i have a notify list, im no longer watching anyone */
+ watchlist* wl;
+ if (user->GetExt("watchlist", 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 x = whos_watching_me->find(i->first);
+ if (x != whos_watching_me->end())
+ {
+ /* People are watching this user, am i one of them? */
+ std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user);
+ if (n != x->second.end())
+ /* I'm no longer watching you... */
+ x->second.erase(n);
+
+ if (!x->second.size())
+ whos_watching_me->erase(user->nick);
+ }
+ }
+
+ /* User's quitting, we're done with this. */
+ delete wl;
+ }
+ }
+
+ virtual void OnGarbageCollect()
+ {
+ 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;
+ }
+
+ virtual void OnCleanup(int target_type, void* item)
+ {
+ if (target_type == TYPE_USER)
+ {
+ watchlist* wl;
+ userrec* user = (userrec*)item;
+
+ if (user->GetExt("watchlist", wl))
+ {
+ user->Shrink("watchlist");
+ delete wl;
+ }
+ }
+ }
+
+ virtual void OnPostConnect(userrec* user)
+ {
+ watchentries::iterator x = whos_watching_me->find(user->nick);
+ if (x != whos_watching_me->end())
+ {
+ for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++)
+ {
+ if (!user->Visibility || user->Visibility->VisibleTo(user))
+ (*n)->WriteServ("600 %s %s %s %s %lu :arrived online", (*n)->nick, user->nick, user->ident, user->dhost, user->age);
+
+ watchlist* wl;
+ if ((*n)->GetExt("watchlist", wl))
+ /* We were on somebody's notify list, set ourselves online */
+ (*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
+ }
+ }
+ }
+
+ virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
+ {
+ watchentries::iterator new_online = whos_watching_me->find(user->nick);
+ watchentries::iterator new_offline = whos_watching_me->find(assign(oldnick));
+
+ if (new_online != whos_watching_me->end())
+ {
+ for (std::deque<userrec*>::iterator n = new_online->second.begin(); n != new_online->second.end(); n++)
+ {
+ watchlist* wl;
+ if ((*n)->GetExt("watchlist", wl))
+ {
+ (*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
+ if (!user->Visibility || user->Visibility->VisibleTo(user))
+ (*n)->WriteServ("600 %s %s %s :arrived online", (*n)->nick, user->nick, (*wl)[user->nick].c_str());
+ }
+ }
+ }
+
+ if (new_offline != whos_watching_me->end())
+ {
+ for (std::deque<userrec*>::iterator n = new_offline->second.begin(); n != new_offline->second.end(); n++)
+ {
+ watchlist* wl;
+ if ((*n)->GetExt("watchlist", wl))
+ {
+ if (!user->Visibility || user->Visibility->VisibleTo(user))
+ (*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick, oldnick.c_str(), user->ident, user->dhost, user->age);
+ (*wl)[oldnick.c_str()] = "";
+ }
+ }
+ }
+ }
+
+ virtual void On005Numeric(std::string &output)
+ {
+ // we don't really have a limit...
+ output = output + " WATCH=" + ConvToStr(maxwatch);
+ }
+
+ virtual ~Modulewatch()
+ {
+ delete whos_watching_me;
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1,1,0,1,VF_VENDOR,API_VERSION);
+ }
+};
+
+MODULE_INIT(Modulewatch)
+
diff --git a/src/modules/m_xmlsocket.cpp b/src/modules/m_xmlsocket.cpp
index 12da3762e..7f37f66c9 100644
--- a/src/modules/m_xmlsocket.cpp
+++ b/src/modules/m_xmlsocket.cpp
@@ -1 +1,170 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "channels.h" #include "modules.h" #include "hashcomp.h" /* $ModDesc: Provides XMLSocket support for clients */ class ModuleXMLSocket : public Module { ConfigReader* Conf; std::vector<int> listenports; public: ModuleXMLSocket(InspIRCd* Me) : Module(Me) { OnRehash(NULL,""); } virtual void OnRehash(userrec* user, const std::string &param) { Conf = new ConfigReader(ServerInstance); for (unsigned int i = 0; i < listenports.size(); i++) { ServerInstance->Config->DelIOHook(listenports[i]); } listenports.clear(); for (int i = 0; i < Conf->Enumerate("bind"); i++) { // For each <bind> tag std::string x = Conf->ReadValue("bind", "type", i); if (((x.empty()) || (x == "clients")) && (Conf->ReadFlag("bind", "xmlsocket", i))) { // Get the port we're meant to be listening on with SSL std::string port = Conf->ReadValue("bind", "port", i); irc::portparser portrange(port, false); long portno = -1; while ((portno = portrange.GetToken())) { try { if (ServerInstance->Config->AddIOHook(portno, this)) { listenports.push_back(portno); for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) if (ServerInstance->Config->ports[i]->GetPort() == portno) ServerInstance->Config->ports[i]->SetDescription("xml"); } else { ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d, maybe you have another similar module loaded?", portno); } } catch (ModuleException &e) { ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another similar module loaded?", portno, e.GetReason()); } } } } DELETE(Conf); } virtual ~ModuleXMLSocket() { } virtual void OnUnloadModule(Module* mod, const std::string &name) { if (mod == this) { for(unsigned int i = 0; i < listenports.size(); i++) { ServerInstance->Config->DelIOHook(listenports[i]); for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++) if (ServerInstance->Config->ports[j]->GetPort() == listenports[i]) ServerInstance->Config->ports[j]->SetDescription("plaintext"); } } } virtual Version GetVersion() { return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); } void Implements(char* List) { List[I_OnUnloadModule] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnRehash] = 1; } virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd)); if (user == NULL) return -1; int result = user->ReadData(buffer, count); if ((result == -1) && (errno == EAGAIN)) return -1; else if (result < 1) return 0; /* XXX: The core is more than happy to split lines purely on an \n * rather than a \r\n. This is good for us as it means that the size * of data we are receiving is exactly the same as the size of data * we asked for, and we dont need to re-implement our own socket * buffering (See below) */ for (int n = 0; n < result; n++) if (buffer[n] == 0) buffer[n] = '\n'; readresult = result; return result; } virtual int OnRawSocketWrite(int fd, const char* buffer, int count) { userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd)); if (user == NULL) return -1; /* We want to alter the buffer, so we have to make a copy */ char * tmpbuffer = new char[count + 1]; memcpy(tmpbuffer, buffer, count); /* XXX: This will actually generate lines "looking\0\0like\0\0this" * rather than lines "looking\0like\0this". This shouldnt be a problem * to the client, but it saves us a TON of processing and the need * to re-implement socket buffering, as the data we are sending is * exactly the same length as the data we are receiving. */ for (int n = 0; n < count; n++) if ((tmpbuffer[n] == '\r') || (tmpbuffer[n] == '\n')) tmpbuffer[n] = 0; user->AddWriteBuf(std::string(tmpbuffer,count)); delete [] tmpbuffer; return 1; } }; MODULE_INIT(ModuleXMLSocket) \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "modules.h"
+#include "hashcomp.h"
+
+/* $ModDesc: Provides XMLSocket support for clients */
+
+class ModuleXMLSocket : public Module
+{
+ ConfigReader* Conf;
+ std::vector<int> listenports;
+
+ public:
+
+ ModuleXMLSocket(InspIRCd* Me)
+ : Module(Me)
+ {
+ OnRehash(NULL,"");
+ }
+
+ virtual void OnRehash(userrec* user, const std::string &param)
+ {
+
+ Conf = new ConfigReader(ServerInstance);
+
+ for (unsigned int i = 0; i < listenports.size(); i++)
+ {
+ ServerInstance->Config->DelIOHook(listenports[i]);
+ }
+
+ listenports.clear();
+
+ for (int i = 0; i < Conf->Enumerate("bind"); i++)
+ {
+ // For each <bind> tag
+ std::string x = Conf->ReadValue("bind", "type", i);
+ if (((x.empty()) || (x == "clients")) && (Conf->ReadFlag("bind", "xmlsocket", i)))
+ {
+ // Get the port we're meant to be listening on with SSL
+ std::string port = Conf->ReadValue("bind", "port", i);
+ irc::portparser portrange(port, false);
+ long portno = -1;
+ while ((portno = portrange.GetToken()))
+ {
+ try
+ {
+ if (ServerInstance->Config->AddIOHook(portno, this))
+ {
+ listenports.push_back(portno);
+ for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
+ if (ServerInstance->Config->ports[i]->GetPort() == portno)
+ ServerInstance->Config->ports[i]->SetDescription("xml");
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d, maybe you have another similar module loaded?", portno);
+ }
+ }
+ catch (ModuleException &e)
+ {
+ ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another similar module loaded?", portno, e.GetReason());
+ }
+ }
+ }
+ }
+
+ DELETE(Conf);
+ }
+
+ virtual ~ModuleXMLSocket()
+ {
+ }
+
+ virtual void OnUnloadModule(Module* mod, const std::string &name)
+ {
+ if (mod == this)
+ {
+ for(unsigned int i = 0; i < listenports.size(); i++)
+ {
+ ServerInstance->Config->DelIOHook(listenports[i]);
+ for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
+ if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
+ ServerInstance->Config->ports[j]->SetDescription("plaintext");
+ }
+ }
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnUnloadModule] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnRehash] = 1;
+ }
+
+ virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
+ {
+ userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd));
+
+ if (user == NULL)
+ return -1;
+
+ int result = user->ReadData(buffer, count);
+
+ if ((result == -1) && (errno == EAGAIN))
+ return -1;
+ else if (result < 1)
+ return 0;
+
+ /* XXX: The core is more than happy to split lines purely on an \n
+ * rather than a \r\n. This is good for us as it means that the size
+ * of data we are receiving is exactly the same as the size of data
+ * we asked for, and we dont need to re-implement our own socket
+ * buffering (See below)
+ */
+ for (int n = 0; n < result; n++)
+ if (buffer[n] == 0)
+ buffer[n] = '\n';
+
+ readresult = result;
+ return result;
+ }
+
+ virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
+ {
+ userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd));
+
+ if (user == NULL)
+ return -1;
+
+ /* We want to alter the buffer, so we have to make a copy */
+ char * tmpbuffer = new char[count + 1];
+ memcpy(tmpbuffer, buffer, count);
+
+ /* XXX: This will actually generate lines "looking\0\0like\0\0this"
+ * rather than lines "looking\0like\0this". This shouldnt be a problem
+ * to the client, but it saves us a TON of processing and the need
+ * to re-implement socket buffering, as the data we are sending is
+ * exactly the same length as the data we are receiving.
+ */
+ for (int n = 0; n < count; n++)
+ if ((tmpbuffer[n] == '\r') || (tmpbuffer[n] == '\n'))
+ tmpbuffer[n] = 0;
+
+ user->AddWriteBuf(std::string(tmpbuffer,count));
+ delete [] tmpbuffer;
+
+ return 1;
+ }
+
+};
+
+MODULE_INIT(ModuleXMLSocket)
+
diff --git a/src/modules/transport.h b/src/modules/transport.h
index d92cf0376..ba8e3973b 100644
--- a/src/modules/transport.h
+++ b/src/modules/transport.h
@@ -1 +1,231 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __TRANSPORT_H__ #define __TRANSPORT_H__ #include <map> #include <string> /** A generic container for certificate data */ typedef std::map<std::string,std::string> ssl_data; /** A shorthand way of representing an iterator into ssl_data */ typedef ssl_data::iterator ssl_data_iter; /** 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 Extensible::Extend() and the * key 'ssl_cert'. */ class ssl_cert { /** Always contains an empty string */ const std::string empty; public: /** The data for this certificate */ ssl_data data; /** Default constructor, initializes 'empty' */ ssl_cert() : empty("") { } /** Get certificate distinguished name * @return Certificate DN */ const std::string& GetDN() { ssl_data_iter ssldi = data.find("dn"); if (ssldi != data.end()) return ssldi->second; else return empty; } /** Get Certificate issuer * @return Certificate issuer */ const std::string& GetIssuer() { ssl_data_iter ssldi = data.find("issuer"); if (ssldi != data.end()) return ssldi->second; else return empty; } /** 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() { ssl_data_iter ssldi = data.find("error"); if (ssldi != data.end()) return ssldi->second; else return empty; } /** Get key fingerprint. * @return The key fingerprint as a hex string. */ const std::string& GetFingerprint() { ssl_data_iter ssldi = data.find("fingerprint"); if (ssldi != data.end()) return ssldi->second; else return empty; } /** Get trust status * @return True if this is a trusted certificate * (the certificate chain validates) */ bool IsTrusted() { ssl_data_iter ssldi = data.find("trusted"); if (ssldi != data.end()) return (ssldi->second == "1"); else return false; } /** Get validity status * @return True if the certificate itself is * correctly formed. */ bool IsInvalid() { ssl_data_iter ssldi = data.find("invalid"); if (ssldi != data.end()) return (ssldi->second == "1"); else return false; } /** Get signer status * @return True if the certificate appears to be * self-signed. */ bool IsUnknownSigner() { ssl_data_iter ssldi = data.find("unknownsigner"); if (ssldi != data.end()) return (ssldi->second == "1"); else return false; } /** Get revokation status. * @return True if the certificate is revoked. * Note that this only works properly for GnuTLS * right now. */ bool IsRevoked() { ssl_data_iter ssldi = data.find("revoked"); if (ssldi != data.end()) return (ssldi->second == "1"); else return false; } }; /** Used to represent a request to a transport provider module */ class ISHRequest : public Request { public: InspSocket* Sock; ISHRequest(Module* Me, Module* Target, const char* rtype, InspSocket* sock) : Request(Me, Target, rtype), Sock(sock) { } }; /** Used to represent a request to attach a cert to an InspSocket */ class InspSocketAttachCertRequest : public ISHRequest { public: /** Initialize the request as an attach cert message */ InspSocketAttachCertRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_ATTACH", is) { } }; /** Used to check if a handshake is complete on an InspSocket yet */ class InspSocketHSCompleteRequest : public ISHRequest { public: /** Initialize the request as a 'handshake complete?' message */ InspSocketHSCompleteRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HSDONE", is) { } }; /** Used to hook a transport provider to an InspSocket */ class InspSocketHookRequest : public ISHRequest { public: /** Initialize request as a hook message */ InspSocketHookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HOOK", is) { } }; /** Used to unhook a transport provider from an InspSocket */ class InspSocketUnhookRequest : public ISHRequest { public: /** Initialize request as an unhook message */ InspSocketUnhookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_UNHOOK", is) { } }; class InspSocketNameRequest : public ISHRequest { public: /** Initialize request as a get name message */ InspSocketNameRequest(Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_NAME", NULL) { } }; #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __TRANSPORT_H__
+#define __TRANSPORT_H__
+
+#include <map>
+#include <string>
+
+/** A generic container for certificate data
+ */
+typedef std::map<std::string,std::string> ssl_data;
+
+/** A shorthand way of representing an iterator into ssl_data
+ */
+typedef ssl_data::iterator ssl_data_iter;
+
+/** 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 Extensible::Extend() and the
+ * key 'ssl_cert'.
+ */
+class ssl_cert
+{
+ /** Always contains an empty string
+ */
+ const std::string empty;
+
+ public:
+ /** The data for this certificate
+ */
+ ssl_data data;
+
+ /** Default constructor, initializes 'empty'
+ */
+ ssl_cert() : empty("")
+ {
+ }
+
+ /** Get certificate distinguished name
+ * @return Certificate DN
+ */
+ const std::string& GetDN()
+ {
+ ssl_data_iter ssldi = data.find("dn");
+
+ if (ssldi != data.end())
+ return ssldi->second;
+ else
+ return empty;
+ }
+
+ /** Get Certificate issuer
+ * @return Certificate issuer
+ */
+ const std::string& GetIssuer()
+ {
+ ssl_data_iter ssldi = data.find("issuer");
+
+ if (ssldi != data.end())
+ return ssldi->second;
+ else
+ return empty;
+ }
+
+ /** 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()
+ {
+ ssl_data_iter ssldi = data.find("error");
+
+ if (ssldi != data.end())
+ return ssldi->second;
+ else
+ return empty;
+ }
+
+ /** Get key fingerprint.
+ * @return The key fingerprint as a hex string.
+ */
+ const std::string& GetFingerprint()
+ {
+ ssl_data_iter ssldi = data.find("fingerprint");
+
+ if (ssldi != data.end())
+ return ssldi->second;
+ else
+ return empty;
+ }
+
+ /** Get trust status
+ * @return True if this is a trusted certificate
+ * (the certificate chain validates)
+ */
+ bool IsTrusted()
+ {
+ ssl_data_iter ssldi = data.find("trusted");
+
+ if (ssldi != data.end())
+ return (ssldi->second == "1");
+ else
+ return false;
+ }
+
+ /** Get validity status
+ * @return True if the certificate itself is
+ * correctly formed.
+ */
+ bool IsInvalid()
+ {
+ ssl_data_iter ssldi = data.find("invalid");
+
+ if (ssldi != data.end())
+ return (ssldi->second == "1");
+ else
+ return false;
+ }
+
+ /** Get signer status
+ * @return True if the certificate appears to be
+ * self-signed.
+ */
+ bool IsUnknownSigner()
+ {
+ ssl_data_iter ssldi = data.find("unknownsigner");
+
+ if (ssldi != data.end())
+ return (ssldi->second == "1");
+ else
+ return false;
+ }
+
+ /** Get revokation status.
+ * @return True if the certificate is revoked.
+ * Note that this only works properly for GnuTLS
+ * right now.
+ */
+ bool IsRevoked()
+ {
+ ssl_data_iter ssldi = data.find("revoked");
+
+ if (ssldi != data.end())
+ return (ssldi->second == "1");
+ else
+ return false;
+ }
+};
+
+/** Used to represent a request to a transport provider module
+ */
+class ISHRequest : public Request
+{
+ public:
+ InspSocket* Sock;
+
+ ISHRequest(Module* Me, Module* Target, const char* rtype, InspSocket* sock) : Request(Me, Target, rtype), Sock(sock)
+ {
+ }
+};
+
+/** Used to represent a request to attach a cert to an InspSocket
+ */
+class InspSocketAttachCertRequest : public ISHRequest
+{
+ public:
+ /** Initialize the request as an attach cert message */
+ InspSocketAttachCertRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_ATTACH", is)
+ {
+ }
+};
+
+/** Used to check if a handshake is complete on an InspSocket yet
+ */
+class InspSocketHSCompleteRequest : public ISHRequest
+{
+ public:
+ /** Initialize the request as a 'handshake complete?' message */
+ InspSocketHSCompleteRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HSDONE", is)
+ {
+ }
+};
+
+/** Used to hook a transport provider to an InspSocket
+ */
+class InspSocketHookRequest : public ISHRequest
+{
+ public:
+ /** Initialize request as a hook message */
+ InspSocketHookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HOOK", is)
+ {
+ }
+};
+
+/** Used to unhook a transport provider from an InspSocket
+ */
+class InspSocketUnhookRequest : public ISHRequest
+{
+ public:
+ /** Initialize request as an unhook message */
+ InspSocketUnhookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_UNHOOK", is)
+ {
+ }
+};
+
+class InspSocketNameRequest : public ISHRequest
+{
+ public:
+ /** Initialize request as a get name message */
+ InspSocketNameRequest(Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_NAME", NULL)
+ {
+ }
+};
+
+#endif
+
diff --git a/src/snomasks.cpp b/src/snomasks.cpp
index 05571ce59..ba6d3d7d5 100644
--- a/src/snomasks.cpp
+++ b/src/snomasks.cpp
@@ -1 +1,102 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <stdarg.h> #include "configreader.h" #include "users.h" #include "snomasks.h" SnomaskManager::SnomaskManager(InspIRCd* Instance) : ServerInstance(Instance) { SnoMasks.clear(); this->SetupDefaults(); } SnomaskManager::~SnomaskManager() { } bool SnomaskManager::EnableSnomask(char letter, const std::string &type) { if (SnoMasks.find(letter) == SnoMasks.end()) { SnoMasks[letter] = type; return true; } return false; } bool SnomaskManager::DisableSnomask(char letter) { SnoList::iterator n = SnoMasks.find(letter); if (n != SnoMasks.end()) { SnoMasks.erase(n); return true; } return false; } void SnomaskManager::WriteToSnoMask(char letter, const std::string &text) { /* Only send to snomask chars which are enabled */ SnoList::iterator n = SnoMasks.find(letter); if (n != SnoMasks.end()) { /* Only opers can receive snotices, so we iterate the oper list */ for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++) { userrec* a = *i; if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE] && a->modes[UM_SNOMASK] && a->IsNoticeMaskSet(n->first)) { /* send server notices to all with +ns */ a->WriteServ("NOTICE %s :*** %s: %s",a->nick, n->second.c_str(), text.c_str()); } } } } 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)); } bool SnomaskManager::IsEnabled(char letter) { return (SnoMasks.find(letter) != SnoMasks.end()); } void SnomaskManager::SetupDefaults() { this->EnableSnomask('c',"CONNECT"); /* Local connect notices */ this->EnableSnomask('C',"REMOTECONNECT"); /* Remote connect notices */ this->EnableSnomask('q',"QUIT"); /* Local quit notices */ this->EnableSnomask('Q',"REMOTEQUIT"); /* Remote quit notices */ this->EnableSnomask('k',"KILL"); /* Kill notices */ this->EnableSnomask('K',"REMOTEKILL"); /* Remote kill notices */ this->EnableSnomask('l',"LINK"); /* Link notices */ this->EnableSnomask('o',"OPER"); /* Oper up/down notices */ this->EnableSnomask('d',"DEBUG"); /* Debug notices */ this->EnableSnomask('x',"XLINE"); /* Xline notice (g/z/q/k/e) */ this->EnableSnomask('t',"STATS"); /* Local or remote stats request */ this->EnableSnomask('f',"FLOOD"); /* Flooding notices */ } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <stdarg.h>
+#include "configreader.h"
+#include "users.h"
+#include "snomasks.h"
+
+SnomaskManager::SnomaskManager(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ SnoMasks.clear();
+ this->SetupDefaults();
+}
+
+SnomaskManager::~SnomaskManager()
+{
+}
+
+bool SnomaskManager::EnableSnomask(char letter, const std::string &type)
+{
+ if (SnoMasks.find(letter) == SnoMasks.end())
+ {
+ SnoMasks[letter] = type;
+ return true;
+ }
+ return false;
+}
+
+bool SnomaskManager::DisableSnomask(char letter)
+{
+ SnoList::iterator n = SnoMasks.find(letter);
+ if (n != SnoMasks.end())
+ {
+ SnoMasks.erase(n);
+ return true;
+ }
+ return false;
+}
+
+void SnomaskManager::WriteToSnoMask(char letter, const std::string &text)
+{
+ /* Only send to snomask chars which are enabled */
+ SnoList::iterator n = SnoMasks.find(letter);
+ if (n != SnoMasks.end())
+ {
+ /* Only opers can receive snotices, so we iterate the oper list */
+ for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
+ {
+ userrec* a = *i;
+ if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE] && a->modes[UM_SNOMASK] && a->IsNoticeMaskSet(n->first))
+ {
+ /* send server notices to all with +ns */
+ a->WriteServ("NOTICE %s :*** %s: %s",a->nick, n->second.c_str(), text.c_str());
+ }
+ }
+ }
+}
+
+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));
+}
+
+bool SnomaskManager::IsEnabled(char letter)
+{
+ return (SnoMasks.find(letter) != SnoMasks.end());
+}
+
+void SnomaskManager::SetupDefaults()
+{
+ this->EnableSnomask('c',"CONNECT"); /* Local connect notices */
+ this->EnableSnomask('C',"REMOTECONNECT"); /* Remote connect notices */
+ this->EnableSnomask('q',"QUIT"); /* Local quit notices */
+ this->EnableSnomask('Q',"REMOTEQUIT"); /* Remote quit notices */
+ this->EnableSnomask('k',"KILL"); /* Kill notices */
+ this->EnableSnomask('K',"REMOTEKILL"); /* Remote kill notices */
+ this->EnableSnomask('l',"LINK"); /* Link notices */
+ this->EnableSnomask('o',"OPER"); /* Oper up/down notices */
+ this->EnableSnomask('d',"DEBUG"); /* Debug notices */
+ this->EnableSnomask('x',"XLINE"); /* Xline notice (g/z/q/k/e) */
+ this->EnableSnomask('t',"STATS"); /* Local or remote stats request */
+ this->EnableSnomask('f',"FLOOD"); /* Flooding notices */
+}
+
diff --git a/src/socket.cpp b/src/socket.cpp
index d400a0323..31fbffb61 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -1 +1,568 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <string> #include "configreader.h" #include "socket.h" #include "socketengine.h" #include "wildcard.h" using namespace irc::sockets; /* Used when comparing CIDR masks for the modulus bits left over. * A lot of ircd's seem to do this: * ((-1) << (8 - (mask % 8))) * But imho, it sucks in comparison to a nice neat lookup table. */ const unsigned char inverted_bits[8] = { 0x00, /* 00000000 - 0 bits - never actually used */ 0x80, /* 10000000 - 1 bits */ 0xC0, /* 11000000 - 2 bits */ 0xE0, /* 11100000 - 3 bits */ 0xF0, /* 11110000 - 4 bits */ 0xF8, /* 11111000 - 5 bits */ 0xFC, /* 11111100 - 6 bits */ 0xFE /* 11111110 - 7 bits */ }; ListenSocket::ListenSocket(InspIRCd* Instance, int port, char* addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port) { this->SetFd(OpenTCPSocket(addr)); if (this->GetFd() > -1) { if (!Instance->BindSocket(this->fd,port,addr)) this->fd = -1; #ifdef IPV6 if ((!*addr) || (strchr(addr,':'))) this->family = AF_INET6; else #endif this->family = AF_INET; Instance->SE->AddFd(this); } } ListenSocket::~ListenSocket() { if (this->GetFd() > -1) { ServerInstance->SE->DelFd(this); ServerInstance->Log(DEBUG,"Shut down listener on fd %d", this->fd); if (shutdown(this->fd, 2) || close(this->fd)) ServerInstance->Log(DEBUG,"Failed to cancel listener: %s", strerror(errno)); this->fd = -1; } } void ListenSocket::HandleEvent(EventType et, int errornum) { sockaddr* sock_us = new sockaddr[2]; // our port number sockaddr* client = new sockaddr[2]; socklen_t uslen, length; // length of our port number int incomingSockfd, in_port; #ifdef IPV6 if (this->family == AF_INET6) { uslen = sizeof(sockaddr_in6); length = sizeof(sockaddr_in6); } else #endif { uslen = sizeof(sockaddr_in); length = sizeof(sockaddr_in); } incomingSockfd = _accept (this->GetFd(), (sockaddr*)client, &length); if ((incomingSockfd > -1) && (!_getsockname(incomingSockfd, sock_us, &uslen))) { char buf[MAXBUF]; #ifdef IPV6 if (this->family == AF_INET6) { inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf)); in_port = ntohs(((sockaddr_in6*)sock_us)->sin6_port); } else #endif { inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf)); in_port = ntohs(((sockaddr_in*)sock_us)->sin_port); } NonBlocking(incomingSockfd); if (ServerInstance->Config->GetIOHook(in_port)) { try { ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, buf, in_port); } catch (CoreException& modexcept) { ServerInstance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } } ServerInstance->stats->statsAccept++; userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, this->family, client); } else { shutdown(incomingSockfd,2); close(incomingSockfd); ServerInstance->stats->statsRefused++; } delete[] client; delete[] sock_us; } /* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */ bool irc::sockets::MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits) { unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */ unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */ /* First compare the whole bytes, if they dont match, return false */ if (memcmp(address, mask, divisor)) return false; /* Now if there are any remainder bits, we compare them with logic AND */ if (modulus) if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus])) /* If they dont match, return false */ return false; /* The address matches the mask, to mask_bits bits of mask */ return true; } /* Match CIDR, but dont attempt to match() against leading *!*@ sections */ bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask) { return MatchCIDR(address, cidr_mask, false); } /* 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 * If you have a lot of hosts to match, youre probably better off building your mask once * and then using the lower level MatchCIDRBits directly. * * This will also attempt to match any leading usernames or nicknames on the mask, using * match(), when match_with_username is true. */ bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username) { unsigned char addr_raw[16]; unsigned char mask_raw[16]; unsigned int bits = 0; char* mask = NULL; /* The caller is trying to match ident@<mask>/bits. * Chop off the ident@ portion, use match() on it * seperately. */ if (match_with_username) { /* Duplicate the strings, and try to find the position * of the @ symbol in each */ char* address_dupe = strdup(address); char* cidr_dupe = strdup(cidr_mask); /* Use strchr not strrchr, because its going to be nearer to the left */ char* username_mask_pos = strrchr(cidr_dupe, '@'); char* username_addr_pos = strrchr(address_dupe, '@'); /* Both strings have an @ symbol in them */ if (username_mask_pos && username_addr_pos) { /* Zero out the location of the @ symbol */ *username_mask_pos = *username_addr_pos = 0; /* Try and match() the strings before the @ * symbols, and recursively call MatchCIDR without * username matching enabled to match the host part. */ bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false)); /* Free the stuff we created */ free(address_dupe); free(cidr_dupe); /* Return a result */ return result; } else { /* One or both didnt have an @ in, * just match as CIDR */ free(address_dupe); free(cidr_dupe); mask = strdup(cidr_mask); } } else { /* Make a copy of the cidr mask string, * we're going to change it */ mask = strdup(cidr_mask); } in_addr address_in4; in_addr mask_in4; /* Use strrchr for this, its nearer to the right */ char* bits_chars = strrchr(mask,'/'); if (bits_chars) { bits = atoi(bits_chars + 1); *bits_chars = 0; } else { /* No 'number of bits' field! */ free(mask); return false; } #ifdef SUPPORT_IP6LINKS in6_addr address_in6; in6_addr mask_in6; if (inet_pton(AF_INET6, address, &address_in6) > 0) { if (inet_pton(AF_INET6, mask, &mask_in6) > 0) { memcpy(&addr_raw, &address_in6.s6_addr, 16); memcpy(&mask_raw, &mask_in6.s6_addr, 16); if (bits > 128) bits = 128; } else { /* The address was valid ipv6, but the mask * that goes with it wasnt. */ free(mask); return false; } } else #endif if (inet_pton(AF_INET, address, &address_in4) > 0) { if (inet_pton(AF_INET, mask, &mask_in4) > 0) { memcpy(&addr_raw, &address_in4.s_addr, 4); memcpy(&mask_raw, &mask_in4.s_addr, 4); if (bits > 32) bits = 32; } else { /* The address was valid ipv4, * but the mask that went with it wasnt. */ free(mask); return false; } } else { /* The address was neither ipv4 or ipv6 */ free(mask); return false; } /* Low-level-match the bits in the raw data */ free(mask); return MatchCIDRBits(addr_raw, mask_raw, bits); } void irc::sockets::Blocking(int s) { #ifndef WIN32 int flags = fcntl(s, F_GETFL, 0); fcntl(s, F_SETFL, flags ^ O_NONBLOCK); #else unsigned long opt = 0; ioctlsocket(s, FIONBIO, &opt); #endif } void irc::sockets::NonBlocking(int s) { #ifndef WIN32 int flags = fcntl(s, F_GETFL, 0); fcntl(s, F_SETFL, flags | O_NONBLOCK); #else unsigned long opt = 1; ioctlsocket(s, FIONBIO, &opt); #endif } /** 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, char* addr, bool dolisten) { /* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */ sockaddr* server = new sockaddr[2]; memset(server,0,sizeof(sockaddr)*2); int ret, size; if (*addr == '*') *addr = 0; #ifdef IPV6 if (*addr) { /* There is an address here. Is it ipv6? */ if (strchr(addr,':')) { /* Yes it is */ in6_addr addy; if (inet_pton(AF_INET6, addr, &addy) < 1) { delete[] server; return false; } ((sockaddr_in6*)server)->sin6_family = AF_INET6; memcpy(&(((sockaddr_in6*)server)->sin6_addr), &addy, sizeof(in6_addr)); ((sockaddr_in6*)server)->sin6_port = htons(port); size = sizeof(sockaddr_in6); } else { /* No, its not */ in_addr addy; if (inet_pton(AF_INET, addr, &addy) < 1) { delete[] server; return false; } ((sockaddr_in*)server)->sin_family = AF_INET; ((sockaddr_in*)server)->sin_addr = addy; ((sockaddr_in*)server)->sin_port = htons(port); size = sizeof(sockaddr_in); } } else { if (port == -1) { /* Port -1: Means UDP IPV4 port binding - Special case * used by DNS engine. */ ((sockaddr_in*)server)->sin_family = AF_INET; ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY); ((sockaddr_in*)server)->sin_port = 0; size = sizeof(sockaddr_in); } else { /* Theres no address here, default to ipv6 bind to all */ ((sockaddr_in6*)server)->sin6_family = AF_INET6; memset(&(((sockaddr_in6*)server)->sin6_addr), 0, sizeof(in6_addr)); ((sockaddr_in6*)server)->sin6_port = htons(port); size = sizeof(sockaddr_in6); } } #else /* If we aren't built with ipv6, the choice becomes simple */ ((sockaddr_in*)server)->sin_family = AF_INET; if (*addr) { /* There is an address here. */ in_addr addy; if (inet_pton(AF_INET, addr, &addy) < 1) { delete[] server; return false; } ((sockaddr_in*)server)->sin_addr = addy; } else { /* Bind ipv4 to all */ ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY); } /* Bind ipv4 port number */ ((sockaddr_in*)server)->sin_port = htons(port); size = sizeof(sockaddr_in); #endif ret = bind(sockfd, server, size); delete[] server; if (ret < 0) { return false; } else { if (dolisten) { if (listen(sockfd, Config->MaxConn) == -1) { this->Log(DEFAULT,"ERROR in listen(): %s",strerror(errno)); return false; } else { this->Log(DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port); NonBlocking(sockfd); return true; } } else { this->Log(DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port); return true; } } } // Open a TCP Socket int irc::sockets::OpenTCPSocket(char* addr, int socktype) { int sockfd; int on = 1; struct linger linger = { 0 }; #ifdef IPV6 if (strchr(addr,':') || (!*addr)) sockfd = socket (PF_INET6, socktype, 0); else sockfd = socket (PF_INET, socktype, 0); if (sockfd < 0) #else if ((sockfd = socket (PF_INET, socktype, 0)) < 0) #endif { return ERROR; } else { setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */ linger.l_onoff = 1; linger.l_linger = 1; setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger)); return (sockfd); } } /* XXX: Probably belongs in class InspIRCd */ int InspIRCd::BindPorts(bool bail, int &ports_found, FailedPortList &failed_ports) { char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF]; int bound = 0; bool started_with_nothing = (Config->ports.size() == 0); std::vector<std::pair<std::string, int> > old_ports; /* XXX: Make a copy of the old ip/port pairs here */ for (std::vector<ListenSocket*>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o) old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort())); for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++) { Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF); Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF); Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF); if ((!*Type) || (!strcmp(Type,"clients"))) { irc::portparser portrange(configToken, false); int portno = -1; while ((portno = portrange.GetToken())) { if (*Addr == '*') *Addr = 0; bool skip = false; for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n) { if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno)) { skip = true; /* XXX: Here, erase from our copy of the list */ for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k) { if ((k->first == Addr) && (k->second == portno)) { old_ports.erase(k); break; } } } } if (!skip) { ListenSocket* ll = new ListenSocket(this, portno, Addr); if (ll->GetFd() > -1) { bound++; Config->ports.push_back(ll); } else { failed_ports.push_back(std::make_pair(Addr, portno)); } ports_found++; } } } } /* XXX: Here, anything left in our copy list, close as removed */ if (!started_with_nothing) { for (size_t k = 0; k < old_ports.size(); ++k) { for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n) { if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second)) { this->Log(DEFAULT,"Port binding %s:%d was removed from the config file, closing.", old_ports[k].first.c_str(), old_ports[k].second); delete *n; Config->ports.erase(n); break; } } } } return bound; } const char* irc::sockets::insp_ntoa(insp_inaddr n) { static char buf[1024]; inet_ntop(AF_FAMILY, &n, buf, sizeof(buf)); return buf; } int irc::sockets::insp_aton(const char* a, insp_inaddr* n) { return inet_pton(AF_FAMILY, a, n); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <string>
+#include "configreader.h"
+#include "socket.h"
+#include "socketengine.h"
+#include "wildcard.h"
+
+using namespace irc::sockets;
+
+/* Used when comparing CIDR masks for the modulus bits left over.
+ * A lot of ircd's seem to do this:
+ * ((-1) << (8 - (mask % 8)))
+ * But imho, it sucks in comparison to a nice neat lookup table.
+ */
+const unsigned char inverted_bits[8] = { 0x00, /* 00000000 - 0 bits - never actually used */
+ 0x80, /* 10000000 - 1 bits */
+ 0xC0, /* 11000000 - 2 bits */
+ 0xE0, /* 11100000 - 3 bits */
+ 0xF0, /* 11110000 - 4 bits */
+ 0xF8, /* 11111000 - 5 bits */
+ 0xFC, /* 11111100 - 6 bits */
+ 0xFE /* 11111110 - 7 bits */
+};
+
+
+ListenSocket::ListenSocket(InspIRCd* Instance, int port, char* addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port)
+{
+ this->SetFd(OpenTCPSocket(addr));
+ if (this->GetFd() > -1)
+ {
+ if (!Instance->BindSocket(this->fd,port,addr))
+ this->fd = -1;
+#ifdef IPV6
+ if ((!*addr) || (strchr(addr,':')))
+ this->family = AF_INET6;
+ else
+#endif
+ this->family = AF_INET;
+ Instance->SE->AddFd(this);
+ }
+}
+
+ListenSocket::~ListenSocket()
+{
+ if (this->GetFd() > -1)
+ {
+ ServerInstance->SE->DelFd(this);
+ ServerInstance->Log(DEBUG,"Shut down listener on fd %d", this->fd);
+ if (shutdown(this->fd, 2) || close(this->fd))
+ ServerInstance->Log(DEBUG,"Failed to cancel listener: %s", strerror(errno));
+ this->fd = -1;
+ }
+}
+
+void ListenSocket::HandleEvent(EventType et, int errornum)
+{
+ sockaddr* sock_us = new sockaddr[2]; // our port number
+ sockaddr* client = new sockaddr[2];
+ socklen_t uslen, length; // length of our port number
+ int incomingSockfd, in_port;
+
+#ifdef IPV6
+ if (this->family == AF_INET6)
+ {
+ uslen = sizeof(sockaddr_in6);
+ length = sizeof(sockaddr_in6);
+ }
+ else
+#endif
+ {
+ uslen = sizeof(sockaddr_in);
+ length = sizeof(sockaddr_in);
+ }
+
+ incomingSockfd = _accept (this->GetFd(), (sockaddr*)client, &length);
+
+ if ((incomingSockfd > -1) && (!_getsockname(incomingSockfd, sock_us, &uslen)))
+ {
+ char buf[MAXBUF];
+#ifdef IPV6
+ if (this->family == AF_INET6)
+ {
+ inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
+ in_port = ntohs(((sockaddr_in6*)sock_us)->sin6_port);
+ }
+ else
+#endif
+ {
+ inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf));
+ in_port = ntohs(((sockaddr_in*)sock_us)->sin_port);
+ }
+
+ NonBlocking(incomingSockfd);
+ if (ServerInstance->Config->GetIOHook(in_port))
+ {
+ try
+ {
+ ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, buf, in_port);
+ }
+ catch (CoreException& modexcept)
+ {
+ ServerInstance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+ ServerInstance->stats->statsAccept++;
+ userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, this->family, client);
+ }
+ else
+ {
+ shutdown(incomingSockfd,2);
+ close(incomingSockfd);
+ ServerInstance->stats->statsRefused++;
+ }
+ delete[] client;
+ delete[] sock_us;
+}
+
+/* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */
+bool irc::sockets::MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits)
+{
+ unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */
+ unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */
+
+ /* First compare the whole bytes, if they dont match, return false */
+ if (memcmp(address, mask, divisor))
+ return false;
+
+ /* Now if there are any remainder bits, we compare them with logic AND */
+ if (modulus)
+ if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus]))
+ /* If they dont match, return false */
+ return false;
+
+ /* The address matches the mask, to mask_bits bits of mask */
+ return true;
+}
+
+/* Match CIDR, but dont attempt to match() against leading *!*@ sections */
+bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask)
+{
+ return MatchCIDR(address, cidr_mask, false);
+}
+
+/* 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
+ * If you have a lot of hosts to match, youre probably better off building your mask once
+ * and then using the lower level MatchCIDRBits directly.
+ *
+ * This will also attempt to match any leading usernames or nicknames on the mask, using
+ * match(), when match_with_username is true.
+ */
+bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username)
+{
+ unsigned char addr_raw[16];
+ unsigned char mask_raw[16];
+ unsigned int bits = 0;
+ char* mask = NULL;
+
+ /* The caller is trying to match ident@<mask>/bits.
+ * Chop off the ident@ portion, use match() on it
+ * seperately.
+ */
+ if (match_with_username)
+ {
+ /* Duplicate the strings, and try to find the position
+ * of the @ symbol in each */
+ char* address_dupe = strdup(address);
+ char* cidr_dupe = strdup(cidr_mask);
+
+ /* Use strchr not strrchr, because its going to be nearer to the left */
+ char* username_mask_pos = strrchr(cidr_dupe, '@');
+ char* username_addr_pos = strrchr(address_dupe, '@');
+
+ /* Both strings have an @ symbol in them */
+ if (username_mask_pos && username_addr_pos)
+ {
+ /* Zero out the location of the @ symbol */
+ *username_mask_pos = *username_addr_pos = 0;
+
+ /* Try and match() the strings before the @
+ * symbols, and recursively call MatchCIDR without
+ * username matching enabled to match the host part.
+ */
+ bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false));
+
+ /* Free the stuff we created */
+ free(address_dupe);
+ free(cidr_dupe);
+
+ /* Return a result */
+ return result;
+ }
+ else
+ {
+ /* One or both didnt have an @ in,
+ * just match as CIDR
+ */
+ free(address_dupe);
+ free(cidr_dupe);
+ mask = strdup(cidr_mask);
+ }
+ }
+ else
+ {
+ /* Make a copy of the cidr mask string,
+ * we're going to change it
+ */
+ mask = strdup(cidr_mask);
+ }
+
+ in_addr address_in4;
+ in_addr mask_in4;
+
+
+ /* Use strrchr for this, its nearer to the right */
+ char* bits_chars = strrchr(mask,'/');
+
+ if (bits_chars)
+ {
+ bits = atoi(bits_chars + 1);
+ *bits_chars = 0;
+ }
+ else
+ {
+ /* No 'number of bits' field! */
+ free(mask);
+ return false;
+ }
+
+#ifdef SUPPORT_IP6LINKS
+ in6_addr address_in6;
+ in6_addr mask_in6;
+
+ if (inet_pton(AF_INET6, address, &address_in6) > 0)
+ {
+ if (inet_pton(AF_INET6, mask, &mask_in6) > 0)
+ {
+ memcpy(&addr_raw, &address_in6.s6_addr, 16);
+ memcpy(&mask_raw, &mask_in6.s6_addr, 16);
+
+ if (bits > 128)
+ bits = 128;
+ }
+ else
+ {
+ /* The address was valid ipv6, but the mask
+ * that goes with it wasnt.
+ */
+ free(mask);
+ return false;
+ }
+ }
+ else
+#endif
+ if (inet_pton(AF_INET, address, &address_in4) > 0)
+ {
+ if (inet_pton(AF_INET, mask, &mask_in4) > 0)
+ {
+ memcpy(&addr_raw, &address_in4.s_addr, 4);
+ memcpy(&mask_raw, &mask_in4.s_addr, 4);
+
+ if (bits > 32)
+ bits = 32;
+ }
+ else
+ {
+ /* The address was valid ipv4,
+ * but the mask that went with it wasnt.
+ */
+ free(mask);
+ return false;
+ }
+ }
+ else
+ {
+ /* The address was neither ipv4 or ipv6 */
+ free(mask);
+ return false;
+ }
+
+ /* Low-level-match the bits in the raw data */
+ free(mask);
+ return MatchCIDRBits(addr_raw, mask_raw, bits);
+}
+
+void irc::sockets::Blocking(int s)
+{
+#ifndef WIN32
+ int flags = fcntl(s, F_GETFL, 0);
+ fcntl(s, F_SETFL, flags ^ O_NONBLOCK);
+#else
+ unsigned long opt = 0;
+ ioctlsocket(s, FIONBIO, &opt);
+#endif
+}
+
+void irc::sockets::NonBlocking(int s)
+{
+#ifndef WIN32
+ int flags = fcntl(s, F_GETFL, 0);
+ fcntl(s, F_SETFL, flags | O_NONBLOCK);
+#else
+ unsigned long opt = 1;
+ ioctlsocket(s, FIONBIO, &opt);
+#endif
+}
+
+/** 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, char* addr, bool dolisten)
+{
+ /* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */
+ sockaddr* server = new sockaddr[2];
+ memset(server,0,sizeof(sockaddr)*2);
+
+ int ret, size;
+
+ if (*addr == '*')
+ *addr = 0;
+
+#ifdef IPV6
+ if (*addr)
+ {
+ /* There is an address here. Is it ipv6? */
+ if (strchr(addr,':'))
+ {
+ /* Yes it is */
+ in6_addr addy;
+ if (inet_pton(AF_INET6, addr, &addy) < 1)
+ {
+ delete[] server;
+ return false;
+ }
+
+ ((sockaddr_in6*)server)->sin6_family = AF_INET6;
+ memcpy(&(((sockaddr_in6*)server)->sin6_addr), &addy, sizeof(in6_addr));
+ ((sockaddr_in6*)server)->sin6_port = htons(port);
+ size = sizeof(sockaddr_in6);
+ }
+ else
+ {
+ /* No, its not */
+ in_addr addy;
+ if (inet_pton(AF_INET, addr, &addy) < 1)
+ {
+ delete[] server;
+ return false;
+ }
+
+ ((sockaddr_in*)server)->sin_family = AF_INET;
+ ((sockaddr_in*)server)->sin_addr = addy;
+ ((sockaddr_in*)server)->sin_port = htons(port);
+ size = sizeof(sockaddr_in);
+ }
+ }
+ else
+ {
+ if (port == -1)
+ {
+ /* Port -1: Means UDP IPV4 port binding - Special case
+ * used by DNS engine.
+ */
+ ((sockaddr_in*)server)->sin_family = AF_INET;
+ ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
+ ((sockaddr_in*)server)->sin_port = 0;
+ size = sizeof(sockaddr_in);
+ }
+ else
+ {
+ /* Theres no address here, default to ipv6 bind to all */
+ ((sockaddr_in6*)server)->sin6_family = AF_INET6;
+ memset(&(((sockaddr_in6*)server)->sin6_addr), 0, sizeof(in6_addr));
+ ((sockaddr_in6*)server)->sin6_port = htons(port);
+ size = sizeof(sockaddr_in6);
+ }
+ }
+#else
+ /* If we aren't built with ipv6, the choice becomes simple */
+ ((sockaddr_in*)server)->sin_family = AF_INET;
+ if (*addr)
+ {
+ /* There is an address here. */
+ in_addr addy;
+ if (inet_pton(AF_INET, addr, &addy) < 1)
+ {
+ delete[] server;
+ return false;
+ }
+ ((sockaddr_in*)server)->sin_addr = addy;
+ }
+ else
+ {
+ /* Bind ipv4 to all */
+ ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ /* Bind ipv4 port number */
+ ((sockaddr_in*)server)->sin_port = htons(port);
+ size = sizeof(sockaddr_in);
+#endif
+ ret = bind(sockfd, server, size);
+ delete[] server;
+
+ if (ret < 0)
+ {
+ return false;
+ }
+ else
+ {
+ if (dolisten)
+ {
+ if (listen(sockfd, Config->MaxConn) == -1)
+ {
+ this->Log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
+ return false;
+ }
+ else
+ {
+ this->Log(DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
+ NonBlocking(sockfd);
+ return true;
+ }
+ }
+ else
+ {
+ this->Log(DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
+ return true;
+ }
+ }
+}
+
+// Open a TCP Socket
+int irc::sockets::OpenTCPSocket(char* addr, int socktype)
+{
+ int sockfd;
+ int on = 1;
+ struct linger linger = { 0 };
+#ifdef IPV6
+ if (strchr(addr,':') || (!*addr))
+ sockfd = socket (PF_INET6, socktype, 0);
+ else
+ sockfd = socket (PF_INET, socktype, 0);
+ if (sockfd < 0)
+#else
+ if ((sockfd = socket (PF_INET, socktype, 0)) < 0)
+#endif
+ {
+ return ERROR;
+ }
+ else
+ {
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
+ /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
+ linger.l_onoff = 1;
+ linger.l_linger = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
+ return (sockfd);
+ }
+}
+
+/* XXX: Probably belongs in class InspIRCd */
+int InspIRCd::BindPorts(bool bail, int &ports_found, FailedPortList &failed_ports)
+{
+ char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
+ int bound = 0;
+ bool started_with_nothing = (Config->ports.size() == 0);
+ std::vector<std::pair<std::string, int> > old_ports;
+
+ /* XXX: Make a copy of the old ip/port pairs here */
+ for (std::vector<ListenSocket*>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o)
+ old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort()));
+
+ for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
+ {
+ Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
+ Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
+ Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
+
+ if ((!*Type) || (!strcmp(Type,"clients")))
+ {
+ irc::portparser portrange(configToken, false);
+ int portno = -1;
+ while ((portno = portrange.GetToken()))
+ {
+ if (*Addr == '*')
+ *Addr = 0;
+
+ bool skip = false;
+ for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
+ {
+ if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno))
+ {
+ skip = true;
+ /* XXX: Here, erase from our copy of the list */
+ for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k)
+ {
+ if ((k->first == Addr) && (k->second == portno))
+ {
+ old_ports.erase(k);
+ break;
+ }
+ }
+ }
+ }
+ if (!skip)
+ {
+ ListenSocket* ll = new ListenSocket(this, portno, Addr);
+ if (ll->GetFd() > -1)
+ {
+ bound++;
+ Config->ports.push_back(ll);
+ }
+ else
+ {
+ failed_ports.push_back(std::make_pair(Addr, portno));
+ }
+ ports_found++;
+ }
+ }
+ }
+ }
+
+ /* XXX: Here, anything left in our copy list, close as removed */
+ if (!started_with_nothing)
+ {
+ for (size_t k = 0; k < old_ports.size(); ++k)
+ {
+ for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
+ {
+ if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second))
+ {
+ this->Log(DEFAULT,"Port binding %s:%d was removed from the config file, closing.", old_ports[k].first.c_str(), old_ports[k].second);
+ delete *n;
+ Config->ports.erase(n);
+ break;
+ }
+ }
+ }
+ }
+
+ return bound;
+}
+
+const char* irc::sockets::insp_ntoa(insp_inaddr n)
+{
+ static char buf[1024];
+ inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
+ return buf;
+}
+
+int irc::sockets::insp_aton(const char* a, insp_inaddr* n)
+{
+ return inet_pton(AF_FAMILY, a, n);
+}
+
diff --git a/src/socketengine.cpp b/src/socketengine.cpp
index 6a4e653db..48f7e11bf 100644
--- a/src/socketengine.cpp
+++ b/src/socketengine.cpp
@@ -1 +1,93 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "socketengine.h" int EventHandler::GetFd() { return this->fd; } void EventHandler::SetFd(int FD) { this->fd = FD; } bool EventHandler::Readable() { return true; } bool EventHandler::Writeable() { return false; } void SocketEngine::WantWrite(EventHandler* eh) { } SocketEngine::SocketEngine(InspIRCd* Instance) : ServerInstance(Instance) { memset(ref, 0, sizeof(ref)); } SocketEngine::~SocketEngine() { } bool SocketEngine::AddFd(EventHandler* eh) { return true; } bool SocketEngine::HasFd(int fd) { if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; return ref[fd]; } EventHandler* SocketEngine::GetRef(int fd) { if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return 0; return ref[fd]; } bool SocketEngine::DelFd(EventHandler* eh, bool force) { return true; } int SocketEngine::GetMaxFds() { return 0; } int SocketEngine::GetRemainingFds() { return 0; } int SocketEngine::DispatchEvents() { return 0; } std::string SocketEngine::GetName() { return "misconfigured"; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "socketengine.h"
+
+int EventHandler::GetFd()
+{
+ return this->fd;
+}
+
+void EventHandler::SetFd(int FD)
+{
+ this->fd = FD;
+}
+
+bool EventHandler::Readable()
+{
+ return true;
+}
+
+bool EventHandler::Writeable()
+{
+ return false;
+}
+
+void SocketEngine::WantWrite(EventHandler* eh)
+{
+}
+
+SocketEngine::SocketEngine(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ memset(ref, 0, sizeof(ref));
+}
+
+SocketEngine::~SocketEngine()
+{
+}
+
+bool SocketEngine::AddFd(EventHandler* eh)
+{
+ return true;
+}
+
+bool SocketEngine::HasFd(int fd)
+{
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+ return ref[fd];
+}
+
+EventHandler* SocketEngine::GetRef(int fd)
+{
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return 0;
+ return ref[fd];
+}
+
+bool SocketEngine::DelFd(EventHandler* eh, bool force)
+{
+ return true;
+}
+
+int SocketEngine::GetMaxFds()
+{
+ return 0;
+}
+
+int SocketEngine::GetRemainingFds()
+{
+ return 0;
+}
+
+int SocketEngine::DispatchEvents()
+{
+ return 0;
+}
+
+std::string SocketEngine::GetName()
+{
+ return "misconfigured";
+}
+
diff --git a/src/socketengine_epoll.cpp b/src/socketengine_epoll.cpp
index 7a7f46d1b..4ed68ca57 100644
--- a/src/socketengine_epoll.cpp
+++ b/src/socketengine_epoll.cpp
@@ -1 +1,157 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "exitcodes.h" #include <sys/epoll.h> #include "socketengine_epoll.h" EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance) { EngineHandle = epoll_create(MAX_DESCRIPTORS); if (EngineHandle == -1) { ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); ServerInstance->Log(SPARSE,"ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now."); printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n"); InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE); } CurrentSetSize = 0; } EPollEngine::~EPollEngine() { close(EngineHandle); } bool EPollEngine::AddFd(EventHandler* eh) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; if (GetRemainingFds() <= 1) return false; if (ref[fd]) return false; ref[fd] = eh; struct epoll_event ev; memset(&ev,0,sizeof(struct epoll_event)); eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; ev.data.fd = fd; int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev); if (i < 0) { return false; } ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); CurrentSetSize++; return true; } void EPollEngine::WantWrite(EventHandler* eh) { /** Use oneshot so that the system removes the writeable * status for us and saves us a call. */ struct epoll_event ev; memset(&ev,0,sizeof(struct epoll_event)); ev.events = EPOLLOUT; ev.data.fd = eh->GetFd(); epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev); } bool EPollEngine::DelFd(EventHandler* eh, bool force) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; struct epoll_event ev; memset(&ev,0,sizeof(struct epoll_event)); eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; ev.data.fd = fd; int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev); if (i < 0 && !force) return false; CurrentSetSize--; ref[fd] = NULL; ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); return true; } int EPollEngine::GetMaxFds() { return MAX_DESCRIPTORS; } int EPollEngine::GetRemainingFds() { return MAX_DESCRIPTORS - CurrentSetSize; } int EPollEngine::DispatchEvents() { socklen_t codesize; int errcode; int i = epoll_wait(EngineHandle, events, MAX_DESCRIPTORS, 1000); for (int j = 0; j < i; j++) { if (events[j].events & EPOLLHUP) { if (ref[events[j].data.fd]) ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0); continue; } if (events[j].events & EPOLLERR) { /* Get error number */ if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) errcode = errno; if (ref[events[j].data.fd]) ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode); continue; } if (events[j].events & EPOLLOUT) { struct epoll_event ev; memset(&ev,0,sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = events[j].data.fd; epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev); if (ref[events[j].data.fd]) ref[events[j].data.fd]->HandleEvent(EVENT_WRITE); } else { if (ref[events[j].data.fd]) ref[events[j].data.fd]->HandleEvent(EVENT_READ); } } return i; } std::string EPollEngine::GetName() { return "epoll"; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "exitcodes.h"
+#include <sys/epoll.h>
+#include "socketengine_epoll.h"
+
+EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance)
+{
+ EngineHandle = epoll_create(MAX_DESCRIPTORS);
+
+ if (EngineHandle == -1)
+ {
+ ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
+ ServerInstance->Log(SPARSE,"ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
+ printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno));
+ printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n");
+ InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
+ }
+ CurrentSetSize = 0;
+}
+
+EPollEngine::~EPollEngine()
+{
+ close(EngineHandle);
+}
+
+bool EPollEngine::AddFd(EventHandler* eh)
+{
+ int fd = eh->GetFd();
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ if (GetRemainingFds() <= 1)
+ return false;
+
+ if (ref[fd])
+ return false;
+
+ ref[fd] = eh;
+ struct epoll_event ev;
+ memset(&ev,0,sizeof(struct epoll_event));
+ eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT;
+ ev.data.fd = fd;
+ int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
+ if (i < 0)
+ {
+ return false;
+ }
+
+ ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
+ CurrentSetSize++;
+ return true;
+}
+
+void EPollEngine::WantWrite(EventHandler* eh)
+{
+ /** Use oneshot so that the system removes the writeable
+ * status for us and saves us a call.
+ */
+ struct epoll_event ev;
+ memset(&ev,0,sizeof(struct epoll_event));
+ ev.events = EPOLLOUT;
+ ev.data.fd = eh->GetFd();
+ epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
+}
+
+bool EPollEngine::DelFd(EventHandler* eh, bool force)
+{
+ int fd = eh->GetFd();
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ struct epoll_event ev;
+ memset(&ev,0,sizeof(struct epoll_event));
+ eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT;
+ ev.data.fd = fd;
+ int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
+
+ if (i < 0 && !force)
+ return false;
+
+ CurrentSetSize--;
+ ref[fd] = NULL;
+
+ ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
+ return true;
+}
+
+int EPollEngine::GetMaxFds()
+{
+ return MAX_DESCRIPTORS;
+}
+
+int EPollEngine::GetRemainingFds()
+{
+ return MAX_DESCRIPTORS - CurrentSetSize;
+}
+
+int EPollEngine::DispatchEvents()
+{
+ socklen_t codesize;
+ int errcode;
+ int i = epoll_wait(EngineHandle, events, MAX_DESCRIPTORS, 1000);
+ for (int j = 0; j < i; j++)
+ {
+ if (events[j].events & EPOLLHUP)
+ {
+ if (ref[events[j].data.fd])
+ ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0);
+ continue;
+ }
+ if (events[j].events & EPOLLERR)
+ {
+ /* Get error number */
+ if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+ errcode = errno;
+ if (ref[events[j].data.fd])
+ ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode);
+ continue;
+ }
+ if (events[j].events & EPOLLOUT)
+ {
+ struct epoll_event ev;
+ memset(&ev,0,sizeof(struct epoll_event));
+ ev.events = EPOLLIN;
+ ev.data.fd = events[j].data.fd;
+ epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev);
+ if (ref[events[j].data.fd])
+ ref[events[j].data.fd]->HandleEvent(EVENT_WRITE);
+ }
+ else
+ {
+ if (ref[events[j].data.fd])
+ ref[events[j].data.fd]->HandleEvent(EVENT_READ);
+ }
+ }
+
+ return i;
+}
+
+std::string EPollEngine::GetName()
+{
+ return "epoll";
+}
+
diff --git a/src/socketengine_iocp.cpp b/src/socketengine_iocp.cpp
index 833bc097f..89fd8717f 100644
--- a/src/socketengine_iocp.cpp
+++ b/src/socketengine_iocp.cpp
@@ -1 +1,376 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "socketengine_iocp.h" #include <mswsock.h> IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance) { /* Create completion port */ m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0); /* Null variables out. */ CurrentSetSize = 0; EngineHandle = 0; memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS); } IOCPEngine::~IOCPEngine() { CloseHandle(m_completionPort); } bool IOCPEngine::AddFd(EventHandler* eh) { int fake_fd = GenerateFd(eh->GetFd()); int is_accept = 0; int opt_len = sizeof(int); if(fake_fd < 0) return false; /* are we a listen socket? */ getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len); /* set up the read event so the socket can actually receive data :P */ eh->m_internalFd = fake_fd; eh->m_writeEvent = 0; eh->m_acceptEvent = 0; unsigned long completion_key = (ULONG_PTR)eh->m_internalFd; /* assign the socket to the completion port */ if(!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0)) return false; /* set up binding, increase set size */ ref[fake_fd] = eh; ++CurrentSetSize; /* setup initial events */ if(is_accept) PostAcceptEvent(eh); else PostReadEvent(eh); /* log message */ ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh); /* post a write event if there is data to be written */ if(eh->Writeable()) WantWrite(eh); /* we're all good =) */ try { m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) ); } catch (...) { /* Ohshi-, map::insert failed :/ */ return false; } return true; } bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */) { int fake_fd = eh->m_internalFd; int fd = eh->GetFd(); if(ref[fake_fd] == 0) return false; ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh); /* Cancel pending i/o operations. */ if (CancelIo((HANDLE)fd) == FALSE) return false; /* Free the buffer, and delete the event. */ if(eh->m_readEvent != 0) { if(((Overlapped*)eh->m_readEvent)->m_params != 0) delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params); delete ((Overlapped*)eh->m_readEvent); } if(eh->m_writeEvent != 0) delete ((Overlapped*)eh->m_writeEvent); if(eh->m_acceptEvent != 0) { delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params); delete ((Overlapped*)eh->m_acceptEvent); } /* Clear binding */ ref[fake_fd] = 0; m_binding.erase(eh->GetFd()); /* decrement set size */ --CurrentSetSize; /* success */ return true; } void IOCPEngine::WantWrite(EventHandler* eh) { /* Post event - write begin */ if(!eh->m_writeEvent) { ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd; Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0); eh->m_writeEvent = (void*)ov; PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); } } bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param) { Overlapped * ov = new Overlapped(type, param); ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd; return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); } void IOCPEngine::PostReadEvent(EventHandler * eh) { Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0); DWORD flags = 0; DWORD r_length = 0; WSABUF buf; /* by passing a null buffer pointer, we can have this working in the same way as epoll.. * its slower, but it saves modifying all network code. */ buf.buf = 0; buf.len = 0; /* determine socket type. */ DWORD sock_type; int sock_len = sizeof(DWORD); if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1) { /* wtfhax? */ PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); delete ov; return; } switch(sock_type) { case SOCK_DGRAM: /* UDP Socket */ { udp_overlap * uv = new udp_overlap; uv->udp_sockaddr_len = sizeof(sockaddr); buf.buf = (char*)uv->udp_buffer; buf.len = sizeof(uv->udp_buffer); ov->m_params = (unsigned long)uv; if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0)) { int err = WSAGetLastError(); if(err != WSA_IO_PENDING) { delete ov; PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); return; } } } break; case SOCK_STREAM: /* TCP Socket */ { if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR) { if(WSAGetLastError() != WSA_IO_PENDING) { delete ov; PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); return; } } } break; default: { printf("unknwon socket type: %u\n", sock_type); return; } break; } eh->m_readEvent = (void*)ov; } int IOCPEngine::DispatchEvents() { DWORD len; LPOVERLAPPED overlap; Overlapped * ov; EventHandler * eh; ULONG_PTR intfd; int ret; unsigned long bytes_recv; while(GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000)) { // woot, we got an event on a socket :P eh = ref[intfd]; ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap); if(eh == 0) continue; switch(ov->m_event) { case SOCKET_IO_EVENT_WRITE_READY: { eh->m_writeEvent = 0; eh->HandleEvent(EVENT_WRITE, 0); } break; case SOCKET_IO_EVENT_READ_READY: { if(ov->m_params) { // if we had params, it means we are a udp socket with a udp_overlap pointer in this long. udp_overlap * uv = (udp_overlap*)ov->m_params; uv->udp_len = len; this->udp_ov = uv; eh->m_readEvent = 0; eh->HandleEvent(EVENT_READ, 0); this->udp_ov = 0; delete uv; PostReadEvent(eh); } else { ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv); eh->m_readEvent = 0; if(ret != 0 || bytes_recv == 0) { /* end of file */ PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */ } else { eh->HandleEvent(EVENT_READ, 0); PostReadEvent(eh); } } } break; case SOCKET_IO_EVENT_ACCEPT: { /* this is kinda messy.. :/ */ eh->HandleEvent(EVENT_READ, ov->m_params); delete ((accept_overlap*)ov->m_params); eh->m_acceptEvent = 0; PostAcceptEvent(eh); } break; case SOCKET_IO_EVENT_ERROR: { eh->HandleEvent(EVENT_ERROR, ov->m_params); } break; } delete ov; } return 0; } void IOCPEngine::PostAcceptEvent(EventHandler * eh) { int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED); int len = sizeof(sockaddr_in) + 16; DWORD dwBytes; accept_overlap* ao = new accept_overlap; memset(ao->buf, 0, 1024); ao->socket = fd; Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao); eh->m_acceptEvent = (void*)ov; if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE) { int err = WSAGetLastError(); if(err != WSA_IO_PENDING) { printf("PostAcceptEvent err: %d\n", err); } } } std::string IOCPEngine::GetName() { return "iocp"; } int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent) { Overlapped* ovl = (Overlapped*)acceptevent; accept_overlap* ov = (accept_overlap*)ovl->m_params; sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; memcpy(addr, client_address, sizeof(sockaddr_in)); *addrlen = sizeof(sockaddr_in); return ov->socket; } int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent) { Overlapped* ovl = (Overlapped*)acceptevent; accept_overlap* ov = (accept_overlap*)ovl->m_params; sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; memcpy(name, server_address, sizeof(sockaddr_in)); *namelen = sizeof(sockaddr_in); return 0; } int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov) { memcpy(buf, ov->udp_buffer, ov->udp_len); memcpy(from, ov->udp_sockaddr, *fromlen); return ov->udp_len; } EventHandler * IOCPEngine::GetRef(int fd) { map<int, EventHandler*>::iterator itr = m_binding.find(fd); return (itr == m_binding.end()) ? 0 : itr->second; } bool IOCPEngine::HasFd(int fd) { return (GetRef(fd) != 0); } EventHandler * IOCPEngine::GetIntRef(int fd) { if(fd < 0 || fd > MAX_DESCRIPTORS) return 0; return ref[fd]; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "socketengine_iocp.h"
+#include <mswsock.h>
+
+IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
+{
+ /* Create completion port */
+ m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
+
+ /* Null variables out. */
+ CurrentSetSize = 0;
+ EngineHandle = 0;
+ memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
+}
+
+IOCPEngine::~IOCPEngine()
+{
+ CloseHandle(m_completionPort);
+}
+
+bool IOCPEngine::AddFd(EventHandler* eh)
+{
+ int fake_fd = GenerateFd(eh->GetFd());
+ int is_accept = 0;
+ int opt_len = sizeof(int);
+ if(fake_fd < 0)
+ return false;
+
+ /* are we a listen socket? */
+ getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
+
+ /* set up the read event so the socket can actually receive data :P */
+ eh->m_internalFd = fake_fd;
+ eh->m_writeEvent = 0;
+ eh->m_acceptEvent = 0;
+
+ unsigned long completion_key = (ULONG_PTR)eh->m_internalFd;
+ /* assign the socket to the completion port */
+ if(!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
+ return false;
+
+ /* set up binding, increase set size */
+ ref[fake_fd] = eh;
+ ++CurrentSetSize;
+
+ /* setup initial events */
+ if(is_accept)
+ PostAcceptEvent(eh);
+ else
+ PostReadEvent(eh);
+
+ /* log message */
+ ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh);
+
+ /* post a write event if there is data to be written */
+ if(eh->Writeable())
+ WantWrite(eh);
+
+ /* we're all good =) */
+ try
+ {
+ m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
+ }
+ catch (...)
+ {
+ /* Ohshi-, map::insert failed :/ */
+ return false;
+ }
+
+ return true;
+}
+
+bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
+{
+ int fake_fd = eh->m_internalFd;
+ int fd = eh->GetFd();
+
+ if(ref[fake_fd] == 0)
+ return false;
+
+ ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh);
+
+ /* Cancel pending i/o operations. */
+ if (CancelIo((HANDLE)fd) == FALSE)
+ return false;
+
+ /* Free the buffer, and delete the event. */
+ if(eh->m_readEvent != 0)
+ {
+ if(((Overlapped*)eh->m_readEvent)->m_params != 0)
+ delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params);
+
+ delete ((Overlapped*)eh->m_readEvent);
+ }
+
+ if(eh->m_writeEvent != 0)
+ delete ((Overlapped*)eh->m_writeEvent);
+
+ if(eh->m_acceptEvent != 0)
+ {
+ delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params);
+ delete ((Overlapped*)eh->m_acceptEvent);
+ }
+
+ /* Clear binding */
+ ref[fake_fd] = 0;
+ m_binding.erase(eh->GetFd());
+
+ /* decrement set size */
+ --CurrentSetSize;
+
+ /* success */
+ return true;
+}
+
+void IOCPEngine::WantWrite(EventHandler* eh)
+{
+ /* Post event - write begin */
+ if(!eh->m_writeEvent)
+ {
+ ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
+ Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
+ eh->m_writeEvent = (void*)ov;
+ PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
+ }
+}
+
+bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
+{
+ Overlapped * ov = new Overlapped(type, param);
+ ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
+ return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
+}
+
+void IOCPEngine::PostReadEvent(EventHandler * eh)
+{
+ Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
+ DWORD flags = 0;
+ DWORD r_length = 0;
+ WSABUF buf;
+
+ /* by passing a null buffer pointer, we can have this working in the same way as epoll..
+ * its slower, but it saves modifying all network code.
+ */
+ buf.buf = 0;
+ buf.len = 0;
+
+ /* determine socket type. */
+ DWORD sock_type;
+ int sock_len = sizeof(DWORD);
+ if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
+ {
+ /* wtfhax? */
+ PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
+ delete ov;
+ return;
+ }
+ switch(sock_type)
+ {
+ case SOCK_DGRAM: /* UDP Socket */
+ {
+ udp_overlap * uv = new udp_overlap;
+ uv->udp_sockaddr_len = sizeof(sockaddr);
+ buf.buf = (char*)uv->udp_buffer;
+ buf.len = sizeof(uv->udp_buffer);
+ ov->m_params = (unsigned long)uv;
+ if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
+ {
+ int err = WSAGetLastError();
+ if(err != WSA_IO_PENDING)
+ {
+ delete ov;
+ PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
+ return;
+ }
+ }
+ }
+ break;
+
+ case SOCK_STREAM: /* TCP Socket */
+ {
+ if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
+ {
+ if(WSAGetLastError() != WSA_IO_PENDING)
+ {
+ delete ov;
+ PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ printf("unknwon socket type: %u\n", sock_type);
+ return;
+ }
+ break;
+ }
+ eh->m_readEvent = (void*)ov;
+}
+
+int IOCPEngine::DispatchEvents()
+{
+ DWORD len;
+ LPOVERLAPPED overlap;
+ Overlapped * ov;
+ EventHandler * eh;
+ ULONG_PTR intfd;
+ int ret;
+ unsigned long bytes_recv;
+
+ while(GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
+ {
+ // woot, we got an event on a socket :P
+ eh = ref[intfd];
+ ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
+ if(eh == 0) continue;
+ switch(ov->m_event)
+ {
+ case SOCKET_IO_EVENT_WRITE_READY:
+ {
+ eh->m_writeEvent = 0;
+ eh->HandleEvent(EVENT_WRITE, 0);
+ }
+ break;
+
+ case SOCKET_IO_EVENT_READ_READY:
+ {
+ if(ov->m_params)
+ {
+ // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
+ udp_overlap * uv = (udp_overlap*)ov->m_params;
+ uv->udp_len = len;
+ this->udp_ov = uv;
+ eh->m_readEvent = 0;
+ eh->HandleEvent(EVENT_READ, 0);
+ this->udp_ov = 0;
+ delete uv;
+ PostReadEvent(eh);
+ }
+ else
+ {
+ ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
+ eh->m_readEvent = 0;
+ if(ret != 0 || bytes_recv == 0)
+ {
+ /* end of file */
+ PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
+ }
+ else
+ {
+ eh->HandleEvent(EVENT_READ, 0);
+ PostReadEvent(eh);
+ }
+ }
+ }
+ break;
+
+ case SOCKET_IO_EVENT_ACCEPT:
+ {
+ /* this is kinda messy.. :/ */
+ eh->HandleEvent(EVENT_READ, ov->m_params);
+ delete ((accept_overlap*)ov->m_params);
+ eh->m_acceptEvent = 0;
+ PostAcceptEvent(eh);
+ }
+ break;
+
+ case SOCKET_IO_EVENT_ERROR:
+ {
+ eh->HandleEvent(EVENT_ERROR, ov->m_params);
+ }
+ break;
+ }
+
+ delete ov;
+ }
+
+ return 0;
+}
+
+void IOCPEngine::PostAcceptEvent(EventHandler * eh)
+{
+ int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
+ int len = sizeof(sockaddr_in) + 16;
+ DWORD dwBytes;
+ accept_overlap* ao = new accept_overlap;
+ memset(ao->buf, 0, 1024);
+ ao->socket = fd;
+
+ Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
+ eh->m_acceptEvent = (void*)ov;
+
+ if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
+ {
+ int err = WSAGetLastError();
+ if(err != WSA_IO_PENDING)
+ {
+ printf("PostAcceptEvent err: %d\n", err);
+ }
+ }
+}
+
+
+std::string IOCPEngine::GetName()
+{
+ return "iocp";
+}
+
+int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
+{
+ Overlapped* ovl = (Overlapped*)acceptevent;
+ accept_overlap* ov = (accept_overlap*)ovl->m_params;
+
+ sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
+ sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
+
+ memcpy(addr, client_address, sizeof(sockaddr_in));
+ *addrlen = sizeof(sockaddr_in);
+
+ return ov->socket;
+}
+
+int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
+{
+ Overlapped* ovl = (Overlapped*)acceptevent;
+ accept_overlap* ov = (accept_overlap*)ovl->m_params;
+
+ sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
+ sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
+
+ memcpy(name, server_address, sizeof(sockaddr_in));
+ *namelen = sizeof(sockaddr_in);
+
+ return 0;
+}
+
+int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov)
+{
+ memcpy(buf, ov->udp_buffer, ov->udp_len);
+ memcpy(from, ov->udp_sockaddr, *fromlen);
+ return ov->udp_len;
+}
+
+EventHandler * IOCPEngine::GetRef(int fd)
+{
+ map<int, EventHandler*>::iterator itr = m_binding.find(fd);
+ return (itr == m_binding.end()) ? 0 : itr->second;
+}
+
+bool IOCPEngine::HasFd(int fd)
+{
+ return (GetRef(fd) != 0);
+}
+
+EventHandler * IOCPEngine::GetIntRef(int fd)
+{
+ if(fd < 0 || fd > MAX_DESCRIPTORS)
+ return 0;
+ return ref[fd];
+}
+
diff --git a/src/socketengine_kqueue.cpp b/src/socketengine_kqueue.cpp
index de9a78f4e..7fcdae2b6 100644
--- a/src/socketengine_kqueue.cpp
+++ b/src/socketengine_kqueue.cpp
@@ -1 +1,158 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "exitcodes.h" #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> #include "socketengine_kqueue.h" KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance) { EngineHandle = kqueue(); if (EngineHandle == -1) { ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now."); printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); printf("ERROR: this is a fatal error, exiting now."); InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE); } CurrentSetSize = 0; } KQueueEngine::~KQueueEngine() { close(EngineHandle); } bool KQueueEngine::AddFd(EventHandler* eh) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; if (GetRemainingFds() <= 1) return false; if (ref[fd]) return false; ref[fd] = eh; struct kevent ke; EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL); int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); if (i == -1) return false; CurrentSetSize++; ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); return true; } bool KQueueEngine::DelFd(EventHandler* eh, bool force) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; struct kevent ke; EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL); if ((j < 0) && (i < 0) && !force) return false; CurrentSetSize--; ref[fd] = NULL; ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); return true; } void KQueueEngine::WantWrite(EventHandler* eh) { /** When changing an item in a kqueue, there is no 'modify' call * as in epoll. Instead, we add the item again, and this overwrites * the original setting rather than adding it twice. See man kqueue. */ struct kevent ke; EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL); kevent(EngineHandle, &ke, 1, 0, 0, NULL); } int KQueueEngine::GetMaxFds() { return MAX_DESCRIPTORS; } int KQueueEngine::GetRemainingFds() { return MAX_DESCRIPTORS - CurrentSetSize; } int KQueueEngine::DispatchEvents() { ts.tv_nsec = 0; ts.tv_sec = 1; int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts); for (int j = 0; j < i; j++) { if (ke_list[j].flags & EV_EOF) { /* We love you kqueue, oh yes we do *sings*! * kqueue gives us the error number directly in the EOF state! * Unlike smelly epoll and select, where we have to getsockopt * to get the error, this saves us time and cpu cycles. Go BSD! */ if (ref[ke_list[j].ident]) ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags); continue; } if (ke_list[j].flags & EVFILT_WRITE) { /* This looks wrong but its right. As above, theres no modify * call in kqueue. See the manpage. */ struct kevent ke; EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL); kevent(EngineHandle, &ke, 1, 0, 0, NULL); if (ref[ke_list[j].ident]) ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE); } else { if (ref[ke_list[j].ident]) ref[ke_list[j].ident]->HandleEvent(EVENT_READ); } } return i; } std::string KQueueEngine::GetName() { return "kqueue"; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "exitcodes.h"
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include "socketengine_kqueue.h"
+
+
+KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance)
+{
+ EngineHandle = kqueue();
+ if (EngineHandle == -1)
+ {
+ ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
+ ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now.");
+ printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
+ printf("ERROR: this is a fatal error, exiting now.");
+ InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
+ }
+ CurrentSetSize = 0;
+}
+
+KQueueEngine::~KQueueEngine()
+{
+ close(EngineHandle);
+}
+
+bool KQueueEngine::AddFd(EventHandler* eh)
+{
+ int fd = eh->GetFd();
+
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ if (GetRemainingFds() <= 1)
+ return false;
+
+ if (ref[fd])
+ return false;
+
+ ref[fd] = eh;
+
+ struct kevent ke;
+ EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+
+ int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+ if (i == -1)
+ return false;
+
+ CurrentSetSize++;
+
+ ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
+ return true;
+}
+
+bool KQueueEngine::DelFd(EventHandler* eh, bool force)
+{
+ int fd = eh->GetFd();
+
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ struct kevent ke;
+ EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
+
+ int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+
+ EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
+
+ int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+
+ if ((j < 0) && (i < 0) && !force)
+ return false;
+
+ CurrentSetSize--;
+ ref[fd] = NULL;
+
+ ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
+ return true;
+}
+
+void KQueueEngine::WantWrite(EventHandler* eh)
+{
+ /** When changing an item in a kqueue, there is no 'modify' call
+ * as in epoll. Instead, we add the item again, and this overwrites
+ * the original setting rather than adding it twice. See man kqueue.
+ */
+ struct kevent ke;
+ EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
+ kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+}
+
+int KQueueEngine::GetMaxFds()
+{
+ return MAX_DESCRIPTORS;
+}
+
+int KQueueEngine::GetRemainingFds()
+{
+ return MAX_DESCRIPTORS - CurrentSetSize;
+}
+
+int KQueueEngine::DispatchEvents()
+{
+ ts.tv_nsec = 0;
+ ts.tv_sec = 1;
+ int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts);
+ for (int j = 0; j < i; j++)
+ {
+ if (ke_list[j].flags & EV_EOF)
+ {
+ /* We love you kqueue, oh yes we do *sings*!
+ * kqueue gives us the error number directly in the EOF state!
+ * Unlike smelly epoll and select, where we have to getsockopt
+ * to get the error, this saves us time and cpu cycles. Go BSD!
+ */
+ if (ref[ke_list[j].ident])
+ ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
+ continue;
+ }
+ if (ke_list[j].flags & EVFILT_WRITE)
+ {
+ /* This looks wrong but its right. As above, theres no modify
+ * call in kqueue. See the manpage.
+ */
+ struct kevent ke;
+ EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+ if (ref[ke_list[j].ident])
+ ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE);
+ }
+ else
+ {
+ if (ref[ke_list[j].ident])
+ ref[ke_list[j].ident]->HandleEvent(EVENT_READ);
+ }
+ }
+
+ return i;
+}
+
+std::string KQueueEngine::GetName()
+{
+ return "kqueue";
+}
diff --git a/src/socketengine_ports.cpp b/src/socketengine_ports.cpp
index d3704f0a4..869e0a6fb 100644
--- a/src/socketengine_ports.cpp
+++ b/src/socketengine_ports.cpp
@@ -1 +1,129 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "exitcodes.h" #include <port.h> #include "socketengine_ports.h" PortsEngine::PortsEngine(InspIRCd* Instance) : SocketEngine(Instance) { EngineHandle = port_create(); if (EngineHandle == -1) { ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); ServerInstance->Log(SPARSE,"ERROR: This is a fatal error, exiting now."); printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); printf("ERROR: This is a fatal error, exiting now.\n"); InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE); } CurrentSetSize = 0; } PortsEngine::~PortsEngine() { close(EngineHandle); } bool PortsEngine::AddFd(EventHandler* eh) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; if (GetRemainingFds() <= 1) return false; if (ref[fd]) return false; ref[fd] = eh; port_associate(EngineHandle, PORT_SOURCE_FD, fd, eh->Readable() ? POLLRDNORM : POLLWRNORM, eh); ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); CurrentSetSize++; return true; } void PortsEngine::WantWrite(EventHandler* eh) { port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), POLLWRNORM, eh); } bool PortsEngine::DelFd(EventHandler* eh, bool force) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; port_dissociate(EngineHandle, PORT_SOURCE_FD, fd); CurrentSetSize--; ref[fd] = NULL; ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); return true; } int PortsEngine::GetMaxFds() { return MAX_DESCRIPTORS; } int PortsEngine::GetRemainingFds() { return MAX_DESCRIPTORS - CurrentSetSize; } int PortsEngine::DispatchEvents() { struct timespec poll_time; poll_time.tv_sec = 1; poll_time.tv_nsec = 0; unsigned int nget = 1; // used to denote a retrieve request. int i = port_getn(EngineHandle, this->events, MAX_DESCRIPTORS, &nget, &poll_time); // first handle an error condition if (i == -1) return i; for (i = 0; i < nget; i++) { switch (this->events[i].portev_source) { case PORT_SOURCE_FD: { int fd = this->events[i].portev_object; if (ref[fd]) { // reinsert port for next time around port_associate(EngineHandle, PORT_SOURCE_FD, fd, POLLRDNORM, ref[fd]); ref[fd]->HandleEvent((this->events[i].portev_events & POLLRDNORM) ? EVENT_READ : EVENT_WRITE); } } default: break; } } return i; } std::string PortsEngine::GetName() { return "ports"; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "exitcodes.h"
+#include <port.h>
+#include "socketengine_ports.h"
+
+PortsEngine::PortsEngine(InspIRCd* Instance) : SocketEngine(Instance)
+{
+ EngineHandle = port_create();
+
+ if (EngineHandle == -1)
+ {
+ ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
+ ServerInstance->Log(SPARSE,"ERROR: This is a fatal error, exiting now.");
+ printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno));
+ printf("ERROR: This is a fatal error, exiting now.\n");
+ InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
+ }
+ CurrentSetSize = 0;
+}
+
+PortsEngine::~PortsEngine()
+{
+ close(EngineHandle);
+}
+
+bool PortsEngine::AddFd(EventHandler* eh)
+{
+ int fd = eh->GetFd();
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ if (GetRemainingFds() <= 1)
+ return false;
+
+ if (ref[fd])
+ return false;
+
+ ref[fd] = eh;
+ port_associate(EngineHandle, PORT_SOURCE_FD, fd, eh->Readable() ? POLLRDNORM : POLLWRNORM, eh);
+
+ ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
+ CurrentSetSize++;
+ return true;
+}
+
+void PortsEngine::WantWrite(EventHandler* eh)
+{
+ port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), POLLWRNORM, eh);
+}
+
+bool PortsEngine::DelFd(EventHandler* eh, bool force)
+{
+ int fd = eh->GetFd();
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
+
+ CurrentSetSize--;
+ ref[fd] = NULL;
+
+ ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
+ return true;
+}
+
+int PortsEngine::GetMaxFds()
+{
+ return MAX_DESCRIPTORS;
+}
+
+int PortsEngine::GetRemainingFds()
+{
+ return MAX_DESCRIPTORS - CurrentSetSize;
+}
+
+int PortsEngine::DispatchEvents()
+{
+ struct timespec poll_time;
+
+ poll_time.tv_sec = 1;
+ poll_time.tv_nsec = 0;
+
+ unsigned int nget = 1; // used to denote a retrieve request.
+ int i = port_getn(EngineHandle, this->events, MAX_DESCRIPTORS, &nget, &poll_time);
+
+ // first handle an error condition
+ if (i == -1)
+ return i;
+
+ for (i = 0; i < nget; i++)
+ {
+ switch (this->events[i].portev_source)
+ {
+ case PORT_SOURCE_FD:
+ {
+ int fd = this->events[i].portev_object;
+ if (ref[fd])
+ {
+ // reinsert port for next time around
+ port_associate(EngineHandle, PORT_SOURCE_FD, fd, POLLRDNORM, ref[fd]);
+ ref[fd]->HandleEvent((this->events[i].portev_events & POLLRDNORM) ? EVENT_READ : EVENT_WRITE);
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ return i;
+}
+
+std::string PortsEngine::GetName()
+{
+ return "ports";
+}
+
diff --git a/src/socketengine_select.cpp b/src/socketengine_select.cpp
index 73e909193..ef5f2071f 100644
--- a/src/socketengine_select.cpp
+++ b/src/socketengine_select.cpp
@@ -1 +1,167 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <sys/select.h> #include "socketengine_select.h" SelectEngine::SelectEngine(InspIRCd* Instance) : SocketEngine(Instance) { EngineHandle = 0; CurrentSetSize = 0; memset(writeable, 0, sizeof(writeable)); } SelectEngine::~SelectEngine() { } bool SelectEngine::AddFd(EventHandler* eh) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; if (GetRemainingFds() <= 1) return false; fds[fd] = fd; if (ref[fd]) return false; ref[fd] = eh; CurrentSetSize++; ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); return true; } void SelectEngine::WantWrite(EventHandler* eh) { writeable[eh->GetFd()] = true; } bool SelectEngine::DelFd(EventHandler* eh, bool force) { int fd = eh->GetFd(); if ((fd < 0) || (fd > MAX_DESCRIPTORS)) return false; std::map<int,int>::iterator t = fds.find(fd); if (t != fds.end()) fds.erase(t); CurrentSetSize--; ref[fd] = NULL; ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); return true; } int SelectEngine::GetMaxFds() { return FD_SETSIZE; } int SelectEngine::GetRemainingFds() { return FD_SETSIZE - CurrentSetSize; } int SelectEngine::DispatchEvents() { int result = 0; timeval tval; int sresult = 0; EventHandler* ev[MAX_DESCRIPTORS]; socklen_t codesize; int errcode; FD_ZERO(&wfdset); FD_ZERO(&rfdset); FD_ZERO(&errfdset); for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++) { if (ref[a->second]->Readable()) FD_SET (a->second, &rfdset); else FD_SET (a->second, &wfdset); if (writeable[a->second]) FD_SET (a->second, &wfdset); FD_SET (a->second, &errfdset); } tval.tv_sec = 1; tval.tv_usec = 0; sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval); if (sresult > 0) { for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++) { if ((FD_ISSET (a->second, &rfdset)) || (FD_ISSET (a->second, &wfdset)) || FD_ISSET (a->second, &errfdset)) { ev[result++] = ref[a->second]; } } } /** An event handler may remove its own descriptor from the list, therefore it is not * safe to directly iterate over the list and dispatch events there with STL iterators. * Thats a shame because it makes this code slower and more resource intensive, but maybe * the user should stop using select(), as select() smells anyway. */ for (int i = 0; i < result; i++) { if (ev[i]) { if (FD_ISSET (ev[i]->GetFd(), &errfdset)) { if (ev[i]) { if (getsockopt(ev[i]->GetFd(), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) errcode = errno; ev[i]->HandleEvent(EVENT_ERROR, errcode); } continue; } if (ev[i]) { if (writeable[ev[i]->GetFd()]) { if (ev[i]) ev[i]->HandleEvent(EVENT_WRITE); writeable[ev[i]->GetFd()] = false; } else { if (ev[i]) ev[i]->HandleEvent(ev[i]->Readable() ? EVENT_READ : EVENT_WRITE); } } } } return result; } std::string SelectEngine::GetName() { return "select"; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <sys/select.h>
+#include "socketengine_select.h"
+
+
+SelectEngine::SelectEngine(InspIRCd* Instance) : SocketEngine(Instance)
+{
+ EngineHandle = 0;
+ CurrentSetSize = 0;
+ memset(writeable, 0, sizeof(writeable));
+}
+
+SelectEngine::~SelectEngine()
+{
+}
+
+bool SelectEngine::AddFd(EventHandler* eh)
+{
+ int fd = eh->GetFd();
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ if (GetRemainingFds() <= 1)
+ return false;
+
+ fds[fd] = fd;
+
+ if (ref[fd])
+ return false;
+
+ ref[fd] = eh;
+
+ CurrentSetSize++;
+
+ ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
+ return true;
+}
+
+void SelectEngine::WantWrite(EventHandler* eh)
+{
+ writeable[eh->GetFd()] = true;
+}
+
+bool SelectEngine::DelFd(EventHandler* eh, bool force)
+{
+ int fd = eh->GetFd();
+
+ if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ return false;
+
+ std::map<int,int>::iterator t = fds.find(fd);
+ if (t != fds.end())
+ fds.erase(t);
+
+ CurrentSetSize--;
+ ref[fd] = NULL;
+
+ ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
+ return true;
+}
+
+int SelectEngine::GetMaxFds()
+{
+ return FD_SETSIZE;
+}
+
+int SelectEngine::GetRemainingFds()
+{
+ return FD_SETSIZE - CurrentSetSize;
+}
+
+int SelectEngine::DispatchEvents()
+{
+ int result = 0;
+ timeval tval;
+ int sresult = 0;
+ EventHandler* ev[MAX_DESCRIPTORS];
+ socklen_t codesize;
+ int errcode;
+
+ FD_ZERO(&wfdset);
+ FD_ZERO(&rfdset);
+ FD_ZERO(&errfdset);
+
+ for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++)
+ {
+ if (ref[a->second]->Readable())
+ FD_SET (a->second, &rfdset);
+ else
+ FD_SET (a->second, &wfdset);
+ if (writeable[a->second])
+ FD_SET (a->second, &wfdset);
+
+ FD_SET (a->second, &errfdset);
+ }
+ tval.tv_sec = 1;
+ tval.tv_usec = 0;
+ sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
+ if (sresult > 0)
+ {
+ for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++)
+ {
+ if ((FD_ISSET (a->second, &rfdset)) || (FD_ISSET (a->second, &wfdset)) || FD_ISSET (a->second, &errfdset))
+ {
+ ev[result++] = ref[a->second];
+ }
+ }
+ }
+
+ /** An event handler may remove its own descriptor from the list, therefore it is not
+ * safe to directly iterate over the list and dispatch events there with STL iterators.
+ * Thats a shame because it makes this code slower and more resource intensive, but maybe
+ * the user should stop using select(), as select() smells anyway.
+ */
+ for (int i = 0; i < result; i++)
+ {
+ if (ev[i])
+ {
+ if (FD_ISSET (ev[i]->GetFd(), &errfdset))
+ {
+ if (ev[i])
+ {
+ if (getsockopt(ev[i]->GetFd(), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+ errcode = errno;
+
+ ev[i]->HandleEvent(EVENT_ERROR, errcode);
+ }
+ continue;
+ }
+ if (ev[i])
+ {
+ if (writeable[ev[i]->GetFd()])
+ {
+ if (ev[i])
+ ev[i]->HandleEvent(EVENT_WRITE);
+ writeable[ev[i]->GetFd()] = false;
+
+ }
+ else
+ {
+ if (ev[i])
+ ev[i]->HandleEvent(ev[i]->Readable() ? EVENT_READ : EVENT_WRITE);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+std::string SelectEngine::GetName()
+{
+ return "select";
+}
diff --git a/src/timer.cpp b/src/timer.cpp
index 90cf8bd95..c04107502 100644
--- a/src/timer.cpp
+++ b/src/timer.cpp
@@ -1 +1,135 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "timer.h" TimerManager::TimerManager(InspIRCd* Instance) : CantDeleteHere(false), ServerInstance(Instance) { } void TimerManager::TickTimers(time_t TIME) { this->CantDeleteHere = true; timerlist::iterator found = Timers.find(TIME); if (found != Timers.end()) { timergroup* x = found->second; /* There are pending timers to trigger. * WARNING: Timers may delete themselves from within * their own Tick methods! see the comment below in * the DelTimer method. */ for (timergroup::iterator y = x->begin(); y != x->end(); y++) { InspTimer* n = *y; n->Tick(TIME); if (n->GetRepeat()) { AddTimer(n, n->GetSecs()); } else { DELETE(n); } } Timers.erase(found); DELETE(x); } this->CantDeleteHere = false; } void TimerManager::DelTimer(InspTimer* T) { if (this->CantDeleteHere) { /* If a developer tries to delete a timer from within its own Tick method, * then chances are this is just going to totally fuck over the timergroup * and timerlist iterators and cause a crash. Thanks to peavey and Bricker * for noticing this bug. * If we're within the tick loop when the DelTimer is called (signified * by the var 'CantDeleteHere') then we simply return for non-repeating * timers, and cancel the repeat on repeating timers. We can do this because * we know that the timer tick loop will safely delete the timer for us * anyway and therefore we avoid stack corruption. */ if (T->GetRepeat()) T->CancelRepeat(); else return; } timerlist::iterator found = Timers.find(T->GetTimer()); if (found != Timers.end()) { timergroup* x = found->second; for (timergroup::iterator y = x->begin(); y != x->end(); y++) { InspTimer* n = *y; if (n == T) { DELETE(n); x->erase(y); if (!x->size()) { Timers.erase(found); DELETE(x); } return; } } } } /** Because some muppets may do odd things, and their ircd may lock up due * to crappy 3rd party modules, or they may change their system time a bit, * this accounts for shifts of up to 120 secs by looking behind for missed * timers and executing them. This is only executed once every 5 secs. * If you move your clock BACK, and your timers move further ahead as a result, * then tough titty you'll just have to wait. */ void TimerManager::TickMissedTimers(time_t TIME) { for (time_t n = TIME-1; n > TIME-120; n--) this->TickTimers(TIME); } void TimerManager::AddTimer(InspTimer* T, long secs_from_now) { timergroup* x = NULL; int time_to_trigger = 0; if (!secs_from_now) time_to_trigger = T->GetTimer(); else time_to_trigger = secs_from_now + ServerInstance->Time(); timerlist::iterator found = Timers.find(time_to_trigger); if (found != Timers.end()) { x = found->second; } else { x = new timergroup; Timers[time_to_trigger] = x; } x->push_back(T); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "timer.h"
+
+TimerManager::TimerManager(InspIRCd* Instance) : CantDeleteHere(false), ServerInstance(Instance)
+{
+}
+
+void TimerManager::TickTimers(time_t TIME)
+{
+ this->CantDeleteHere = true;
+ timerlist::iterator found = Timers.find(TIME);
+
+ if (found != Timers.end())
+ {
+ timergroup* x = found->second;
+ /* There are pending timers to trigger.
+ * WARNING: Timers may delete themselves from within
+ * their own Tick methods! see the comment below in
+ * the DelTimer method.
+ */
+ for (timergroup::iterator y = x->begin(); y != x->end(); y++)
+ {
+ InspTimer* n = *y;
+ n->Tick(TIME);
+ if (n->GetRepeat())
+ {
+ AddTimer(n, n->GetSecs());
+ }
+ else
+ {
+ DELETE(n);
+ }
+ }
+
+ Timers.erase(found);
+ DELETE(x);
+ }
+
+ this->CantDeleteHere = false;
+}
+
+void TimerManager::DelTimer(InspTimer* T)
+{
+ if (this->CantDeleteHere)
+ {
+ /* If a developer tries to delete a timer from within its own Tick method,
+ * then chances are this is just going to totally fuck over the timergroup
+ * and timerlist iterators and cause a crash. Thanks to peavey and Bricker
+ * for noticing this bug.
+ * If we're within the tick loop when the DelTimer is called (signified
+ * by the var 'CantDeleteHere') then we simply return for non-repeating
+ * timers, and cancel the repeat on repeating timers. We can do this because
+ * we know that the timer tick loop will safely delete the timer for us
+ * anyway and therefore we avoid stack corruption.
+ */
+ if (T->GetRepeat())
+ T->CancelRepeat();
+ else
+ return;
+ }
+
+ timerlist::iterator found = Timers.find(T->GetTimer());
+
+ if (found != Timers.end())
+ {
+ timergroup* x = found->second;
+ for (timergroup::iterator y = x->begin(); y != x->end(); y++)
+ {
+ InspTimer* n = *y;
+ if (n == T)
+ {
+ DELETE(n);
+ x->erase(y);
+ if (!x->size())
+ {
+ Timers.erase(found);
+ DELETE(x);
+ }
+ return;
+ }
+ }
+ }
+}
+
+/** Because some muppets may do odd things, and their ircd may lock up due
+ * to crappy 3rd party modules, or they may change their system time a bit,
+ * this accounts for shifts of up to 120 secs by looking behind for missed
+ * timers and executing them. This is only executed once every 5 secs.
+ * If you move your clock BACK, and your timers move further ahead as a result,
+ * then tough titty you'll just have to wait.
+ */
+void TimerManager::TickMissedTimers(time_t TIME)
+{
+ for (time_t n = TIME-1; n > TIME-120; n--)
+ this->TickTimers(TIME);
+}
+
+void TimerManager::AddTimer(InspTimer* T, long secs_from_now)
+{
+ timergroup* x = NULL;
+
+ int time_to_trigger = 0;
+ if (!secs_from_now)
+ time_to_trigger = T->GetTimer();
+ else
+ time_to_trigger = secs_from_now + ServerInstance->Time();
+
+ timerlist::iterator found = Timers.find(time_to_trigger);
+
+ if (found != Timers.end())
+ {
+ x = found->second;
+ }
+ else
+ {
+ x = new timergroup;
+ Timers[time_to_trigger] = x;
+ }
+
+ x->push_back(T);
+}
+
diff --git a/src/userprocess.cpp b/src/userprocess.cpp
index c2b2f0b83..b27844fb6 100644
--- a/src/userprocess.cpp
+++ b/src/userprocess.cpp
@@ -1 +1,305 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "xline.h" #include "socketengine.h" #include "command_parse.h" void InspIRCd::FloodQuitUser(userrec* current) { this->Log(DEFAULT,"Excess flood from: %s@%s", current->ident, current->host); this->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s", current->registered == REG_ALL ? current->nick : "", current->registered == REG_ALL ? "!" : "", current->ident, current->host); current->SetWriteError("Excess flood"); if (current->registered != REG_ALL) { XLines->add_zline(120,this->Config->ServerName,"Flood from unregistered connection",current->GetIPString()); XLines->apply_lines(APPLY_ZLINES); } } void InspIRCd::ProcessUser(userrec* cu) { int result = EAGAIN; if (cu->GetFd() == FD_MAGIC_NUMBER) return; if (this->Config->GetIOHook(cu->GetPort())) { int result2 = 0; int MOD_RESULT = 0; try { MOD_RESULT = this->Config->GetIOHook(cu->GetPort())->OnRawSocketRead(cu->GetFd(),ReadBuffer,sizeof(ReadBuffer),result2); } catch (CoreException& modexcept) { this->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } if (MOD_RESULT < 0) { result = -EAGAIN; } else { result = result2; } } else { result = cu->ReadData(ReadBuffer, sizeof(ReadBuffer)); } if ((result) && (result != -EAGAIN)) { userrec *current; int currfd; int floodlines = 0; this->stats->statsRecv += result; /* * perform a check on the raw buffer as an array (not a string!) to remove * character 0 which is illegal in the RFC - replace them with spaces. * XXX - no garauntee there's not \0's in the middle of the data, * and no reason for it to be terminated either. -- Om */ for (int checker = 0; checker < result; checker++) { if (ReadBuffer[checker] == 0) ReadBuffer[checker] = ' '; } if (result > 0) ReadBuffer[result] = '\0'; current = cu; currfd = current->GetFd(); // add the data to the users buffer if (result > 0) { if (!current->AddBuffer(ReadBuffer)) { // AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good. if (current->registered == REG_ALL) { // Make sure they arn't flooding long lines. if (TIME > current->reset_due) { current->reset_due = TIME + current->threshold; current->lines_in = 0; } current->lines_in++; if (current->flood && current->lines_in > current->flood) FloodQuitUser(current); else { current->WriteServ("NOTICE %s :Your previous line was too long and was not delivered (Over %d chars) Please shorten it.", current->nick, MAXBUF-2); current->recvq.clear(); } } else FloodQuitUser(current); return; } // while there are complete lines to process... while (current->BufferIsReady()) { if (TIME > current->reset_due) { current->reset_due = TIME + current->threshold; current->lines_in = 0; } if (++current->lines_in > current->flood && current->flood) { FloodQuitUser(current); return; } if ((++floodlines > current->flood) && (current->flood != 0)) { FloodQuitUser(current); return; } // use GetBuffer to copy single lines into the sanitized string std::string single_line = current->GetBuffer(); current->bytes_in += single_line.length(); current->cmds_in++; if (single_line.length() > MAXBUF - 2) /* MAXBUF is 514 to allow for neccessary line terminators */ single_line.resize(MAXBUF - 2); /* So to trim to 512 here, we use MAXBUF - 2 */ EventHandler* old_comp = this->SE->GetRef(currfd); this->Parser->ProcessBuffer(single_line,current); /* * look for the user's record in case it's changed... if theyve quit, * we cant do anything more with their buffer, so bail. * there used to be an ugly, slow loop here. Now we have a reference * table, life is much easier (and FASTER) */ EventHandler* new_comp = this->SE->GetRef(currfd); if (new_comp != old_comp) return; } return; } if ((result == -1) && (errno != EAGAIN) && (errno != EINTR)) { cu->SetWriteError(strerror(errno)); return; } } // result EAGAIN means nothing read else if ((result == EAGAIN) || (result == -EAGAIN)) { /* do nothing */ } else if (result == 0) { cu->SetWriteError("Connection closed"); return; } } /** * 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(time_t TIME) { /* Is it time yet? */ if (TIME < next_call) return; else { /* Time we actually need to call this again */ const time_t DUMMY_VALUE = 32768; next_call = TIME + DUMMY_VALUE; /* XXX: IT IS NOT SAFE TO USE AN ITERATOR HERE. DON'T EVEN THINK ABOUT IT. */ for (unsigned long count2 = 0; count2 != this->local_users.size(); count2++) { if (count2 >= this->local_users.size()) break; userrec* curr = this->local_users[count2]; if (curr) { /* * registration timeout -- didnt send USER/NICK/HOST * in the time specified in their connection class. */ if ((TIME > curr->timeout) && (curr->registered != REG_ALL)) { curr->muted = true; GlobalCulls.AddItem(curr,"Registration timeout"); continue; } else { if ((curr->registered != REG_ALL) && (next_call > (time_t)curr->timeout)) next_call = curr->timeout; } /* * user has signed on with USER/NICK/PASS, and dns has completed, all the modules * say this user is ok to proceed, fully connect them. */ bool ready = AllModulesReportReady(curr); if ((TIME > curr->signon) && (curr->registered == REG_NICKUSER) && (ready)) { if (!curr->dns_done) { curr->WriteServ("NOTICE Auth :*** Could not resolve your hostname: Request timed out; using your IP address (%s) instead.", curr->GetIPString()); curr->dns_done = true; } this->stats->statsDnsBad++; curr->FullConnect(); continue; } else { if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon)) next_call = curr->signon; } if ((curr->dns_done) && (curr->registered == REG_NICKUSER) && (ready)) { curr->FullConnect(); continue; } else { if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon + this->Config->dns_timeout)) next_call = curr->signon + this->Config->dns_timeout; } // It's time to PING this user. Send them a ping. if ((TIME > curr->nping) && (curr->registered == REG_ALL)) { // This user didn't answer the last ping, remove them if (!curr->lastping) { /* Everybody loves boobies. */ time_t time = this->Time(false) - (curr->nping - curr->pingmax); char message[MAXBUF]; snprintf(message, MAXBUF, "Ping timeout: %ld second%s", time, time > 1 ? "s" : ""); curr->muted = true; GlobalCulls.AddItem(curr, message); curr->lastping = 1; curr->nping = TIME+curr->pingmax; continue; } curr->Write("PING :%s",this->Config->ServerName); curr->lastping = 0; curr->nping = TIME+curr->pingmax; } else { if ((curr->registered == REG_ALL) && (next_call > curr->nping)) next_call = curr->nping; } } } /* If theres nothing to do, trigger in the next second, something might come up */ time_t delta = next_call - TIME; if (delta == DUMMY_VALUE) { next_call = TIME + 1; delta = 1; } } } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "socketengine.h"
+#include "command_parse.h"
+
+void InspIRCd::FloodQuitUser(userrec* current)
+{
+ this->Log(DEFAULT,"Excess flood from: %s@%s", current->ident, current->host);
+ this->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s",
+ current->registered == REG_ALL ? current->nick : "",
+ current->registered == REG_ALL ? "!" : "", current->ident, current->host);
+ current->SetWriteError("Excess flood");
+ if (current->registered != REG_ALL)
+ {
+ XLines->add_zline(120,this->Config->ServerName,"Flood from unregistered connection",current->GetIPString());
+ XLines->apply_lines(APPLY_ZLINES);
+ }
+}
+
+void InspIRCd::ProcessUser(userrec* cu)
+{
+ int result = EAGAIN;
+
+ if (cu->GetFd() == FD_MAGIC_NUMBER)
+ return;
+
+ if (this->Config->GetIOHook(cu->GetPort()))
+ {
+ int result2 = 0;
+ int MOD_RESULT = 0;
+
+ try
+ {
+ MOD_RESULT = this->Config->GetIOHook(cu->GetPort())->OnRawSocketRead(cu->GetFd(),ReadBuffer,sizeof(ReadBuffer),result2);
+ }
+ catch (CoreException& modexcept)
+ {
+ this->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+
+ if (MOD_RESULT < 0)
+ {
+ result = -EAGAIN;
+ }
+ else
+ {
+ result = result2;
+ }
+ }
+ else
+ {
+ result = cu->ReadData(ReadBuffer, sizeof(ReadBuffer));
+ }
+
+ if ((result) && (result != -EAGAIN))
+ {
+ userrec *current;
+ int currfd;
+ int floodlines = 0;
+
+ this->stats->statsRecv += result;
+ /*
+ * perform a check on the raw buffer as an array (not a string!) to remove
+ * character 0 which is illegal in the RFC - replace them with spaces.
+ * XXX - no garauntee there's not \0's in the middle of the data,
+ * and no reason for it to be terminated either. -- Om
+ */
+
+ for (int checker = 0; checker < result; checker++)
+ {
+ if (ReadBuffer[checker] == 0)
+ ReadBuffer[checker] = ' ';
+ }
+
+ if (result > 0)
+ ReadBuffer[result] = '\0';
+
+ current = cu;
+ currfd = current->GetFd();
+
+ // add the data to the users buffer
+ if (result > 0)
+ {
+ if (!current->AddBuffer(ReadBuffer))
+ {
+ // AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good.
+ if (current->registered == REG_ALL)
+ {
+ // Make sure they arn't flooding long lines.
+ if (TIME > current->reset_due)
+ {
+ current->reset_due = TIME + current->threshold;
+ current->lines_in = 0;
+ }
+
+ current->lines_in++;
+
+ if (current->flood && current->lines_in > current->flood)
+ FloodQuitUser(current);
+ else
+ {
+ current->WriteServ("NOTICE %s :Your previous line was too long and was not delivered (Over %d chars) Please shorten it.", current->nick, MAXBUF-2);
+ current->recvq.clear();
+ }
+ }
+ else
+ FloodQuitUser(current);
+
+ return;
+ }
+
+ // while there are complete lines to process...
+ while (current->BufferIsReady())
+ {
+ if (TIME > current->reset_due)
+ {
+ current->reset_due = TIME + current->threshold;
+ current->lines_in = 0;
+ }
+
+ if (++current->lines_in > current->flood && current->flood)
+ {
+ FloodQuitUser(current);
+ return;
+ }
+
+ if ((++floodlines > current->flood) && (current->flood != 0))
+ {
+ FloodQuitUser(current);
+ return;
+ }
+
+ // use GetBuffer to copy single lines into the sanitized string
+ std::string single_line = current->GetBuffer();
+ current->bytes_in += single_line.length();
+ current->cmds_in++;
+ if (single_line.length() > MAXBUF - 2) /* MAXBUF is 514 to allow for neccessary line terminators */
+ single_line.resize(MAXBUF - 2); /* So to trim to 512 here, we use MAXBUF - 2 */
+
+ EventHandler* old_comp = this->SE->GetRef(currfd);
+
+ this->Parser->ProcessBuffer(single_line,current);
+ /*
+ * look for the user's record in case it's changed... if theyve quit,
+ * we cant do anything more with their buffer, so bail.
+ * there used to be an ugly, slow loop here. Now we have a reference
+ * table, life is much easier (and FASTER)
+ */
+ EventHandler* new_comp = this->SE->GetRef(currfd);
+
+ if (new_comp != old_comp)
+ return;
+ }
+
+ return;
+ }
+
+ if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
+ {
+ cu->SetWriteError(strerror(errno));
+ return;
+ }
+ }
+
+ // result EAGAIN means nothing read
+ else if ((result == EAGAIN) || (result == -EAGAIN))
+ {
+ /* do nothing */
+ }
+ else if (result == 0)
+ {
+ cu->SetWriteError("Connection closed");
+ return;
+ }
+}
+
+/**
+ * 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(time_t TIME)
+{
+ /* Is it time yet? */
+ if (TIME < next_call)
+ return;
+ else
+ {
+ /* Time we actually need to call this again */
+ const time_t DUMMY_VALUE = 32768;
+ next_call = TIME + DUMMY_VALUE;
+
+ /* XXX: IT IS NOT SAFE TO USE AN ITERATOR HERE. DON'T EVEN THINK ABOUT IT. */
+ for (unsigned long count2 = 0; count2 != this->local_users.size(); count2++)
+ {
+ if (count2 >= this->local_users.size())
+ break;
+
+ userrec* curr = this->local_users[count2];
+
+ if (curr)
+ {
+ /*
+ * registration timeout -- didnt send USER/NICK/HOST
+ * in the time specified in their connection class.
+ */
+ if ((TIME > curr->timeout) && (curr->registered != REG_ALL))
+ {
+ curr->muted = true;
+ GlobalCulls.AddItem(curr,"Registration timeout");
+ continue;
+ }
+ else
+ {
+ if ((curr->registered != REG_ALL) && (next_call > (time_t)curr->timeout))
+ next_call = curr->timeout;
+ }
+
+ /*
+ * user has signed on with USER/NICK/PASS, and dns has completed, all the modules
+ * say this user is ok to proceed, fully connect them.
+ */
+ bool ready = AllModulesReportReady(curr);
+ if ((TIME > curr->signon) && (curr->registered == REG_NICKUSER) && (ready))
+ {
+ if (!curr->dns_done)
+ {
+ curr->WriteServ("NOTICE Auth :*** Could not resolve your hostname: Request timed out; using your IP address (%s) instead.", curr->GetIPString());
+ curr->dns_done = true;
+ }
+ this->stats->statsDnsBad++;
+ curr->FullConnect();
+ continue;
+ }
+ else
+ {
+ if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon))
+ next_call = curr->signon;
+ }
+
+ if ((curr->dns_done) && (curr->registered == REG_NICKUSER) && (ready))
+ {
+ curr->FullConnect();
+ continue;
+ }
+ else
+ {
+ if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon + this->Config->dns_timeout))
+ next_call = curr->signon + this->Config->dns_timeout;
+ }
+
+ // It's time to PING this user. Send them a ping.
+ if ((TIME > curr->nping) && (curr->registered == REG_ALL))
+ {
+ // This user didn't answer the last ping, remove them
+ if (!curr->lastping)
+ {
+ /* Everybody loves boobies. */
+ time_t time = this->Time(false) - (curr->nping - curr->pingmax);
+ char message[MAXBUF];
+ snprintf(message, MAXBUF, "Ping timeout: %ld second%s", time, time > 1 ? "s" : "");
+ curr->muted = true;
+ GlobalCulls.AddItem(curr, message);
+ curr->lastping = 1;
+ curr->nping = TIME+curr->pingmax;
+ continue;
+ }
+ curr->Write("PING :%s",this->Config->ServerName);
+ curr->lastping = 0;
+ curr->nping = TIME+curr->pingmax;
+ }
+ else
+ {
+ if ((curr->registered == REG_ALL) && (next_call > curr->nping))
+ next_call = curr->nping;
+ }
+ }
+ }
+
+ /* If theres nothing to do, trigger in the next second, something might come up */
+ time_t delta = next_call - TIME;
+ if (delta == DUMMY_VALUE)
+ {
+ next_call = TIME + 1;
+ delta = 1;
+ }
+ }
+}
diff --git a/src/users.cpp b/src/users.cpp
index 250586f1d..7da7d6b09 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -1 +1,2007 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "configreader.h" #include "channels.h" #include "users.h" #include <stdarg.h> #include "socketengine.h" #include "wildcard.h" #include "xline.h" #include "commands/cmd_whowas.h" static unsigned long already_sent[MAX_DESCRIPTORS] = {0}; /* XXX: Used for speeding up WriteCommon operations */ unsigned long uniq_id = 0; bool InitTypes(ServerConfig* conf, const char* tag) { if (conf->opertypes.size()) { for (opertype_t::iterator n = conf->opertypes.begin(); n != conf->opertypes.end(); n++) { if (n->second) delete[] n->second; } } conf->opertypes.clear(); return true; } bool InitClasses(ServerConfig* conf, const char* tag) { if (conf->operclass.size()) { for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++) { if (n->second) delete[] n->second; } } conf->operclass.clear(); return true; } bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* TypeName = values[0].GetString(); const char* Classes = values[1].GetString(); conf->opertypes[TypeName] = strnewdup(Classes); return true; } bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* ClassName = values[0].GetString(); const char* CommandList = values[1].GetString(); conf->operclass[ClassName] = strnewdup(CommandList); return true; } bool DoneClassesAndTypes(ServerConfig* conf, const char* tag) { return true; } std::string userrec::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->IsEnabled(d)) { if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding)) { if ((oldadding != adding) || (!output.length())) output += (adding ? '+' : '-'); this->SetNoticeMask(d, adding); output += d; } } oldadding = adding; } break; default: if ((*c >= 'A') && (*c <= 'z') && (ServerInstance->SNO->IsEnabled(*c))) { if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding)) { if ((oldadding != adding) || (!output.length())) output += (adding ? '+' : '-'); this->SetNoticeMask(*c, adding); output += *c; } } oldadding = adding; break; } *c++; } return output; } void userrec::StartDNSLookup() { try { bool cached; const char* ip = this->GetIPString(); /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */ if (!strncmp(ip, "0::ffff:", 8)) res_reverse = new UserResolver(this->ServerInstance, this, ip + 8, DNS_QUERY_PTR4, cached); else res_reverse = new UserResolver(this->ServerInstance, this, ip, this->GetProtocolFamily() == AF_INET ? DNS_QUERY_PTR4 : DNS_QUERY_PTR6, cached); this->ServerInstance->AddResolver(res_reverse, cached); } catch (CoreException& e) { ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); } } UserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_resolve, QueryType qt, bool &cache) : Resolver(Instance, to_resolve, qt, cache), bound_user(user) { this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA); this->bound_fd = user->GetFd(); } void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) { if ((!this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)) { this->bound_user->stored_host = result; try { /* Check we didnt time out */ if (this->bound_user->registered != REG_ALL) { bool cached; #ifdef IPV6 if (this->bound_user->GetProtocolFamily() == AF_INET6) { /* IPV6 forward lookup (with possibility of 4in6) */ const char* ip = this->bound_user->GetIPString(); bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, (!strncmp(ip, "0::ffff:", 8) ? DNS_QUERY_A : DNS_QUERY_AAAA), cached); } else /* IPV4 lookup (mixed protocol mode) */ #endif /* IPV4 lookup (ipv4 only mode) */ bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached); this->ServerInstance->AddResolver(bound_user->res_forward, cached); } } catch (CoreException& e) { ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); } } else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)) { /* Both lookups completed */ std::string result2("0::ffff:"); result2.append(result); if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2) { std::string hostname = this->bound_user->stored_host; if (hostname.length() < 65) { /* Check we didnt time out */ if ((this->bound_user->registered != REG_ALL) && (!this->bound_user->dns_done)) { /* Hostnames starting with : are not a good thing (tm) */ if (*(hostname.c_str()) == ':') hostname.insert(0, "0"); this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : "")); this->bound_user->dns_done = true; strlcpy(this->bound_user->dhost, hostname.c_str(),64); strlcpy(this->bound_user->host, hostname.c_str(),64); /* Invalidate cache */ this->bound_user->InvalidateCache(); } } else { if (!this->bound_user->dns_done) { this->bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", this->bound_user->GetIPString()); this->bound_user->dns_done = true; } } } else { if (!this->bound_user->dns_done) { this->bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", this->bound_user->GetIPString()); this->bound_user->dns_done = true; } } } } void UserResolver::OnError(ResolverError e, const std::string &errormessage) { if (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user) { /* Since dns timeout is implemented outside of the resolver, this was a race condition that could result in this message being sent *after* * the user was fully connected. This check fixes that issue - Special */ if (!this->bound_user->dns_done) { /* Error message here */ this->bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), this->bound_user->GetIPString()); this->bound_user->dns_done = true; } } } bool userrec::IsNoticeMaskSet(unsigned char sm) { return (snomasks[sm-65]); } void userrec::SetNoticeMask(unsigned char sm, bool value) { snomasks[sm-65] = value; } const char* userrec::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 userrec::IsModeSet(unsigned char m) { return (modes[m-65]); } void userrec::SetMode(unsigned char m, bool value) { modes[m-65] = value; } const char* userrec::FormatModes() { static char data[MAXBUF]; int offset = 0; for (int n = 0; n < 64; n++) { if (modes[n]) data[offset++] = n+65; } data[offset] = 0; return data; } void userrec::DecrementModes() { for (int n = 0; n < 64; n++) { if (modes[n]) { ModeHandler* mh = ServerInstance->Modes->FindMode(n+65, MODETYPE_USER); if (mh) mh->ChangeCount(-1); } } } userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance) { // the PROPER way to do it, AVOID bzero at *ALL* costs *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0; server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName); reset_due = ServerInstance->Time(); age = ServerInstance->Time(true); lines_in = lastping = signon = idle_lastmsg = nping = registered = 0; ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0; muted = exempt = haspassed = dns_done = false; fd = -1; recvq.clear(); sendq.clear(); WriteError.clear(); res_forward = res_reverse = NULL; Visibility = NULL; ip = NULL; chans.clear(); invites.clear(); memset(modes,0,sizeof(modes)); memset(snomasks,0,sizeof(snomasks)); /* Invalidate cache */ operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL; } void userrec::RemoveCloneCounts() { clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString()); if (x != ServerInstance->local_clones.end()) { x->second--; if (!x->second) { ServerInstance->local_clones.erase(x); } } clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString()); if (y != ServerInstance->global_clones.end()) { y->second--; if (!y->second) { ServerInstance->global_clones.erase(y); } } } userrec::~userrec() { this->InvalidateCache(); this->DecrementModes(); if (operquit) free(operquit); if (ip) { this->RemoveCloneCounts(); if (this->GetProtocolFamily() == AF_INET) { delete (sockaddr_in*)ip; } #ifdef SUPPORT_IP6LINKS else { delete (sockaddr_in6*)ip; } #endif } } char* userrec::MakeHost() { if (this->cached_makehost) return this->cached_makehost; char nhost[MAXBUF]; /* This is much faster than snprintf */ char* t = nhost; for(char* n = ident; *n; n++) *t++ = *n; *t++ = '@'; for(char* n = host; *n; n++) *t++ = *n; *t = 0; this->cached_makehost = strdup(nhost); return this->cached_makehost; } char* userrec::MakeHostIP() { if (this->cached_hostip) return this->cached_hostip; char ihost[MAXBUF]; /* This is much faster than snprintf */ char* t = ihost; for(char* n = ident; *n; n++) *t++ = *n; *t++ = '@'; for(const char* n = this->GetIPString(); *n; n++) *t++ = *n; *t = 0; this->cached_hostip = strdup(ihost); return this->cached_hostip; } void userrec::CloseSocket() { shutdown(this->fd,2); close(this->fd); } char* userrec::GetFullHost() { if (this->cached_fullhost) return this->cached_fullhost; char result[MAXBUF]; char* t = result; for(char* n = nick; *n; n++) *t++ = *n; *t++ = '!'; for(char* n = ident; *n; n++) *t++ = *n; *t++ = '@'; for(char* n = dhost; *n; n++) *t++ = *n; *t = 0; this->cached_fullhost = strdup(result); return this->cached_fullhost; } char* userrec::MakeWildHost() { static char nresult[MAXBUF]; char* t = nresult; *t++ = '*'; *t++ = '!'; *t++ = '*'; *t++ = '@'; for(char* n = dhost; *n; n++) *t++ = *n; *t = 0; return nresult; } int userrec::ReadData(void* buffer, size_t size) { if (IS_LOCAL(this)) { #ifndef WIN32 return read(this->fd, buffer, size); #else return recv(this->fd, (char*)buffer, size, 0); #endif } else return 0; } char* userrec::GetFullRealHost() { if (this->cached_fullrealhost) return this->cached_fullrealhost; char fresult[MAXBUF]; char* t = fresult; for(char* n = nick; *n; n++) *t++ = *n; *t++ = '!'; for(char* n = ident; *n; n++) *t++ = *n; *t++ = '@'; for(char* n = host; *n; n++) *t++ = *n; *t = 0; this->cached_fullrealhost = strdup(fresult); return this->cached_fullrealhost; } bool userrec::IsInvited(const irc::string &channel) { for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++) { if (channel == *i) { return true; } } return false; } InvitedList* userrec::GetInviteList() { return &invites; } void userrec::InviteTo(const irc::string &channel) { invites.push_back(channel); } void userrec::RemoveInvite(const irc::string &channel) { for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++) { if (channel == *i) { invites.erase(i); return; } } } bool userrec::HasPermission(const std::string &command) { char* mycmd; char* savept; char* savept2; /* * users on remote servers can completely bypass all permissions based checks. * This prevents desyncs when one server has different type/class tags to another. * That having been said, this does open things up to the possibility of source changes * allowing remote kills, etc - but if they have access to the src, they most likely have * access to the conf - so it's an end to a means either way. */ if (!IS_LOCAL(this)) return true; // are they even an oper at all? if (IS_OPER(this)) { opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper); if (iter_opertype != ServerInstance->Config->opertypes.end()) { char* Classes = strdup(iter_opertype->second); char* myclass = strtok_r(Classes," ",&savept); while (myclass) { operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass); if (iter_operclass != ServerInstance->Config->operclass.end()) { char* CommandList = strdup(iter_operclass->second); mycmd = strtok_r(CommandList," ",&savept2); while (mycmd) { if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*')) { free(Classes); free(CommandList); return true; } mycmd = strtok_r(NULL," ",&savept2); } free(CommandList); } myclass = strtok_r(NULL," ",&savept); } free(Classes); } } return false; } /** NOTE: We cannot pass a const reference to this method. * The string is changed by the workings of the method, * so that if we pass const ref, we end up copying it to * something we can change anyway. Makes sense to just let * the compiler do that copy for us. */ bool userrec::AddBuffer(std::string a) { try { std::string::size_type i = a.rfind('\r'); while (i != std::string::npos) { a.erase(i, 1); i = a.rfind('\r'); } if (a.length()) recvq.append(a); if (recvq.length() > (unsigned)this->recvqmax) { this->SetWriteError("RecvQ exceeded"); ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax); return false; } return true; } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::AddBuffer()"); return false; } } bool userrec::BufferIsReady() { return (recvq.find('\n') != std::string::npos); } void userrec::ClearBuffer() { recvq.clear(); } std::string userrec::GetBuffer() { try { if (!recvq.length()) return ""; /* Strip any leading \r or \n off the string. * Usually there are only one or two of these, * so its is computationally cheap to do. */ std::string::iterator t = recvq.begin(); while (t != recvq.end() && (*t == '\r' || *t == '\n')) { recvq.erase(t); t = recvq.begin(); } for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++) { /* Find the first complete line, return it as the * result, and leave the recvq as whats left */ if (*x == '\n') { std::string ret = std::string(recvq.begin(), x); recvq.erase(recvq.begin(), x + 1); return ret; } } return ""; } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::GetBuffer()"); return ""; } } void userrec::AddWriteBuf(const std::string &data) { if (*this->GetWriteError()) return; if (sendq.length() + data.length() > (unsigned)this->sendqmax) { /* * Fix by brain - Set the error text BEFORE calling writeopers, because * if we dont it'll recursively call here over and over again trying * to repeatedly add the text to the sendq! */ this->SetWriteError("SendQ exceeded"); ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax); return; } try { if (data.length() > MAXBUF - 2) /* MAXBUF has a value of 514, to account for line terminators */ sendq.append(data.substr(0,MAXBUF - 4)).append("\r\n"); /* MAXBUF-4 = 510 */ else sendq.append(data); } catch (...) { this->SetWriteError("SendQ exceeded"); ServerInstance->WriteOpers("*** User %s SendQ got an exception",this->nick); } } // send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it) void userrec::FlushWriteBuf() { try { if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError())) { sendq.clear(); } if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER)) { int old_sendq_length = sendq.length(); #ifndef WIN32 int n_sent = write(this->fd, this->sendq.data(), this->sendq.length()); #else int n_sent = send(this->fd, (const char*)this->sendq.data(), this->sendq.length(), 0); #endif if (n_sent == -1) { if (errno == EAGAIN) { /* The socket buffer is full. This isnt fatal, * try again later. */ this->ServerInstance->SE->WantWrite(this); } else { /* Fatal error, set write error and bail */ this->SetWriteError(strerror(errno)); return; } } else { /* advance the queue */ if (n_sent) this->sendq = this->sendq.substr(n_sent); /* update the user's stats counters */ this->bytes_out += n_sent; this->cmds_out++; if (n_sent != old_sendq_length) this->ServerInstance->SE->WantWrite(this); } } } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::FlushWriteBuf()"); } if (this->sendq.empty()) { FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this)); } } void userrec::SetWriteError(const std::string &error) { try { // don't try to set the error twice, its already set take the first string. if (this->WriteError.empty()) this->WriteError = error; } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::SetWriteError()"); } } const char* userrec::GetWriteError() { return this->WriteError.c_str(); } void userrec::Oper(const std::string &opertype) { try { this->modes[UM_OPERATOR] = 1; this->WriteServ("MODE %s :+o", this->nick); FOREACH_MOD(I_OnOper, OnOper(this, opertype)); ServerInstance->Log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str()); strlcpy(this->oper, opertype.c_str(), NICKMAX - 1); ServerInstance->all_opers.push_back(this); FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype)); } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::Oper()"); } } void userrec::UnOper() { try { if (IS_OPER(this)) { // unset their oper type (what IS_OPER checks), and remove +o *this->oper = 0; this->modes[UM_OPERATOR] = 0; // remove them from the opers list. for (std::vector<userrec*>::iterator a = ServerInstance->all_opers.begin(); a < ServerInstance->all_opers.end(); a++) { if (*a == this) { ServerInstance->all_opers.erase(a); return; } } } } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::UnOper()"); } } void userrec::QuitUser(InspIRCd* Instance, userrec *user, const std::string &quitreason, const char* operreason) { user->muted = true; Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason); } /* adds or updates an entry in the whowas list */ void userrec::AddToWhoWas() { command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS"); if (whowas_command) { std::deque<classbase*> params; params.push_back(this); whowas_command->HandleInternal(WHOWAS_ADD, params); } } /* add a client connection to the sockets list */ void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip) { std::string tempnick = ConvToStr(socket) + "-unknown"; user_hash::iterator iter = Instance->clientlist->find(tempnick); char ipaddr[MAXBUF]; #ifdef IPV6 if (socketfamily == AF_INET6) inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr)); else #endif inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr)); userrec* New; int j = 0; Instance->unregistered_count++; /* * fix by brain. * as these nicknames are 'RFC impossible', we can be sure nobody is going to be * using one as a registered connection. As they are per fd, we can also safely assume * that we wont have collisions. Therefore, if the nick exists in the list, its only * used by a dead socket, erase the iterator so that the new client may reclaim it. * this was probably the cause of 'server ignores me when i hammer it with reconnects' * issue in earlier alphas/betas */ if (iter != Instance->clientlist->end()) { userrec* goner = iter->second; DELETE(goner); Instance->clientlist->erase(iter); } New = new userrec(Instance); (*(Instance->clientlist))[tempnick] = New; New->fd = socket; strlcpy(New->nick,tempnick.c_str(),NICKMAX-1); New->server = Instance->FindServerNamePtr(Instance->Config->ServerName); /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */ strcpy(New->ident, "unknown"); New->registered = REG_NONE; New->signon = Instance->Time() + Instance->Config->dns_timeout; New->lastping = 1; New->SetSockAddr(socketfamily, ipaddr, port); /* Smarter than your average bear^H^H^H^Hset of strlcpys. */ for (const char* temp = New->GetIPString(); *temp && j < 64; temp++, j++) New->dhost[j] = New->host[j] = *temp; New->dhost[j] = New->host[j] = 0; Instance->AddLocalClone(New); Instance->AddGlobalClone(New); /* * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved. * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t */ ConnectClass* i = New->GetClass(); if (!i) { userrec::QuitUser(Instance, New, "Access denied by configuration"); return; } New->CheckClass(); New->pingmax = i->GetPingTime(); New->nping = Instance->Time() + i->GetPingTime() + Instance->Config->dns_timeout; New->timeout = Instance->Time() + i->GetRegTimeout(); New->flood = i->GetFlood(); New->threshold = i->GetThreshold(); New->sendqmax = i->GetSendqMax(); New->recvqmax = i->GetRecvqMax(); Instance->local_users.push_back(New); if ((Instance->local_users.size() > Instance->Config->SoftLimit) || (Instance->local_users.size() >= MAXCLIENTS)) { Instance->WriteOpers("*** Warning: softlimit value has been reached: %d clients", Instance->Config->SoftLimit); userrec::QuitUser(Instance, New,"No more connections allowed"); return; } /* * 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!) */ #ifndef WINDOWS if ((unsigned int)socket >= MAX_DESCRIPTORS) { userrec::QuitUser(Instance, New, "Server is full"); return; } #endif New->exempt = (Instance->XLines->matches_exception(New) != NULL); if (!New->exempt) { ZLine* r = Instance->XLines->matches_zline(ipaddr); if (r) { char reason[MAXBUF]; if (*Instance->Config->MoronBanner) New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner); snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason); userrec::QuitUser(Instance, New, reason); return; } } if (socket > -1) { if (!Instance->SE->AddFd(New)) { userrec::QuitUser(Instance, New, "Internal error handling connection"); return; } } /* NOTE: even if dns lookups are *off*, we still need to display this. * BOPM and other stuff requires it. */ New->WriteServ("NOTICE Auth :*** Looking up your hostname..."); } unsigned long userrec::GlobalCloneCount() { clonemap::iterator x = ServerInstance->global_clones.find(this->GetIPString()); if (x != ServerInstance->global_clones.end()) return x->second; else return 0; } unsigned long userrec::LocalCloneCount() { clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString()); if (x != ServerInstance->local_clones.end()) return x->second; else return 0; } /* * Check class restrictions */ void userrec::CheckClass() { ConnectClass* a = this->GetClass(); if ((!a) || (a->GetType() == CC_DENY)) { userrec::QuitUser(ServerInstance, this, "Unauthorised connection"); return; } else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal())) { userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)"); ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString()); return; } else if ((a->GetMaxGlobal()) && (this->GlobalCloneCount() > a->GetMaxGlobal())) { userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)"); ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString()); return; } } void userrec::FullConnect() { ServerInstance->stats->statsConnects++; this->idle_lastmsg = ServerInstance->Time(); /* * You may be thinking "wtf, we checked this in userrec::AddClient!" - and yes, we did, BUT. * At the time AddClient is called, we don't have a resolved host, by here we probably do - which * may put the user into a totally seperate class with different restrictions! so we *must* check again. * Don't remove this! -- w00t */ this->CheckClass(); /* Check the password, if one is required by the user's connect class. * This CANNOT be in CheckClass(), because that is called prior to PASS as well! */ if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed)) { userrec::QuitUser(ServerInstance, this, "Invalid password"); return; } if (!this->exempt) { GLine* r = ServerInstance->XLines->matches_gline(this); if (r) { this->muted = true; char reason[MAXBUF]; if (*ServerInstance->Config->MoronBanner) this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner); snprintf(reason,MAXBUF,"G-Lined: %s",r->reason); ServerInstance->GlobalCulls.AddItem(this, reason); return; } KLine* n = ServerInstance->XLines->matches_kline(this); if (n) { this->muted = true; char reason[MAXBUF]; if (*ServerInstance->Config->MoronBanner) this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner); snprintf(reason,MAXBUF,"K-Lined: %s",n->reason); ServerInstance->GlobalCulls.AddItem(this, reason); return; } } this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network); this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host); this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION); this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__); this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str()); ServerInstance->Config->Send005(this); this->ShowMOTD(); /* Now registered */ if (ServerInstance->unregistered_count) ServerInstance->unregistered_count--; /* Trigger LUSERS output, give modules a chance too */ int MOD_RESULT = 0; FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", NULL, 0, this, true, "LUSERS")); if (!MOD_RESULT) ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this); /* * fix 3 by brain, move registered = 7 below these so that spurious modes and host * changes dont go out onto the network and produce 'fake direction'. */ FOREACH_MOD(I_OnUserConnect,OnUserConnect(this)); this->registered = REG_ALL; FOREACH_MOD(I_OnPostConnect,OnPostConnect(this)); ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString(), this->fullname); } /** userrec::UpdateNick() * re-allocates a nick in the user_hash after they change nicknames, * returns a pointer to the new user as it may have moved */ userrec* userrec::UpdateNickHash(const char* New) { try { //user_hash::iterator newnick; user_hash::iterator oldnick = ServerInstance->clientlist->find(this->nick); if (!strcasecmp(this->nick,New)) return oldnick->second; if (oldnick == ServerInstance->clientlist->end()) return NULL; /* doesnt exist */ userrec* olduser = oldnick->second; (*(ServerInstance->clientlist))[New] = olduser; ServerInstance->clientlist->erase(oldnick); return olduser; } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::UpdateNickHash()"); return NULL; } } void userrec::InvalidateCache() { /* Invalidate cache */ if (cached_fullhost) free(cached_fullhost); if (cached_hostip) free(cached_hostip); if (cached_makehost) free(cached_makehost); if (cached_fullrealhost) free(cached_fullrealhost); cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL; } bool userrec::ForceNickChange(const char* newnick) { try { int MOD_RESULT = 0; this->InvalidateCache(); FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick)); if (MOD_RESULT) { ServerInstance->stats->statsCollisions++; return false; } if (ServerInstance->XLines->matches_qline(newnick)) { ServerInstance->stats->statsCollisions++; return false; } if (this->registered == REG_ALL) { return (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS); } return false; } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::ForceNickChange()"); return false; } } void userrec::SetSockAddr(int protocol_family, const char* ip, int port) { switch (protocol_family) { #ifdef SUPPORT_IP6LINKS case AF_INET6: { sockaddr_in6* sin = new sockaddr_in6; sin->sin6_family = AF_INET6; sin->sin6_port = port; inet_pton(AF_INET6, ip, &sin->sin6_addr); this->ip = (sockaddr*)sin; } break; #endif case AF_INET: { sockaddr_in* sin = new sockaddr_in; sin->sin_family = AF_INET; sin->sin_port = port; inet_pton(AF_INET, ip, &sin->sin_addr); this->ip = (sockaddr*)sin; } break; default: ServerInstance->Log(DEBUG,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick); break; } } int userrec::GetPort() { if (this->ip == NULL) return 0; switch (this->GetProtocolFamily()) { #ifdef SUPPORT_IP6LINKS case AF_INET6: { sockaddr_in6* sin = (sockaddr_in6*)this->ip; return sin->sin6_port; } break; #endif case AF_INET: { sockaddr_in* sin = (sockaddr_in*)this->ip; return sin->sin_port; } break; default: break; } return 0; } int userrec::GetProtocolFamily() { if (this->ip == NULL) return 0; sockaddr_in* sin = (sockaddr_in*)this->ip; return sin->sin_family; } const char* userrec::GetIPString() { static char buf[1024]; if (this->ip == NULL) return ""; switch (this->GetProtocolFamily()) { #ifdef SUPPORT_IP6LINKS case AF_INET6: { static char temp[1024]; sockaddr_in6* sin = (sockaddr_in6*)this->ip; inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf)); /* IP addresses starting with a : on irc are a Bad Thing (tm) */ if (*buf == ':') { strlcpy(&temp[1], buf, sizeof(temp) - 1); *temp = '0'; return temp; } return buf; } break; #endif case AF_INET: { sockaddr_in* sin = (sockaddr_in*)this->ip; inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf)); return buf; } break; default: break; } return ""; } const char* userrec::GetIPString(char* buf) { if (this->ip == NULL) { *buf = 0; return buf; } switch (this->GetProtocolFamily()) { #ifdef SUPPORT_IP6LINKS case AF_INET6: { static char temp[1024]; sockaddr_in6* sin = (sockaddr_in6*)this->ip; inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf)); /* IP addresses starting with a : on irc are a Bad Thing (tm) */ if (*buf == ':') { strlcpy(&temp[1], buf, sizeof(temp) - 1); *temp = '0'; strlcpy(buf, temp, sizeof(temp)); } return buf; } break; #endif case AF_INET: { sockaddr_in* sin = (sockaddr_in*)this->ip; inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf)); return buf; } break; default: break; } return ""; } /** NOTE: We cannot pass a const reference to this method. * The string is changed by the workings of the method, * so that if we pass const ref, we end up copying it to * something we can change anyway. Makes sense to just let * the compiler do that copy for us. */ void userrec::Write(std::string text) { #ifdef WINDOWS if ((this->fd < 0) || (this->m_internalFd > MAX_DESCRIPTORS)) #else if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS)) #endif return; try { /* ServerInstance->Log(DEBUG,"C[%d] <- %s", this->GetFd(), text.c_str()); * WARNING: The above debug line is VERY loud, do NOT * enable it till we have a good way of filtering it * out of the logs (e.g. 1.2 would be good). */ text.append("\r\n"); } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::Write() std::string::append"); return; } if (ServerInstance->Config->GetIOHook(this->GetPort())) { try { /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to * implement their own buffering mechanisms */ ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length()); } catch (CoreException& modexcept) { ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); } } else { this->AddWriteBuf(text); } ServerInstance->stats->statsSent += text.length(); this->ServerInstance->SE->WantWrite(this); } /** Write() */ void userrec::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)); } void userrec::WriteServ(const std::string& text) { char textbuffer[MAXBUF]; snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str()); this->Write(std::string(textbuffer)); } /** WriteServ() * Same as Write(), except `text' is prefixed with `:server.name '. */ void userrec::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)); } void userrec::WriteFrom(userrec *user, const std::string &text) { char tb[MAXBUF]; snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); this->Write(std::string(tb)); } /* write text from an originating user to originating user */ void userrec::WriteFrom(userrec *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)); } /* write text to an destination user from a source user (e.g. user privmsg) */ void userrec::WriteTo(userrec *dest, const char *data, ...) { char textbuffer[MAXBUF]; va_list argsPtr; va_start(argsPtr, data); vsnprintf(textbuffer, MAXBUF, data, argsPtr); va_end(argsPtr); this->WriteTo(dest, std::string(textbuffer)); } void userrec::WriteTo(userrec *dest, const std::string &data) { dest->WriteFrom(this, data); } void userrec::WriteCommon(const char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; if (this->registered != REG_ALL) return; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteCommon(std::string(textbuffer)); } void userrec::WriteCommon(const std::string &text) { try { bool sent_to_at_least_one = false; char tb[MAXBUF]; if (this->registered != REG_ALL) return; uniq_id++; /* We dont want to be doing this n times, just once */ snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str()); std::string out = tb; for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++) { CUList* ulist = v->first->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id)) { already_sent[i->first->fd] = uniq_id; i->first->Write(out); sent_to_at_least_one = true; } } } /* * if the user was not in any channels, no users will receive the text. Make sure the user * receives their OWN message for WriteCommon */ if (!sent_to_at_least_one) { this->Write(std::string(tb)); } } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::WriteCommon()"); } } /* write a formatted string to all users who share at least one common * channel, NOT including the source user e.g. for use in QUIT */ void userrec::WriteCommonExcept(const char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteCommonExcept(std::string(textbuffer)); } void userrec::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text) { char tb1[MAXBUF]; char tb2[MAXBUF]; if (this->registered != REG_ALL) return; uniq_id++; snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str()); snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str()); std::string out1 = tb1; std::string out2 = tb2; for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++) { CUList *ulist = v->first->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (this != i->first) { if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id)) { already_sent[i->first->fd] = uniq_id; i->first->Write(IS_OPER(i->first) ? out2 : out1); } } } } } void userrec::WriteCommonExcept(const std::string &text) { char tb1[MAXBUF]; std::string out1; if (this->registered != REG_ALL) return; uniq_id++; snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str()); out1 = tb1; for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++) { CUList *ulist = v->first->GetUsers(); for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { if (this != i->first) { if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id)) { already_sent[i->first->fd] = uniq_id; i->first->Write(out1); } } } } } void userrec::WriteWallOps(const std::string &text) { if (!IS_OPER(this) && IS_LOCAL(this)) return; std::string wallop("WALLOPS :"); wallop.append(text); for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) { userrec* t = *i; if (t->modes[UM_WALLOPS]) this->WriteTo(t,wallop); } } void userrec::WriteWallOps(const char* text, ...) { char textbuffer[MAXBUF]; va_list argsPtr; va_start(argsPtr, text); vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); this->WriteWallOps(std::string(textbuffer)); } /* return 0 or 1 depending if users u and u2 share one or more common channels * (used by QUIT, NICK etc which arent channel specific notices) * * The old algorithm in 1.0 for this was relatively inefficient, iterating over * the first users channels then the second users channels within the outer loop, * therefore it was a maximum of x*y iterations (upon returning 0 and checking * all possible iterations). However this new function instead checks against the * channel's userlist in the inner loop which is a std::map<userrec*,userrec*> * and saves us time as we already know what pointer value we are after. * Don't quote me on the maths as i am not a mathematician or computer scientist, * but i believe this algorithm is now x+(log y) maximum iterations instead. */ bool userrec::SharesChannelWith(userrec *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++) { /* 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->first->HasUser(other)) return true; } return false; } bool userrec::ChangeName(const char* gecos) { if (!strcmp(gecos, this->fullname)) return true; if (IS_LOCAL(this)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos)); if (MOD_RESULT) return false; FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos)); } strlcpy(this->fullname,gecos,MAXGECOS+1); return true; } bool userrec::ChangeDisplayedHost(const char* host) { if (!strcmp(host, this->dhost)) return true; if (IS_LOCAL(this)) { int MOD_RESULT = 0; FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host)); if (MOD_RESULT) return false; FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host)); } if (this->ServerInstance->Config->CycleHosts) this->WriteCommonExcept("QUIT :Changing hosts"); /* Fix by Om: userrec::dhost is 65 long, this was truncating some long hosts */ strlcpy(this->dhost,host,64); this->InvalidateCache(); if (this->ServerInstance->Config->CycleHosts) { for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name); std::string n = this->ServerInstance->Modes->ModeString(this, i->first); if (n.length() > 0) i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str()); } } if (IS_LOCAL(this)) this->WriteServ("396 %s %s :is now your displayed host",this->nick,this->dhost); return true; } bool userrec::ChangeIdent(const char* newident) { if (!strcmp(newident, this->ident)) return true; if (this->ServerInstance->Config->CycleHosts) this->WriteCommonExcept("%s","QUIT :Changing ident"); strlcpy(this->ident, newident, IDENTMAX+2); this->InvalidateCache(); if (this->ServerInstance->Config->CycleHosts) { for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name); std::string n = this->ServerInstance->Modes->ModeString(this, i->first); if (n.length() > 0) i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str()); } } return true; } void userrec::SendAll(const char* command, 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(), command, textbuffer); std::string fmt = formatbuffer; for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) { (*i)->Write(fmt); } } std::string userrec::ChannelList(userrec* source) { try { std::string list; for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { /* If the target is the same as the sender, let them see all their channels. * If the channel is NOT private/secret OR the user shares a common channel * If the user is an oper, and the <options:operspywhois> option is set. */ if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->modes[CM_PRIVATE]) && (!i->first->modes[CM_SECRET])) || (i->first->HasUser(source)))) { list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" "); } } return list; } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::ChannelList()"); return ""; } } void userrec::SplitChanList(userrec* dest, const std::string &cl) { std::string line; std::ostringstream prefix; std::string::size_type start, pos, length; try { prefix << this->nick << " " << dest->nick << " :"; line = prefix.str(); int namelen = strlen(ServerInstance->Config->ServerName) + 6; for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1) { length = (pos == std::string::npos) ? cl.length() : pos; if (line.length() + namelen + length - start > 510) { ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str()); line = prefix.str(); } if(pos == std::string::npos) { line.append(cl.substr(start, length - start)); break; } else { line.append(cl.substr(start, length - start + 1)); } } if (line.length()) { ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str()); } } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::SplitChanList()"); } } /* 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* userrec::GetClass() { for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str())))) { if (i->GetPort()) { if (this->GetPort() == i->GetPort()) return &(*i); else continue; } else return &(*i); } } return NULL; } void userrec::PurgeEmptyChannels() { std::vector<chanrec*> to_delete; // firstly decrement the count on each channel for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++) { f->first->RemoveAllPrefixes(this); if (f->first->DelUser(this) == 0) { /* No users left in here, mark it for deletion */ try { to_delete.push_back(f->first); } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::PurgeEmptyChannels to_delete.push_back()"); } } } for (std::vector<chanrec*>::iterator n = to_delete.begin(); n != to_delete.end(); n++) { chanrec* thischan = *n; chan_hash::iterator i2 = ServerInstance->chanlist->find(thischan->name); if (i2 != ServerInstance->chanlist->end()) { FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second)); DELETE(i2->second); ServerInstance->chanlist->erase(i2); this->chans.erase(*n); } } this->UnOper(); } void userrec::ShowMOTD() { if (!ServerInstance->Config->MOTD.size()) { this->WriteServ("422 %s :Message of the day file is missing.",this->nick); return; } this->WriteServ("375 %s :%s message of the day", this->nick, ServerInstance->Config->ServerName); for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++) this->WriteServ("372 %s :- %s",this->nick,i->c_str()); this->WriteServ("376 %s :End of message of the day.", this->nick); } void userrec::ShowRULES() { if (!ServerInstance->Config->RULES.size()) { this->WriteServ("NOTICE %s :Rules file is missing.",this->nick); return; } this->WriteServ("NOTICE %s :%s rules",this->nick,ServerInstance->Config->ServerName); for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++) this->WriteServ("NOTICE %s :%s",this->nick,i->c_str()); this->WriteServ("NOTICE %s :End of %s rules.",this->nick,ServerInstance->Config->ServerName); } void userrec::HandleEvent(EventType et, int errornum) { /* WARNING: May delete this user! */ int thisfd = this->GetFd(); try { switch (et) { case EVENT_READ: ServerInstance->ProcessUser(this); break; case EVENT_WRITE: this->FlushWriteBuf(); break; case EVENT_ERROR: /** This should be safe, but dont DARE do anything after it -- Brain */ this->SetWriteError(errornum ? strerror(errornum) : "EOF from client"); break; } } catch (...) { ServerInstance->Log(DEBUG,"Exception in userrec::HandleEvent intercepted"); } /* If the user has raised an error whilst being processed, quit them now we're safe to */ if ((ServerInstance->SE->GetRef(thisfd) == this)) { if (!WriteError.empty()) { userrec::QuitUser(ServerInstance, this, GetWriteError()); } } } void userrec::SetOperQuit(const std::string &oquit) { if (operquit) return; operquit = strdup(oquit.c_str()); } const char* userrec::GetOperQuit() { return operquit ? operquit : ""; } VisData::VisData() { } VisData::~VisData() { } bool VisData::VisibleTo(userrec* user) { return true; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "channels.h"
+#include "users.h"
+#include <stdarg.h>
+#include "socketengine.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "commands/cmd_whowas.h"
+
+static unsigned long already_sent[MAX_DESCRIPTORS] = {0};
+
+/* XXX: Used for speeding up WriteCommon operations */
+unsigned long uniq_id = 0;
+
+bool InitTypes(ServerConfig* conf, const char* tag)
+{
+ if (conf->opertypes.size())
+ {
+ for (opertype_t::iterator n = conf->opertypes.begin(); n != conf->opertypes.end(); n++)
+ {
+ if (n->second)
+ delete[] n->second;
+ }
+ }
+
+ conf->opertypes.clear();
+ return true;
+}
+
+bool InitClasses(ServerConfig* conf, const char* tag)
+{
+ if (conf->operclass.size())
+ {
+ for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++)
+ {
+ if (n->second)
+ delete[] n->second;
+ }
+ }
+
+ conf->operclass.clear();
+ return true;
+}
+
+bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* TypeName = values[0].GetString();
+ const char* Classes = values[1].GetString();
+
+ conf->opertypes[TypeName] = strnewdup(Classes);
+ return true;
+}
+
+bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* ClassName = values[0].GetString();
+ const char* CommandList = values[1].GetString();
+
+ conf->operclass[ClassName] = strnewdup(CommandList);
+ return true;
+}
+
+bool DoneClassesAndTypes(ServerConfig* conf, const char* tag)
+{
+ return true;
+}
+
+std::string userrec::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->IsEnabled(d))
+ {
+ if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
+ {
+ if ((oldadding != adding) || (!output.length()))
+ output += (adding ? '+' : '-');
+
+ this->SetNoticeMask(d, adding);
+
+ output += d;
+ }
+ }
+ oldadding = adding;
+ }
+ break;
+ default:
+ if ((*c >= 'A') && (*c <= 'z') && (ServerInstance->SNO->IsEnabled(*c)))
+ {
+ if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
+ {
+ if ((oldadding != adding) || (!output.length()))
+ output += (adding ? '+' : '-');
+
+ this->SetNoticeMask(*c, adding);
+
+ output += *c;
+ }
+ }
+ oldadding = adding;
+ break;
+ }
+
+ *c++;
+ }
+
+ return output;
+}
+
+void userrec::StartDNSLookup()
+{
+ try
+ {
+ bool cached;
+ const char* ip = this->GetIPString();
+
+ /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */
+ if (!strncmp(ip, "0::ffff:", 8))
+ res_reverse = new UserResolver(this->ServerInstance, this, ip + 8, DNS_QUERY_PTR4, cached);
+ else
+ res_reverse = new UserResolver(this->ServerInstance, this, ip, this->GetProtocolFamily() == AF_INET ? DNS_QUERY_PTR4 : DNS_QUERY_PTR6, cached);
+
+ this->ServerInstance->AddResolver(res_reverse, cached);
+ }
+ catch (CoreException& e)
+ {
+ ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
+ }
+}
+
+UserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_resolve, QueryType qt, bool &cache) :
+ Resolver(Instance, to_resolve, qt, cache), bound_user(user)
+{
+ this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);
+ this->bound_fd = user->GetFd();
+}
+
+void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+{
+ if ((!this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
+ {
+ this->bound_user->stored_host = result;
+ try
+ {
+ /* Check we didnt time out */
+ if (this->bound_user->registered != REG_ALL)
+ {
+ bool cached;
+#ifdef IPV6
+ if (this->bound_user->GetProtocolFamily() == AF_INET6)
+ {
+ /* IPV6 forward lookup (with possibility of 4in6) */
+ const char* ip = this->bound_user->GetIPString();
+ bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, (!strncmp(ip, "0::ffff:", 8) ? DNS_QUERY_A : DNS_QUERY_AAAA), cached);
+ }
+ else
+ /* IPV4 lookup (mixed protocol mode) */
+#endif
+ /* IPV4 lookup (ipv4 only mode) */
+ bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached);
+ this->ServerInstance->AddResolver(bound_user->res_forward, cached);
+ }
+ }
+ catch (CoreException& e)
+ {
+ ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
+ }
+ }
+ else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
+ {
+ /* Both lookups completed */
+ std::string result2("0::ffff:");
+ result2.append(result);
+ if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2)
+ {
+ std::string hostname = this->bound_user->stored_host;
+ if (hostname.length() < 65)
+ {
+ /* Check we didnt time out */
+ if ((this->bound_user->registered != REG_ALL) && (!this->bound_user->dns_done))
+ {
+ /* Hostnames starting with : are not a good thing (tm) */
+ if (*(hostname.c_str()) == ':')
+ hostname.insert(0, "0");
+
+ this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));
+ this->bound_user->dns_done = true;
+ strlcpy(this->bound_user->dhost, hostname.c_str(),64);
+ strlcpy(this->bound_user->host, hostname.c_str(),64);
+ /* Invalidate cache */
+ this->bound_user->InvalidateCache();
+ }
+ }
+ else
+ {
+ if (!this->bound_user->dns_done)
+ {
+ this->bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", this->bound_user->GetIPString());
+ this->bound_user->dns_done = true;
+ }
+ }
+ }
+ else
+ {
+ if (!this->bound_user->dns_done)
+ {
+ this->bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", this->bound_user->GetIPString());
+ this->bound_user->dns_done = true;
+ }
+ }
+ }
+}
+
+void UserResolver::OnError(ResolverError e, const std::string &errormessage)
+{
+ if (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)
+ {
+ /* Since dns timeout is implemented outside of the resolver, this was a race condition that could result in this message being sent *after*
+ * the user was fully connected. This check fixes that issue - Special */
+ if (!this->bound_user->dns_done)
+ {
+ /* Error message here */
+ this->bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), this->bound_user->GetIPString());
+ this->bound_user->dns_done = true;
+ }
+ }
+}
+
+
+bool userrec::IsNoticeMaskSet(unsigned char sm)
+{
+ return (snomasks[sm-65]);
+}
+
+void userrec::SetNoticeMask(unsigned char sm, bool value)
+{
+ snomasks[sm-65] = value;
+}
+
+const char* userrec::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 userrec::IsModeSet(unsigned char m)
+{
+ return (modes[m-65]);
+}
+
+void userrec::SetMode(unsigned char m, bool value)
+{
+ modes[m-65] = value;
+}
+
+const char* userrec::FormatModes()
+{
+ static char data[MAXBUF];
+ int offset = 0;
+ for (int n = 0; n < 64; n++)
+ {
+ if (modes[n])
+ data[offset++] = n+65;
+ }
+ data[offset] = 0;
+ return data;
+}
+
+void userrec::DecrementModes()
+{
+ for (int n = 0; n < 64; n++)
+ {
+ if (modes[n])
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(n+65, MODETYPE_USER);
+ if (mh)
+ mh->ChangeCount(-1);
+ }
+ }
+}
+
+userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance)
+{
+ // the PROPER way to do it, AVOID bzero at *ALL* costs
+ *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0;
+ server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);
+ reset_due = ServerInstance->Time();
+ age = ServerInstance->Time(true);
+ lines_in = lastping = signon = idle_lastmsg = nping = registered = 0;
+ ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;
+ muted = exempt = haspassed = dns_done = false;
+ fd = -1;
+ recvq.clear();
+ sendq.clear();
+ WriteError.clear();
+ res_forward = res_reverse = NULL;
+ Visibility = NULL;
+ ip = NULL;
+ chans.clear();
+ invites.clear();
+ memset(modes,0,sizeof(modes));
+ memset(snomasks,0,sizeof(snomasks));
+ /* Invalidate cache */
+ operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
+}
+
+void userrec::RemoveCloneCounts()
+{
+ clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
+ if (x != ServerInstance->local_clones.end())
+ {
+ x->second--;
+ if (!x->second)
+ {
+ ServerInstance->local_clones.erase(x);
+ }
+ }
+
+ clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString());
+ if (y != ServerInstance->global_clones.end())
+ {
+ y->second--;
+ if (!y->second)
+ {
+ ServerInstance->global_clones.erase(y);
+ }
+ }
+}
+
+userrec::~userrec()
+{
+ this->InvalidateCache();
+ this->DecrementModes();
+ if (operquit)
+ free(operquit);
+ if (ip)
+ {
+ this->RemoveCloneCounts();
+
+ if (this->GetProtocolFamily() == AF_INET)
+ {
+ delete (sockaddr_in*)ip;
+ }
+#ifdef SUPPORT_IP6LINKS
+ else
+ {
+ delete (sockaddr_in6*)ip;
+ }
+#endif
+ }
+}
+
+char* userrec::MakeHost()
+{
+ if (this->cached_makehost)
+ return this->cached_makehost;
+
+ char nhost[MAXBUF];
+ /* This is much faster than snprintf */
+ char* t = nhost;
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(char* n = host; *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_makehost = strdup(nhost);
+
+ return this->cached_makehost;
+}
+
+char* userrec::MakeHostIP()
+{
+ if (this->cached_hostip)
+ return this->cached_hostip;
+
+ char ihost[MAXBUF];
+ /* This is much faster than snprintf */
+ char* t = ihost;
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(const char* n = this->GetIPString(); *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_hostip = strdup(ihost);
+
+ return this->cached_hostip;
+}
+
+void userrec::CloseSocket()
+{
+ shutdown(this->fd,2);
+ close(this->fd);
+}
+
+char* userrec::GetFullHost()
+{
+ if (this->cached_fullhost)
+ return this->cached_fullhost;
+
+ char result[MAXBUF];
+ char* t = result;
+ for(char* n = nick; *n; n++)
+ *t++ = *n;
+ *t++ = '!';
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(char* n = dhost; *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_fullhost = strdup(result);
+
+ return this->cached_fullhost;
+}
+
+char* userrec::MakeWildHost()
+{
+ static char nresult[MAXBUF];
+ char* t = nresult;
+ *t++ = '*'; *t++ = '!';
+ *t++ = '*'; *t++ = '@';
+ for(char* n = dhost; *n; n++)
+ *t++ = *n;
+ *t = 0;
+ return nresult;
+}
+
+int userrec::ReadData(void* buffer, size_t size)
+{
+ if (IS_LOCAL(this))
+ {
+#ifndef WIN32
+ return read(this->fd, buffer, size);
+#else
+ return recv(this->fd, (char*)buffer, size, 0);
+#endif
+ }
+ else
+ return 0;
+}
+
+
+char* userrec::GetFullRealHost()
+{
+ if (this->cached_fullrealhost)
+ return this->cached_fullrealhost;
+
+ char fresult[MAXBUF];
+ char* t = fresult;
+ for(char* n = nick; *n; n++)
+ *t++ = *n;
+ *t++ = '!';
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(char* n = host; *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_fullrealhost = strdup(fresult);
+
+ return this->cached_fullrealhost;
+}
+
+bool userrec::IsInvited(const irc::string &channel)
+{
+ for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
+ {
+ if (channel == *i)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+InvitedList* userrec::GetInviteList()
+{
+ return &invites;
+}
+
+void userrec::InviteTo(const irc::string &channel)
+{
+ invites.push_back(channel);
+}
+
+void userrec::RemoveInvite(const irc::string &channel)
+{
+ for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
+ {
+ if (channel == *i)
+ {
+ invites.erase(i);
+ return;
+ }
+ }
+}
+
+bool userrec::HasPermission(const std::string &command)
+{
+ char* mycmd;
+ char* savept;
+ char* savept2;
+
+ /*
+ * users on remote servers can completely bypass all permissions based checks.
+ * This prevents desyncs when one server has different type/class tags to another.
+ * That having been said, this does open things up to the possibility of source changes
+ * allowing remote kills, etc - but if they have access to the src, they most likely have
+ * access to the conf - so it's an end to a means either way.
+ */
+ if (!IS_LOCAL(this))
+ return true;
+
+ // are they even an oper at all?
+ if (IS_OPER(this))
+ {
+ opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);
+ if (iter_opertype != ServerInstance->Config->opertypes.end())
+ {
+ char* Classes = strdup(iter_opertype->second);
+ char* myclass = strtok_r(Classes," ",&savept);
+ while (myclass)
+ {
+ operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass);
+ if (iter_operclass != ServerInstance->Config->operclass.end())
+ {
+ char* CommandList = strdup(iter_operclass->second);
+ mycmd = strtok_r(CommandList," ",&savept2);
+ while (mycmd)
+ {
+ if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
+ {
+ free(Classes);
+ free(CommandList);
+ return true;
+ }
+ mycmd = strtok_r(NULL," ",&savept2);
+ }
+ free(CommandList);
+ }
+ myclass = strtok_r(NULL," ",&savept);
+ }
+ free(Classes);
+ }
+ }
+ return false;
+}
+
+/** NOTE: We cannot pass a const reference to this method.
+ * The string is changed by the workings of the method,
+ * so that if we pass const ref, we end up copying it to
+ * something we can change anyway. Makes sense to just let
+ * the compiler do that copy for us.
+ */
+bool userrec::AddBuffer(std::string a)
+{
+ try
+ {
+ std::string::size_type i = a.rfind('\r');
+
+ while (i != std::string::npos)
+ {
+ a.erase(i, 1);
+ i = a.rfind('\r');
+ }
+
+ if (a.length())
+ recvq.append(a);
+
+ if (recvq.length() > (unsigned)this->recvqmax)
+ {
+ this->SetWriteError("RecvQ exceeded");
+ ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);
+ return false;
+ }
+
+ return true;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::AddBuffer()");
+ return false;
+ }
+}
+
+bool userrec::BufferIsReady()
+{
+ return (recvq.find('\n') != std::string::npos);
+}
+
+void userrec::ClearBuffer()
+{
+ recvq.clear();
+}
+
+std::string userrec::GetBuffer()
+{
+ try
+ {
+ if (!recvq.length())
+ return "";
+
+ /* Strip any leading \r or \n off the string.
+ * Usually there are only one or two of these,
+ * so its is computationally cheap to do.
+ */
+ std::string::iterator t = recvq.begin();
+ while (t != recvq.end() && (*t == '\r' || *t == '\n'))
+ {
+ recvq.erase(t);
+ t = recvq.begin();
+ }
+
+ for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++)
+ {
+ /* Find the first complete line, return it as the
+ * result, and leave the recvq as whats left
+ */
+ if (*x == '\n')
+ {
+ std::string ret = std::string(recvq.begin(), x);
+ recvq.erase(recvq.begin(), x + 1);
+ return ret;
+ }
+ }
+ return "";
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::GetBuffer()");
+ return "";
+ }
+}
+
+void userrec::AddWriteBuf(const std::string &data)
+{
+ if (*this->GetWriteError())
+ return;
+
+ if (sendq.length() + data.length() > (unsigned)this->sendqmax)
+ {
+ /*
+ * Fix by brain - Set the error text BEFORE calling writeopers, because
+ * if we dont it'll recursively call here over and over again trying
+ * to repeatedly add the text to the sendq!
+ */
+ this->SetWriteError("SendQ exceeded");
+ ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);
+ return;
+ }
+
+ try
+ {
+ if (data.length() > MAXBUF - 2) /* MAXBUF has a value of 514, to account for line terminators */
+ sendq.append(data.substr(0,MAXBUF - 4)).append("\r\n"); /* MAXBUF-4 = 510 */
+ else
+ sendq.append(data);
+ }
+ catch (...)
+ {
+ this->SetWriteError("SendQ exceeded");
+ ServerInstance->WriteOpers("*** User %s SendQ got an exception",this->nick);
+ }
+}
+
+// send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)
+void userrec::FlushWriteBuf()
+{
+ try
+ {
+ if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError()))
+ {
+ sendq.clear();
+ }
+ if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER))
+ {
+ int old_sendq_length = sendq.length();
+#ifndef WIN32
+ int n_sent = write(this->fd, this->sendq.data(), this->sendq.length());
+#else
+ int n_sent = send(this->fd, (const char*)this->sendq.data(), this->sendq.length(), 0);
+#endif
+ if (n_sent == -1)
+ {
+ if (errno == EAGAIN)
+ {
+ /* The socket buffer is full. This isnt fatal,
+ * try again later.
+ */
+ this->ServerInstance->SE->WantWrite(this);
+ }
+ else
+ {
+ /* Fatal error, set write error and bail
+ */
+ this->SetWriteError(strerror(errno));
+ return;
+ }
+ }
+ else
+ {
+ /* advance the queue */
+ if (n_sent)
+ this->sendq = this->sendq.substr(n_sent);
+ /* update the user's stats counters */
+ this->bytes_out += n_sent;
+ this->cmds_out++;
+ if (n_sent != old_sendq_length)
+ this->ServerInstance->SE->WantWrite(this);
+ }
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::FlushWriteBuf()");
+ }
+
+ if (this->sendq.empty())
+ {
+ FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this));
+ }
+}
+
+void userrec::SetWriteError(const std::string &error)
+{
+ try
+ {
+ // don't try to set the error twice, its already set take the first string.
+ if (this->WriteError.empty())
+ this->WriteError = error;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::SetWriteError()");
+ }
+}
+
+const char* userrec::GetWriteError()
+{
+ return this->WriteError.c_str();
+}
+
+void userrec::Oper(const std::string &opertype)
+{
+ try
+ {
+ this->modes[UM_OPERATOR] = 1;
+ this->WriteServ("MODE %s :+o", this->nick);
+ FOREACH_MOD(I_OnOper, OnOper(this, opertype));
+ ServerInstance->Log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str());
+ strlcpy(this->oper, opertype.c_str(), NICKMAX - 1);
+ ServerInstance->all_opers.push_back(this);
+ FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype));
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::Oper()");
+ }
+}
+
+void userrec::UnOper()
+{
+ try
+ {
+ if (IS_OPER(this))
+ {
+ // unset their oper type (what IS_OPER checks), and remove +o
+ *this->oper = 0;
+ this->modes[UM_OPERATOR] = 0;
+
+ // remove them from the opers list.
+ for (std::vector<userrec*>::iterator a = ServerInstance->all_opers.begin(); a < ServerInstance->all_opers.end(); a++)
+ {
+ if (*a == this)
+ {
+ ServerInstance->all_opers.erase(a);
+ return;
+ }
+ }
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::UnOper()");
+ }
+}
+
+void userrec::QuitUser(InspIRCd* Instance, userrec *user, const std::string &quitreason, const char* operreason)
+{
+ user->muted = true;
+ Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason);
+}
+
+/* adds or updates an entry in the whowas list */
+void userrec::AddToWhoWas()
+{
+ command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
+ if (whowas_command)
+ {
+ std::deque<classbase*> params;
+ params.push_back(this);
+ whowas_command->HandleInternal(WHOWAS_ADD, params);
+ }
+}
+
+/* add a client connection to the sockets list */
+void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip)
+{
+ std::string tempnick = ConvToStr(socket) + "-unknown";
+ user_hash::iterator iter = Instance->clientlist->find(tempnick);
+ char ipaddr[MAXBUF];
+#ifdef IPV6
+ if (socketfamily == AF_INET6)
+ inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr));
+ else
+#endif
+ inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));
+ userrec* New;
+ int j = 0;
+
+ Instance->unregistered_count++;
+
+ /*
+ * fix by brain.
+ * as these nicknames are 'RFC impossible', we can be sure nobody is going to be
+ * using one as a registered connection. As they are per fd, we can also safely assume
+ * that we wont have collisions. Therefore, if the nick exists in the list, its only
+ * used by a dead socket, erase the iterator so that the new client may reclaim it.
+ * this was probably the cause of 'server ignores me when i hammer it with reconnects'
+ * issue in earlier alphas/betas
+ */
+ if (iter != Instance->clientlist->end())
+ {
+ userrec* goner = iter->second;
+ DELETE(goner);
+ Instance->clientlist->erase(iter);
+ }
+
+ New = new userrec(Instance);
+ (*(Instance->clientlist))[tempnick] = New;
+ New->fd = socket;
+ strlcpy(New->nick,tempnick.c_str(),NICKMAX-1);
+
+ New->server = Instance->FindServerNamePtr(Instance->Config->ServerName);
+ /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */
+ strcpy(New->ident, "unknown");
+
+ New->registered = REG_NONE;
+ New->signon = Instance->Time() + Instance->Config->dns_timeout;
+ New->lastping = 1;
+
+ New->SetSockAddr(socketfamily, ipaddr, port);
+
+ /* Smarter than your average bear^H^H^H^Hset of strlcpys. */
+ for (const char* temp = New->GetIPString(); *temp && j < 64; temp++, j++)
+ New->dhost[j] = New->host[j] = *temp;
+ New->dhost[j] = New->host[j] = 0;
+
+ Instance->AddLocalClone(New);
+ Instance->AddGlobalClone(New);
+
+ /*
+ * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
+ * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
+ */
+ ConnectClass* i = New->GetClass();
+
+ if (!i)
+ {
+ userrec::QuitUser(Instance, New, "Access denied by configuration");
+ return;
+ }
+
+ New->CheckClass();
+
+ New->pingmax = i->GetPingTime();
+ New->nping = Instance->Time() + i->GetPingTime() + Instance->Config->dns_timeout;
+ New->timeout = Instance->Time() + i->GetRegTimeout();
+ New->flood = i->GetFlood();
+ New->threshold = i->GetThreshold();
+ New->sendqmax = i->GetSendqMax();
+ New->recvqmax = i->GetRecvqMax();
+
+ Instance->local_users.push_back(New);
+
+ if ((Instance->local_users.size() > Instance->Config->SoftLimit) || (Instance->local_users.size() >= MAXCLIENTS))
+ {
+ Instance->WriteOpers("*** Warning: softlimit value has been reached: %d clients", Instance->Config->SoftLimit);
+ userrec::QuitUser(Instance, New,"No more connections allowed");
+ return;
+ }
+
+ /*
+ * 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!)
+ */
+#ifndef WINDOWS
+ if ((unsigned int)socket >= MAX_DESCRIPTORS)
+ {
+ userrec::QuitUser(Instance, New, "Server is full");
+ return;
+ }
+#endif
+
+ New->exempt = (Instance->XLines->matches_exception(New) != NULL);
+ if (!New->exempt)
+ {
+ ZLine* r = Instance->XLines->matches_zline(ipaddr);
+ if (r)
+ {
+ char reason[MAXBUF];
+ if (*Instance->Config->MoronBanner)
+ New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner);
+ snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason);
+ userrec::QuitUser(Instance, New, reason);
+ return;
+ }
+ }
+
+ if (socket > -1)
+ {
+ if (!Instance->SE->AddFd(New))
+ {
+ userrec::QuitUser(Instance, New, "Internal error handling connection");
+ return;
+ }
+ }
+
+ /* NOTE: even if dns lookups are *off*, we still need to display this.
+ * BOPM and other stuff requires it.
+ */
+ New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
+}
+
+unsigned long userrec::GlobalCloneCount()
+{
+ clonemap::iterator x = ServerInstance->global_clones.find(this->GetIPString());
+ if (x != ServerInstance->global_clones.end())
+ return x->second;
+ else
+ return 0;
+}
+
+unsigned long userrec::LocalCloneCount()
+{
+ clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
+ if (x != ServerInstance->local_clones.end())
+ return x->second;
+ else
+ return 0;
+}
+
+/*
+ * Check class restrictions
+ */
+void userrec::CheckClass()
+{
+ ConnectClass* a = this->GetClass();
+
+ if ((!a) || (a->GetType() == CC_DENY))
+ {
+ userrec::QuitUser(ServerInstance, this, "Unauthorised connection");
+ return;
+ }
+ else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal()))
+ {
+ userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)");
+ ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
+ return;
+ }
+ else if ((a->GetMaxGlobal()) && (this->GlobalCloneCount() > a->GetMaxGlobal()))
+ {
+ userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)");
+ ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
+ return;
+ }
+}
+
+void userrec::FullConnect()
+{
+ ServerInstance->stats->statsConnects++;
+ this->idle_lastmsg = ServerInstance->Time();
+
+ /*
+ * You may be thinking "wtf, we checked this in userrec::AddClient!" - and yes, we did, BUT.
+ * At the time AddClient is called, we don't have a resolved host, by here we probably do - which
+ * may put the user into a totally seperate class with different restrictions! so we *must* check again.
+ * Don't remove this! -- w00t
+ */
+ this->CheckClass();
+
+ /* Check the password, if one is required by the user's connect class.
+ * This CANNOT be in CheckClass(), because that is called prior to PASS as well!
+ */
+ if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed))
+ {
+ userrec::QuitUser(ServerInstance, this, "Invalid password");
+ return;
+ }
+
+ if (!this->exempt)
+ {
+ GLine* r = ServerInstance->XLines->matches_gline(this);
+
+ if (r)
+ {
+ this->muted = true;
+ char reason[MAXBUF];
+ if (*ServerInstance->Config->MoronBanner)
+ this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner);
+ snprintf(reason,MAXBUF,"G-Lined: %s",r->reason);
+ ServerInstance->GlobalCulls.AddItem(this, reason);
+ return;
+ }
+
+ KLine* n = ServerInstance->XLines->matches_kline(this);
+
+ if (n)
+ {
+ this->muted = true;
+ char reason[MAXBUF];
+ if (*ServerInstance->Config->MoronBanner)
+ this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner);
+ snprintf(reason,MAXBUF,"K-Lined: %s",n->reason);
+ ServerInstance->GlobalCulls.AddItem(this, reason);
+ return;
+ }
+ }
+
+ this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network);
+ this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host);
+ this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION);
+ this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__);
+ this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str());
+
+ ServerInstance->Config->Send005(this);
+
+ this->ShowMOTD();
+
+ /* Now registered */
+ if (ServerInstance->unregistered_count)
+ ServerInstance->unregistered_count--;
+
+ /* Trigger LUSERS output, give modules a chance too */
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", NULL, 0, this, true, "LUSERS"));
+ if (!MOD_RESULT)
+ ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this);
+
+ /*
+ * fix 3 by brain, move registered = 7 below these so that spurious modes and host
+ * changes dont go out onto the network and produce 'fake direction'.
+ */
+ FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
+
+ this->registered = REG_ALL;
+
+ FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
+
+ ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString(), this->fullname);
+}
+
+/** userrec::UpdateNick()
+ * re-allocates a nick in the user_hash after they change nicknames,
+ * returns a pointer to the new user as it may have moved
+ */
+userrec* userrec::UpdateNickHash(const char* New)
+{
+ try
+ {
+ //user_hash::iterator newnick;
+ user_hash::iterator oldnick = ServerInstance->clientlist->find(this->nick);
+
+ if (!strcasecmp(this->nick,New))
+ return oldnick->second;
+
+ if (oldnick == ServerInstance->clientlist->end())
+ return NULL; /* doesnt exist */
+
+ userrec* olduser = oldnick->second;
+ (*(ServerInstance->clientlist))[New] = olduser;
+ ServerInstance->clientlist->erase(oldnick);
+ return olduser;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::UpdateNickHash()");
+ return NULL;
+ }
+}
+
+void userrec::InvalidateCache()
+{
+ /* Invalidate cache */
+ if (cached_fullhost)
+ free(cached_fullhost);
+ if (cached_hostip)
+ free(cached_hostip);
+ if (cached_makehost)
+ free(cached_makehost);
+ if (cached_fullrealhost)
+ free(cached_fullrealhost);
+ cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
+}
+
+bool userrec::ForceNickChange(const char* newnick)
+{
+ try
+ {
+ int MOD_RESULT = 0;
+
+ this->InvalidateCache();
+
+ FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick));
+
+ if (MOD_RESULT)
+ {
+ ServerInstance->stats->statsCollisions++;
+ return false;
+ }
+
+ if (ServerInstance->XLines->matches_qline(newnick))
+ {
+ ServerInstance->stats->statsCollisions++;
+ return false;
+ }
+
+ if (this->registered == REG_ALL)
+ {
+ return (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS);
+ }
+ return false;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::ForceNickChange()");
+ return false;
+ }
+}
+
+void userrec::SetSockAddr(int protocol_family, const char* ip, int port)
+{
+ switch (protocol_family)
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ sockaddr_in6* sin = new sockaddr_in6;
+ sin->sin6_family = AF_INET6;
+ sin->sin6_port = port;
+ inet_pton(AF_INET6, ip, &sin->sin6_addr);
+ this->ip = (sockaddr*)sin;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = new sockaddr_in;
+ sin->sin_family = AF_INET;
+ sin->sin_port = port;
+ inet_pton(AF_INET, ip, &sin->sin_addr);
+ this->ip = (sockaddr*)sin;
+ }
+ break;
+ default:
+ ServerInstance->Log(DEBUG,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
+ break;
+ }
+}
+
+int userrec::GetPort()
+{
+ if (this->ip == NULL)
+ return 0;
+
+ switch (this->GetProtocolFamily())
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ sockaddr_in6* sin = (sockaddr_in6*)this->ip;
+ return sin->sin6_port;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ return sin->sin_port;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int userrec::GetProtocolFamily()
+{
+ if (this->ip == NULL)
+ return 0;
+
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ return sin->sin_family;
+}
+
+const char* userrec::GetIPString()
+{
+ static char buf[1024];
+
+ if (this->ip == NULL)
+ return "";
+
+ switch (this->GetProtocolFamily())
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ static char temp[1024];
+
+ sockaddr_in6* sin = (sockaddr_in6*)this->ip;
+ inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
+ /* IP addresses starting with a : on irc are a Bad Thing (tm) */
+ if (*buf == ':')
+ {
+ strlcpy(&temp[1], buf, sizeof(temp) - 1);
+ *temp = '0';
+ return temp;
+ }
+ return buf;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
+ return buf;
+ }
+ break;
+ default:
+ break;
+ }
+ return "";
+}
+
+const char* userrec::GetIPString(char* buf)
+{
+ if (this->ip == NULL)
+ {
+ *buf = 0;
+ return buf;
+ }
+
+ switch (this->GetProtocolFamily())
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ static char temp[1024];
+
+ sockaddr_in6* sin = (sockaddr_in6*)this->ip;
+ inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
+ /* IP addresses starting with a : on irc are a Bad Thing (tm) */
+ if (*buf == ':')
+ {
+ strlcpy(&temp[1], buf, sizeof(temp) - 1);
+ *temp = '0';
+ strlcpy(buf, temp, sizeof(temp));
+ }
+ return buf;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
+ return buf;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return "";
+}
+
+/** NOTE: We cannot pass a const reference to this method.
+ * The string is changed by the workings of the method,
+ * so that if we pass const ref, we end up copying it to
+ * something we can change anyway. Makes sense to just let
+ * the compiler do that copy for us.
+ */
+void userrec::Write(std::string text)
+{
+#ifdef WINDOWS
+ if ((this->fd < 0) || (this->m_internalFd > MAX_DESCRIPTORS))
+#else
+ if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS))
+#endif
+ return;
+
+ try
+ {
+ /* ServerInstance->Log(DEBUG,"C[%d] <- %s", this->GetFd(), text.c_str());
+ * WARNING: The above debug line is VERY loud, do NOT
+ * enable it till we have a good way of filtering it
+ * out of the logs (e.g. 1.2 would be good).
+ */
+ text.append("\r\n");
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::Write() std::string::append");
+ return;
+ }
+
+ if (ServerInstance->Config->GetIOHook(this->GetPort()))
+ {
+ try
+ {
+ /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
+ * implement their own buffering mechanisms
+ */
+ ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length());
+ }
+ catch (CoreException& modexcept)
+ {
+ ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+ else
+ {
+ this->AddWriteBuf(text);
+ }
+ ServerInstance->stats->statsSent += text.length();
+ this->ServerInstance->SE->WantWrite(this);
+}
+
+/** Write()
+ */
+void userrec::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));
+}
+
+void userrec::WriteServ(const std::string& text)
+{
+ char textbuffer[MAXBUF];
+
+ snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str());
+ this->Write(std::string(textbuffer));
+}
+
+/** WriteServ()
+ * Same as Write(), except `text' is prefixed with `:server.name '.
+ */
+void userrec::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));
+}
+
+
+void userrec::WriteFrom(userrec *user, const std::string &text)
+{
+ char tb[MAXBUF];
+
+ snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
+
+ this->Write(std::string(tb));
+}
+
+
+/* write text from an originating user to originating user */
+
+void userrec::WriteFrom(userrec *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));
+}
+
+
+/* write text to an destination user from a source user (e.g. user privmsg) */
+
+void userrec::WriteTo(userrec *dest, const char *data, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, data);
+ vsnprintf(textbuffer, MAXBUF, data, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteTo(dest, std::string(textbuffer));
+}
+
+void userrec::WriteTo(userrec *dest, const std::string &data)
+{
+ dest->WriteFrom(this, data);
+}
+
+
+void userrec::WriteCommon(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ if (this->registered != REG_ALL)
+ return;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteCommon(std::string(textbuffer));
+}
+
+void userrec::WriteCommon(const std::string &text)
+{
+ try
+ {
+ bool sent_to_at_least_one = false;
+ char tb[MAXBUF];
+
+ if (this->registered != REG_ALL)
+ return;
+
+ uniq_id++;
+
+ /* We dont want to be doing this n times, just once */
+ snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
+ std::string out = tb;
+
+ for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
+ {
+ CUList* ulist = v->first->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
+ {
+ already_sent[i->first->fd] = uniq_id;
+ i->first->Write(out);
+ sent_to_at_least_one = true;
+ }
+ }
+ }
+
+ /*
+ * if the user was not in any channels, no users will receive the text. Make sure the user
+ * receives their OWN message for WriteCommon
+ */
+ if (!sent_to_at_least_one)
+ {
+ this->Write(std::string(tb));
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::WriteCommon()");
+ }
+}
+
+
+/* write a formatted string to all users who share at least one common
+ * channel, NOT including the source user e.g. for use in QUIT
+ */
+
+void userrec::WriteCommonExcept(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteCommonExcept(std::string(textbuffer));
+}
+
+void userrec::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
+{
+ char tb1[MAXBUF];
+ char tb2[MAXBUF];
+
+ if (this->registered != REG_ALL)
+ return;
+
+ uniq_id++;
+ snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str());
+ snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str());
+ std::string out1 = tb1;
+ std::string out2 = tb2;
+
+ for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
+ {
+ CUList *ulist = v->first->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (this != i->first)
+ {
+ if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
+ {
+ already_sent[i->first->fd] = uniq_id;
+ i->first->Write(IS_OPER(i->first) ? out2 : out1);
+ }
+ }
+ }
+ }
+}
+
+void userrec::WriteCommonExcept(const std::string &text)
+{
+ char tb1[MAXBUF];
+ std::string out1;
+
+ if (this->registered != REG_ALL)
+ return;
+
+ uniq_id++;
+ snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
+ out1 = tb1;
+
+ for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
+ {
+ CUList *ulist = v->first->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (this != i->first)
+ {
+ if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
+ {
+ already_sent[i->first->fd] = uniq_id;
+ i->first->Write(out1);
+ }
+ }
+ }
+ }
+
+}
+
+void userrec::WriteWallOps(const std::string &text)
+{
+ if (!IS_OPER(this) && IS_LOCAL(this))
+ return;
+
+ std::string wallop("WALLOPS :");
+ wallop.append(text);
+
+ for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
+ {
+ userrec* t = *i;
+ if (t->modes[UM_WALLOPS])
+ this->WriteTo(t,wallop);
+ }
+}
+
+void userrec::WriteWallOps(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteWallOps(std::string(textbuffer));
+}
+
+/* return 0 or 1 depending if users u and u2 share one or more common channels
+ * (used by QUIT, NICK etc which arent channel specific notices)
+ *
+ * The old algorithm in 1.0 for this was relatively inefficient, iterating over
+ * the first users channels then the second users channels within the outer loop,
+ * therefore it was a maximum of x*y iterations (upon returning 0 and checking
+ * all possible iterations). However this new function instead checks against the
+ * channel's userlist in the inner loop which is a std::map<userrec*,userrec*>
+ * and saves us time as we already know what pointer value we are after.
+ * Don't quote me on the maths as i am not a mathematician or computer scientist,
+ * but i believe this algorithm is now x+(log y) maximum iterations instead.
+ */
+bool userrec::SharesChannelWith(userrec *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++)
+ {
+ /* 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->first->HasUser(other))
+ return true;
+ }
+ return false;
+}
+
+bool userrec::ChangeName(const char* gecos)
+{
+ if (!strcmp(gecos, this->fullname))
+ return true;
+
+ if (IS_LOCAL(this))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos));
+ if (MOD_RESULT)
+ return false;
+ FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
+ }
+ strlcpy(this->fullname,gecos,MAXGECOS+1);
+
+ return true;
+}
+
+bool userrec::ChangeDisplayedHost(const char* host)
+{
+ if (!strcmp(host, this->dhost))
+ return true;
+
+ if (IS_LOCAL(this))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host));
+ if (MOD_RESULT)
+ return false;
+ FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host));
+ }
+ if (this->ServerInstance->Config->CycleHosts)
+ this->WriteCommonExcept("QUIT :Changing hosts");
+
+ /* Fix by Om: userrec::dhost is 65 long, this was truncating some long hosts */
+ strlcpy(this->dhost,host,64);
+
+ this->InvalidateCache();
+
+ if (this->ServerInstance->Config->CycleHosts)
+ {
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
+ std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
+ if (n.length() > 0)
+ i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
+ }
+ }
+
+ if (IS_LOCAL(this))
+ this->WriteServ("396 %s %s :is now your displayed host",this->nick,this->dhost);
+
+ return true;
+}
+
+bool userrec::ChangeIdent(const char* newident)
+{
+ if (!strcmp(newident, this->ident))
+ return true;
+
+ if (this->ServerInstance->Config->CycleHosts)
+ this->WriteCommonExcept("%s","QUIT :Changing ident");
+
+ strlcpy(this->ident, newident, IDENTMAX+2);
+
+ this->InvalidateCache();
+
+ if (this->ServerInstance->Config->CycleHosts)
+ {
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
+ std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
+ if (n.length() > 0)
+ i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
+ }
+ }
+
+ return true;
+}
+
+void userrec::SendAll(const char* command, 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(), command, textbuffer);
+ std::string fmt = formatbuffer;
+
+ for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
+ {
+ (*i)->Write(fmt);
+ }
+}
+
+
+std::string userrec::ChannelList(userrec* source)
+{
+ try
+ {
+ std::string list;
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ /* If the target is the same as the sender, let them see all their channels.
+ * If the channel is NOT private/secret OR the user shares a common channel
+ * If the user is an oper, and the <options:operspywhois> option is set.
+ */
+ if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->modes[CM_PRIVATE]) && (!i->first->modes[CM_SECRET])) || (i->first->HasUser(source))))
+ {
+ list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" ");
+ }
+ }
+ return list;
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::ChannelList()");
+ return "";
+ }
+}
+
+void userrec::SplitChanList(userrec* dest, const std::string &cl)
+{
+ std::string line;
+ std::ostringstream prefix;
+ std::string::size_type start, pos, length;
+
+ try
+ {
+ prefix << this->nick << " " << dest->nick << " :";
+ line = prefix.str();
+ int namelen = strlen(ServerInstance->Config->ServerName) + 6;
+
+ for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
+ {
+ length = (pos == std::string::npos) ? cl.length() : pos;
+
+ if (line.length() + namelen + length - start > 510)
+ {
+ ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
+ line = prefix.str();
+ }
+
+ if(pos == std::string::npos)
+ {
+ line.append(cl.substr(start, length - start));
+ break;
+ }
+ else
+ {
+ line.append(cl.substr(start, length - start + 1));
+ }
+ }
+
+ if (line.length())
+ {
+ ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::SplitChanList()");
+ }
+}
+
+
+/* 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* userrec::GetClass()
+{
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
+ {
+ if (i->GetPort())
+ {
+ if (this->GetPort() == i->GetPort())
+ return &(*i);
+ else
+ continue;
+ }
+ else
+ return &(*i);
+ }
+ }
+ return NULL;
+}
+
+void userrec::PurgeEmptyChannels()
+{
+ std::vector<chanrec*> to_delete;
+
+ // firstly decrement the count on each channel
+ for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
+ {
+ f->first->RemoveAllPrefixes(this);
+ if (f->first->DelUser(this) == 0)
+ {
+ /* No users left in here, mark it for deletion */
+ try
+ {
+ to_delete.push_back(f->first);
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::PurgeEmptyChannels to_delete.push_back()");
+ }
+ }
+ }
+
+ for (std::vector<chanrec*>::iterator n = to_delete.begin(); n != to_delete.end(); n++)
+ {
+ chanrec* thischan = *n;
+ chan_hash::iterator i2 = ServerInstance->chanlist->find(thischan->name);
+ if (i2 != ServerInstance->chanlist->end())
+ {
+ FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second));
+ DELETE(i2->second);
+ ServerInstance->chanlist->erase(i2);
+ this->chans.erase(*n);
+ }
+ }
+
+ this->UnOper();
+}
+
+void userrec::ShowMOTD()
+{
+ if (!ServerInstance->Config->MOTD.size())
+ {
+ this->WriteServ("422 %s :Message of the day file is missing.",this->nick);
+ return;
+ }
+ this->WriteServ("375 %s :%s message of the day", this->nick, ServerInstance->Config->ServerName);
+
+ for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++)
+ this->WriteServ("372 %s :- %s",this->nick,i->c_str());
+
+ this->WriteServ("376 %s :End of message of the day.", this->nick);
+}
+
+void userrec::ShowRULES()
+{
+ if (!ServerInstance->Config->RULES.size())
+ {
+ this->WriteServ("NOTICE %s :Rules file is missing.",this->nick);
+ return;
+ }
+ this->WriteServ("NOTICE %s :%s rules",this->nick,ServerInstance->Config->ServerName);
+
+ for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++)
+ this->WriteServ("NOTICE %s :%s",this->nick,i->c_str());
+
+ this->WriteServ("NOTICE %s :End of %s rules.",this->nick,ServerInstance->Config->ServerName);
+}
+
+void userrec::HandleEvent(EventType et, int errornum)
+{
+ /* WARNING: May delete this user! */
+ int thisfd = this->GetFd();
+
+ try
+ {
+ switch (et)
+ {
+ case EVENT_READ:
+ ServerInstance->ProcessUser(this);
+ break;
+ case EVENT_WRITE:
+ this->FlushWriteBuf();
+ break;
+ case EVENT_ERROR:
+ /** This should be safe, but dont DARE do anything after it -- Brain */
+ this->SetWriteError(errornum ? strerror(errornum) : "EOF from client");
+ break;
+ }
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in userrec::HandleEvent intercepted");
+ }
+
+ /* If the user has raised an error whilst being processed, quit them now we're safe to */
+ if ((ServerInstance->SE->GetRef(thisfd) == this))
+ {
+ if (!WriteError.empty())
+ {
+ userrec::QuitUser(ServerInstance, this, GetWriteError());
+ }
+ }
+}
+
+void userrec::SetOperQuit(const std::string &oquit)
+{
+ if (operquit)
+ return;
+
+ operquit = strdup(oquit.c_str());
+}
+
+const char* userrec::GetOperQuit()
+{
+ return operquit ? operquit : "";
+}
+
+VisData::VisData()
+{
+}
+
+VisData::~VisData()
+{
+}
+
+bool VisData::VisibleTo(userrec* user)
+{
+ return true;
+}
+
diff --git a/src/version.sh b/src/version.sh
index bb0c9124d..e9733c2a4 100755
--- a/src/version.sh
+++ b/src/version.sh
@@ -1 +1,2 @@
-#!sh echo "InspIRCd-1.1.9+IsleOfMull" \ No newline at end of file
+#!sh
+echo "InspIRCd-1.1.9+IsleOfMull"
diff --git a/src/wildcard.cpp b/src/wildcard.cpp
index eeb6190f2..8587f1714 100644
--- a/src/wildcard.cpp
+++ b/src/wildcard.cpp
@@ -1 +1,148 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include <string> #include "hashcomp.h" #include "inspstring.h" using irc::sockets::MatchCIDR; // Wed 27 Apr 2005 - Brain // I've taken our our old wildcard routine - // although comprehensive, it was topheavy and very // slow, and ate masses of cpu when doing lots of // comparisons. This is the 'de-facto' routine used // by many, nobody really knows who wrote it first // or what license its under, i've seen examples of it // (unattributed to any author) all over the 'net. // For now, we'll just consider this public domain. CoreExport bool csmatch(const char *str, const char *mask) { unsigned char *cp = NULL, *mp = NULL; unsigned char* string = (unsigned char*)str; unsigned char* wild = (unsigned char*)mask; while ((*string) && (*wild != '*')) { if ((*wild != *string) && (*wild != '?')) { return 0; } wild++; string++; } while (*string) { if (*wild == '*') { if (!*++wild) { return 1; } mp = wild; cp = string+1; } else if ((*wild == *string) || (*wild == '?')) { wild++; string++; } else { wild = mp; string = cp++; } } while (*wild == '*') { wild++; } return !*wild; } CoreExport bool match(const char *str, const char *mask) { unsigned char *cp = NULL, *mp = NULL; unsigned char* string = (unsigned char*)str; unsigned char* wild = (unsigned char*)mask; while ((*string) && (*wild != '*')) { if ((lowermap[*wild] != lowermap[*string]) && (*wild != '?')) { return 0; } wild++; string++; } while (*string) { if (*wild == '*') { if (!*++wild) { return 1; } mp = wild; cp = string+1; } else if ((lowermap[*wild] == lowermap[*string]) || (*wild == '?')) { wild++; string++; } else { wild = mp; string = cp++; } } while (*wild == '*') { wild++; } return !*wild; } /* Overloaded function that has the option of using cidr */ CoreExport bool match(const char *str, const char *mask, bool use_cidr_match) { if (use_cidr_match && MatchCIDR(str, mask, true)) return true; return match(str, mask); } CoreExport bool match(bool case_sensitive, const char *str, const char *mask, bool use_cidr_match) { if (use_cidr_match && MatchCIDR(str, mask, true)) return true; return csmatch(str, mask); } CoreExport bool match(bool case_sensitive, const char *str, const char *mask) { return case_sensitive ? csmatch(str, mask) : match(str, mask); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include <string>
+#include "hashcomp.h"
+#include "inspstring.h"
+
+using irc::sockets::MatchCIDR;
+
+// Wed 27 Apr 2005 - Brain
+// I've taken our our old wildcard routine -
+// although comprehensive, it was topheavy and very
+// slow, and ate masses of cpu when doing lots of
+// comparisons. This is the 'de-facto' routine used
+// by many, nobody really knows who wrote it first
+// or what license its under, i've seen examples of it
+// (unattributed to any author) all over the 'net.
+// For now, we'll just consider this public domain.
+
+CoreExport bool csmatch(const char *str, const char *mask)
+{
+ unsigned char *cp = NULL, *mp = NULL;
+ unsigned char* string = (unsigned char*)str;
+ unsigned char* wild = (unsigned char*)mask;
+
+ while ((*string) && (*wild != '*'))
+ {
+ if ((*wild != *string) && (*wild != '?'))
+ {
+ return 0;
+ }
+ wild++;
+ string++;
+ }
+
+ while (*string)
+ {
+ if (*wild == '*')
+ {
+ if (!*++wild)
+ {
+ return 1;
+ }
+ mp = wild;
+ cp = string+1;
+ }
+ else
+ if ((*wild == *string) || (*wild == '?'))
+ {
+ wild++;
+ string++;
+ }
+ else
+ {
+ wild = mp;
+ string = cp++;
+ }
+
+ }
+
+ while (*wild == '*')
+ {
+ wild++;
+ }
+
+ return !*wild;
+}
+
+CoreExport bool match(const char *str, const char *mask)
+{
+ unsigned char *cp = NULL, *mp = NULL;
+ unsigned char* string = (unsigned char*)str;
+ unsigned char* wild = (unsigned char*)mask;
+
+ while ((*string) && (*wild != '*'))
+ {
+ if ((lowermap[*wild] != lowermap[*string]) && (*wild != '?'))
+ {
+ return 0;
+ }
+ wild++;
+ string++;
+ }
+
+ while (*string)
+ {
+ if (*wild == '*')
+ {
+ if (!*++wild)
+ {
+ return 1;
+ }
+ mp = wild;
+ cp = string+1;
+ }
+ else
+ if ((lowermap[*wild] == lowermap[*string]) || (*wild == '?'))
+ {
+ wild++;
+ string++;
+ }
+ else
+ {
+ wild = mp;
+ string = cp++;
+ }
+
+ }
+
+ while (*wild == '*')
+ {
+ wild++;
+ }
+
+ return !*wild;
+}
+
+/* Overloaded function that has the option of using cidr */
+CoreExport bool match(const char *str, const char *mask, bool use_cidr_match)
+{
+ if (use_cidr_match && MatchCIDR(str, mask, true))
+ return true;
+ return match(str, mask);
+}
+
+CoreExport bool match(bool case_sensitive, const char *str, const char *mask, bool use_cidr_match)
+{
+ if (use_cidr_match && MatchCIDR(str, mask, true))
+ return true;
+ return csmatch(str, mask);
+}
+
+CoreExport bool match(bool case_sensitive, const char *str, const char *mask)
+{
+ return case_sensitive ? csmatch(str, mask) : match(str, mask);
+}
+
diff --git a/src/xline.cpp b/src/xline.cpp
index 87b1d3e5e..def9db42b 100644
--- a/src/xline.cpp
+++ b/src/xline.cpp
@@ -1 +1,897 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "users.h" #include "modules.h" #include "wildcard.h" #include "xline.h" /* Version two, now with optimized expiry! * * Because the old way was horrendously slow, the new way of expiring xlines is very * very efficient. I have improved the efficiency of the algorithm in two ways: * * (1) There are now two lists of items for each linetype. One list holds temporary * items, and the other list holds permanent items (ones which will expire). * Items which are on the permanent list are NEVER checked at all by the * expire_lines() function. * (2) The temporary xline lists are always kept in strict numerical order, keyed by * current time + duration. This means that the line which is due to expire the * soonest is always pointed at by vector::begin(), so a simple while loop can * very efficiently, very quickly and above all SAFELY pick off the first few * items in the vector which need zapping. * * -- Brain */ bool InitXLine(ServerConfig* conf, const char* tag) { return true; } bool DoneZLine(ServerConfig* conf, const char* tag) { conf->GetInstance()->XLines->apply_lines(APPLY_ZLINES|APPLY_PERM_ONLY); return true; } bool DoneQLine(ServerConfig* conf, const char* tag) { conf->GetInstance()->XLines->apply_lines(APPLY_QLINES|APPLY_PERM_ONLY); return true; } bool DoneKLine(ServerConfig* conf, const char* tag) { conf->GetInstance()->XLines->apply_lines(APPLY_KLINES|APPLY_PERM_ONLY); return true; } bool DoneELine(ServerConfig* conf, const char* tag) { /* Yes, this is supposed to do nothing, we dont 'apply' these */ return true; } bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* reason = values[0].GetString(); const char* ipmask = values[1].GetString(); conf->GetInstance()->XLines->add_zline(0,"<Config>",reason,ipmask); return true; } bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* reason = values[0].GetString(); const char* nick = values[1].GetString(); conf->GetInstance()->XLines->add_qline(0,"<Config>",reason,nick); return true; } bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* reason = values[0].GetString(); const char* host = values[1].GetString(); conf->GetInstance()->XLines->add_kline(0,"<Config>",reason,host); return true; } bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) { const char* reason = values[0].GetString(); const char* host = values[1].GetString(); conf->GetInstance()->XLines->add_eline(0,"<Config>",reason,host); return true; } IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host) { IdentHostPair n = std::make_pair<std::string,std::string>("*","*"); std::string::size_type x = ident_and_host.find('@'); if (x != std::string::npos) { n.second = ident_and_host.substr(x + 1,ident_and_host.length()); n.first = ident_and_host.substr(0, x); if (!n.first.length()) n.first.assign("*"); if (!n.second.length()) n.second.assign("*"); } else { n.second = ident_and_host; } return n; } // adds a g:line bool XLineManager::add_gline(long duration, const char* source,const char* reason,const char* hostmask) { IdentHostPair ih = IdentSplit(hostmask); if (del_gline(hostmask, true)) return false; GLine* item = new GLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str()); if (duration) { glines.push_back(item); sort(glines.begin(), glines.end(),XLineManager::GSortComparison); } else { pglines.push_back(item); } return true; } // adds an e:line (exception to bans) bool XLineManager::add_eline(long duration, const char* source, const char* reason, const char* hostmask) { IdentHostPair ih = IdentSplit(hostmask); if (del_eline(hostmask, true)) return false; ELine* item = new ELine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str()); if (duration) { elines.push_back(item); sort(elines.begin(), elines.end(),XLineManager::ESortComparison); } else { pelines.push_back(item); } return true; } // adds a q:line bool XLineManager::add_qline(long duration, const char* source, const char* reason, const char* nickname) { if (del_qline(nickname, true)) return false; QLine* item = new QLine(ServerInstance->Time(), duration, source, reason, nickname); if (duration) { qlines.push_back(item); sort(qlines.begin(), qlines.end(),XLineManager::QSortComparison); } else { pqlines.push_back(item); } return true; } // adds a z:line bool XLineManager::add_zline(long duration, const char* source, const char* reason, const char* ipaddr) { if (strchr(ipaddr,'@')) { while (*ipaddr != '@') ipaddr++; ipaddr++; } if (del_zline(ipaddr, true)) return false; ZLine* item = new ZLine(ServerInstance->Time(), duration, source, reason, ipaddr); if (duration) { zlines.push_back(item); sort(zlines.begin(), zlines.end(),XLineManager::ZSortComparison); } else { pzlines.push_back(item); } return true; } // adds a k:line bool XLineManager::add_kline(long duration, const char* source, const char* reason, const char* hostmask) { IdentHostPair ih = IdentSplit(hostmask); if (del_kline(hostmask, true)) return false; KLine* item = new KLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str()); if (duration) { klines.push_back(item); sort(klines.begin(), klines.end(),XLineManager::KSortComparison); } else { pklines.push_back(item); } return true; } // deletes a g:line, returns true if the line existed and was removed bool XLineManager::del_gline(const char* hostmask, bool simulate) { IdentHostPair ih = IdentSplit(hostmask); for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) { if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) { if (!simulate) { delete *i; glines.erase(i); } return true; } } for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) { if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) { if (!simulate) { delete *i; pglines.erase(i); } return true; } } return false; } // deletes a e:line, returns true if the line existed and was removed bool XLineManager::del_eline(const char* hostmask, bool simulate) { IdentHostPair ih = IdentSplit(hostmask); for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) { if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) { if (!simulate) { delete *i; elines.erase(i); } return true; } } for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) { if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) { if (!simulate) { delete *i; pelines.erase(i); } return true; } } return false; } // deletes a q:line, returns true if the line existed and was removed bool XLineManager::del_qline(const char* nickname, bool simulate) { for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) { if (!strcasecmp(nickname,(*i)->nick)) { if (!simulate) { delete *i; qlines.erase(i); } return true; } } for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) { if (!strcasecmp(nickname,(*i)->nick)) { if (!simulate) { delete *i; pqlines.erase(i); } return true; } } return false; } // deletes a z:line, returns true if the line existed and was removed bool XLineManager::del_zline(const char* ipaddr, bool simulate) { for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) { if (!strcasecmp(ipaddr,(*i)->ipaddr)) { if (!simulate) { delete *i; zlines.erase(i); } return true; } } for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) { if (!strcasecmp(ipaddr,(*i)->ipaddr)) { if (!simulate) { delete *i; pzlines.erase(i); } return true; } } return false; } // deletes a k:line, returns true if the line existed and was removed bool XLineManager::del_kline(const char* hostmask, bool simulate) { IdentHostPair ih = IdentSplit(hostmask); for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++) { if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) { if (!simulate) { delete *i; klines.erase(i); } return true; } } for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++) { if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) { if (!simulate) { delete *i; pklines.erase(i); } return true; } } return false; } // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match QLine* XLineManager::matches_qline(const char* nick, bool permonly) { if ((qlines.empty()) && (pqlines.empty())) return NULL; if (!permonly) { for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) if (match(nick,(*i)->nick)) return (*i); } for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) if (match(nick,(*i)->nick)) return (*i); return NULL; } // returns a pointer to the reason if a host matches a gline, NULL if it didnt match GLine* XLineManager::matches_gline(userrec* user, bool permonly) { if ((glines.empty()) && (pglines.empty())) return NULL; if (!permonly) { for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) { if ((match(user->ident,(*i)->identmask))) { if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) { return (*i); } } } } for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) { if ((match(user->ident,(*i)->identmask))) { if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) { return (*i); } } } return NULL; } ELine* XLineManager::matches_exception(userrec* user, bool permonly) { if ((elines.empty()) && (pelines.empty())) return NULL; char host2[MAXBUF]; snprintf(host2,MAXBUF,"*@%s",user->host); if (!permonly) { for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) { if ((match(user->ident,(*i)->identmask))) { if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) { return (*i); } } } } for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) { if ((match(user->ident,(*i)->identmask))) { if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) { return (*i); } } } return NULL; } void XLineManager::gline_set_creation_time(const char* host, time_t create_time) { for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) { if (!strcasecmp(host,(*i)->hostmask)) { (*i)->set_time = create_time; (*i)->expiry = create_time + (*i)->duration; return; } } for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) { if (!strcasecmp(host,(*i)->hostmask)) { (*i)->set_time = create_time; return; } } return ; } void XLineManager::eline_set_creation_time(const char* host, time_t create_time) { for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) { if (!strcasecmp(host,(*i)->hostmask)) { (*i)->set_time = create_time; (*i)->expiry = create_time + (*i)->duration; return; } } for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) { if (!strcasecmp(host,(*i)->hostmask)) { (*i)->set_time = create_time; return; } } return; } void XLineManager::qline_set_creation_time(const char* nick, time_t create_time) { for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) { if (!strcasecmp(nick,(*i)->nick)) { (*i)->set_time = create_time; (*i)->expiry = create_time + (*i)->duration; return; } } for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) { if (!strcasecmp(nick,(*i)->nick)) { (*i)->set_time = create_time; return; } } return; } void XLineManager::zline_set_creation_time(const char* ip, time_t create_time) { for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) { if (!strcasecmp(ip,(*i)->ipaddr)) { (*i)->set_time = create_time; (*i)->expiry = create_time + (*i)->duration; return; } } for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) { if (!strcasecmp(ip,(*i)->ipaddr)) { (*i)->set_time = create_time; return; } } return; } // returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match ZLine* XLineManager::matches_zline(const char* ipaddr, bool permonly) { if ((zlines.empty()) && (pzlines.empty())) return NULL; if (!permonly) { for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) if (match(ipaddr,(*i)->ipaddr, true)) return (*i); } for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) if (match(ipaddr,(*i)->ipaddr, true)) return (*i); return NULL; } // returns a pointer to the reason if a host matches a kline, NULL if it didnt match KLine* XLineManager::matches_kline(userrec* user, bool permonly) { if ((klines.empty()) && (pklines.empty())) return NULL; if (!permonly) { for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++) { if ((match(user->ident,(*i)->identmask))) { if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) { return (*i); } } } } for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++) { if ((match(user->ident,(*i)->identmask))) { if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) { return (*i); } } } return NULL; } bool XLineManager::GSortComparison ( const GLine* one, const GLine* two ) { return (one->expiry) < (two->expiry); } bool XLineManager::ESortComparison ( const ELine* one, const ELine* two ) { return (one->expiry) < (two->expiry); } bool XLineManager::ZSortComparison ( const ZLine* one, const ZLine* two ) { return (one->expiry) < (two->expiry); } bool XLineManager::KSortComparison ( const KLine* one, const KLine* two ) { return (one->expiry) < (two->expiry); } bool XLineManager::QSortComparison ( const QLine* one, const QLine* two ) { return (one->expiry) < (two->expiry); } // removes lines that have expired void XLineManager::expire_lines() { time_t current = ServerInstance->Time(); /* Because we now store all our XLines in sorted order using ((*i)->duration + (*i)->set_time) as a key, this * means that to expire the XLines we just need to do a while, picking off the top few until there are * none left at the head of the queue that are after the current time. */ while ((glines.size()) && (current > (*glines.begin())->expiry)) { std::vector<GLine*>::iterator i = glines.begin(); ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration); glines.erase(i); } while ((elines.size()) && (current > (*elines.begin())->expiry)) { std::vector<ELine*>::iterator i = elines.begin(); ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration); elines.erase(i); } while ((zlines.size()) && (current > (*zlines.begin())->expiry)) { std::vector<ZLine*>::iterator i = zlines.begin(); ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %d seconds ago)",(*i)->ipaddr,(*i)->source,(*i)->duration); zlines.erase(i); } while ((klines.size()) && (current > (*klines.begin())->expiry)) { std::vector<KLine*>::iterator i = klines.begin(); ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration); klines.erase(i); } while ((qlines.size()) && (current > (*qlines.begin())->expiry)) { std::vector<QLine*>::iterator i = qlines.begin(); ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Q-Line %s (set by %s %d seconds ago)",(*i)->nick,(*i)->source,(*i)->duration); qlines.erase(i); } } // applies lines, removing clients and changing nicks etc as applicable void XLineManager::apply_lines(const int What) { if (!What) return; if (What & APPLY_PERM_ONLY) { char reason[MAXBUF]; if ((!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size())) return; XLine* check = NULL; for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++) { userrec* u = (userrec*)(*u2); if (elines.size() || pelines.size()) if (matches_exception(u)) continue; if ((What & APPLY_GLINES) && pglines.size()) { if ((check = matches_gline(u,true))) { snprintf(reason,MAXBUF,"G-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } if ((What & APPLY_KLINES) && pklines.size()) { if ((check = matches_kline(u,true))) { snprintf(reason,MAXBUF,"K-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } if ((What & APPLY_QLINES) && pqlines.size()) { if ((check = matches_qline(u->nick,true))) { snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } if ((What & APPLY_ZLINES) && pzlines.size()) { if ((check = matches_zline(u->GetIPString(),true))) { snprintf(reason,MAXBUF,"Z-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } } } else { char reason[MAXBUF]; if ((!glines.size()) && (!klines.size()) && (!zlines.size()) && (!qlines.size()) && (!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size())) return; XLine* check = NULL; for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++) { userrec* u = (userrec*)(*u2); if (elines.size() || pelines.size()) { // ignore people matching exempts if (matches_exception(u)) continue; } if ((What & APPLY_GLINES) && (glines.size() || pglines.size())) { if ((check = matches_gline(u))) { snprintf(reason,MAXBUF,"G-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } if ((What & APPLY_KLINES) && (klines.size() || pklines.size())) { if ((check = matches_kline(u))) { snprintf(reason,MAXBUF,"K-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } if ((What & APPLY_QLINES) && (qlines.size() || pqlines.size())) { if ((check = matches_qline(u->nick))) { snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } if ((What & APPLY_ZLINES) && (zlines.size() || pzlines.size())) { if ((check = matches_zline(u->GetIPString()))) { snprintf(reason,MAXBUF,"Z-Lined: %s", check->reason); if (*ServerInstance->Config->MoronBanner) u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); if (ServerInstance->Config->HideBans) ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason); else ServerInstance->GlobalCulls.AddItem(u, reason); } } } } } void XLineManager::stats_k(userrec* user, string_list &results) { std::string sn = ServerInstance->Config->ServerName; for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++) results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++) results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); } void XLineManager::stats_g(userrec* user, string_list &results) { std::string sn = ServerInstance->Config->ServerName; for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); } void XLineManager::stats_q(userrec* user, string_list &results) { std::string sn = ServerInstance->Config->ServerName; for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); } void XLineManager::stats_z(userrec* user, string_list &results) { std::string sn = ServerInstance->Config->ServerName; for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); } void XLineManager::stats_e(userrec* user, string_list &results) { std::string sn = ServerInstance->Config->ServerName; for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); } XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance) { } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "xline.h"
+
+/* Version two, now with optimized expiry!
+ *
+ * Because the old way was horrendously slow, the new way of expiring xlines is very
+ * very efficient. I have improved the efficiency of the algorithm in two ways:
+ *
+ * (1) There are now two lists of items for each linetype. One list holds temporary
+ * items, and the other list holds permanent items (ones which will expire).
+ * Items which are on the permanent list are NEVER checked at all by the
+ * expire_lines() function.
+ * (2) The temporary xline lists are always kept in strict numerical order, keyed by
+ * current time + duration. This means that the line which is due to expire the
+ * soonest is always pointed at by vector::begin(), so a simple while loop can
+ * very efficiently, very quickly and above all SAFELY pick off the first few
+ * items in the vector which need zapping.
+ *
+ * -- Brain
+ */
+
+bool InitXLine(ServerConfig* conf, const char* tag)
+{
+ return true;
+}
+
+bool DoneZLine(ServerConfig* conf, const char* tag)
+{
+ conf->GetInstance()->XLines->apply_lines(APPLY_ZLINES|APPLY_PERM_ONLY);
+ return true;
+}
+
+bool DoneQLine(ServerConfig* conf, const char* tag)
+{
+ conf->GetInstance()->XLines->apply_lines(APPLY_QLINES|APPLY_PERM_ONLY);
+ return true;
+}
+
+bool DoneKLine(ServerConfig* conf, const char* tag)
+{
+ conf->GetInstance()->XLines->apply_lines(APPLY_KLINES|APPLY_PERM_ONLY);
+ return true;
+}
+
+bool DoneELine(ServerConfig* conf, const char* tag)
+{
+ /* Yes, this is supposed to do nothing, we dont 'apply' these */
+ return true;
+}
+
+bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* reason = values[0].GetString();
+ const char* ipmask = values[1].GetString();
+
+ conf->GetInstance()->XLines->add_zline(0,"<Config>",reason,ipmask);
+ return true;
+}
+
+bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* reason = values[0].GetString();
+ const char* nick = values[1].GetString();
+
+ conf->GetInstance()->XLines->add_qline(0,"<Config>",reason,nick);
+ return true;
+}
+
+bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* reason = values[0].GetString();
+ const char* host = values[1].GetString();
+
+ conf->GetInstance()->XLines->add_kline(0,"<Config>",reason,host);
+ return true;
+}
+
+bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
+{
+ const char* reason = values[0].GetString();
+ const char* host = values[1].GetString();
+
+ conf->GetInstance()->XLines->add_eline(0,"<Config>",reason,host);
+ return true;
+}
+
+IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
+{
+ IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
+ std::string::size_type x = ident_and_host.find('@');
+ if (x != std::string::npos)
+ {
+ n.second = ident_and_host.substr(x + 1,ident_and_host.length());
+ n.first = ident_and_host.substr(0, x);
+ if (!n.first.length())
+ n.first.assign("*");
+ if (!n.second.length())
+ n.second.assign("*");
+ }
+ else
+ {
+ n.second = ident_and_host;
+ }
+
+ return n;
+}
+
+// adds a g:line
+
+bool XLineManager::add_gline(long duration, const char* source,const char* reason,const char* hostmask)
+{
+ IdentHostPair ih = IdentSplit(hostmask);
+
+ if (del_gline(hostmask, true))
+ return false;
+
+ GLine* item = new GLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
+
+ if (duration)
+ {
+ glines.push_back(item);
+ sort(glines.begin(), glines.end(),XLineManager::GSortComparison);
+ }
+ else
+ {
+ pglines.push_back(item);
+ }
+
+ return true;
+}
+
+// adds an e:line (exception to bans)
+
+bool XLineManager::add_eline(long duration, const char* source, const char* reason, const char* hostmask)
+{
+ IdentHostPair ih = IdentSplit(hostmask);
+
+ if (del_eline(hostmask, true))
+ return false;
+
+ ELine* item = new ELine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
+
+ if (duration)
+ {
+ elines.push_back(item);
+ sort(elines.begin(), elines.end(),XLineManager::ESortComparison);
+ }
+ else
+ {
+ pelines.push_back(item);
+ }
+ return true;
+}
+
+// adds a q:line
+
+bool XLineManager::add_qline(long duration, const char* source, const char* reason, const char* nickname)
+{
+ if (del_qline(nickname, true))
+ return false;
+
+ QLine* item = new QLine(ServerInstance->Time(), duration, source, reason, nickname);
+
+ if (duration)
+ {
+ qlines.push_back(item);
+ sort(qlines.begin(), qlines.end(),XLineManager::QSortComparison);
+ }
+ else
+ {
+ pqlines.push_back(item);
+ }
+ return true;
+}
+
+// adds a z:line
+
+bool XLineManager::add_zline(long duration, const char* source, const char* reason, const char* ipaddr)
+{
+ if (strchr(ipaddr,'@'))
+ {
+ while (*ipaddr != '@')
+ ipaddr++;
+ ipaddr++;
+ }
+
+ if (del_zline(ipaddr, true))
+ return false;
+
+ ZLine* item = new ZLine(ServerInstance->Time(), duration, source, reason, ipaddr);
+
+ if (duration)
+ {
+ zlines.push_back(item);
+ sort(zlines.begin(), zlines.end(),XLineManager::ZSortComparison);
+ }
+ else
+ {
+ pzlines.push_back(item);
+ }
+ return true;
+}
+
+// adds a k:line
+
+bool XLineManager::add_kline(long duration, const char* source, const char* reason, const char* hostmask)
+{
+ IdentHostPair ih = IdentSplit(hostmask);
+
+ if (del_kline(hostmask, true))
+ return false;
+
+ KLine* item = new KLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
+
+ if (duration)
+ {
+ klines.push_back(item);
+ sort(klines.begin(), klines.end(),XLineManager::KSortComparison);
+ }
+ else
+ {
+ pklines.push_back(item);
+ }
+ return true;
+}
+
+// deletes a g:line, returns true if the line existed and was removed
+
+bool XLineManager::del_gline(const char* hostmask, bool simulate)
+{
+ IdentHostPair ih = IdentSplit(hostmask);
+ for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
+ {
+ if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ glines.erase(i);
+ }
+ return true;
+ }
+ }
+ for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
+ {
+ if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ pglines.erase(i);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// deletes a e:line, returns true if the line existed and was removed
+
+bool XLineManager::del_eline(const char* hostmask, bool simulate)
+{
+ IdentHostPair ih = IdentSplit(hostmask);
+ for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
+ {
+ if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ elines.erase(i);
+ }
+ return true;
+ }
+ }
+ for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
+ {
+ if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ pelines.erase(i);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// deletes a q:line, returns true if the line existed and was removed
+
+bool XLineManager::del_qline(const char* nickname, bool simulate)
+{
+ for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
+ {
+ if (!strcasecmp(nickname,(*i)->nick))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ qlines.erase(i);
+ }
+ return true;
+ }
+ }
+ for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
+ {
+ if (!strcasecmp(nickname,(*i)->nick))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ pqlines.erase(i);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// deletes a z:line, returns true if the line existed and was removed
+
+bool XLineManager::del_zline(const char* ipaddr, bool simulate)
+{
+ for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
+ {
+ if (!strcasecmp(ipaddr,(*i)->ipaddr))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ zlines.erase(i);
+ }
+ return true;
+ }
+ }
+ for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
+ {
+ if (!strcasecmp(ipaddr,(*i)->ipaddr))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ pzlines.erase(i);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// deletes a k:line, returns true if the line existed and was removed
+
+bool XLineManager::del_kline(const char* hostmask, bool simulate)
+{
+ IdentHostPair ih = IdentSplit(hostmask);
+ for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
+ {
+ if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ klines.erase(i);
+ }
+ return true;
+ }
+ }
+ for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++)
+ {
+ if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
+ {
+ if (!simulate)
+ {
+ delete *i;
+ pklines.erase(i);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
+
+QLine* XLineManager::matches_qline(const char* nick, bool permonly)
+{
+ if ((qlines.empty()) && (pqlines.empty()))
+ return NULL;
+ if (!permonly)
+ {
+ for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
+ if (match(nick,(*i)->nick))
+ return (*i);
+ }
+ for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
+ if (match(nick,(*i)->nick))
+ return (*i);
+ return NULL;
+}
+
+// returns a pointer to the reason if a host matches a gline, NULL if it didnt match
+
+GLine* XLineManager::matches_gline(userrec* user, bool permonly)
+{
+ if ((glines.empty()) && (pglines.empty()))
+ return NULL;
+ if (!permonly)
+ {
+ for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
+ {
+ if ((match(user->ident,(*i)->identmask)))
+ {
+ if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
+ {
+ return (*i);
+ }
+ }
+ }
+ }
+ for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
+ {
+ if ((match(user->ident,(*i)->identmask)))
+ {
+ if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
+ {
+ return (*i);
+ }
+ }
+ }
+ return NULL;
+}
+
+ELine* XLineManager::matches_exception(userrec* user, bool permonly)
+{
+ if ((elines.empty()) && (pelines.empty()))
+ return NULL;
+ char host2[MAXBUF];
+ snprintf(host2,MAXBUF,"*@%s",user->host);
+ if (!permonly)
+ {
+ for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
+ {
+ if ((match(user->ident,(*i)->identmask)))
+ {
+ if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
+ {
+ return (*i);
+ }
+ }
+ }
+ }
+ for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
+ {
+ if ((match(user->ident,(*i)->identmask)))
+ {
+ if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
+ {
+ return (*i);
+ }
+ }
+ }
+ return NULL;
+}
+
+
+void XLineManager::gline_set_creation_time(const char* host, time_t create_time)
+{
+ for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
+ {
+ if (!strcasecmp(host,(*i)->hostmask))
+ {
+ (*i)->set_time = create_time;
+ (*i)->expiry = create_time + (*i)->duration;
+ return;
+ }
+ }
+ for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
+ {
+ if (!strcasecmp(host,(*i)->hostmask))
+ {
+ (*i)->set_time = create_time;
+ return;
+ }
+ }
+ return ;
+}
+
+void XLineManager::eline_set_creation_time(const char* host, time_t create_time)
+{
+ for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
+ {
+ if (!strcasecmp(host,(*i)->hostmask))
+ {
+ (*i)->set_time = create_time;
+ (*i)->expiry = create_time + (*i)->duration;
+ return;
+ }
+ }
+ for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
+ {
+ if (!strcasecmp(host,(*i)->hostmask))
+ {
+ (*i)->set_time = create_time;
+ return;
+ }
+ }
+ return;
+}
+
+void XLineManager::qline_set_creation_time(const char* nick, time_t create_time)
+{
+ for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
+ {
+ if (!strcasecmp(nick,(*i)->nick))
+ {
+ (*i)->set_time = create_time;
+ (*i)->expiry = create_time + (*i)->duration;
+ return;
+ }
+ }
+ for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
+ {
+ if (!strcasecmp(nick,(*i)->nick))
+ {
+ (*i)->set_time = create_time;
+ return;
+ }
+ }
+ return;
+}
+
+void XLineManager::zline_set_creation_time(const char* ip, time_t create_time)
+{
+ for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
+ {
+ if (!strcasecmp(ip,(*i)->ipaddr))
+ {
+ (*i)->set_time = create_time;
+ (*i)->expiry = create_time + (*i)->duration;
+ return;
+ }
+ }
+ for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
+ {
+ if (!strcasecmp(ip,(*i)->ipaddr))
+ {
+ (*i)->set_time = create_time;
+ return;
+ }
+ }
+ return;
+}
+
+// returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match
+
+ZLine* XLineManager::matches_zline(const char* ipaddr, bool permonly)
+{
+ if ((zlines.empty()) && (pzlines.empty()))
+ return NULL;
+ if (!permonly)
+ {
+ for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
+ if (match(ipaddr,(*i)->ipaddr, true))
+ return (*i);
+ }
+ for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
+ if (match(ipaddr,(*i)->ipaddr, true))
+ return (*i);
+ return NULL;
+}
+
+// returns a pointer to the reason if a host matches a kline, NULL if it didnt match
+
+KLine* XLineManager::matches_kline(userrec* user, bool permonly)
+{
+ if ((klines.empty()) && (pklines.empty()))
+ return NULL;
+ if (!permonly)
+ {
+ for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
+ {
+ if ((match(user->ident,(*i)->identmask)))
+ {
+ if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
+ {
+ return (*i);
+ }
+ }
+ }
+ }
+ for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++)
+ {
+ if ((match(user->ident,(*i)->identmask)))
+ {
+ if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
+ {
+ return (*i);
+ }
+ }
+ }
+ return NULL;
+}
+
+bool XLineManager::GSortComparison ( const GLine* one, const GLine* two )
+{
+ return (one->expiry) < (two->expiry);
+}
+
+bool XLineManager::ESortComparison ( const ELine* one, const ELine* two )
+{
+ return (one->expiry) < (two->expiry);
+}
+
+bool XLineManager::ZSortComparison ( const ZLine* one, const ZLine* two )
+{
+ return (one->expiry) < (two->expiry);
+}
+
+bool XLineManager::KSortComparison ( const KLine* one, const KLine* two )
+{
+ return (one->expiry) < (two->expiry);
+}
+
+bool XLineManager::QSortComparison ( const QLine* one, const QLine* two )
+{
+ return (one->expiry) < (two->expiry);
+}
+
+// removes lines that have expired
+
+void XLineManager::expire_lines()
+{
+ time_t current = ServerInstance->Time();
+
+ /* Because we now store all our XLines in sorted order using ((*i)->duration + (*i)->set_time) as a key, this
+ * means that to expire the XLines we just need to do a while, picking off the top few until there are
+ * none left at the head of the queue that are after the current time.
+ */
+
+ while ((glines.size()) && (current > (*glines.begin())->expiry))
+ {
+ std::vector<GLine*>::iterator i = glines.begin();
+ ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration);
+ glines.erase(i);
+ }
+
+ while ((elines.size()) && (current > (*elines.begin())->expiry))
+ {
+ std::vector<ELine*>::iterator i = elines.begin();
+ ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration);
+ elines.erase(i);
+ }
+
+ while ((zlines.size()) && (current > (*zlines.begin())->expiry))
+ {
+ std::vector<ZLine*>::iterator i = zlines.begin();
+ ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %d seconds ago)",(*i)->ipaddr,(*i)->source,(*i)->duration);
+ zlines.erase(i);
+ }
+
+ while ((klines.size()) && (current > (*klines.begin())->expiry))
+ {
+ std::vector<KLine*>::iterator i = klines.begin();
+ ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration);
+ klines.erase(i);
+ }
+
+ while ((qlines.size()) && (current > (*qlines.begin())->expiry))
+ {
+ std::vector<QLine*>::iterator i = qlines.begin();
+ ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Q-Line %s (set by %s %d seconds ago)",(*i)->nick,(*i)->source,(*i)->duration);
+ qlines.erase(i);
+ }
+
+}
+
+// applies lines, removing clients and changing nicks etc as applicable
+
+void XLineManager::apply_lines(const int What)
+{
+ if (!What)
+ return;
+
+ if (What & APPLY_PERM_ONLY)
+ {
+ char reason[MAXBUF];
+
+ if ((!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size()))
+ return;
+
+ XLine* check = NULL;
+ for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
+ {
+ userrec* u = (userrec*)(*u2);
+
+ if (elines.size() || pelines.size())
+ if (matches_exception(u))
+ continue;
+
+ if ((What & APPLY_GLINES) && pglines.size())
+ {
+ if ((check = matches_gline(u,true)))
+ {
+ snprintf(reason,MAXBUF,"G-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+
+ if ((What & APPLY_KLINES) && pklines.size())
+ {
+ if ((check = matches_kline(u,true)))
+ {
+ snprintf(reason,MAXBUF,"K-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+
+ if ((What & APPLY_QLINES) && pqlines.size())
+ {
+ if ((check = matches_qline(u->nick,true)))
+ {
+ snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+
+ if ((What & APPLY_ZLINES) && pzlines.size())
+ {
+ if ((check = matches_zline(u->GetIPString(),true)))
+ {
+ snprintf(reason,MAXBUF,"Z-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+ }
+ }
+ else
+ {
+ char reason[MAXBUF];
+
+ if ((!glines.size()) && (!klines.size()) && (!zlines.size()) && (!qlines.size()) &&
+ (!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size()))
+ return;
+
+ XLine* check = NULL;
+ for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
+ {
+ userrec* u = (userrec*)(*u2);
+
+ if (elines.size() || pelines.size())
+ {
+ // ignore people matching exempts
+ if (matches_exception(u))
+ continue;
+ }
+ if ((What & APPLY_GLINES) && (glines.size() || pglines.size()))
+ {
+ if ((check = matches_gline(u)))
+ {
+ snprintf(reason,MAXBUF,"G-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+ if ((What & APPLY_KLINES) && (klines.size() || pklines.size()))
+ {
+ if ((check = matches_kline(u)))
+ {
+ snprintf(reason,MAXBUF,"K-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+ if ((What & APPLY_QLINES) && (qlines.size() || pqlines.size()))
+ {
+ if ((check = matches_qline(u->nick)))
+ {
+ snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+ if ((What & APPLY_ZLINES) && (zlines.size() || pzlines.size()))
+ {
+ if ((check = matches_zline(u->GetIPString())))
+ {
+ snprintf(reason,MAXBUF,"Z-Lined: %s", check->reason);
+ if (*ServerInstance->Config->MoronBanner)
+ u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
+ if (ServerInstance->Config->HideBans)
+ ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason);
+ else
+ ServerInstance->GlobalCulls.AddItem(u, reason);
+ }
+ }
+ }
+ }
+}
+
+void XLineManager::stats_k(userrec* user, string_list &results)
+{
+ std::string sn = ServerInstance->Config->ServerName;
+ for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
+ results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+ for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++)
+ results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+}
+
+void XLineManager::stats_g(userrec* user, string_list &results)
+{
+ std::string sn = ServerInstance->Config->ServerName;
+ for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
+ results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+ for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
+ results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+}
+
+void XLineManager::stats_q(userrec* user, string_list &results)
+{
+ std::string sn = ServerInstance->Config->ServerName;
+ for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
+ results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+ for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
+ results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+}
+
+void XLineManager::stats_z(userrec* user, string_list &results)
+{
+ std::string sn = ServerInstance->Config->ServerName;
+ for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
+ results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+ for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
+ results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+}
+
+void XLineManager::stats_e(userrec* user, string_list &results)
+{
+ std::string sn = ServerInstance->Config->ServerName;
+ for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
+ results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+ for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
+ results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
+}
+
+XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance)
+{
+}
diff --git a/win/cert.pem b/win/cert.pem
index 1d1298d7c..d0eaf68e7 100644
--- a/win/cert.pem
+++ b/win/cert.pem
@@ -1 +1,18 @@
------BEGIN CERTIFICATE----- MIIC1DCCAj+gAwIBAgIDcGbSMAsGCSqGSIb3DQEBBTCBgTEQMA4GA1UEBhMHRW5n bGFuZDElMCMGA1UEChMcRGVmYXVsdCBJbnNwSVJDZCBDZXJ0aWZpY2F0ZTEeMBwG A1UECxMVU2VydmVyIEFkbWluaXN0cmF0aW9uMQswCQYDVQQIEwJVSzEZMBcGA1UE AxMQaXJjLmluc3BpcmNkLm9yZzAeFw0wNzA2MTMyMTUyMTNaFw0wOTA1MTMyMTUy MTNaMIGBMRAwDgYDVQQGEwdFbmdsYW5kMSUwIwYDVQQKExxEZWZhdWx0IEluc3BJ UkNkIENlcnRpZmljYXRlMR4wHAYDVQQLExVTZXJ2ZXIgQWRtaW5pc3RyYXRpb24x CzAJBgNVBAgTAlVLMRkwFwYDVQQDExBpcmMuaW5zcGlyY2Qub3JnMIGcMAsGCSqG SIb3DQEBAQOBjAAwgYgCgYDIbKvMTTogBZxTi1yn4ncVK09Wr+F2AxP63HWTzxnE wNhcURSaUqpCzVIfcpr7/jKn+8I17MzaMvG8m+sPKngPK5WMN440p12uitkS+uzk LbJ7J/Z335ar6nZOtbIO+aTDRzUTnNHGHRgdQj4GGvx89l0u7vQM3R2f9Oe2lWlc 1wIDAQABo18wXTAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr BgEFBQcDATAPBgNVHQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQTdpXUljwHWvbEggnP BZMFhd1MvjALBgkqhkiG9w0BAQUDgYEARi9LL+mCxLWffiTHzGMO4ul0E0bXIzD5 QzFI/llFzX4+fcuZJUFPgpBFJzxOqSO9RZAXHfm7x9sUMNpFP4ir4b2phQGr0QDd 6nPHmcwuyiQISPIL3xcgrb2CuzQa/Wqmkxi5vXHf1CQQijJ1UA/FCPD6f+Dulcdq UAtrNsUBhLY= -----END CERTIFICATE----- \ No newline at end of file
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAj+gAwIBAgIDcGbSMAsGCSqGSIb3DQEBBTCBgTEQMA4GA1UEBhMHRW5n
+bGFuZDElMCMGA1UEChMcRGVmYXVsdCBJbnNwSVJDZCBDZXJ0aWZpY2F0ZTEeMBwG
+A1UECxMVU2VydmVyIEFkbWluaXN0cmF0aW9uMQswCQYDVQQIEwJVSzEZMBcGA1UE
+AxMQaXJjLmluc3BpcmNkLm9yZzAeFw0wNzA2MTMyMTUyMTNaFw0wOTA1MTMyMTUy
+MTNaMIGBMRAwDgYDVQQGEwdFbmdsYW5kMSUwIwYDVQQKExxEZWZhdWx0IEluc3BJ
+UkNkIENlcnRpZmljYXRlMR4wHAYDVQQLExVTZXJ2ZXIgQWRtaW5pc3RyYXRpb24x
+CzAJBgNVBAgTAlVLMRkwFwYDVQQDExBpcmMuaW5zcGlyY2Qub3JnMIGcMAsGCSqG
+SIb3DQEBAQOBjAAwgYgCgYDIbKvMTTogBZxTi1yn4ncVK09Wr+F2AxP63HWTzxnE
+wNhcURSaUqpCzVIfcpr7/jKn+8I17MzaMvG8m+sPKngPK5WMN440p12uitkS+uzk
+LbJ7J/Z335ar6nZOtbIO+aTDRzUTnNHGHRgdQj4GGvx89l0u7vQM3R2f9Oe2lWlc
+1wIDAQABo18wXTAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
+BgEFBQcDATAPBgNVHQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQTdpXUljwHWvbEggnP
+BZMFhd1MvjALBgkqhkiG9w0BAQUDgYEARi9LL+mCxLWffiTHzGMO4ul0E0bXIzD5
+QzFI/llFzX4+fcuZJUFPgpBFJzxOqSO9RZAXHfm7x9sUMNpFP4ir4b2phQGr0QDd
+6nPHmcwuyiQISPIL3xcgrb2CuzQa/Wqmkxi5vXHf1CQQijJ1UA/FCPD6f+Dulcdq
+UAtrNsUBhLY=
+-----END CERTIFICATE-----
diff --git a/win/colours.h b/win/colours.h
index 8c16a98bf..6a5853c21 100644
--- a/win/colours.h
+++ b/win/colours.h
@@ -1 +1,109 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #ifndef __COLOURS_H #define __COLOURS_H #define TRED FOREGROUND_RED | FOREGROUND_INTENSITY #define TGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY #define TYELLOW FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY #define TNORMAL FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE #define TWHITE TNORMAL | FOREGROUND_INTENSITY #define TBLUE FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY inline void sc(WORD color) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); } /* Handles colors in printf */ int printf_c(const char * format, ...) { // Better hope we're not multithreaded, otherwise we'll have chickens crossing the road other side to get the to :P static char message[500]; static char temp[500]; int color1, color2; /* parse arguments */ va_list ap; va_start(ap, format); vsnprintf(message, 500, format, ap); va_end(ap); /* search for unix-style escape sequences */ int t; int c = 0; const char * p = message; while (*p != 0) { if (*p == '\033') { // Escape sequence -> copy into the temp buffer, and parse the color. p++; t = 0; while ((*p) && (*p != 'm')) { temp[t++] = *p; ++p; } temp[t] = 0; p++; if (*temp == '[') { if (sscanf(temp, "[%u;%u", &color1, &color2) == 2) { switch(color2) { case 32: // Green SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY); // Yellow break; default: // Unknown // White SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); break; } } else { switch (*(temp+1)) { case '0': // Returning to normal colour. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); break; case '1': // White SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), TWHITE); break; default: char message[50]; sprintf(message, "Unknown color code: %s", temp); MessageBox(0, message, message, MB_OK); break; } } } } putchar(*p); ++c; ++p; } return c; } #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __COLOURS_H
+#define __COLOURS_H
+
+#define TRED FOREGROUND_RED | FOREGROUND_INTENSITY
+#define TGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY
+#define TYELLOW FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
+#define TNORMAL FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE
+#define TWHITE TNORMAL | FOREGROUND_INTENSITY
+#define TBLUE FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY
+
+inline void sc(WORD color) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); }
+
+/* Handles colors in printf */
+int printf_c(const char * format, ...)
+{
+ // Better hope we're not multithreaded, otherwise we'll have chickens crossing the road other side to get the to :P
+ static char message[500];
+ static char temp[500];
+ int color1, color2;
+
+ /* parse arguments */
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(message, 500, format, ap);
+ va_end(ap);
+
+ /* search for unix-style escape sequences */
+ int t;
+ int c = 0;
+ const char * p = message;
+ while (*p != 0)
+ {
+ if (*p == '\033')
+ {
+ // Escape sequence -> copy into the temp buffer, and parse the color.
+ p++;
+ t = 0;
+ while ((*p) && (*p != 'm'))
+ {
+ temp[t++] = *p;
+ ++p;
+ }
+
+ temp[t] = 0;
+ p++;
+
+ if (*temp == '[')
+ {
+ if (sscanf(temp, "[%u;%u", &color1, &color2) == 2)
+ {
+ switch(color2)
+ {
+ case 32: // Green
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY); // Yellow
+ break;
+
+ default: // Unknown
+ // White
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ break;
+ }
+ }
+ else
+ {
+ switch (*(temp+1))
+ {
+ case '0':
+ // Returning to normal colour.
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ break;
+
+ case '1':
+ // White
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), TWHITE);
+ break;
+
+ default:
+ char message[50];
+ sprintf(message, "Unknown color code: %s", temp);
+ MessageBox(0, message, message, MB_OK);
+ break;
+ }
+ }
+ }
+ }
+
+ putchar(*p);
+ ++c;
+ ++p;
+ }
+
+ return c;
+}
+
+#endif
+
diff --git a/win/configure.cpp b/win/configure.cpp
index 25c9e62cf..adb10a6b9 100644
--- a/win/configure.cpp
+++ b/win/configure.cpp
@@ -1 +1,509 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #define _CRT_SECURE_NO_DEPRECATE #include <windows.h> #include <stdio.h> #include <string> #include <time.h> #include "colours.h" using namespace std; void Run(); void Banner(); void WriteCompileModules(); void WriteCompileCommands(); /* detects if we are running windows xp or higher (5.1) */ bool iswinxp() { OSVERSIONINFO vi; vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&vi); if(vi.dwMajorVersion >= 5) return true; return false; } int get_int_option(const char * text, int def) { static char buffer[500]; int ret; printf_c("%s\n[\033[1;32m%u\033[0m] -> ", text, def); fgets(buffer, 500, stdin); if(sscanf(buffer, "%u", &ret) != 1) ret = def; printf("\n"); return ret; } bool get_bool_option(const char * text, bool def) { static char buffer[500]; char ret[100]; printf_c("%s [\033[1;32m%c\033[0m] -> ", text, def ? 'y' : 'n'); fgets(buffer, 500, stdin); if(sscanf(buffer, "%s", ret) != 1) strcpy(ret, def ? "y" : "n"); printf("\n"); return !strncmp(ret, "y", 1); } void get_string_option(const char * text, char * def, char * buf) { static char buffer[500]; printf_c("%s\n[\033[1;32m%s\033[0m] -> ", text, def); fgets(buffer, 500, stdin); if(sscanf(buffer, "%s", buf) != 1) strcpy(buf, def); printf("\n"); } // escapes a string for use in a c++ file bool escape_string(char * str, size_t size) { size_t len = strlen(str); char * d_str = (char*)malloc(len * 2); size_t i = 0; size_t j = 0; for(; i < len; ++i) { if(str[i] == '\\') { d_str[j++] = '\\'; d_str[j++] = '\\'; } else { d_str[j++] = str[i]; } } d_str[j++] = 0; if(j > size) { free(d_str); return false; } strcpy(str, d_str); free(d_str); return true; } /* gets the svn revision */ int get_svn_revision(char * buffer, size_t len) { /* again.. I am lazy :p cbf to pipe output of svn info to us, so i'll just read the file */ /* 8 dir 7033 */ char buf[1000]; FILE * f = fopen("..\\.svn\\entries", "r"); if(!f) goto bad_rev; if(!fgets(buf, 1000, f)) goto bad_rev; if(!fgets(buf, 1000, f)) goto bad_rev; if(!fgets(buf, 1000, f)) goto bad_rev; if(!fgets(buf, 1000, f)) goto bad_rev; int rev = atoi(buf); if(rev == 0) goto bad_rev; sprintf(buffer, "%u", rev); fclose(f); return rev; bad_rev: strcpy(buffer, "non-svn"); if(f) fclose(f); return 0; } int __stdcall WinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN int nShowCmd ) { FILE * j = fopen("inspircd_config.h", "r"); if (j) { if (MessageBox(0, "inspircd_config.h already exists. Remove it and build from clean?", "Configure program", MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2) != IDYES) { fclose(j); exit(0); } } AllocConsole(); // pipe standard handles to this console freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); Banner(); Run(); WriteCompileCommands(); WriteCompileModules(); FreeConsole(); return 0; } void Banner() { printf_c("\nWelcome to the \033[1mInspIRCd\033[0m Configuration program! (\033[1minteractive mode\033[0m)\n" "\033[1mPackage maintainers: Type ./configure --help for non-interactive help\033[0m\n\n"); printf_c("*** If you are unsure of any of these values, leave it blank for ***\n" "*** standard settings that will work, and your server will run ***\n" "*** using them. Please consult your IRC network admin if in doubt. ***\n\n" "Press \033[1m<RETURN>\033[0m to accept the default for any option, or enter\n" "a new value. Please note: You will \033[1mHAVE\033[0m to read the docs\n" "dir, otherwise you won't have a config file!\n\n"); } void Run() { int max_fd = 1024; bool use_iocp = false; bool support_ip6links = false; char mod_path[MAX_PATH]; char config_file[MAX_PATH]; char library_dir[MAX_PATH]; char base_path[MAX_PATH]; char bin_dir[MAX_PATH]; char revision_text[MAX_PATH]; int max_clients = 1024; int nicklen = 31; int chanlen = 64; int modechanges = 20; int identlen = 12; int quitlen = 255; int topiclen = 500; int kicklen = 255; int rllen = 128; int awaylen = 200; int revision = get_svn_revision(revision_text, MAX_PATH); char version[514]; // grab version FILE * fI = fopen("..\\src\\version.sh", "r"); if(fI) { fgets(version, 514, fI); fgets(version, 514, fI); char * p2 = version; while(*p2 != '\"') ++p2; ++p2; strcpy(version, p2); p2 = version; while(*p2 != '\"') ++p2; *p2 = 0; fclose(fI); } else strcpy(version, "InspIRCD-Unknown"); #ifdef WIN64 printf_c("Your operating system is: \033[1;32mwindows_x64 \033[0m\n"); #else printf_c("Your operating system is: \033[1;32mwindows_x32 \033[0m\n"); #endif printf_c("InspIRCd revision ID: \033[1;32m%s \033[0m\n\n", revision ? revision_text : "(Non-SVN build)"); max_fd = get_int_option("What is the maximum file descriptor count you would like to allow?", 1024); // detect windows if(iswinxp()) { printf_c("You are running Windows 2000 or above, and IOCP support is most likely available.\n" "This removes the socket number limitation of select and is much more efficent.\n" "If you are unsure, answer yes.\n\n"); use_iocp = get_bool_option("Do you want to use the IOCP implementation?", true); } support_ip6links = get_bool_option("\nYou have chosen to build an \033[1;32mIPV4-only\033[0m server.\nWould you like to enable support for linking to IPV6-enabled InspIRCd servers?\nIf you are using a recent operating system and are unsure, answer yes.\nIf you answer 'no' here, your InspIRCd server will be unable\nto parse IPV6 addresses (e.g. for CIDR bans)", true); printf_c("\033[1mAll paths are relative to the binary directory.\033[0m\n"); get_string_option("In what directory do you wish to install the InspIRCd base?", "..", base_path); get_string_option("In what directory are the configuration files?", "../conf", config_file); get_string_option("In what directory are the modules to be compiled to?", "../modules", mod_path); get_string_option("In what directory is the IRCd binary to be placed?", ".", bin_dir); get_string_option("In what directory are the IRCd libraries to be placed?", "../lib", library_dir); printf_c("The following questions will ask you for various figures relating\n" "To your IRCd install. Please note that these should usually be left\n" "as defaults unless you have a real reason to change them. If they\n" "changed, then the values must be identical on all servers on your\n" "network, or malfunctions and/or crashes may occur, with the exception\n" "of the 'maximum number of clients' setting which may be different on\n" "different servers on the network.\n\n"); max_clients = get_int_option("Please enter the maximum number of clients at any one time?", 1024); nicklen = get_int_option("Please enter the maximum length of nicknames?", 31); chanlen = get_int_option("Please enter the maximum length of channel names?", 64); modechanges = get_int_option("Please enter the maximum number of mode changes in one line?", 20); identlen = get_int_option("Please enter the maximum length of an ident (username)?", 12); quitlen = get_int_option("Please enter the maximum length of a quit message?", 255); topiclen = get_int_option("Please enter the maximum length of a channel topic?", 307); kicklen = get_int_option("Please enter the maximum length of a kick message?", 255); rllen = get_int_option("Please enter the maximum length of a GECOS (real name)?", 128); awaylen = get_int_option("Please enter the maximum length of an away message?", 200); printf_c("\n\033[1;32mPre-build configuration is complete!\n\n"); sc(TNORMAL); // dump all the options back out printf_c("\033[0mBase install path:\033[1;32m %s\n", base_path); printf_c("\033[0mConfig path:\033[1;32m %s\n", config_file); printf_c("\033[0mModule path:\033[1;32m %s\n", mod_path); printf_c("\033[0mLibrary path:\033[1;32m %s\n", library_dir); printf_c("\033[0mSocket Engine:\033[1;32m %s\n", use_iocp ? "iocp" : "select"); printf_c("\033[0mMax file descriptors:\033[1;32m %u\n", max_fd); printf_c("\033[0mMax connections:\033[1;32m %u\n", max_clients); printf_c("\033[0mMax nickname length:\033[1;32m %u\n", nicklen); printf_c("\033[0mMax channel length:\033[1;32m %u\n", chanlen); printf_c("\033[0mMax mode length:\033[1;32m %u\n", modechanges); printf_c("\033[0mMax ident length:\033[1;32m %u\n", identlen); printf_c("\033[0mMax quit length:\033[1;32m %u\n", quitlen); printf_c("\033[0mMax topic length:\033[1;32m %u\n", topiclen); printf_c("\033[0mMax kick length:\033[1;32m %u\n", kicklen); printf_c("\033[0mMax name length:\033[1;32m %u\n", rllen); printf_c("\033[0mMax away length:\033[1;32m %u\n", awaylen); printf("\n"); sc(TNORMAL); if(get_bool_option("Are these settings correct?", true) == false) { Run(); return; } printf("\n"); // escape the pathes escape_string(config_file, MAX_PATH); escape_string(mod_path, MAX_PATH); escape_string(library_dir, MAX_PATH); printf("\nWriting inspircd_config.h..."); FILE * f = fopen("inspircd_config.h", "w"); fprintf(f, "/* Auto generated by configure, do not modify! */\n"); fprintf(f, "#ifndef __CONFIGURATION_AUTO__\n"); fprintf(f, "#define __CONFIGURATION_AUTO__\n\n"); if(use_iocp) fprintf(f, "#define CONFIG_USE_IOCP 1\n\n"); fprintf(f, "#define CONFIG_FILE \"%s/inspircd.conf\"\n", config_file); fprintf(f, "#define MOD_PATH \"%s\"\n", mod_path); fprintf(f, "#define MAX_DESCRIPTORS %u\n", max_fd); fprintf(f, "#define MAXCLIENTS %u\n", max_clients); fprintf(f, "#define MAXCLIENTS_S \"%u\"\n", max_clients); fprintf(f, "#define SOMAXCONN_S \"128\"\n"); fprintf(f, "#define NICKMAX %u\n", nicklen+1); fprintf(f, "#define CHANMAX %u\n", chanlen+1); fprintf(f, "#define MAXMODES %u\n", modechanges); fprintf(f, "#define IDENTMAX %u\n", identlen); fprintf(f, "#define MAXQUIT %u\n", quitlen); fprintf(f, "#define MAXTOPIC %u\n", topiclen); fprintf(f, "#define MAXKICK %u\n", kicklen); fprintf(f, "#define MAXGECOS %u\n", rllen); fprintf(f, "#define MAXAWAY %u\n", awaylen); fprintf(f, "#define LIBRARYDIR \"%s\"\n", library_dir); fprintf(f, "#define VERSION \"%s\"\n", version); fprintf(f, "#define REVISION \"%s\"\n", revision_text); if(support_ip6links) fprintf(f, "#define SUPPORT_IP6LINKS 1\n"); OSVERSIONINFO vi; vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&vi); #ifdef WIN64 fprintf(f, "#define SYSTEM \"Windows_x64 %u.%u.%u %s\"\n", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.szCSDVersion); #else fprintf(f, "#define SYSTEM \"Windows_x32 %u.%u.%u %s\"\n", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.szCSDVersion); #endif fprintf(f, "#define MAXBUF 514\n"); fprintf(f, "\n#include \"inspircd_win32wrapper.h\"\n\n"); fprintf(f, "#endif\n\n"); fclose(f); sc(TGREEN); printf(" done\n"); sc(TNORMAL); printf("Writing inspircd_se_config.h..."); f = fopen("inspircd_se_config.h", "w"); fprintf(f, "/* Auto generated by configure, do not modify or commit to svn! */\n"); fprintf(f, "#ifndef __CONFIGURATION_SOCKETENGINE__\n"); fprintf(f, "#define __CONFIGURATION_SOCKETENGINE__\n\n"); fprintf(f, "#include \"socketengine_%s.h\"\n\n", use_iocp ? "iocp" : "select"); fprintf(f, "#endif\n\n"); fclose(f); sc(TGREEN); printf(" done\n"); sc(TNORMAL); printf("Writing command and module compilation scripts..."); WriteCompileCommands(); WriteCompileModules(); sc(TGREEN); printf(" done\n"); sc(TNORMAL); printf("\nconfigure is done.. exiting!\n"); } void WriteCompileCommands() { char commands[300][100]; int command_count = 0; printf("\n Finding Command Sources...\n"); WIN32_FIND_DATA fd; HANDLE fh = FindFirstFile("..\\src\\cmd_*.cpp", &fd); if(fh == INVALID_HANDLE_VALUE) printf_c("\033[1;32m No command sources could be found! This \033[1m*could*\033[1;32m be a bad thing.. :P\033[0m"); else { sc(TGREEN); do { strcpy(commands[command_count], fd.cFileName); commands[command_count][strlen(fd.cFileName) - 4] = 0; printf(" %s\n", commands[command_count]); ++command_count; } while(FindNextFile(fh, &fd)); sc(TNORMAL); } // Write our spiffy new makefile :D // I am such a lazy fucker :P FILE * f = fopen("..\\src\\commands.mak", "w"); time_t t = time(NULL); fprintf(f, "# Generated at %s\n", ctime(&t)); fprintf(f, "all: makedir "); // dump modules.. first time :) for(int i = 0; i < command_count; ++i) fprintf(f, "%s.so ", commands[i]); fprintf(f, "\n.cpp.obj:\n"); #ifdef WIN64 // /MACHINE:X64 #ifdef _DEBUG fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\debug_x64\\bin\\inspircd.lib /OUT:\"..\\bin\\debug_x64\\lib\\$*.so\" /PDB:\"..\\bin\\debug_x64\\lib\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\bin\\debug_x64\\lib\\$*.lib\"\n\n"); CreateDirectory("..\\src\\debug", NULL); CreateDirectory("..\\bin\\debug\\bin", NULL); CreateDirectory("..\\bin\\debug\\lib", NULL); CreateDirectory("..\\bin\\debug\\modules", NULL); #else fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\release_x64\\bin\\inspircd.lib /OUT:\"..\\bin\\release_x64\\lib\\$*.so\" /PDB:\"..\\bin\\release_x64\\lib\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\bin\\release_x64\\lib\\$*.lib\"\n\n"); CreateDirectory("..\\src\\release", NULL); CreateDirectory("..\\bin\\release\\bin", NULL); CreateDirectory("..\\bin\\release\\lib", NULL); CreateDirectory("..\\bin\\release\\modules", NULL); #endif #else #ifdef _DEBUG fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\debug\\bin\\inspircd.lib /OUT:\"..\\bin\\debug\\lib\\$*.so\" /PDB:\"..\\bin\\debug\\lib\\$*.pdb\" /IMPLIB:\"..\\bin\\debug\\lib\\$*.lib\"\n\n"); CreateDirectory("..\\src\\debug", NULL); CreateDirectory("..\\bin\\debug\\bin", NULL); CreateDirectory("..\\bin\\debug\\lib", NULL); CreateDirectory("..\\bin\\debug\\modules", NULL); #else fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\release\\bin\\inspircd.lib /OUT:\"..\\bin\\release\\lib\\$*.so\" /PDB:\"..\\bin\\release\\lib\\$*.pdb\" /IMPLIB:\"..\\bin\\release\\lib\\$*.lib\"\n\n"); CreateDirectory("..\\src\\release", NULL); CreateDirectory("..\\bin\\release\\bin", NULL); CreateDirectory("..\\bin\\release\\lib", NULL); CreateDirectory("..\\bin\\release\\modules", NULL); #endif #endif fprintf(f, "makedir:\n if not exist debug mkdir debug\n\n"); // dump modules.. again the second and last time :) for(int i = 0; i < command_count; ++i) fprintf(f, "%s.so : %s.obj\n", commands[i], commands[i]); fprintf(f, "\n"); fclose(f); } void WriteCompileModules() { char modules[300][100]; int module_count = 0; printf("Finding Modules...\n"); WIN32_FIND_DATA fd; HANDLE fh = FindFirstFile("..\\src\\modules\\m_*.cpp", &fd); if(fh == INVALID_HANDLE_VALUE) printf_c("\033[1;32m No module sources could be found! This \033[1m*could*\033[1;32m be a bad thing.. :P\033[0m"); else { sc(TGREEN); do { strcpy(modules[module_count], fd.cFileName); modules[module_count][strlen(fd.cFileName) - 4] = 0; printf(" %s\n", modules[module_count]); ++module_count; } while(FindNextFile(fh, &fd)); sc(TNORMAL); } // Write our spiffy new makefile :D // I am such a lazy fucker :P FILE * f = fopen("..\\src\\modules\\modules.mak", "w"); time_t t = time(NULL); fprintf(f, "# Generated at %s\n", ctime(&t)); fprintf(f, "all: makedir "); // dump modules.. first time :) for(int i = 0; i < module_count; ++i) fprintf(f, "%s.so ", modules[i]); fprintf(f, "\n.cpp.obj:\n"); #ifdef WIN64 // /MACHINE:X64 #ifdef _DEBUG fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\debug_x64\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\debug_x64\\modules\\$*.so\" /PDB:\"..\\..\\bin\\debug_x64\\modules\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\..\\bin\\debug_x64\\modules\\$*.lib\"\n\n"); CreateDirectory("..\\src\\modules\\debug_x64", NULL); #else fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\release_x64\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\release_x64\\modules\\$*.so\" /PDB:\"..\\..\\bin\\release_x64\\modules\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\..\\bin\\release_x64\\modules\\$*.lib\"\n\n"); CreateDirectory("..\\src\\modules\\release_x64", NULL); #endif #else #ifdef _DEBUG fprintf(f, " cl /nologo -Dssize_t=long /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\debug\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\debug\\modules\\$*.so\" /PDB:\"..\\..\\bin\\debug\\modules\\$*.pdb\" /IMPLIB:\"..\\..\\bin\\debug\\modules\\$*.lib\"\n\n"); CreateDirectory("..\\src\\modules\\debug", NULL); #else fprintf(f, " cl /nologo -Dssize_t=long /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\release\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\release\\modules\\$*.so\" /PDB:\"..\\..\\bin\\release\\modules\\$*.pdb\" /IMPLIB:\"..\\..\\bin\\release\\modules\\$*.lib\"\n\n"); CreateDirectory("..\\src\\modules\\release", NULL); #endif #endif fprintf(f, "makedir:\n if not exist debug mkdir debug\n\n"); // dump modules.. again the second and last time :) for(int i = 0; i < module_count; ++i) fprintf(f, "%s.so : %s.obj\n", modules[i], modules[i]); fprintf(f, "\n"); fclose(f); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+#include <windows.h>
+#include <stdio.h>
+#include <string>
+#include <time.h>
+#include "colours.h"
+
+using namespace std;
+void Run();
+void Banner();
+void WriteCompileModules();
+void WriteCompileCommands();
+
+/* detects if we are running windows xp or higher (5.1) */
+bool iswinxp()
+{
+ OSVERSIONINFO vi;
+ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&vi);
+ if(vi.dwMajorVersion >= 5)
+ return true;
+
+ return false;
+}
+
+int get_int_option(const char * text, int def)
+{
+ static char buffer[500];
+ int ret;
+ printf_c("%s\n[\033[1;32m%u\033[0m] -> ", text, def);
+ fgets(buffer, 500, stdin);
+ if(sscanf(buffer, "%u", &ret) != 1)
+ ret = def;
+
+ printf("\n");
+ return ret;
+}
+
+bool get_bool_option(const char * text, bool def)
+{
+ static char buffer[500];
+ char ret[100];
+ printf_c("%s [\033[1;32m%c\033[0m] -> ", text, def ? 'y' : 'n');
+ fgets(buffer, 500, stdin);
+ if(sscanf(buffer, "%s", ret) != 1)
+ strcpy(ret, def ? "y" : "n");
+
+ printf("\n");
+ return !strncmp(ret, "y", 1);
+}
+
+void get_string_option(const char * text, char * def, char * buf)
+{
+ static char buffer[500];
+ printf_c("%s\n[\033[1;32m%s\033[0m] -> ", text, def);
+ fgets(buffer, 500, stdin);
+ if(sscanf(buffer, "%s", buf) != 1)
+ strcpy(buf, def);
+
+ printf("\n");
+}
+
+// escapes a string for use in a c++ file
+bool escape_string(char * str, size_t size)
+{
+ size_t len = strlen(str);
+ char * d_str = (char*)malloc(len * 2);
+
+ size_t i = 0;
+ size_t j = 0;
+
+ for(; i < len; ++i)
+ {
+ if(str[i] == '\\')
+ {
+ d_str[j++] = '\\';
+ d_str[j++] = '\\';
+ }
+ else
+ {
+ d_str[j++] = str[i];
+ }
+ }
+
+ d_str[j++] = 0;
+
+ if(j > size)
+ {
+ free(d_str);
+ return false;
+ }
+
+ strcpy(str, d_str);
+ free(d_str);
+ return true;
+}
+
+/* gets the svn revision */
+int get_svn_revision(char * buffer, size_t len)
+{
+ /* again.. I am lazy :p cbf to pipe output of svn info to us, so i'll just read the file */
+ /*
+ 8
+
+ dir
+ 7033
+ */
+ char buf[1000];
+ FILE * f = fopen("..\\.svn\\entries", "r");
+ if(!f) goto bad_rev;
+
+ if(!fgets(buf, 1000, f)) goto bad_rev;
+ if(!fgets(buf, 1000, f)) goto bad_rev;
+ if(!fgets(buf, 1000, f)) goto bad_rev;
+ if(!fgets(buf, 1000, f)) goto bad_rev;
+ int rev = atoi(buf);
+ if(rev == 0) goto bad_rev;
+ sprintf(buffer, "%u", rev);
+ fclose(f);
+ return rev;
+
+bad_rev:
+ strcpy(buffer, "non-svn");
+ if(f) fclose(f);
+ return 0;
+}
+
+int __stdcall WinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN int nShowCmd )
+{
+ FILE * j = fopen("inspircd_config.h", "r");
+ if (j)
+ {
+ if (MessageBox(0, "inspircd_config.h already exists. Remove it and build from clean?", "Configure program", MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2) != IDYES)
+ {
+ fclose(j);
+ exit(0);
+ }
+ }
+
+ AllocConsole();
+
+ // pipe standard handles to this console
+ freopen("CONIN$", "r", stdin);
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+
+ Banner();
+ Run();
+ WriteCompileCommands();
+ WriteCompileModules();
+ FreeConsole();
+ return 0;
+}
+
+void Banner()
+{
+ printf_c("\nWelcome to the \033[1mInspIRCd\033[0m Configuration program! (\033[1minteractive mode\033[0m)\n"
+ "\033[1mPackage maintainers: Type ./configure --help for non-interactive help\033[0m\n\n");
+ printf_c("*** If you are unsure of any of these values, leave it blank for ***\n"
+ "*** standard settings that will work, and your server will run ***\n"
+ "*** using them. Please consult your IRC network admin if in doubt. ***\n\n"
+ "Press \033[1m<RETURN>\033[0m to accept the default for any option, or enter\n"
+ "a new value. Please note: You will \033[1mHAVE\033[0m to read the docs\n"
+ "dir, otherwise you won't have a config file!\n\n");
+
+}
+
+void Run()
+{
+ int max_fd = 1024;
+ bool use_iocp = false;
+ bool support_ip6links = false;
+ char mod_path[MAX_PATH];
+ char config_file[MAX_PATH];
+ char library_dir[MAX_PATH];
+ char base_path[MAX_PATH];
+ char bin_dir[MAX_PATH];
+ char revision_text[MAX_PATH];
+
+ int max_clients = 1024;
+ int nicklen = 31;
+ int chanlen = 64;
+ int modechanges = 20;
+ int identlen = 12;
+ int quitlen = 255;
+ int topiclen = 500;
+ int kicklen = 255;
+ int rllen = 128;
+ int awaylen = 200;
+ int revision = get_svn_revision(revision_text, MAX_PATH);
+ char version[514];
+
+ // grab version
+ FILE * fI = fopen("..\\src\\version.sh", "r");
+ if(fI)
+ {
+ fgets(version, 514, fI);
+ fgets(version, 514, fI);
+ char * p2 = version;
+ while(*p2 != '\"')
+ ++p2;
+ ++p2;
+ strcpy(version, p2);
+ p2 = version;
+ while(*p2 != '\"')
+ ++p2;
+ *p2 = 0;
+ fclose(fI);
+ }
+ else
+ strcpy(version, "InspIRCD-Unknown");
+#ifdef WIN64
+ printf_c("Your operating system is: \033[1;32mwindows_x64 \033[0m\n");
+#else
+ printf_c("Your operating system is: \033[1;32mwindows_x32 \033[0m\n");
+#endif
+ printf_c("InspIRCd revision ID: \033[1;32m%s \033[0m\n\n", revision ? revision_text : "(Non-SVN build)");
+
+ max_fd = get_int_option("What is the maximum file descriptor count you would like to allow?", 1024);
+
+ // detect windows
+ if(iswinxp())
+ {
+ printf_c("You are running Windows 2000 or above, and IOCP support is most likely available.\n"
+ "This removes the socket number limitation of select and is much more efficent.\n"
+ "If you are unsure, answer yes.\n\n");
+
+ use_iocp = get_bool_option("Do you want to use the IOCP implementation?", true);
+ }
+
+ support_ip6links = get_bool_option("\nYou have chosen to build an \033[1;32mIPV4-only\033[0m server.\nWould you like to enable support for linking to IPV6-enabled InspIRCd servers?\nIf you are using a recent operating system and are unsure, answer yes.\nIf you answer 'no' here, your InspIRCd server will be unable\nto parse IPV6 addresses (e.g. for CIDR bans)",
+ true);
+
+ printf_c("\033[1mAll paths are relative to the binary directory.\033[0m\n");
+ get_string_option("In what directory do you wish to install the InspIRCd base?", "..", base_path);
+ get_string_option("In what directory are the configuration files?", "../conf", config_file);
+ get_string_option("In what directory are the modules to be compiled to?", "../modules", mod_path);
+ get_string_option("In what directory is the IRCd binary to be placed?", ".", bin_dir);
+ get_string_option("In what directory are the IRCd libraries to be placed?", "../lib", library_dir);
+
+ printf_c("The following questions will ask you for various figures relating\n"
+ "To your IRCd install. Please note that these should usually be left\n"
+ "as defaults unless you have a real reason to change them. If they\n"
+ "changed, then the values must be identical on all servers on your\n"
+ "network, or malfunctions and/or crashes may occur, with the exception\n"
+ "of the 'maximum number of clients' setting which may be different on\n"
+ "different servers on the network.\n\n");
+
+
+ max_clients = get_int_option("Please enter the maximum number of clients at any one time?", 1024);
+ nicklen = get_int_option("Please enter the maximum length of nicknames?", 31);
+ chanlen = get_int_option("Please enter the maximum length of channel names?", 64);
+ modechanges = get_int_option("Please enter the maximum number of mode changes in one line?", 20);
+ identlen = get_int_option("Please enter the maximum length of an ident (username)?", 12);
+ quitlen = get_int_option("Please enter the maximum length of a quit message?", 255);
+ topiclen = get_int_option("Please enter the maximum length of a channel topic?", 307);
+ kicklen = get_int_option("Please enter the maximum length of a kick message?", 255);
+ rllen = get_int_option("Please enter the maximum length of a GECOS (real name)?", 128);
+ awaylen = get_int_option("Please enter the maximum length of an away message?", 200);
+
+ printf_c("\n\033[1;32mPre-build configuration is complete!\n\n"); sc(TNORMAL);
+
+ // dump all the options back out
+ printf_c("\033[0mBase install path:\033[1;32m %s\n", base_path);
+ printf_c("\033[0mConfig path:\033[1;32m %s\n", config_file);
+ printf_c("\033[0mModule path:\033[1;32m %s\n", mod_path);
+ printf_c("\033[0mLibrary path:\033[1;32m %s\n", library_dir);
+ printf_c("\033[0mSocket Engine:\033[1;32m %s\n", use_iocp ? "iocp" : "select");
+ printf_c("\033[0mMax file descriptors:\033[1;32m %u\n", max_fd);
+ printf_c("\033[0mMax connections:\033[1;32m %u\n", max_clients);
+ printf_c("\033[0mMax nickname length:\033[1;32m %u\n", nicklen);
+ printf_c("\033[0mMax channel length:\033[1;32m %u\n", chanlen);
+ printf_c("\033[0mMax mode length:\033[1;32m %u\n", modechanges);
+ printf_c("\033[0mMax ident length:\033[1;32m %u\n", identlen);
+ printf_c("\033[0mMax quit length:\033[1;32m %u\n", quitlen);
+ printf_c("\033[0mMax topic length:\033[1;32m %u\n", topiclen);
+ printf_c("\033[0mMax kick length:\033[1;32m %u\n", kicklen);
+ printf_c("\033[0mMax name length:\033[1;32m %u\n", rllen);
+ printf_c("\033[0mMax away length:\033[1;32m %u\n", awaylen);
+ printf("\n"); sc(TNORMAL);
+ if(get_bool_option("Are these settings correct?", true) == false)
+ {
+ Run();
+ return;
+ }
+ printf("\n");
+
+ // escape the pathes
+ escape_string(config_file, MAX_PATH);
+ escape_string(mod_path, MAX_PATH);
+ escape_string(library_dir, MAX_PATH);
+
+ printf("\nWriting inspircd_config.h...");
+ FILE * f = fopen("inspircd_config.h", "w");
+ fprintf(f, "/* Auto generated by configure, do not modify! */\n");
+ fprintf(f, "#ifndef __CONFIGURATION_AUTO__\n");
+ fprintf(f, "#define __CONFIGURATION_AUTO__\n\n");
+ if(use_iocp)
+ fprintf(f, "#define CONFIG_USE_IOCP 1\n\n");
+
+ fprintf(f, "#define CONFIG_FILE \"%s/inspircd.conf\"\n", config_file);
+ fprintf(f, "#define MOD_PATH \"%s\"\n", mod_path);
+ fprintf(f, "#define MAX_DESCRIPTORS %u\n", max_fd);
+ fprintf(f, "#define MAXCLIENTS %u\n", max_clients);
+ fprintf(f, "#define MAXCLIENTS_S \"%u\"\n", max_clients);
+ fprintf(f, "#define SOMAXCONN_S \"128\"\n");
+ fprintf(f, "#define NICKMAX %u\n", nicklen+1);
+ fprintf(f, "#define CHANMAX %u\n", chanlen+1);
+ fprintf(f, "#define MAXMODES %u\n", modechanges);
+ fprintf(f, "#define IDENTMAX %u\n", identlen);
+ fprintf(f, "#define MAXQUIT %u\n", quitlen);
+ fprintf(f, "#define MAXTOPIC %u\n", topiclen);
+ fprintf(f, "#define MAXKICK %u\n", kicklen);
+ fprintf(f, "#define MAXGECOS %u\n", rllen);
+ fprintf(f, "#define MAXAWAY %u\n", awaylen);
+ fprintf(f, "#define LIBRARYDIR \"%s\"\n", library_dir);
+ fprintf(f, "#define VERSION \"%s\"\n", version);
+ fprintf(f, "#define REVISION \"%s\"\n", revision_text);
+ if(support_ip6links)
+ fprintf(f, "#define SUPPORT_IP6LINKS 1\n");
+
+ OSVERSIONINFO vi;
+ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&vi);
+#ifdef WIN64
+ fprintf(f, "#define SYSTEM \"Windows_x64 %u.%u.%u %s\"\n", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.szCSDVersion);
+#else
+ fprintf(f, "#define SYSTEM \"Windows_x32 %u.%u.%u %s\"\n", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.szCSDVersion);
+#endif
+ fprintf(f, "#define MAXBUF 514\n");
+
+ fprintf(f, "\n#include \"inspircd_win32wrapper.h\"\n\n");
+ fprintf(f, "#endif\n\n");
+ fclose(f);
+
+ sc(TGREEN); printf(" done\n"); sc(TNORMAL);
+ printf("Writing inspircd_se_config.h...");
+
+ f = fopen("inspircd_se_config.h", "w");
+ fprintf(f, "/* Auto generated by configure, do not modify or commit to svn! */\n");
+ fprintf(f, "#ifndef __CONFIGURATION_SOCKETENGINE__\n");
+ fprintf(f, "#define __CONFIGURATION_SOCKETENGINE__\n\n");
+ fprintf(f, "#include \"socketengine_%s.h\"\n\n", use_iocp ? "iocp" : "select");
+ fprintf(f, "#endif\n\n");
+ fclose(f);
+
+ sc(TGREEN); printf(" done\n"); sc(TNORMAL);
+ printf("Writing command and module compilation scripts...");
+ WriteCompileCommands();
+ WriteCompileModules();
+ sc(TGREEN); printf(" done\n"); sc(TNORMAL);
+
+ printf("\nconfigure is done.. exiting!\n");
+}
+
+void WriteCompileCommands()
+{
+ char commands[300][100];
+ int command_count = 0;
+ printf("\n Finding Command Sources...\n");
+ WIN32_FIND_DATA fd;
+ HANDLE fh = FindFirstFile("..\\src\\cmd_*.cpp", &fd);
+ if(fh == INVALID_HANDLE_VALUE)
+ printf_c("\033[1;32m No command sources could be found! This \033[1m*could*\033[1;32m be a bad thing.. :P\033[0m");
+ else
+ {
+ sc(TGREEN);
+ do
+ {
+ strcpy(commands[command_count], fd.cFileName);
+ commands[command_count][strlen(fd.cFileName) - 4] = 0;
+ printf(" %s\n", commands[command_count]);
+ ++command_count;
+ } while(FindNextFile(fh, &fd));
+ sc(TNORMAL);
+ }
+
+ // Write our spiffy new makefile :D
+ // I am such a lazy fucker :P
+ FILE * f = fopen("..\\src\\commands.mak", "w");
+
+ time_t t = time(NULL);
+ fprintf(f, "# Generated at %s\n", ctime(&t));
+ fprintf(f, "all: makedir ");
+
+ // dump modules.. first time :)
+ for(int i = 0; i < command_count; ++i)
+ fprintf(f, "%s.so ", commands[i]);
+
+ fprintf(f, "\n.cpp.obj:\n");
+#ifdef WIN64
+ // /MACHINE:X64
+ #ifdef _DEBUG
+ fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\debug_x64\\bin\\inspircd.lib /OUT:\"..\\bin\\debug_x64\\lib\\$*.so\" /PDB:\"..\\bin\\debug_x64\\lib\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\bin\\debug_x64\\lib\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\debug", NULL);
+ CreateDirectory("..\\bin\\debug\\bin", NULL);
+ CreateDirectory("..\\bin\\debug\\lib", NULL);
+ CreateDirectory("..\\bin\\debug\\modules", NULL);
+ #else
+ fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\release_x64\\bin\\inspircd.lib /OUT:\"..\\bin\\release_x64\\lib\\$*.so\" /PDB:\"..\\bin\\release_x64\\lib\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\bin\\release_x64\\lib\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\release", NULL);
+ CreateDirectory("..\\bin\\release\\bin", NULL);
+ CreateDirectory("..\\bin\\release\\lib", NULL);
+ CreateDirectory("..\\bin\\release\\modules", NULL);
+ #endif
+#else
+ #ifdef _DEBUG
+ fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\debug\\bin\\inspircd.lib /OUT:\"..\\bin\\debug\\lib\\$*.so\" /PDB:\"..\\bin\\debug\\lib\\$*.pdb\" /IMPLIB:\"..\\bin\\debug\\lib\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\debug", NULL);
+ CreateDirectory("..\\bin\\debug\\bin", NULL);
+ CreateDirectory("..\\bin\\debug\\lib", NULL);
+ CreateDirectory("..\\bin\\debug\\modules", NULL);
+ #else
+ fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../include\" /I \"../include/modes\" /I \"../include/commands\" /I \"../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\win\\inspircd_memory_functions.cpp /link ..\\bin\\release\\bin\\inspircd.lib /OUT:\"..\\bin\\release\\lib\\$*.so\" /PDB:\"..\\bin\\release\\lib\\$*.pdb\" /IMPLIB:\"..\\bin\\release\\lib\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\release", NULL);
+ CreateDirectory("..\\bin\\release\\bin", NULL);
+ CreateDirectory("..\\bin\\release\\lib", NULL);
+ CreateDirectory("..\\bin\\release\\modules", NULL);
+ #endif
+#endif
+
+ fprintf(f, "makedir:\n if not exist debug mkdir debug\n\n");
+
+ // dump modules.. again the second and last time :)
+ for(int i = 0; i < command_count; ++i)
+ fprintf(f, "%s.so : %s.obj\n", commands[i], commands[i]);
+
+ fprintf(f, "\n");
+ fclose(f);
+}
+
+void WriteCompileModules()
+{
+ char modules[300][100];
+ int module_count = 0;
+
+ printf("Finding Modules...\n");
+ WIN32_FIND_DATA fd;
+ HANDLE fh = FindFirstFile("..\\src\\modules\\m_*.cpp", &fd);
+ if(fh == INVALID_HANDLE_VALUE)
+ printf_c("\033[1;32m No module sources could be found! This \033[1m*could*\033[1;32m be a bad thing.. :P\033[0m");
+ else
+ {
+ sc(TGREEN);
+ do
+ {
+ strcpy(modules[module_count], fd.cFileName);
+ modules[module_count][strlen(fd.cFileName) - 4] = 0;
+ printf(" %s\n", modules[module_count]);
+ ++module_count;
+ } while(FindNextFile(fh, &fd));
+ sc(TNORMAL);
+ }
+
+ // Write our spiffy new makefile :D
+ // I am such a lazy fucker :P
+ FILE * f = fopen("..\\src\\modules\\modules.mak", "w");
+
+ time_t t = time(NULL);
+ fprintf(f, "# Generated at %s\n", ctime(&t));
+ fprintf(f, "all: makedir ");
+
+ // dump modules.. first time :)
+ for(int i = 0; i < module_count; ++i)
+ fprintf(f, "%s.so ", modules[i]);
+
+ fprintf(f, "\n.cpp.obj:\n");
+#ifdef WIN64
+ // /MACHINE:X64
+ #ifdef _DEBUG
+ fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\debug_x64\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\debug_x64\\modules\\$*.so\" /PDB:\"..\\..\\bin\\debug_x64\\modules\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\..\\bin\\debug_x64\\modules\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\modules\\debug_x64", NULL);
+ #else
+ fprintf(f, " cl /nologo /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\release_x64\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\release_x64\\modules\\$*.so\" /PDB:\"..\\..\\bin\\release_x64\\modules\\$*.pdb\" /MACHINE:X64 /IMPLIB:\"..\\..\\bin\\release_x64\\modules\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\modules\\release_x64", NULL);
+ #endif
+#else
+ #ifdef _DEBUG
+ fprintf(f, " cl /nologo -Dssize_t=long /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /Gm /EHsc /Gm /RTC1 /MTd /Fo\"Debug/\" /Fd\"Debug/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\debug\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\debug\\modules\\$*.so\" /PDB:\"..\\..\\bin\\debug\\modules\\$*.pdb\" /IMPLIB:\"..\\..\\bin\\debug\\modules\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\modules\\debug", NULL);
+ #else
+ fprintf(f, " cl /nologo -Dssize_t=long /LD /Od /I \".\" /I \"../../include\" /I \"../../include/modes\" /I \"../../include/modules\" /I \"../../win\" /D \"WIN32\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"DLL_BUILD\" /EHsc /Gm /MT /Fo\"Release/\" /Fd\"Release/vc70.pdb\" /W2 /Wp64 /Zi /TP $*.cpp ..\\..\\win\\inspircd_memory_functions.cpp /link ..\\..\\bin\\release\\bin\\inspircd.lib ws2_32.lib /OUT:\"..\\..\\bin\\release\\modules\\$*.so\" /PDB:\"..\\..\\bin\\release\\modules\\$*.pdb\" /IMPLIB:\"..\\..\\bin\\release\\modules\\$*.lib\"\n\n");
+ CreateDirectory("..\\src\\modules\\release", NULL);
+ #endif
+#endif
+
+ fprintf(f, "makedir:\n if not exist debug mkdir debug\n\n");
+
+ // dump modules.. again the second and last time :)
+ for(int i = 0; i < module_count; ++i)
+ fprintf(f, "%s.so : %s.obj\n", modules[i], modules[i]);
+
+ fprintf(f, "\n");
+ fclose(f);
+}
diff --git a/win/inspircd.nsi b/win/inspircd.nsi
index 092fae309..96b2e498e 100644
--- a/win/inspircd.nsi
+++ b/win/inspircd.nsi
@@ -1 +1,272 @@
-; * +------------------------------------+ ; * | Inspire Internet Relay Chat Daemon | ; * +------------------------------------+ ; * ; * InspIRCd: (C) 2002-2007 InspIRCd Development Team ; * See: http://www.inspircd.org/wiki/index.php/Credits ; * ; * This program is free but copyrighted software; see ; * the file COPYING for details. ; * ; * --------------------------------------------------- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; SET THE BUILD TO BE PACKAGED HERE ;;;; !define BUILD "release" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; HM NIS Edit Wizard helper defines !define PRODUCT_NAME "InspIRCd" !define PRODUCT_VERSION "1.1" !define PRODUCT_PUBLISHER "InspIRCd Development Team" !define PRODUCT_WEB_SITE "http://www.inspircd.org/" !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\inspircd.exe" !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" !define PRODUCT_UNINST_ROOT_KEY "HKLM" !define DOT_MAJOR "2" !define DOT_MINOR "0" SetCompressor bzip2 ; MUI 1.67 compatible ------ !include "MUI.nsh" ; MUI Settings !define MUI_ABORTWARNING !define MUI_ICON "inspircd.ico" !define MUI_UNICON "inspircd.ico" ; Welcome page !insertmacro MUI_PAGE_WELCOME ; License page !define MUI_LICENSEPAGE_CHECKBOX !insertmacro MUI_PAGE_LICENSE "..\docs\COPYING" ; directory page Page directory ; Components page !insertmacro MUI_PAGE_COMPONENTS ; Instfiles page !insertmacro MUI_PAGE_INSTFILES ; Finish page !define MUI_FINISHPAGE_RUN "$INSTDIR\InspGUI.exe" !insertmacro MUI_PAGE_FINISH ; Uninstaller pages !insertmacro MUI_UNPAGE_INSTFILES ; Language files !insertmacro MUI_LANGUAGE "English" ; Reserve files !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ; MUI end ------ Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" OutFile "Setup.exe" InstallDir "$PROGRAMFILES\InspIRCd" InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" "" ShowInstDetails show ShowUnInstDetails show Function IsDotNetInstalled StrCpy $0 "0" StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ;registry entry to look in. StrCpy $2 0 StartEnum: ;Enumerate the versions installed. EnumRegKey $3 HKLM "$1\policy" $2 ;If we don't find any versions installed, it's not here. StrCmp $3 "" noDotNet notEmpty ;We found something. notEmpty: ;Find out if the RegKey starts with 'v'. ;If it doesn't, goto the next key. StrCpy $4 $3 1 0 StrCmp $4 "v" +1 goNext StrCpy $4 $3 1 1 ;It starts with 'v'. Now check to see how the installed major version ;relates to our required major version. ;If it's equal check the minor version, if it's greater, ;we found a good RegKey. IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg ;Check the minor version. If it's equal or greater to our requested ;version then we're good. StrCpy $4 $3 1 3 IntCmp $4 ${DOT_MINOR} yesDotNetReg goNext yesDotNetReg goNext: ;Go to the next RegKey. IntOp $2 $2 + 1 goto StartEnum yesDotNetReg: ;Now that we've found a good RegKey, let's make sure it's actually ;installed by getting the install path and checking to see if the ;mscorlib.dll exists. EnumRegValue $2 HKLM "$1\policy\$3" 0 ;$2 should equal whatever comes after the major and minor versions ;(ie, v1.1.4322) StrCmp $2 "" noDotNet ReadRegStr $4 HKLM $1 "InstallRoot" ;Hopefully the install root isn't empty. StrCmp $4 "" noDotNet ;build the actuall directory path to mscorlib.dll. StrCpy $4 "$4$3.$2\mscorlib.dll" IfFileExists $4 yesDotNet noDotNet noDotNet: MessageBox MB_OK "You do not have have v${DOT_MAJOR}.${DOT_MINOR} or greater of the .NET framework installed. This is required for the InspIRCd Monitor, however you can still launch the IRCd manually." yesDotNet: ;Everything checks out. Go on with the rest of the installation. FunctionEnd Section "Binary Executable" SEC01 Call IsDotNetInstalled SetOutPath "$INSTDIR" SetOverwrite ifnewer File "..\bin\${BUILD}\InspGUI.exe" CreateDirectory "$SMPROGRAMS\InspIRCd" CreateShortCut "$SMPROGRAMS\InspIRCd\InspIRCd.lnk" "$INSTDIR\InspGUI.exe" SetOutPath "$INSTDIR\bin" SetOverwrite ifnewer File "..\bin\${BUILD}\bin\inspircd.exe" SectionEnd Section "Config Files" SEC02 SetOutPath "$INSTDIR\conf" File "..\conf\inspircd.motd.example" File "..\conf\inspircd.helpop-full.example" File "..\conf\inspircd.helpop.example" File "..\conf\inspircd.filter.example" File "..\docs\inspircd.conf.example" File "..\conf\inspircd.censor.example" File "..\conf\inspircd.rules.example" File "..\conf\inspircd.quotes.example" SectionEnd Section "Command Handlers" SEC03 SetOutPath "$INSTDIR\lib" File "..\bin\${BUILD}\lib\cmd_*.so" SectionEnd Section "Modules" SEC04 SetOutPath "$INSTDIR\modules" File "..\bin\${BUILD}\modules\m_*.so" SectionEnd Section "SSL Modules" SEC05 SetOutPath "$INSTDIR\bin" SetOverwrite ifnewer File "..\bin\${BUILD}\bin\libgcrypt-11.dll" File "..\bin\${BUILD}\bin\libgnutls-13.dll" File "..\bin\${BUILD}\bin\libgnutls-extra-13.dll" File "..\bin\${BUILD}\bin\libgnutls-openssl-13.dll" File "..\bin\${BUILD}\bin\libgpg-error-0.dll" File "..\bin\${BUILD}\bin\libopencdk-8.dll" File "..\bin\${BUILD}\bin\libtasn1-3.dll" SetOutPath "$INSTDIR\modules" File "d:\temp\m_ssl_gnutls.so" File "d:\temp\m_sslinfo.so" File "d:\temp\m_ssl_oper_cert.so" SetOutPath "$INSTDIR\conf" SetOverwrite off File "key.pem" File "cert.pem" SectionEnd Section "Regexp Modules" SEC06 SetOutPath "$INSTDIR\bin" SetOverwrite ifnewer File "..\bin\${BUILD}\bin\pcre.dll" SetOutPath "$INSTDIR\modules" File "d:\temp\m_filter_pcre.so" SectionEnd Section -AdditionalIcons SetOutPath $INSTDIR WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}" CreateShortCut "$SMPROGRAMS\InspIRCd\InspIRCd Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url" CreateShortCut "$SMPROGRAMS\InspIRCd\Uninstall.lnk" "$INSTDIR\uninst.exe" SectionEnd Section -Post WriteUninstaller "$INSTDIR\uninst.exe" WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\inspircd.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\inspircd.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" MessageBox MB_ICONINFORMATION|MB_OK "InspIRCd was successfully installed. Remember to edit your configuration file in $INSTDIR\conf!" SectionEnd ; Section descriptions !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SEC01} "Actual executable" !insertmacro MUI_DESCRIPTION_TEXT ${SEC03} "Command modules" !insertmacro MUI_DESCRIPTION_TEXT ${SEC02} "Default configuration files" !insertmacro MUI_DESCRIPTION_TEXT ${SEC04} "Optional non-SSL modules" !insertmacro MUI_DESCRIPTION_TEXT ${SEC05} "SSL modules and GnuTLS DLL libraries" !insertmacro MUI_DESCRIPTION_TEXT ${SEC06} "Regular expression module and PCRE DLL library" !insertmacro MUI_FUNCTION_DESCRIPTION_END Function un.onUninstSuccess HideWindow MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." FunctionEnd Function .onInit SectionSetFlags ${SEC01} 17 SectionSetFlags ${SEC03} 17 StrCpy $INSTDIR "$PROGRAMFILES\InspIRCd" FunctionEnd Function un.onInit MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2 Abort FunctionEnd Section Uninstall Delete "$INSTDIR\${PRODUCT_NAME}.url" Delete "$INSTDIR\uninst.exe" Delete "$INSTDIR\modules\m_*.so" Delete "$INSTDIR\lib\cmd_*.so" Delete "$INSTDIR\conf\inspircd.quotes.example" Delete "$INSTDIR\conf\inspircd.rules.example" Delete "$INSTDIR\conf\inspircd.censor.example" Delete "$INSTDIR\conf\inspircd.conf.example" Delete "$INSTDIR\conf\inspircd.filter.example" Delete "$INSTDIR\conf\inspircd.helpop.example" Delete "$INSTDIR\conf\inspircd.helpop-full.example" Delete "$INSTDIR\conf\inspircd.motd.example" Delete "$INSTDIR\bin\inspircd.exe" Delete "$INSTDIR\bin\*.dll" Delete "$INSTDIR\InspGUI.exe" Delete "$SMPROGRAMS\InspIRCd\Uninstall.lnk" Delete "$SMPROGRAMS\InspIRCd\InspIRCd Website.lnk" Delete "$SMPROGRAMS\InspIRCd\InspIRCd.lnk" RMDir "$SMPROGRAMS\InspIRCd" RMDir "$INSTDIR\modules" RMDir "$INSTDIR\lib" RMDir "$INSTDIR\conf" RMDir "$INSTDIR\bin" RMDir "$INSTDIR" DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}" SetAutoClose true SectionEnd \ No newline at end of file
+; * +------------------------------------+
+; * | Inspire Internet Relay Chat Daemon |
+; * +------------------------------------+
+; *
+; * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+; * See: http://www.inspircd.org/wiki/index.php/Credits
+; *
+; * This program is free but copyrighted software; see
+; * the file COPYING for details.
+; *
+; * ---------------------------------------------------
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ ;;;; SET THE BUILD TO BE PACKAGED HERE ;;;;
+
+!define BUILD "release"
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; HM NIS Edit Wizard helper defines
+!define PRODUCT_NAME "InspIRCd"
+!define PRODUCT_VERSION "1.1"
+!define PRODUCT_PUBLISHER "InspIRCd Development Team"
+!define PRODUCT_WEB_SITE "http://www.inspircd.org/"
+!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\inspircd.exe"
+!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
+!define PRODUCT_UNINST_ROOT_KEY "HKLM"
+!define DOT_MAJOR "2"
+!define DOT_MINOR "0"
+
+SetCompressor bzip2
+
+; MUI 1.67 compatible ------
+!include "MUI.nsh"
+
+; MUI Settings
+!define MUI_ABORTWARNING
+!define MUI_ICON "inspircd.ico"
+!define MUI_UNICON "inspircd.ico"
+
+; Welcome page
+!insertmacro MUI_PAGE_WELCOME
+; License page
+!define MUI_LICENSEPAGE_CHECKBOX
+!insertmacro MUI_PAGE_LICENSE "..\docs\COPYING"
+; directory page
+Page directory
+; Components page
+!insertmacro MUI_PAGE_COMPONENTS
+; Instfiles page
+!insertmacro MUI_PAGE_INSTFILES
+; Finish page
+!define MUI_FINISHPAGE_RUN "$INSTDIR\InspGUI.exe"
+!insertmacro MUI_PAGE_FINISH
+
+; Uninstaller pages
+!insertmacro MUI_UNPAGE_INSTFILES
+
+; Language files
+!insertmacro MUI_LANGUAGE "English"
+
+; Reserve files
+!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
+
+; MUI end ------
+
+Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
+OutFile "Setup.exe"
+InstallDir "$PROGRAMFILES\InspIRCd"
+InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
+ShowInstDetails show
+ShowUnInstDetails show
+
+Function IsDotNetInstalled
+
+ StrCpy $0 "0"
+ StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ;registry entry to look in.
+ StrCpy $2 0
+
+ StartEnum:
+ ;Enumerate the versions installed.
+ EnumRegKey $3 HKLM "$1\policy" $2
+
+ ;If we don't find any versions installed, it's not here.
+ StrCmp $3 "" noDotNet notEmpty
+
+ ;We found something.
+ notEmpty:
+ ;Find out if the RegKey starts with 'v'.
+ ;If it doesn't, goto the next key.
+ StrCpy $4 $3 1 0
+ StrCmp $4 "v" +1 goNext
+ StrCpy $4 $3 1 1
+
+ ;It starts with 'v'. Now check to see how the installed major version
+ ;relates to our required major version.
+ ;If it's equal check the minor version, if it's greater,
+ ;we found a good RegKey.
+ IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg
+ ;Check the minor version. If it's equal or greater to our requested
+ ;version then we're good.
+ StrCpy $4 $3 1 3
+ IntCmp $4 ${DOT_MINOR} yesDotNetReg goNext yesDotNetReg
+
+ goNext:
+ ;Go to the next RegKey.
+ IntOp $2 $2 + 1
+ goto StartEnum
+
+ yesDotNetReg:
+ ;Now that we've found a good RegKey, let's make sure it's actually
+ ;installed by getting the install path and checking to see if the
+ ;mscorlib.dll exists.
+ EnumRegValue $2 HKLM "$1\policy\$3" 0
+ ;$2 should equal whatever comes after the major and minor versions
+ ;(ie, v1.1.4322)
+ StrCmp $2 "" noDotNet
+ ReadRegStr $4 HKLM $1 "InstallRoot"
+ ;Hopefully the install root isn't empty.
+ StrCmp $4 "" noDotNet
+ ;build the actuall directory path to mscorlib.dll.
+ StrCpy $4 "$4$3.$2\mscorlib.dll"
+ IfFileExists $4 yesDotNet noDotNet
+
+ noDotNet:
+ MessageBox MB_OK "You do not have have v${DOT_MAJOR}.${DOT_MINOR} or greater of the .NET framework installed. This is required for the InspIRCd Monitor, however you can still launch the IRCd manually."
+
+ yesDotNet:
+ ;Everything checks out. Go on with the rest of the installation.
+
+FunctionEnd
+
+Section "Binary Executable" SEC01
+ Call IsDotNetInstalled
+ SetOutPath "$INSTDIR"
+ SetOverwrite ifnewer
+ File "..\bin\${BUILD}\InspGUI.exe"
+ CreateDirectory "$SMPROGRAMS\InspIRCd"
+ CreateShortCut "$SMPROGRAMS\InspIRCd\InspIRCd.lnk" "$INSTDIR\InspGUI.exe"
+ SetOutPath "$INSTDIR\bin"
+ SetOverwrite ifnewer
+ File "..\bin\${BUILD}\bin\inspircd.exe"
+SectionEnd
+
+Section "Config Files" SEC02
+ SetOutPath "$INSTDIR\conf"
+ File "..\conf\inspircd.motd.example"
+ File "..\conf\inspircd.helpop-full.example"
+ File "..\conf\inspircd.helpop.example"
+ File "..\conf\inspircd.filter.example"
+ File "..\docs\inspircd.conf.example"
+ File "..\conf\inspircd.censor.example"
+ File "..\conf\inspircd.rules.example"
+ File "..\conf\inspircd.quotes.example"
+SectionEnd
+
+Section "Command Handlers" SEC03
+ SetOutPath "$INSTDIR\lib"
+ File "..\bin\${BUILD}\lib\cmd_*.so"
+SectionEnd
+
+Section "Modules" SEC04
+ SetOutPath "$INSTDIR\modules"
+ File "..\bin\${BUILD}\modules\m_*.so"
+SectionEnd
+
+Section "SSL Modules" SEC05
+ SetOutPath "$INSTDIR\bin"
+ SetOverwrite ifnewer
+ File "..\bin\${BUILD}\bin\libgcrypt-11.dll"
+ File "..\bin\${BUILD}\bin\libgnutls-13.dll"
+ File "..\bin\${BUILD}\bin\libgnutls-extra-13.dll"
+ File "..\bin\${BUILD}\bin\libgnutls-openssl-13.dll"
+ File "..\bin\${BUILD}\bin\libgpg-error-0.dll"
+ File "..\bin\${BUILD}\bin\libopencdk-8.dll"
+ File "..\bin\${BUILD}\bin\libtasn1-3.dll"
+ SetOutPath "$INSTDIR\modules"
+ File "d:\temp\m_ssl_gnutls.so"
+ File "d:\temp\m_sslinfo.so"
+ File "d:\temp\m_ssl_oper_cert.so"
+ SetOutPath "$INSTDIR\conf"
+ SetOverwrite off
+ File "key.pem"
+ File "cert.pem"
+SectionEnd
+
+Section "Regexp Modules" SEC06
+ SetOutPath "$INSTDIR\bin"
+ SetOverwrite ifnewer
+ File "..\bin\${BUILD}\bin\pcre.dll"
+ SetOutPath "$INSTDIR\modules"
+ File "d:\temp\m_filter_pcre.so"
+SectionEnd
+
+Section -AdditionalIcons
+ SetOutPath $INSTDIR
+ WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"
+ CreateShortCut "$SMPROGRAMS\InspIRCd\InspIRCd Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url"
+ CreateShortCut "$SMPROGRAMS\InspIRCd\Uninstall.lnk" "$INSTDIR\uninst.exe"
+SectionEnd
+
+Section -Post
+ WriteUninstaller "$INSTDIR\uninst.exe"
+ WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\inspircd.exe"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\inspircd.exe"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
+ MessageBox MB_ICONINFORMATION|MB_OK "InspIRCd was successfully installed. Remember to edit your configuration file in $INSTDIR\conf!"
+SectionEnd
+
+; Section descriptions
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC01} "Actual executable"
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC03} "Command modules"
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC02} "Default configuration files"
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC04} "Optional non-SSL modules"
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC05} "SSL modules and GnuTLS DLL libraries"
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC06} "Regular expression module and PCRE DLL library"
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+
+Function un.onUninstSuccess
+ HideWindow
+ MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
+FunctionEnd
+
+Function .onInit
+ SectionSetFlags ${SEC01} 17
+ SectionSetFlags ${SEC03} 17
+ StrCpy $INSTDIR "$PROGRAMFILES\InspIRCd"
+FunctionEnd
+
+Function un.onInit
+ MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
+ Abort
+FunctionEnd
+
+Section Uninstall
+ Delete "$INSTDIR\${PRODUCT_NAME}.url"
+ Delete "$INSTDIR\uninst.exe"
+ Delete "$INSTDIR\modules\m_*.so"
+ Delete "$INSTDIR\lib\cmd_*.so"
+ Delete "$INSTDIR\conf\inspircd.quotes.example"
+ Delete "$INSTDIR\conf\inspircd.rules.example"
+ Delete "$INSTDIR\conf\inspircd.censor.example"
+ Delete "$INSTDIR\conf\inspircd.conf.example"
+ Delete "$INSTDIR\conf\inspircd.filter.example"
+ Delete "$INSTDIR\conf\inspircd.helpop.example"
+ Delete "$INSTDIR\conf\inspircd.helpop-full.example"
+ Delete "$INSTDIR\conf\inspircd.motd.example"
+ Delete "$INSTDIR\bin\inspircd.exe"
+ Delete "$INSTDIR\bin\*.dll"
+ Delete "$INSTDIR\InspGUI.exe"
+ Delete "$SMPROGRAMS\InspIRCd\Uninstall.lnk"
+ Delete "$SMPROGRAMS\InspIRCd\InspIRCd Website.lnk"
+ Delete "$SMPROGRAMS\InspIRCd\InspIRCd.lnk"
+
+ RMDir "$SMPROGRAMS\InspIRCd"
+ RMDir "$INSTDIR\modules"
+ RMDir "$INSTDIR\lib"
+ RMDir "$INSTDIR\conf"
+ RMDir "$INSTDIR\bin"
+ RMDir "$INSTDIR"
+
+ DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
+ DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
+ SetAutoClose true
+SectionEnd
diff --git a/win/inspircd_memory_functions.cpp b/win/inspircd_memory_functions.cpp
index 29c37a42e..afff4287e 100644
--- a/win/inspircd_memory_functions.cpp
+++ b/win/inspircd_memory_functions.cpp
@@ -1 +1,44 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd_win32wrapper.h" #include <exception> #include <new> #include <new.h> /** On windows, all dll files and executables have their own private heap, * whereas on POSIX systems, shared objects loaded into an executable share * the executable's heap. This means that if we pass an arbitrary pointer to * a windows DLL which is not allocated in that dll, without some form of * marshalling, we get a page fault. To fix this, these overrided operators * new and delete use the windows HeapAlloc and HeapFree functions to claim * memory from the windows global heap. This makes windows 'act like' POSIX * when it comes to memory usage between dlls and exes. */ void * ::operator new(size_t iSize) { void* ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, iSize); /* zero memory for unix compatibility */ /* This is the correct behaviour according to C++ standards for out of memory, * not returning null -- Brain */ if (!ptr) throw std::bad_alloc(); else return ptr; } void ::operator delete(void * ptr) { HeapFree(GetProcessHeap(), 0, ptr); } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd_win32wrapper.h"
+#include <exception>
+#include <new>
+#include <new.h>
+
+/** On windows, all dll files and executables have their own private heap,
+ * whereas on POSIX systems, shared objects loaded into an executable share
+ * the executable's heap. This means that if we pass an arbitrary pointer to
+ * a windows DLL which is not allocated in that dll, without some form of
+ * marshalling, we get a page fault. To fix this, these overrided operators
+ * new and delete use the windows HeapAlloc and HeapFree functions to claim
+ * memory from the windows global heap. This makes windows 'act like' POSIX
+ * when it comes to memory usage between dlls and exes.
+ */
+
+void * ::operator new(size_t iSize)
+{
+ void* ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, iSize); /* zero memory for unix compatibility */
+ /* This is the correct behaviour according to C++ standards for out of memory,
+ * not returning null -- Brain
+ */
+ if (!ptr)
+ throw std::bad_alloc();
+ else
+ return ptr;
+}
+
+void ::operator delete(void * ptr)
+{
+ HeapFree(GetProcessHeap(), 0, ptr);
+}
diff --git a/win/inspircd_win32wrapper.cpp b/win/inspircd_win32wrapper.cpp
index 1557c8f6e..b3af867cf 100644
--- a/win/inspircd_win32wrapper.cpp
+++ b/win/inspircd_win32wrapper.cpp
@@ -1 +1,513 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd_win32wrapper.h" #include "inspircd.h" #include <string> #include <errno.h> #include <assert.h> using namespace std; #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif HANDLE hIPCPipe; int inet_aton(const char *cp, struct in_addr *addr) { unsigned long ip = inet_addr(cp); addr->s_addr = ip; return (addr->s_addr == INADDR_NONE) ? 0 : 1; } const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { struct sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { struct sockaddr_in6 in; memset(&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } int geteuid() { return 1; } int inet_pton(int af, const char *src, void *dst) { sockaddr_in sa; int len = sizeof(SOCKADDR); int rv = WSAStringToAddress((LPSTR)src, af, NULL, (LPSOCKADDR)&sa, &len); if(rv >= 0) { if(WSAGetLastError() == 10022) // Invalid Argument rv = 0; else rv = 1; } memcpy(dst, &sa.sin_addr, sizeof(struct in_addr)); return rv; } char * strtok_r(char *_String, const char *_Control, char **_Context) { unsigned char *str; const unsigned char *ctl = (const unsigned char*)_Control; unsigned char map[32]; if (_Context == 0 || !_Control) return 0; if (!(_String != NULL || *_Context != NULL)) return 0; memset(map, 0, 32); do { map[*ctl >> 3] |= (1 << (*ctl & 7)); } while (*ctl++); /* If string is NULL, set str to the saved * pointer (i.e., continue breaking tokens out of the string * from the last strtok call) */ if (_String != NULL) { str = (unsigned char*)_String; } else { str = (unsigned char*)*_Context; } /* Find beginning of token (skip over leading delimiters). Note that * there is no token iff this loop sets str to point to the terminal * null (*str == 0) */ while ((map[*str >> 3] & (1 << (*str & 7))) && *str != 0) { str++; } _String = (char*)str; /* Find the end of the token. If it is not the end of the string, * put a null there. */ for ( ; *str != 0 ; str++ ) { if (map[*str >> 3] & (1 << (*str & 7))) { *str++ = 0; break; } } /* Update context */ *_Context = (char*)str; /* Determine if a token has been found. */ if (_String == (char*)str) { return NULL; } else { return _String; } } void setcolor(int color_code) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color_code); } DIR * opendir(const char * path) { std::string search_path = string(path) + "\\*.*"; WIN32_FIND_DATA fd; HANDLE f = FindFirstFile(search_path.c_str(), &fd); if (f != INVALID_HANDLE_VALUE) { DIR * d = new DIR; memcpy(&d->find_data, &fd, sizeof(WIN32_FIND_DATA)); d->find_handle = f; d->first = true; return d; } else { return 0; } } dirent * readdir(DIR * handle) { if (handle->first) handle->first = false; else { if (!FindNextFile(handle->find_handle, &handle->find_data)) return 0; } strncpy(handle->dirent_pointer.d_name, handle->find_data.cFileName, MAX_PATH); return &handle->dirent_pointer; } void closedir(DIR * handle) { FindClose(handle->find_handle); delete handle; } const char * dlerror() { static char errormessage[500]; DWORD error = GetLastError(); SetLastError(0); if (error == 0) return 0; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)errormessage, 500, 0); return errormessage; } #define TRED FOREGROUND_RED | FOREGROUND_INTENSITY #define TGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY #define TYELLOW FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY #define TNORMAL FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE #define TWHITE TNORMAL | FOREGROUND_INTENSITY #define TBLUE FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY /* Handles colors in printf */ int printf_c(const char * format, ...) { // Better hope we're not multithreaded, otherwise we'll have chickens crossing the road other side to get the to :P static char message[MAXBUF]; static char temp[MAXBUF]; int color1, color2; /* parse arguments */ va_list ap; va_start(ap, format); vsnprintf(message, 500, format, ap); va_end(ap); /* search for unix-style escape sequences */ int t; int c = 0; const char * p = message; while (*p != 0) { if (*p == '\033') { // Escape sequence -> copy into the temp buffer, and parse the color. p++; t = 0; while ((*p) && (*p != 'm')) { temp[t++] = *p; ++p; } temp[t] = 0; p++; if (*temp == '[') { if (sscanf(temp, "[%u;%u", &color1, &color2) == 2) { switch(color2) { case 32: // Green SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY); // Yellow break; default: // Unknown // White SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); break; } } else { switch (*(temp+1)) { case '0': // Returning to normal colour. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); break; case '1': // White SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), TWHITE); break; default: char message[50]; sprintf(message, "Unknown color code: %s", temp); MessageBox(0, message, message, MB_OK); break; } } } } putchar(*p); ++c; ++p; } return c; } int arg_counter = 1; char optarg[514]; int getopt_long_only(int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind) { // burlex todo: handle the shortops, at the moment it only works with longopts. if (___argc == 1 || arg_counter == ___argc) // No arguments (apart from filename) return -1; const char * opt = ___argv[arg_counter]; int return_val = 0; // if we're not an option, return an error. if (strnicmp(opt, "--", 2) != 0) return 1; else opt += 2; // parse argument list int i = 0; for (; __longopts[i].name != 0; ++i) { if (!strnicmp(__longopts[i].name, opt, strlen(__longopts[i].name))) { // woot, found a valid argument =) char * par = 0; if ((arg_counter + 1) != ___argc) { // grab the parameter from the next argument (if its not another argument) if (strnicmp(___argv[arg_counter+1], "--", 2) != 0) { arg_counter++; // Trash this next argument, we won't be needing it. par = ___argv[arg_counter]; } } // increment the argument for next time arg_counter++; // determine action based on type if (__longopts[i].has_arg == required_argument && !par) { // parameter missing and its a required parameter option return 1; } // store argument in optarg if (par) strncpy(optarg, par, 514); if (__longopts[i].flag != 0) { // this is a variable, we have to set it if this argument is found. *__longopts[i].flag = 1; return 0; } else { if (__longopts[i].val == -1 || par == 0) return 1; return __longopts[i].val; } break; } } // return 1 (invalid argument) return 1; } /* IPC Messages */ #define IPC_MESSAGE_REHASH 1 #define IPC_MESSAGE_DIE 2 #define IPC_MESSAGE_RESTART 3 void InitIPC() { static DWORD buflen = 1024; static const char * pipename = "\\\\.\\mailslot\\Inspircd"; hIPCPipe = CreateMailslot(pipename, buflen, 0, 0); if (hIPCPipe == INVALID_HANDLE_VALUE) printf("IPC Pipe could not be created. Are you sure you didn't start InspIRCd twice?\n"); } void CheckIPC(InspIRCd * Instance) { if (hIPCPipe == INVALID_HANDLE_VALUE) return; DWORD bytes; DWORD action; BOOL res = ReadFile(hIPCPipe, &action, sizeof(DWORD), &bytes, 0); if (!res) { if (GetLastError() != ERROR_SEM_TIMEOUT) Instance->Log(DEFAULT, "IPC Pipe Error %u: %s", GetLastError(), dlerror()); return; } switch (action) { case IPC_MESSAGE_REHASH: InspIRCd::Rehash(0); break; case IPC_MESSAGE_DIE: InspIRCd::Exit(0); break; case IPC_MESSAGE_RESTART: Instance->Restart("IPC_MESSAGE_RESTART received by mailslot."); break; } } void CloseIPC() { CloseHandle(hIPCPipe); } /* These three functions were created from looking at how ares does it * (...and they look far tidier in C++) */ /* Get active nameserver */ bool GetNameServer(HKEY regkey, const char *key, char* &output) { DWORD size = 0; DWORD result = RegQueryValueEx(regkey, key, 0, NULL, NULL, &size); if (((result != ERROR_SUCCESS) && (result != ERROR_MORE_DATA)) || (!size)) return false; output = new char[size+1]; if ((RegQueryValueEx(regkey, key, 0, NULL, (LPBYTE)output, &size) != ERROR_SUCCESS) || (!*output)) { delete output; return false; } return true; } /* Check a network interface for its nameserver */ bool GetInterface(HKEY regkey, const char *key, char* &output) { char buf[39]; DWORD size = 39; int idx = 0; HKEY top; while (RegEnumKeyEx(regkey, idx++, buf, &size, 0, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS) { size = 39; if (RegOpenKeyEx(regkey, buf, 0, KEY_QUERY_VALUE, &top) != ERROR_SUCCESS) continue; int rc = GetNameServer(top, key, output); RegCloseKey(top); if (rc) return true; } return false; } std::string FindNameServerWin() { std::string returnval = "127.0.0.1"; HKEY top, key; char* dns = NULL; /* Lets see if the correct registry hive and tree exist */ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0, KEY_READ, &top) == ERROR_SUCCESS) { /* If they do, attempt to get the nameserver name */ RegOpenKeyEx(top, "Interfaces", 0, KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &key); if ((GetNameServer(top, "NameServer", dns)) || (GetNameServer(top, "DhcpNameServer", dns)) || (GetInterface(key, "NameServer", dns)) || (GetInterface(key, "DhcpNameServer", dns))) { if (dns) { returnval = dns; delete dns; } } RegCloseKey(key); RegCloseKey(top); } return returnval; } void ClearConsole() { COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); DWORD cCharsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ DWORD dwConSize; /* number of character cells in the current buffer */ /* get the number of character cells in the current buffer */ if (GetConsoleScreenBufferInfo( hConsole, &csbi )) { dwConSize = csbi.dwSize.X * csbi.dwSize.Y; /* fill the entire screen with blanks */ if (FillConsoleOutputCharacter( hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten )) { /* get the current text attribute */ if (GetConsoleScreenBufferInfo( hConsole, &csbi )) { /* now set the buffer's attributes accordingly */ if (FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )) { /* put the cursor at (0, 0) */ SetConsoleCursorPosition( hConsole, coordScreen ); } } } } return; } \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd_win32wrapper.h"
+#include "inspircd.h"
+#include <string>
+#include <errno.h>
+#include <assert.h>
+using namespace std;
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+HANDLE hIPCPipe;
+
+int inet_aton(const char *cp, struct in_addr *addr)
+{
+ unsigned long ip = inet_addr(cp);
+ addr->s_addr = ip;
+ return (addr->s_addr == INADDR_NONE) ? 0 : 1;
+}
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
+{
+
+ if (af == AF_INET)
+ {
+ struct sockaddr_in in;
+ memset(&in, 0, sizeof(in));
+ in.sin_family = AF_INET;
+ memcpy(&in.sin_addr, src, sizeof(struct in_addr));
+ getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
+ return dst;
+ }
+ else if (af == AF_INET6)
+ {
+ struct sockaddr_in6 in;
+ memset(&in, 0, sizeof(in));
+ in.sin6_family = AF_INET6;
+ memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
+ getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
+ return dst;
+ }
+ return NULL;
+}
+
+int geteuid()
+{
+ return 1;
+}
+
+int inet_pton(int af, const char *src, void *dst)
+{
+ sockaddr_in sa;
+ int len = sizeof(SOCKADDR);
+ int rv = WSAStringToAddress((LPSTR)src, af, NULL, (LPSOCKADDR)&sa, &len);
+ if(rv >= 0)
+ {
+ if(WSAGetLastError() == 10022) // Invalid Argument
+ rv = 0;
+ else
+ rv = 1;
+ }
+ memcpy(dst, &sa.sin_addr, sizeof(struct in_addr));
+ return rv;
+}
+
+char * strtok_r(char *_String, const char *_Control, char **_Context)
+{
+ unsigned char *str;
+ const unsigned char *ctl = (const unsigned char*)_Control;
+ unsigned char map[32];
+
+ if (_Context == 0 || !_Control)
+ return 0;
+
+ if (!(_String != NULL || *_Context != NULL))
+ return 0;
+
+ memset(map, 0, 32);
+
+ do {
+ map[*ctl >> 3] |= (1 << (*ctl & 7));
+ } while (*ctl++);
+
+ /* If string is NULL, set str to the saved
+ * pointer (i.e., continue breaking tokens out of the string
+ * from the last strtok call) */
+ if (_String != NULL)
+ {
+ str = (unsigned char*)_String;
+ }
+ else
+ {
+ str = (unsigned char*)*_Context;
+ }
+
+ /* Find beginning of token (skip over leading delimiters). Note that
+ * there is no token iff this loop sets str to point to the terminal
+ * null (*str == 0) */
+ while ((map[*str >> 3] & (1 << (*str & 7))) && *str != 0)
+ {
+ str++;
+ }
+
+ _String = (char*)str;
+
+ /* Find the end of the token. If it is not the end of the string,
+ * put a null there. */
+ for ( ; *str != 0 ; str++ )
+ {
+ if (map[*str >> 3] & (1 << (*str & 7)))
+ {
+ *str++ = 0;
+ break;
+ }
+ }
+
+ /* Update context */
+ *_Context = (char*)str;
+
+ /* Determine if a token has been found. */
+ if (_String == (char*)str)
+ {
+ return NULL;
+ }
+ else
+ {
+ return _String;
+ }
+}
+
+void setcolor(int color_code)
+{
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color_code);
+}
+
+DIR * opendir(const char * path)
+{
+ std::string search_path = string(path) + "\\*.*";
+ WIN32_FIND_DATA fd;
+ HANDLE f = FindFirstFile(search_path.c_str(), &fd);
+ if (f != INVALID_HANDLE_VALUE)
+ {
+ DIR * d = new DIR;
+ memcpy(&d->find_data, &fd, sizeof(WIN32_FIND_DATA));
+ d->find_handle = f;
+ d->first = true;
+ return d;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+dirent * readdir(DIR * handle)
+{
+ if (handle->first)
+ handle->first = false;
+ else
+ {
+ if (!FindNextFile(handle->find_handle, &handle->find_data))
+ return 0;
+ }
+
+ strncpy(handle->dirent_pointer.d_name, handle->find_data.cFileName, MAX_PATH);
+ return &handle->dirent_pointer;
+}
+
+void closedir(DIR * handle)
+{
+ FindClose(handle->find_handle);
+ delete handle;
+}
+
+const char * dlerror()
+{
+ static char errormessage[500];
+ DWORD error = GetLastError();
+ SetLastError(0);
+ if (error == 0)
+ return 0;
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)errormessage, 500, 0);
+ return errormessage;
+}
+
+#define TRED FOREGROUND_RED | FOREGROUND_INTENSITY
+#define TGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY
+#define TYELLOW FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
+#define TNORMAL FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE
+#define TWHITE TNORMAL | FOREGROUND_INTENSITY
+#define TBLUE FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY
+
+/* Handles colors in printf */
+int printf_c(const char * format, ...)
+{
+ // Better hope we're not multithreaded, otherwise we'll have chickens crossing the road other side to get the to :P
+ static char message[MAXBUF];
+ static char temp[MAXBUF];
+ int color1, color2;
+
+ /* parse arguments */
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(message, 500, format, ap);
+ va_end(ap);
+
+ /* search for unix-style escape sequences */
+ int t;
+ int c = 0;
+ const char * p = message;
+ while (*p != 0)
+ {
+ if (*p == '\033')
+ {
+ // Escape sequence -> copy into the temp buffer, and parse the color.
+ p++;
+ t = 0;
+ while ((*p) && (*p != 'm'))
+ {
+ temp[t++] = *p;
+ ++p;
+ }
+
+ temp[t] = 0;
+ p++;
+
+ if (*temp == '[')
+ {
+ if (sscanf(temp, "[%u;%u", &color1, &color2) == 2)
+ {
+ switch(color2)
+ {
+ case 32: // Green
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY); // Yellow
+ break;
+
+ default: // Unknown
+ // White
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ break;
+ }
+ }
+ else
+ {
+ switch (*(temp+1))
+ {
+ case '0':
+ // Returning to normal colour.
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ break;
+
+ case '1':
+ // White
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), TWHITE);
+ break;
+
+ default:
+ char message[50];
+ sprintf(message, "Unknown color code: %s", temp);
+ MessageBox(0, message, message, MB_OK);
+ break;
+ }
+ }
+ }
+ }
+
+ putchar(*p);
+ ++c;
+ ++p;
+ }
+
+ return c;
+}
+
+int arg_counter = 1;
+char optarg[514];
+int getopt_long_only(int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind)
+{
+ // burlex todo: handle the shortops, at the moment it only works with longopts.
+
+ if (___argc == 1 || arg_counter == ___argc) // No arguments (apart from filename)
+ return -1;
+
+ const char * opt = ___argv[arg_counter];
+ int return_val = 0;
+
+ // if we're not an option, return an error.
+ if (strnicmp(opt, "--", 2) != 0)
+ return 1;
+ else
+ opt += 2;
+
+
+ // parse argument list
+ int i = 0;
+ for (; __longopts[i].name != 0; ++i)
+ {
+ if (!strnicmp(__longopts[i].name, opt, strlen(__longopts[i].name)))
+ {
+ // woot, found a valid argument =)
+ char * par = 0;
+ if ((arg_counter + 1) != ___argc)
+ {
+ // grab the parameter from the next argument (if its not another argument)
+ if (strnicmp(___argv[arg_counter+1], "--", 2) != 0)
+ {
+ arg_counter++; // Trash this next argument, we won't be needing it.
+ par = ___argv[arg_counter];
+ }
+ }
+
+ // increment the argument for next time
+ arg_counter++;
+
+ // determine action based on type
+ if (__longopts[i].has_arg == required_argument && !par)
+ {
+ // parameter missing and its a required parameter option
+ return 1;
+ }
+
+ // store argument in optarg
+ if (par)
+ strncpy(optarg, par, 514);
+
+ if (__longopts[i].flag != 0)
+ {
+ // this is a variable, we have to set it if this argument is found.
+ *__longopts[i].flag = 1;
+ return 0;
+ }
+ else
+ {
+ if (__longopts[i].val == -1 || par == 0)
+ return 1;
+
+ return __longopts[i].val;
+ }
+ break;
+ }
+ }
+
+ // return 1 (invalid argument)
+ return 1;
+}
+
+/* IPC Messages */
+#define IPC_MESSAGE_REHASH 1
+#define IPC_MESSAGE_DIE 2
+#define IPC_MESSAGE_RESTART 3
+
+void InitIPC()
+{
+ static DWORD buflen = 1024;
+ static const char * pipename = "\\\\.\\mailslot\\Inspircd";
+ hIPCPipe = CreateMailslot(pipename, buflen, 0, 0);
+ if (hIPCPipe == INVALID_HANDLE_VALUE)
+ printf("IPC Pipe could not be created. Are you sure you didn't start InspIRCd twice?\n");
+}
+
+void CheckIPC(InspIRCd * Instance)
+{
+ if (hIPCPipe == INVALID_HANDLE_VALUE)
+ return;
+
+ DWORD bytes;
+ DWORD action;
+
+ BOOL res = ReadFile(hIPCPipe, &action, sizeof(DWORD), &bytes, 0);
+ if (!res)
+ {
+ if (GetLastError() != ERROR_SEM_TIMEOUT)
+ Instance->Log(DEFAULT, "IPC Pipe Error %u: %s", GetLastError(), dlerror());
+ return;
+ }
+
+ switch (action)
+ {
+ case IPC_MESSAGE_REHASH:
+ InspIRCd::Rehash(0);
+ break;
+
+ case IPC_MESSAGE_DIE:
+ InspIRCd::Exit(0);
+ break;
+
+ case IPC_MESSAGE_RESTART:
+ Instance->Restart("IPC_MESSAGE_RESTART received by mailslot.");
+ break;
+ }
+}
+
+void CloseIPC()
+{
+ CloseHandle(hIPCPipe);
+}
+
+
+/* These three functions were created from looking at how ares does it
+ * (...and they look far tidier in C++)
+ */
+
+/* Get active nameserver */
+bool GetNameServer(HKEY regkey, const char *key, char* &output)
+{
+ DWORD size = 0;
+ DWORD result = RegQueryValueEx(regkey, key, 0, NULL, NULL, &size);
+ if (((result != ERROR_SUCCESS) && (result != ERROR_MORE_DATA)) || (!size))
+ return false;
+
+ output = new char[size+1];
+
+ if ((RegQueryValueEx(regkey, key, 0, NULL, (LPBYTE)output, &size) != ERROR_SUCCESS) || (!*output))
+ {
+ delete output;
+ return false;
+ }
+ return true;
+}
+
+/* Check a network interface for its nameserver */
+bool GetInterface(HKEY regkey, const char *key, char* &output)
+{
+ char buf[39];
+ DWORD size = 39;
+ int idx = 0;
+ HKEY top;
+
+ while (RegEnumKeyEx(regkey, idx++, buf, &size, 0, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
+ {
+ size = 39;
+ if (RegOpenKeyEx(regkey, buf, 0, KEY_QUERY_VALUE, &top) != ERROR_SUCCESS)
+ continue;
+ int rc = GetNameServer(top, key, output);
+ RegCloseKey(top);
+ if (rc)
+ return true;
+ }
+ return false;
+}
+
+
+std::string FindNameServerWin()
+{
+ std::string returnval = "127.0.0.1";
+ HKEY top, key;
+ char* dns = NULL;
+
+ /* Lets see if the correct registry hive and tree exist */
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0, KEY_READ, &top) == ERROR_SUCCESS)
+ {
+ /* If they do, attempt to get the nameserver name */
+ RegOpenKeyEx(top, "Interfaces", 0, KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &key);
+ if ((GetNameServer(top, "NameServer", dns)) || (GetNameServer(top, "DhcpNameServer", dns))
+ || (GetInterface(key, "NameServer", dns)) || (GetInterface(key, "DhcpNameServer", dns)))
+ {
+ if (dns)
+ {
+ returnval = dns;
+ delete dns;
+ }
+ }
+ RegCloseKey(key);
+ RegCloseKey(top);
+ }
+ return returnval;
+}
+
+
+void ClearConsole()
+{
+ COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */
+ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD cCharsWritten;
+ CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
+ DWORD dwConSize; /* number of character cells in the current buffer */
+
+ /* get the number of character cells in the current buffer */
+
+ if (GetConsoleScreenBufferInfo( hConsole, &csbi ))
+ {
+ dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+ /* fill the entire screen with blanks */
+ if (FillConsoleOutputCharacter( hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten ))
+ {
+ /* get the current text attribute */
+ if (GetConsoleScreenBufferInfo( hConsole, &csbi ))
+ {
+ /* now set the buffer's attributes accordingly */
+ if (FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten ))
+ {
+ /* put the cursor at (0, 0) */
+ SetConsoleCursorPosition( hConsole, coordScreen );
+ }
+ }
+ }
+ }
+ return;
+}
diff --git a/win/inspircd_win32wrapper.h b/win/inspircd_win32wrapper.h
index ee0feb77e..fb6882378 100644
--- a/win/inspircd_win32wrapper.h
+++ b/win/inspircd_win32wrapper.h
@@ -1 +1,189 @@
-/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ /* Windows Port Wrapper Functions/Definitions By Burlex */ #ifndef INSPIRCD_WIN32WRAPPER_H #define INSPIRCD_WIN32WRAPPER_H /* Define the WINDOWS macro. This means we're building on windows to the rest of the server. I think this is more reasonable than using WIN32, especially if we're gonna be doing 64-bit compiles */ #define WINDOWS 1 /* Make builds smaller, leaner and faster */ #define VC_EXTRALEAN /* They just have to be *different*, don't they. */ #define PATH_MAX MAX_PATH /* Begone shitty 'safe STL' warnings */ #define _SCL_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #define _AFX_SECURE_NO_WARNINGS #define _ATL_SECURE_NO_WARNINGS /* Macros for exporting symbols - dependant on what is being compiled */ #ifdef DLL_BUILD #define CoreExport __declspec(dllimport) #define DllExport __declspec(dllexport) #else #define CoreExport __declspec(dllexport) #define DllExport __declspec(dllimport) #endif /* Disable the deprecation warnings.. it spams :P */ #define _CRT_SECURE_NO_DEPRECATE #define _SCL_SECURE_NO_DEPRECATE #include <string> /* Say we're building on windows 2000. Anyone running something older than this * reeeeeeeally needs to upgrade! */ #define _WIN32_WINNT 0x500 /* Normal windows (platform-specific) includes */ #include <winsock2.h> #include <windows.h> #include <ws2tcpip.h> #include <sys/types.h> #include <sys/stat.h> #include <direct.h> #include <process.h> #include <stdio.h> #include <algorithm> /* strcasecmp is not defined on windows by default */ #define strcasecmp _stricmp /* Error macros need to be redirected to winsock error codes */ #define ETIMEDOUT WSAETIMEDOUT #define ECONNREFUSED WSAECONNREFUSED #define EADDRINUSE WSAEADDRINUSE #define EINPROGRESS WSAEWOULDBLOCK /* Remember file descriptors are treated differently on windows ;) */ __inline int close(int socket) { return closesocket(socket); } /* Convert formatted (xxx.xxx.xxx.xxx) string to in_addr struct */ CoreExport int inet_pton(int af, const char * src, void * dst); /* Convert struct to formatted (xxx.xxx.xxx.xxx) string */ CoreExport const char * inet_ntop(int af, const void * src, char * dst, socklen_t cnt); /* Safe printf functions aren't defined in VC2003 */ #define snprintf _snprintf #define vsnprintf _vsnprintf /* Recursive token function doesn't exist in VC++ */ CoreExport char * strtok_r(char *_String, const char *_Control, char **_Context); /* Unix-style sleep (argument is in seconds) */ __inline void sleep(int seconds) { Sleep(seconds * 1000); } /* IPV4 only convert string to address struct */ CoreExport int inet_aton(const char *, struct in_addr *); /* Unix-style get running user id */ CoreExport int geteuid(); /* Handles colors in printf */ CoreExport int printf_c(const char * format, ...); /* getopt() wrapper */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 struct option { char *name; int has_arg; int *flag; int val; }; extern char optarg[514]; int getopt_long_only (int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind); /* Accept Handlers */ struct udp_overlap; CoreExport int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent); CoreExport int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent); CoreExport int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov); /* Module Loading */ #define dlopen(path, state) (void*)LoadLibrary(path) #define dlsym(handle, export) (void*)GetProcAddress((HMODULE)handle, export) #define dlclose(handle) FreeLibrary((HMODULE)handle) const char * dlerror(); /* Unix-style directory searching functions */ #define chmod(filename, mode) struct dirent { char d_name[MAX_PATH]; }; struct DIR { dirent dirent_pointer; HANDLE find_handle; WIN32_FIND_DATA find_data; bool first; }; CoreExport DIR * opendir(const char * path); CoreExport dirent * readdir(DIR * handle); CoreExport void closedir(DIR * handle); /* Disable these stupid warnings.. */ #pragma warning(disable:4800) #pragma warning(disable:4251) #pragma warning(disable:4275) #pragma warning(disable:4244) // warning C4244: '=' : conversion from 'long' to 'short', possible loss of data #pragma warning(disable:4267) // warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data #pragma warning(disable:4805) // warning C4805: '!=' : unsafe mix of type 'char' and type 'bool' in operation #pragma warning(disable:4311) // warning C4311: 'type cast' : pointer truncation from 'accept_overlap *' to 'int' #pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'int' to 'HANDLE' of greater size #pragma warning(disable:4355) // warning C4355: 'this' : used in base member initializer list #pragma warning(disable:4996) // warning C4996: 'std::_Traits_helper::move_s' was declared deprecated /* Mehhhh... typedefs. */ typedef unsigned char uint8_t; typedef unsigned long long uint64_t; typedef signed char int8_t; typedef signed long int32_t; typedef signed long long int64_t; /* Shared memory allocation functions */ void * ::operator new(size_t iSize); void ::operator delete(void * ptr); /* IPC Handlers */ class InspIRCd; void InitIPC(); void CheckIPC(InspIRCd * Instance); void CloseIPC(); /* Look up the nameserver in use from the registry on windows */ std::string FindNameServerWin(); /* Clear a windows console */ void ClearConsole(); #endif \ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* Windows Port
+ Wrapper Functions/Definitions
+ By Burlex */
+
+#ifndef INSPIRCD_WIN32WRAPPER_H
+#define INSPIRCD_WIN32WRAPPER_H
+
+/* Define the WINDOWS macro. This means we're building on windows to the rest of the server.
+ I think this is more reasonable than using WIN32, especially if we're gonna be doing 64-bit compiles */
+#define WINDOWS 1
+
+/* Make builds smaller, leaner and faster */
+#define VC_EXTRALEAN
+
+/* They just have to be *different*, don't they. */
+#define PATH_MAX MAX_PATH
+
+/* Begone shitty 'safe STL' warnings */
+#define _SCL_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#define _AFX_SECURE_NO_WARNINGS
+#define _ATL_SECURE_NO_WARNINGS
+
+/* Macros for exporting symbols - dependant on what is being compiled */
+
+#ifdef DLL_BUILD
+#define CoreExport __declspec(dllimport)
+#define DllExport __declspec(dllexport)
+#else
+#define CoreExport __declspec(dllexport)
+#define DllExport __declspec(dllimport)
+#endif
+
+/* Disable the deprecation warnings.. it spams :P */
+#define _CRT_SECURE_NO_DEPRECATE
+#define _SCL_SECURE_NO_DEPRECATE
+
+#include <string>
+
+/* Say we're building on windows 2000. Anyone running something older than this
+ * reeeeeeeally needs to upgrade! */
+
+#define _WIN32_WINNT 0x500
+
+/* Normal windows (platform-specific) includes */
+#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <direct.h>
+#include <process.h>
+#include <stdio.h>
+#include <algorithm>
+
+/* strcasecmp is not defined on windows by default */
+#define strcasecmp _stricmp
+
+/* Error macros need to be redirected to winsock error codes */
+#define ETIMEDOUT WSAETIMEDOUT
+#define ECONNREFUSED WSAECONNREFUSED
+#define EADDRINUSE WSAEADDRINUSE
+#define EINPROGRESS WSAEWOULDBLOCK
+
+/* Remember file descriptors are treated differently on windows ;) */
+__inline int close(int socket) { return closesocket(socket); }
+
+/* Convert formatted (xxx.xxx.xxx.xxx) string to in_addr struct */
+CoreExport int inet_pton(int af, const char * src, void * dst);
+
+/* Convert struct to formatted (xxx.xxx.xxx.xxx) string */
+CoreExport const char * inet_ntop(int af, const void * src, char * dst, socklen_t cnt);
+
+/* Safe printf functions aren't defined in VC2003 */
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+/* Recursive token function doesn't exist in VC++ */
+CoreExport char * strtok_r(char *_String, const char *_Control, char **_Context);
+
+/* Unix-style sleep (argument is in seconds) */
+__inline void sleep(int seconds) { Sleep(seconds * 1000); }
+
+/* IPV4 only convert string to address struct */
+CoreExport int inet_aton(const char *, struct in_addr *);
+
+/* Unix-style get running user id */
+CoreExport int geteuid();
+
+/* Handles colors in printf */
+CoreExport int printf_c(const char * format, ...);
+
+/* getopt() wrapper */
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+struct option
+{
+ char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+extern char optarg[514];
+int getopt_long_only (int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind);
+
+/* Accept Handlers */
+struct udp_overlap;
+CoreExport int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent);
+CoreExport int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent);
+CoreExport int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov);
+
+/* Module Loading */
+#define dlopen(path, state) (void*)LoadLibrary(path)
+#define dlsym(handle, export) (void*)GetProcAddress((HMODULE)handle, export)
+#define dlclose(handle) FreeLibrary((HMODULE)handle)
+const char * dlerror();
+
+/* Unix-style directory searching functions */
+#define chmod(filename, mode)
+struct dirent
+{
+ char d_name[MAX_PATH];
+};
+
+struct DIR
+{
+ dirent dirent_pointer;
+ HANDLE find_handle;
+ WIN32_FIND_DATA find_data;
+ bool first;
+};
+
+CoreExport DIR * opendir(const char * path);
+CoreExport dirent * readdir(DIR * handle);
+CoreExport void closedir(DIR * handle);
+
+/* Disable these stupid warnings.. */
+#pragma warning(disable:4800)
+#pragma warning(disable:4251)
+#pragma warning(disable:4275)
+#pragma warning(disable:4244) // warning C4244: '=' : conversion from 'long' to 'short', possible loss of data
+#pragma warning(disable:4267) // warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
+#pragma warning(disable:4805) // warning C4805: '!=' : unsafe mix of type 'char' and type 'bool' in operation
+#pragma warning(disable:4311) // warning C4311: 'type cast' : pointer truncation from 'accept_overlap *' to 'int'
+#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'int' to 'HANDLE' of greater size
+#pragma warning(disable:4355) // warning C4355: 'this' : used in base member initializer list
+#pragma warning(disable:4996) // warning C4996: 'std::_Traits_helper::move_s' was declared deprecated
+
+/* Mehhhh... typedefs. */
+
+typedef unsigned char uint8_t;
+typedef unsigned long long uint64_t;
+typedef signed char int8_t;
+typedef signed long int32_t;
+typedef signed long long int64_t;
+
+/* Shared memory allocation functions */
+void * ::operator new(size_t iSize);
+void ::operator delete(void * ptr);
+
+/* IPC Handlers */
+class InspIRCd;
+
+void InitIPC();
+void CheckIPC(InspIRCd * Instance);
+void CloseIPC();
+
+/* Look up the nameserver in use from the registry on windows */
+std::string FindNameServerWin();
+
+/* Clear a windows console */
+void ClearConsole();
+
+#endif
+
diff --git a/win/key.pem b/win/key.pem
index 2cb3eda40..09b298260 100644
--- a/win/key.pem
+++ b/win/key.pem
@@ -1 +1,15 @@
------BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQDIbKvMTTogBZxTi1yn4ncVK09Wr+F2AxP63HWTzxnEwNhcURSa UqpCzVIfcpr7/jKn+8I17MzaMvG8m+sPKngPK5WMN440p12uitkS+uzkLbJ7J/Z3 35ar6nZOtbIO+aTDRzUTnNHGHRgdQj4GGvx89l0u7vQM3R2f9Oe2lWlc1wIDAQAB AoGABh+/7hmr/X9+Y9Udyylxzw1IOtNb9cGpUiB7XT1WQbtMwSFfGkoNVsY0TK6x SqLdRGG+cOxf5AjrdwJin8+B5JLsoFUJ79ouUSye4IpywH6pQPzTW5L/Pqw+lM81 YZB/I7OKwSOkmFvKM8l9Y3U/UdvPeVPU44jAsnTyN9gZ/q0CQQDb+qGe7T8AIm1U rz9Wf8/BBQy6ShoaL5sv0dqLE1/CWkGPnkhm8HA/6udlUiVNBcWlirKeSuzctC23 u/mGU179AkEA6T5TyZ798qKyKpZXqNzyfnq5RMjCdr12rtk+sTYThbHndGonhjKk PqWgQ/Aq3t33J740jsNpz6za6/hPRGp1YwJANE4o5eAljcOh2XP+DHRBkvS/bQA3 qqhNLxan70/BAjZxxlNthcR//EK/mJDqu6C2uUD8bbUFEwlooXp5v13NhQJALGbN FIjL1zDZsfnE3kSRdTpvooSFYI1Y1phMsveUZ9MiOKssswNY+QQWqlhCEQM4VbyD zNmufvZtBpbSoDeT+QJAasQ/yEgYJnC+nbWmiJVuIFYFiWkxYToSUv4yFq2zHj6O hVVCUr60FTMzqzS4BXzWVQVX2ylDJA40dUBTZ9HI7g== -----END RSA PRIVATE KEY----- \ No newline at end of file
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDIbKvMTTogBZxTi1yn4ncVK09Wr+F2AxP63HWTzxnEwNhcURSa
+UqpCzVIfcpr7/jKn+8I17MzaMvG8m+sPKngPK5WMN440p12uitkS+uzkLbJ7J/Z3
+35ar6nZOtbIO+aTDRzUTnNHGHRgdQj4GGvx89l0u7vQM3R2f9Oe2lWlc1wIDAQAB
+AoGABh+/7hmr/X9+Y9Udyylxzw1IOtNb9cGpUiB7XT1WQbtMwSFfGkoNVsY0TK6x
+SqLdRGG+cOxf5AjrdwJin8+B5JLsoFUJ79ouUSye4IpywH6pQPzTW5L/Pqw+lM81
+YZB/I7OKwSOkmFvKM8l9Y3U/UdvPeVPU44jAsnTyN9gZ/q0CQQDb+qGe7T8AIm1U
+rz9Wf8/BBQy6ShoaL5sv0dqLE1/CWkGPnkhm8HA/6udlUiVNBcWlirKeSuzctC23
+u/mGU179AkEA6T5TyZ798qKyKpZXqNzyfnq5RMjCdr12rtk+sTYThbHndGonhjKk
+PqWgQ/Aq3t33J740jsNpz6za6/hPRGp1YwJANE4o5eAljcOh2XP+DHRBkvS/bQA3
+qqhNLxan70/BAjZxxlNthcR//EK/mJDqu6C2uUD8bbUFEwlooXp5v13NhQJALGbN
+FIjL1zDZsfnE3kSRdTpvooSFYI1Y1phMsveUZ9MiOKssswNY+QQWqlhCEQM4VbyD
+zNmufvZtBpbSoDeT+QJAasQ/yEgYJnC+nbWmiJVuIFYFiWkxYToSUv4yFq2zHj6O
+hVVCUr60FTMzqzS4BXzWVQVX2ylDJA40dUBTZ9HI7g==
+-----END RSA PRIVATE KEY-----
diff --git a/win/makeinstaller.bat b/win/makeinstaller.bat
index cd3895a1e..e7ba79abd 100644
--- a/win/makeinstaller.bat
+++ b/win/makeinstaller.bat
@@ -1 +1,7 @@
-@mkdir d:\temp\ move ..\bin\release\modules\m_ssl_gnutls.so d:\temp\ move ..\bin\release\modules\m_sslinfo.so d:\temp\ move ..\bin\release\modules\m_ssl_oper_cert.so d:\temp\ move ..\bin\release\modules\m_filter_pcre.so d:\temp "C:\Program Files\NSIS\makensisw.exe" "inspircd.nsi" move d:\temp\*.so ..\bin\release\modules\ \ No newline at end of file
+@mkdir d:\temp\
+move ..\bin\release\modules\m_ssl_gnutls.so d:\temp\
+move ..\bin\release\modules\m_sslinfo.so d:\temp\
+move ..\bin\release\modules\m_ssl_oper_cert.so d:\temp\
+move ..\bin\release\modules\m_filter_pcre.so d:\temp
+"C:\Program Files\NSIS\makensisw.exe" "inspircd.nsi"
+move d:\temp\*.so ..\bin\release\modules\