summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bancache.cpp146
-rw-r--r--src/base.cpp107
-rw-r--r--src/channels.cpp993
-rw-r--r--src/cidr.cpp8
-rw-r--r--src/clientprotocol.cpp105
-rw-r--r--src/command_parse.cpp404
-rw-r--r--src/commands.cpp116
-rw-r--r--src/commands/cmd_admin.cpp72
-rw-r--r--src/commands/cmd_away.cpp75
-rw-r--r--src/commands/cmd_clearcache.cpp52
-rw-r--r--src/commands/cmd_commands.cpp74
-rw-r--r--src/commands/cmd_connect.cpp55
-rw-r--r--src/commands/cmd_die.cpp68
-rw-r--r--src/commands/cmd_invite.cpp154
-rw-r--r--src/commands/cmd_ison.cpp106
-rw-r--r--src/commands/cmd_join.cpp74
-rw-r--r--src/commands/cmd_kick.cpp85
-rw-r--r--src/commands/cmd_kill.cpp160
-rw-r--r--src/commands/cmd_links.cpp52
-rw-r--r--src/commands/cmd_list.cpp105
-rw-r--r--src/commands/cmd_loadmodule.cpp60
-rw-r--r--src/commands/cmd_map.cpp58
-rw-r--r--src/commands/cmd_mode.cpp53
-rw-r--r--src/commands/cmd_modules.cpp97
-rw-r--r--src/commands/cmd_motd.cpp85
-rw-r--r--src/commands/cmd_names.cpp71
-rw-r--r--src/commands/cmd_notice.cpp229
-rw-r--r--src/commands/cmd_oper.cpp105
-rw-r--r--src/commands/cmd_part.cpp78
-rw-r--r--src/commands/cmd_pass.cpp58
-rw-r--r--src/commands/cmd_ping.cpp49
-rw-r--r--src/commands/cmd_pong.cpp58
-rw-r--r--src/commands/cmd_privmsg.cpp237
-rw-r--r--src/commands/cmd_quit.cpp75
-rw-r--r--src/commands/cmd_reloadmodule.cpp79
-rw-r--r--src/commands/cmd_rules.cpp82
-rw-r--r--src/commands/cmd_server.cpp56
-rw-r--r--src/commands/cmd_squit.cpp55
-rw-r--r--src/commands/cmd_stats.cpp439
-rw-r--r--src/commands/cmd_time.cpp67
-rw-r--r--src/commands/cmd_topic.cpp88
-rw-r--r--src/commands/cmd_unloadmodule.cpp67
-rw-r--r--src/commands/cmd_user.cpp91
-rw-r--r--src/commands/cmd_version.cpp51
-rw-r--r--src/commands/cmd_wallops.cpp59
-rw-r--r--src/commands/cmd_who.cpp408
-rw-r--r--src/commands/cmd_whois.cpp98
-rw-r--r--src/commands/cmd_whowas.cpp348
-rw-r--r--src/configparser.cpp303
-rw-r--r--src/configreader.cpp693
-rw-r--r--src/coremods/core_channel/cmd_invite.cpp185
-rw-r--r--src/coremods/core_channel/cmd_join.cpp60
-rw-r--r--src/coremods/core_channel/cmd_kick.cpp133
-rw-r--r--src/coremods/core_channel/cmd_names.cpp114
-rw-r--r--src/coremods/core_channel/cmd_topic.cpp102
-rw-r--r--src/coremods/core_channel/cmode_k.cpp (renamed from src/modes/cmode_k.cpp)86
-rw-r--r--src/coremods/core_channel/cmode_l.cpp (renamed from src/modes/cmode_l.cpp)25
-rw-r--r--src/coremods/core_channel/core_channel.cpp352
-rw-r--r--src/coremods/core_channel/core_channel.h219
-rw-r--r--src/coremods/core_channel/invite.cpp208
-rw-r--r--src/coremods/core_channel/invite.h127
-rw-r--r--src/coremods/core_dns.cpp850
-rw-r--r--src/coremods/core_hostname_lookup.cpp239
-rw-r--r--src/coremods/core_info/cmd_admin.cpp43
-rw-r--r--src/coremods/core_info/cmd_commands.cpp59
-rw-r--r--src/coremods/core_info/cmd_info.cpp (renamed from src/commands/cmd_info.cpp)70
-rw-r--r--src/coremods/core_info/cmd_modules.cpp85
-rw-r--r--src/coremods/core_info/cmd_motd.cpp63
-rw-r--r--src/coremods/core_info/cmd_time.cpp (renamed from src/modules/m_spanningtree/operquit.cpp)33
-rw-r--r--src/coremods/core_info/cmd_version.cpp (renamed from src/modules/spanningtree.h)33
-rw-r--r--src/coremods/core_info/core_info.cpp159
-rw-r--r--src/coremods/core_info/core_info.h169
-rw-r--r--src/coremods/core_ison.cpp97
-rw-r--r--src/coremods/core_list.cpp215
-rw-r--r--src/coremods/core_loadmodule.cpp125
-rw-r--r--src/coremods/core_lusers.cpp (renamed from src/commands/cmd_lusers.cpp)80
-rw-r--r--src/coremods/core_oper/cmd_die.cpp82
-rw-r--r--src/coremods/core_oper/cmd_kill.cpp154
-rw-r--r--src/coremods/core_oper/cmd_oper.cpp71
-rw-r--r--src/coremods/core_oper/cmd_rehash.cpp (renamed from src/commands/cmd_rehash.cpp)53
-rw-r--r--src/coremods/core_oper/cmd_restart.cpp (renamed from src/commands/cmd_restart.cpp)32
-rw-r--r--src/coremods/core_oper/core_oper.cpp55
-rw-r--r--src/coremods/core_oper/core_oper.h130
-rw-r--r--src/coremods/core_privmsg.cpp376
-rw-r--r--src/coremods/core_reloadmodule.cpp785
-rw-r--r--src/coremods/core_serialize_rfc.cpp222
-rw-r--r--src/coremods/core_stats.cpp417
-rw-r--r--src/coremods/core_stub.cpp156
-rw-r--r--src/coremods/core_user/cmd_away.cpp82
-rw-r--r--src/coremods/core_user/cmd_mode.cpp177
-rw-r--r--src/coremods/core_user/cmd_nick.cpp (renamed from src/commands/cmd_nick.cpp)76
-rw-r--r--src/coremods/core_user/cmd_part.cpp65
-rw-r--r--src/coremods/core_user/cmd_quit.cpp51
-rw-r--r--src/coremods/core_user/cmd_user.cpp80
-rw-r--r--src/coremods/core_user/core_user.cpp184
-rw-r--r--src/coremods/core_user/core_user.h226
-rw-r--r--src/coremods/core_user/umode_o.cpp (renamed from src/modes/umode_o.cpp)15
-rw-r--r--src/coremods/core_user/umode_s.cpp145
-rw-r--r--src/coremods/core_userhost.cpp (renamed from src/commands/cmd_userhost.cpp)77
-rw-r--r--src/coremods/core_wallops.cpp89
-rw-r--r--src/coremods/core_who.cpp591
-rw-r--r--src/coremods/core_whois.cpp370
-rw-r--r--src/coremods/core_whowas.cpp310
-rw-r--r--src/coremods/core_xline/cmd_eline.cpp (renamed from src/commands/cmd_eline.cpp)50
-rw-r--r--src/coremods/core_xline/cmd_gline.cpp (renamed from src/commands/cmd_gline.cpp)44
-rw-r--r--src/coremods/core_xline/cmd_kline.cpp (renamed from src/commands/cmd_kline.cpp)54
-rw-r--r--src/coremods/core_xline/cmd_qline.cpp (renamed from src/commands/cmd_qline.cpp)42
-rw-r--r--src/coremods/core_xline/cmd_zline.cpp (renamed from src/commands/cmd_zline.cpp)46
-rw-r--r--src/coremods/core_xline/core_xline.cpp93
-rw-r--r--src/coremods/core_xline/core_xline.h163
-rw-r--r--src/cull_list.cpp12
-rw-r--r--src/dns.cpp1120
-rw-r--r--src/dynamic.cpp4
-rw-r--r--src/filelogger.cpp20
-rw-r--r--src/fileutils.cpp102
-rw-r--r--src/hashcomp.cpp548
-rw-r--r--src/helperfuncs.cpp387
-rw-r--r--src/inspircd.cpp521
-rw-r--r--src/inspsocket.cpp490
-rw-r--r--src/inspstring.cpp254
-rw-r--r--src/listensocket.cpp157
-rw-r--r--src/listmode.cpp239
-rw-r--r--src/logger.cpp134
-rw-r--r--src/mode.cpp943
-rw-r--r--src/modes/cmode_b.cpp179
-rw-r--r--src/modes/cmode_o.cpp70
-rw-r--r--src/modes/cmode_v.cpp70
-rw-r--r--src/modes/umode_s.cpp72
-rw-r--r--src/modmanager_dynamic.cpp272
-rw-r--r--src/modmanager_static.cpp241
-rw-r--r--src/modulemanager.cpp157
-rw-r--r--src/modules.cpp738
-rw-r--r--src/modules/extra/m_geoip.cpp114
-rw-r--r--src/modules/extra/m_ldap.cpp677
-rw-r--r--src/modules/extra/m_ldapauth.cpp425
-rw-r--r--src/modules/extra/m_ldapoper.cpp255
-rw-r--r--src/modules/extra/m_mssql.cpp870
-rw-r--r--src/modules/extra/m_mysql.cpp132
-rw-r--r--src/modules/extra/m_pgsql.cpp184
-rw-r--r--src/modules/extra/m_regex_pcre.cpp52
-rw-r--r--src/modules/extra/m_regex_posix.cpp46
-rw-r--r--src/modules/extra/m_regex_re2.cpp86
-rw-r--r--src/modules/extra/m_regex_stdlib.cpp62
-rw-r--r--src/modules/extra/m_regex_tre.cpp52
-rw-r--r--src/modules/extra/m_sqlite3.cpp104
-rw-r--r--src/modules/extra/m_ssl_gnutls.cpp1741
-rw-r--r--src/modules/extra/m_ssl_mbedtls.cpp969
-rw-r--r--src/modules/extra/m_ssl_openssl.cpp1328
-rw-r--r--src/modules/extra/m_sslrehashsignal.cpp64
-rw-r--r--src/modules/hash.h76
-rw-r--r--src/modules/httpd.h207
-rw-r--r--src/modules/m_abbreviation.cpp41
-rw-r--r--src/modules/m_alias.cpp202
-rw-r--r--src/modules/m_allowinvite.cpp36
-rw-r--r--src/modules/m_alltime.cpp27
-rw-r--r--src/modules/m_anticaps.cpp304
-rw-r--r--src/modules/m_auditorium.cpp155
-rw-r--r--src/modules/m_autoop.cpp78
-rw-r--r--src/modules/m_banexception.cpp79
-rw-r--r--src/modules/m_banredirect.cpp125
-rw-r--r--src/modules/m_bcrypt.cpp102
-rw-r--r--src/modules/m_blockamsg.cpp87
-rw-r--r--src/modules/m_blockcaps.cpp143
-rw-r--r--src/modules/m_blockcolor.cpp54
-rw-r--r--src/modules/m_botmode.cpp39
-rw-r--r--src/modules/m_callerid.cpp333
-rw-r--r--src/modules/m_cap.cpp475
-rw-r--r--src/modules/m_cap.h95
-rw-r--r--src/modules/m_cban.cpp82
-rw-r--r--src/modules/m_censor.cpp92
-rw-r--r--src/modules/m_cgiirc.cpp569
-rw-r--r--src/modules/m_chancreate.cpp12
-rw-r--r--src/modules/m_chanfilter.cpp114
-rw-r--r--src/modules/m_chanhistory.cpp183
-rw-r--r--src/modules/m_chanlog.cpp90
-rw-r--r--src/modules/m_channames.cpp97
-rw-r--r--src/modules/m_channelban.cpp41
-rw-r--r--src/modules/m_chanprotect.cpp308
-rw-r--r--src/modules/m_check.cpp322
-rw-r--r--src/modules/m_chghost.cpp66
-rw-r--r--src/modules/m_chgident.cpp39
-rw-r--r--src/modules/m_chgname.cpp40
-rw-r--r--src/modules/m_classban.cpp47
-rw-r--r--src/modules/m_clearchan.cpp218
-rw-r--r--src/modules/m_cloaking.cpp301
-rw-r--r--src/modules/m_clones.cpp88
-rw-r--r--src/modules/m_close.cpp32
-rw-r--r--src/modules/m_commonchans.cpp44
-rw-r--r--src/modules/m_conn_join.cpp107
-rw-r--r--src/modules/m_conn_umodes.cpp49
-rw-r--r--src/modules/m_conn_waitpong.cpp35
-rw-r--r--src/modules/m_connectban.cpp60
-rw-r--r--src/modules/m_connflood.cpp34
-rw-r--r--src/modules/m_customprefix.cpp138
-rw-r--r--src/modules/m_customtitle.cpp68
-rw-r--r--src/modules/m_cycle.cpp50
-rw-r--r--src/modules/m_dccallow.cpp231
-rw-r--r--src/modules/m_deaf.cpp111
-rw-r--r--src/modules/m_delayjoin.cpp171
-rw-r--r--src/modules/m_delaymsg.cpp114
-rw-r--r--src/modules/m_denychans.cpp205
-rw-r--r--src/modules/m_devoice.cpp83
-rw-r--r--src/modules/m_dnsbl.cpp404
-rw-r--r--src/modules/m_exemptchanops.cpp104
-rw-r--r--src/modules/m_filter.cpp342
-rw-r--r--src/modules/m_flashpolicyd.cpp165
-rw-r--r--src/modules/m_gecosban.cpp24
-rw-r--r--src/modules/m_globalload.cpp74
-rw-r--r--src/modules/m_globops.cpp11
-rw-r--r--src/modules/m_halfop.cpp104
-rw-r--r--src/modules/m_haproxy.cpp326
-rw-r--r--src/modules/m_helpop.cpp84
-rw-r--r--src/modules/m_hidechans.cpp36
-rw-r--r--src/modules/m_hidelist.cpp87
-rw-r--r--src/modules/m_hidemode.cpp198
-rw-r--r--src/modules/m_hideoper.cpp86
-rw-r--r--src/modules/m_hostchange.cpp281
-rw-r--r--src/modules/m_hostcycle.cpp120
-rw-r--r--src/modules/m_httpd.cpp191
-rw-r--r--src/modules/m_httpd_acl.cpp76
-rw-r--r--src/modules/m_httpd_config.cpp106
-rw-r--r--src/modules/m_httpd_stats.cpp129
-rw-r--r--src/modules/m_ident.cpp158
-rw-r--r--src/modules/m_inviteexception.cpp36
-rw-r--r--src/modules/m_ircv3.cpp296
-rw-r--r--src/modules/m_ircv3_accounttag.cpp62
-rw-r--r--src/modules/m_ircv3_batch.cpp216
-rw-r--r--src/modules/m_ircv3_capnotify.cpp185
-rw-r--r--src/modules/m_ircv3_chghost.cpp61
-rw-r--r--src/modules/m_ircv3_echomessage.cpp79
-rw-r--r--src/modules/m_ircv3_invitenotify.cpp70
-rw-r--r--src/modules/m_ircv3_servertime.cpp72
-rw-r--r--src/modules/m_ircv3_sts.cpp181
-rw-r--r--src/modules/m_joinflood.cpp180
-rw-r--r--src/modules/m_jumpserver.cpp93
-rw-r--r--src/modules/m_kicknorejoin.cpp184
-rw-r--r--src/modules/m_knock.cpp81
-rw-r--r--src/modules/m_ldapauth.cpp447
-rw-r--r--src/modules/m_ldapoper.cpp249
-rw-r--r--src/modules/m_lockserv.cpp84
-rw-r--r--src/modules/m_maphide.cpp24
-rw-r--r--src/modules/m_md5.cpp65
-rw-r--r--src/modules/m_messageflood.cpp140
-rw-r--r--src/modules/m_mlock.cpp27
-rw-r--r--src/modules/m_modenotice.cpp (renamed from src/commands/cmd_modenotice.cpp)31
-rw-r--r--src/modules/m_monitor.cpp444
-rw-r--r--src/modules/m_muteban.cpp41
-rw-r--r--src/modules/m_namedmodes.cpp195
-rw-r--r--src/modules/m_namesx.cpp84
-rw-r--r--src/modules/m_nationalchars.cpp87
-rw-r--r--src/modules/m_nickflood.cpp112
-rw-r--r--src/modules/m_nicklock.cpp81
-rw-r--r--src/modules/m_noctcp.cpp75
-rw-r--r--src/modules/m_nokicks.cpp36
-rw-r--r--src/modules/m_nonicks.cpp64
-rw-r--r--src/modules/m_nonotice.cpp44
-rw-r--r--src/modules/m_nopartmsg.cpp26
-rw-r--r--src/modules/m_ojoin.cpp183
-rw-r--r--src/modules/m_operchans.cpp34
-rw-r--r--src/modules/m_operjoin.cpp70
-rw-r--r--src/modules/m_operlevels.cpp30
-rw-r--r--src/modules/m_operlog.cpp33
-rw-r--r--src/modules/m_opermodes.cpp23
-rw-r--r--src/modules/m_opermotd.cpp45
-rw-r--r--src/modules/m_operprefix.cpp163
-rw-r--r--src/modules/m_override.cpp190
-rw-r--r--src/modules/m_passforward.cpp36
-rw-r--r--src/modules/m_password_hash.cpp94
-rw-r--r--src/modules/m_pbkdf2.cpp226
-rw-r--r--src/modules/m_permchannels.cpp328
-rw-r--r--src/modules/m_randquote.cpp73
-rw-r--r--src/modules/m_redirect.cpp144
-rw-r--r--src/modules/m_regex.h58
-rw-r--r--src/modules/m_regex_glob.cpp19
-rw-r--r--src/modules/m_regonlycreate.cpp25
-rw-r--r--src/modules/m_remove.cpp143
-rw-r--r--src/modules/m_repeat.cpp416
-rw-r--r--src/modules/m_restrictchans.cpp39
-rw-r--r--src/modules/m_restrictmsg.cpp40
-rw-r--r--src/modules/m_ripemd160.cpp481
-rw-r--r--src/modules/m_rline.cpp91
-rw-r--r--src/modules/m_rmode.cpp110
-rw-r--r--src/modules/m_sajoin.cpp88
-rw-r--r--src/modules/m_sakick.cpp54
-rw-r--r--src/modules/m_samode.cpp77
-rw-r--r--src/modules/m_sanick.cpp39
-rw-r--r--src/modules/m_sapart.cpp64
-rw-r--r--src/modules/m_saquit.cpp37
-rw-r--r--src/modules/m_sasl.cpp355
-rw-r--r--src/modules/m_satopic.cpp29
-rw-r--r--src/modules/m_securelist.cpp45
-rw-r--r--src/modules/m_seenicks.cpp10
-rw-r--r--src/modules/m_serverban.cpp25
-rw-r--r--src/modules/m_services_account.cpp250
-rw-r--r--src/modules/m_servprotect.cpp68
-rw-r--r--src/modules/m_sethost.cpp60
-rw-r--r--src/modules/m_setident.cpp27
-rw-r--r--src/modules/m_setidle.cpp31
-rw-r--r--src/modules/m_setname.cpp26
-rw-r--r--src/modules/m_sha1.cpp199
-rw-r--r--src/modules/m_sha256.cpp23
-rw-r--r--src/modules/m_showfile.cpp179
-rw-r--r--src/modules/m_showwhois.cpp60
-rw-r--r--src/modules/m_shun.cpp117
-rw-r--r--src/modules/m_silence.cpp140
-rw-r--r--src/modules/m_spanningtree/addline.cpp66
-rw-r--r--src/modules/m_spanningtree/away.cpp32
-rw-r--r--src/modules/m_spanningtree/cachetimer.h20
-rw-r--r--src/modules/m_spanningtree/capab.cpp184
-rw-r--r--src/modules/m_spanningtree/commandbuilder.h149
-rw-r--r--src/modules/m_spanningtree/commands.h409
-rw-r--r--src/modules/m_spanningtree/compat.cpp658
-rw-r--r--src/modules/m_spanningtree/delline.cpp32
-rw-r--r--src/modules/m_spanningtree/encap.cpp43
-rw-r--r--src/modules/m_spanningtree/fjoin.cpp400
-rw-r--r--src/modules/m_spanningtree/fmode.cpp86
-rw-r--r--src/modules/m_spanningtree/ftopic.cpp78
-rw-r--r--src/modules/m_spanningtree/hmac.cpp90
-rw-r--r--src/modules/m_spanningtree/idle.cpp100
-rw-r--r--src/modules/m_spanningtree/ijoin.cpp75
-rw-r--r--src/modules/m_spanningtree/link.h13
-rw-r--r--src/modules/m_spanningtree/main.cpp839
-rw-r--r--src/modules/m_spanningtree/main.h162
-rw-r--r--src/modules/m_spanningtree/metadata.cpp75
-rw-r--r--src/modules/m_spanningtree/misccommands.cpp42
-rw-r--r--src/modules/m_spanningtree/netburst.cpp386
-rw-r--r--src/modules/m_spanningtree/nick.cpp64
-rw-r--r--src/modules/m_spanningtree/nickcollide.cpp77
-rw-r--r--src/modules/m_spanningtree/num.cpp62
-rw-r--r--src/modules/m_spanningtree/opertype.cpp32
-rw-r--r--src/modules/m_spanningtree/override_map.cpp281
-rw-r--r--src/modules/m_spanningtree/override_squit.cpp24
-rw-r--r--src/modules/m_spanningtree/override_stats.cpp34
-rw-r--r--src/modules/m_spanningtree/override_whois.cpp39
-rw-r--r--src/modules/m_spanningtree/ping.cpp46
-rw-r--r--src/modules/m_spanningtree/pingtimer.cpp102
-rw-r--r--src/modules/m_spanningtree/pingtimer.h77
-rw-r--r--src/modules/m_spanningtree/pong.cpp63
-rw-r--r--src/modules/m_spanningtree/postcommand.cpp108
-rw-r--r--src/modules/m_spanningtree/precommand.cpp19
-rw-r--r--src/modules/m_spanningtree/protocolinterface.cpp161
-rw-r--r--src/modules/m_spanningtree/protocolinterface.h41
-rw-r--r--src/modules/m_spanningtree/push.cpp51
-rw-r--r--src/modules/m_spanningtree/rconnect.cpp42
-rw-r--r--src/modules/m_spanningtree/remoteuser.cpp (renamed from src/modules/account.h)29
-rw-r--r--src/modules/m_spanningtree/remoteuser.h (renamed from src/modules/sasl.h)22
-rw-r--r--src/modules/m_spanningtree/resolvers.cpp97
-rw-r--r--src/modules/m_spanningtree/resolvers.h30
-rw-r--r--src/modules/m_spanningtree/rsquit.cpp50
-rw-r--r--src/modules/m_spanningtree/save.cpp32
-rw-r--r--src/modules/m_spanningtree/server.cpp231
-rw-r--r--src/modules/m_spanningtree/servercommand.cpp60
-rw-r--r--src/modules/m_spanningtree/servercommand.h104
-rw-r--r--src/modules/m_spanningtree/sinfo.cpp55
-rw-r--r--src/modules/m_spanningtree/svsjoin.cpp34
-rw-r--r--src/modules/m_spanningtree/svsnick.cpp52
-rw-r--r--src/modules/m_spanningtree/svspart.cpp15
-rw-r--r--src/modules/m_spanningtree/translate.cpp (renamed from src/modules/m_spanningtree/cachetimer.cpp)41
-rw-r--r--src/modules/m_spanningtree/translate.h30
-rw-r--r--src/modules/m_spanningtree/treeserver.cpp343
-rw-r--r--src/modules/m_spanningtree/treeserver.h207
-rw-r--r--src/modules/m_spanningtree/treesocket.h187
-rw-r--r--src/modules/m_spanningtree/treesocket1.cpp204
-rw-r--r--src/modules/m_spanningtree/treesocket2.cpp405
-rw-r--r--src/modules/m_spanningtree/uid.cpp198
-rw-r--r--src/modules/m_spanningtree/utils.cpp278
-rw-r--r--src/modules/m_spanningtree/utils.h69
-rw-r--r--src/modules/m_spanningtree/version.cpp47
-rw-r--r--src/modules/m_sqlauth.cpp121
-rw-r--r--src/modules/m_sqloper.cpp253
-rw-r--r--src/modules/m_sslinfo.cpp169
-rw-r--r--src/modules/m_sslmodes.cpp180
-rw-r--r--src/modules/m_starttls.cpp111
-rw-r--r--src/modules/m_stripcolor.cpp82
-rw-r--r--src/modules/m_svshold.cpp77
-rw-r--r--src/modules/m_swhois.cpp63
-rw-r--r--src/modules/m_testnet.cpp233
-rw-r--r--src/modules/m_timedbans.cpp159
-rw-r--r--src/modules/m_tline.cpp36
-rw-r--r--src/modules/m_topiclock.cpp66
-rw-r--r--src/modules/m_uhnames.cpp44
-rw-r--r--src/modules/m_uninvite.cpp62
-rw-r--r--src/modules/m_userip.cpp35
-rw-r--r--src/modules/m_vhost.cpp27
-rw-r--r--src/modules/m_watch.cpp606
-rw-r--r--src/modules/m_websocket.cpp405
-rw-r--r--src/modules/m_xline_db.cpp136
-rw-r--r--src/modules/sql.h187
-rw-r--r--src/modules/ssl.h190
-rw-r--r--src/modules/u_listmode.h425
-rw-r--r--src/server.cpp234
-rw-r--r--src/snomasks.cpp149
-rw-r--r--src/socket.cpp382
-rw-r--r--src/socketengine.cpp182
-rw-r--r--src/socketengines/socketengine_epoll.cpp188
-rw-r--r--src/socketengines/socketengine_kqueue.cpp224
-rw-r--r--src/socketengines/socketengine_poll.cpp250
-rw-r--r--src/socketengines/socketengine_ports.cpp220
-rw-r--r--src/socketengines/socketengine_select.cpp150
-rw-r--r--src/testsuite.cpp55
-rw-r--r--src/threadengine.cpp18
-rw-r--r--src/threadengines/threadengine_pthread.cpp84
-rw-r--r--src/threadengines/threadengine_win32.cpp47
-rw-r--r--src/timer.cpp62
-rw-r--r--src/user_resolver.cpp144
-rw-r--r--src/usermanager.cpp458
-rw-r--r--src/userprocess.cpp126
-rw-r--r--src/users.cpp1469
-rwxr-xr-xsrc/version.sh2
-rw-r--r--src/whois.cpp97
-rw-r--r--src/wildcard.cpp46
-rw-r--r--src/xline.cpp160
412 files changed, 34528 insertions, 31915 deletions
diff --git a/src/bancache.cpp b/src/bancache.cpp
index 52449e55e..13e4dc7c7 100644
--- a/src/bancache.cpp
+++ b/src/bancache.cpp
@@ -18,155 +18,89 @@
*/
-/* $Core */
-
#include "inspircd.h"
-#include "bancache.h"
-BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason)
+BanCacheHit::BanCacheHit(const std::string& type, const std::string& reason, time_t seconds)
+ : Type(type)
+ , Reason(reason)
+ , Expiry(ServerInstance->Time() + seconds)
{
- BanCacheHit *b;
-
- if (this->BanHash->find(ip) != this->BanHash->end()) // can't have two cache entries on the same IP, sorry..
- return NULL;
-
- b = new BanCacheHit(ip, type, reason);
-
- this->BanHash->insert(std::make_pair(ip, b));
- return b;
}
BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds)
{
- BanCacheHit *b;
-
- if (this->BanHash->find(ip) != this->BanHash->end()) // can't have two cache entries on the same IP, sorry..
+ BanCacheHit*& b = BanHash[ip];
+ if (b != NULL) // can't have two cache entries on the same IP, sorry..
return NULL;
- b = new BanCacheHit(ip, type, reason, seconds);
-
- this->BanHash->insert(std::make_pair(ip, b));
+ b = new BanCacheHit(type, reason, (seconds ? seconds : 86400));
return b;
}
BanCacheHit *BanCacheManager::GetHit(const std::string &ip)
{
- BanCacheHash::iterator i = this->BanHash->find(ip);
+ BanCacheHash::iterator i = this->BanHash.find(ip);
- if (i == this->BanHash->end())
+ if (i == this->BanHash.end())
return NULL; // free and safe
- else
- {
- if (ServerInstance->Time() > i->second->Expiry)
- {
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "Hit on " + ip + " is out of date, removing!");
- RemoveHit(i->second);
- return NULL; // out of date
- }
- return i->second; // hit.
- }
+ if (RemoveIfExpired(i))
+ return NULL; // expired
+
+ return i->second; // hit.
}
-bool BanCacheManager::RemoveHit(BanCacheHit *b)
+bool BanCacheManager::RemoveIfExpired(BanCacheHash::iterator& it)
{
- BanCacheHash::iterator i;
-
- if (!b)
- return false; // I don't think so.
-
- i = this->BanHash->find(b->IP);
-
- if (i == this->BanHash->end())
- {
- // err..
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveHit(): I got asked to remove a hit that wasn't in the hash(?)");
- }
- else
- {
- this->BanHash->erase(i);
- }
+ if (ServerInstance->Time() < it->second->Expiry)
+ return false;
- delete b;
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "Hit on " + it->first + " is out of date, removing!");
+ delete it->second;
+ it = BanHash.erase(it);
return true;
}
-unsigned int BanCacheManager::RemoveEntries(const std::string &type, bool positive)
+void BanCacheManager::RemoveEntries(const std::string& type, bool positive)
{
- int removed = 0;
-
- BanCacheHash::iterator safei;
-
if (positive)
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing positive hits for " + type);
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing positive hits for " + type);
else
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing negative hits for " + type);
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing all negative hits");
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); )
+ for (BanCacheHash::iterator i = BanHash.begin(); i != BanHash.end(); )
{
- safei = n;
- safei++;
+ if (RemoveIfExpired(i))
+ continue; // updates the iterator if expired
- BanCacheHit *b = n->second;
+ BanCacheHit* b = i->second;
+ bool remove = false;
- /* Safe to delete items here through iterator 'n' */
- if (b->Type == type || !positive) // if removing negative hits, ignore type..
+ if (positive)
{
- if ((positive && !b->Reason.empty()) || b->Reason.empty())
- {
- /* we need to remove this one. */
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + b->IP);
- delete b;
- BanHash->erase(n); // WORD TO THE WISE: don't use RemoveHit here, because we MUST remove the iterator in a safe way.
- removed++;
- }
+ // when removing positive hits, remove only if the type matches
+ remove = b->IsPositive() && (b->Type == type);
+ }
+ else
+ {
+ // when removing negative hits, remove all of them
+ remove = !b->IsPositive();
}
- /* End of safe section */
- n = safei;
- }
-
-
- return removed;
-}
-
-void BanCacheManager::RehashCache()
-{
- BanCacheHash* NewHash = new BanCacheHash();
-
- BanCacheHash::iterator safei;
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); )
- {
- safei = n;
- safei++;
-
- /* Safe to delete items here through iterator 'n' */
- BanCacheHit *b = n->second;
-
- if (ServerInstance->Time() > b->Expiry)
+ if (remove)
{
/* we need to remove this one. */
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + i->first);
delete b;
- BanHash->erase(n); // WORD TO THE WISE: don't use RemoveHit here, because we MUST remove the iterator in a safe way.
+ i = BanHash.erase(i);
}
else
- {
- /* Actually inserts a std::pair */
- NewHash->insert(*n);
- }
-
- /* End of safe section */
-
- n = safei;
+ ++i;
}
-
- delete BanHash;
- BanHash = NewHash;
}
BanCacheManager::~BanCacheManager()
{
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); ++n)
+ for (BanCacheHash::iterator n = BanHash.begin(); n != BanHash.end(); ++n)
delete n->second;
- delete BanHash;
}
diff --git a/src/base.cpp b/src/base.cpp
index 66a3cb140..f698bad2e 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -23,26 +23,32 @@
#include "inspircd.h"
#include "base.h"
#include <time.h>
+#ifdef INSPIRCD_ENABLE_RTTI
#include <typeinfo>
+#endif
classbase::classbase()
{
- if (ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::+ @%p", (void*)this);
+ if (ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::+ @%p", (void*)this);
}
CullResult classbase::cull()
{
- if (ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::-%s @%p",
+ if (ServerInstance)
+#ifdef INSPIRCD_ENABLE_RTTI
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::-%s @%p",
typeid(*this).name(), (void*)this);
+#else
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::- @%p", (void*)this);
+#endif
return CullResult();
}
classbase::~classbase()
{
- if (ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::~ @%p", (void*)this);
+ if (ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::~ @%p", (void*)this);
}
CullResult::CullResult()
@@ -73,15 +79,15 @@ refcountbase::refcountbase() : refcount(0)
refcountbase::~refcountbase()
{
- if (refcount && ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "refcountbase::~ @%p with refcount %d",
+ if (refcount && ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "refcountbase::~ @%p with refcount %d",
(void*)this, refcount);
}
usecountbase::~usecountbase()
{
- if (usecount && ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "usecountbase::~ @%p with refcount %d",
+ if (usecount && ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "usecountbase::~ @%p with refcount %d",
(void*)this, usecount);
}
@@ -89,7 +95,13 @@ ServiceProvider::~ServiceProvider()
{
}
-ExtensionItem::ExtensionItem(const std::string& Key, Module* mod) : ServiceProvider(mod, Key, SERVICE_METADATA)
+void ServiceProvider::RegisterService()
+{
+}
+
+ExtensionItem::ExtensionItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ServiceProvider(mod, Key, SERVICE_METADATA)
+ , type(exttype)
{
}
@@ -132,6 +144,12 @@ void* ExtensionItem::unset_raw(Extensible* container)
return rv;
}
+void ExtensionItem::RegisterService()
+{
+ if (!ServerInstance->Extensions.Register(this))
+ throw ModuleException("Extension already exists: " + name);
+}
+
bool ExtensionManager::Register(ExtensionItem* item)
{
return types.insert(std::make_pair(item->name, item)).second;
@@ -139,10 +157,10 @@ bool ExtensionManager::Register(ExtensionItem* item)
void ExtensionManager::BeginUnregister(Module* module, std::vector<reference<ExtensionItem> >& list)
{
- std::map<std::string, reference<ExtensionItem> >::iterator i = types.begin();
+ ExtMap::iterator i = types.begin();
while (i != types.end())
{
- std::map<std::string, reference<ExtensionItem> >::iterator me = i++;
+ ExtMap::iterator me = i++;
ExtensionItem* item = me->second;
if (item->creator == module)
{
@@ -154,7 +172,7 @@ void ExtensionManager::BeginUnregister(Module* module, std::vector<reference<Ext
ExtensionItem* ExtensionManager::GetItem(const std::string& name)
{
- std::map<std::string, reference<ExtensionItem> >::iterator i = types.find(name);
+ ExtMap::iterator i = types.find(name);
if (i == types.end())
return NULL;
return i->second;
@@ -168,41 +186,41 @@ void Extensible::doUnhookExtensions(const std::vector<reference<ExtensionItem> >
ExtensibleStore::iterator e = extensions.find(item);
if (e != extensions.end())
{
- item->free(e->second);
+ item->free(this, e->second);
extensions.erase(e);
}
}
}
-static struct DummyExtensionItem : LocalExtItem
-{
- DummyExtensionItem() : LocalExtItem("", NULL) {}
- void free(void*) {}
-} dummy;
-
Extensible::Extensible()
+ : culled(false)
{
- extensions[&dummy] = NULL;
}
CullResult Extensible::cull()
{
+ FreeAllExtItems();
+ culled = true;
+ return classbase::cull();
+}
+
+void Extensible::FreeAllExtItems()
+{
for(ExtensibleStore::iterator i = extensions.begin(); i != extensions.end(); ++i)
{
- i->first->free(i->second);
+ i->first->free(this, i->second);
}
extensions.clear();
- return classbase::cull();
}
Extensible::~Extensible()
{
- if (!extensions.empty() && ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG,
- "Extensible destructor called without cull @%p", (void*)this);
+ if ((!extensions.empty() || !culled) && ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Extensible destructor called without cull @%p", (void*)this);
}
-LocalExtItem::LocalExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+LocalExtItem::LocalExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ExtensionItem(Key, exttype, mod)
{
}
@@ -219,8 +237,10 @@ void LocalExtItem::unserialize(SerializeFormat format, Extensible* container, co
{
}
-LocalStringExt::LocalStringExt(const std::string& Key, Module* Owner)
- : SimpleExtItem<std::string>(Key, Owner) { }
+LocalStringExt::LocalStringExt(const std::string& Key, ExtensibleType exttype, Module* Owner)
+ : SimpleExtItem<std::string>(Key, exttype, Owner)
+{
+}
LocalStringExt::~LocalStringExt()
{
@@ -228,12 +248,19 @@ LocalStringExt::~LocalStringExt()
std::string LocalStringExt::serialize(SerializeFormat format, const Extensible* container, void* item) const
{
- if (item && format == FORMAT_USER)
+ if ((item) && (format != FORMAT_NETWORK))
return *static_cast<std::string*>(item);
return "";
}
-LocalIntExt::LocalIntExt(const std::string& Key, Module* mod) : LocalExtItem(Key, mod)
+void LocalStringExt::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format != FORMAT_NETWORK)
+ set(container, value);
+}
+
+LocalIntExt::LocalIntExt(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : LocalExtItem(Key, exttype, mod)
{
}
@@ -243,11 +270,17 @@ LocalIntExt::~LocalIntExt()
std::string LocalIntExt::serialize(SerializeFormat format, const Extensible* container, void* item) const
{
- if (format != FORMAT_USER)
+ if (format == FORMAT_NETWORK)
return "";
return ConvToStr(reinterpret_cast<intptr_t>(item));
}
+void LocalIntExt::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format != FORMAT_NETWORK)
+ set(container, ConvToInt(value));
+}
+
intptr_t LocalIntExt::get(const Extensible* container) const
{
return reinterpret_cast<intptr_t>(get_raw(container));
@@ -261,11 +294,12 @@ intptr_t LocalIntExt::set(Extensible* container, intptr_t value)
return reinterpret_cast<intptr_t>(unset_raw(container));
}
-void LocalIntExt::free(void*)
+void LocalIntExt::free(Extensible* container, void* item)
{
}
-StringExtItem::StringExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+StringExtItem::StringExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ExtensionItem(Key, exttype, mod)
{
}
@@ -303,7 +337,7 @@ void StringExtItem::unset(Extensible* container)
delete static_cast<std::string*>(old);
}
-void StringExtItem::free(void* item)
+void StringExtItem::free(Extensible* container, void* item)
{
delete static_cast<std::string*>(item);
}
@@ -312,4 +346,3 @@ ModuleException::ModuleException(const std::string &message, Module* who)
: CoreException(message, who ? who->ModuleSourceFile : "A Module")
{
}
-
diff --git a/src/channels.cpp b/src/channels.cpp
index df2212a21..c9816db4b 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -23,172 +23,104 @@
*/
-/* $Core */
-
#include "inspircd.h"
-#include <cstdarg>
-#include "mode.h"
+#include "listmode.h"
-Channel::Channel(const std::string &cname, time_t ts)
+namespace
{
- if (!ServerInstance->chanlist->insert(std::make_pair(cname, this)).second)
- throw CoreException("Cannot create duplicate channel " + cname);
-
- this->name = cname;
- this->age = ts ? ts : ServerInstance->Time();
-
- maxbans = topicset = 0;
- modes.reset();
+ ChanModeReference ban(NULL, "ban");
}
-void Channel::SetMode(char mode,bool mode_on)
+Channel::Channel(const std::string &cname, time_t ts)
+ : name(cname), age(ts), topicset(0)
{
- modes[mode-65] = mode_on;
+ if (!ServerInstance->chanlist.insert(std::make_pair(cname, this)).second)
+ throw CoreException("Cannot create duplicate channel " + cname);
}
void Channel::SetMode(ModeHandler* mh, bool on)
{
- modes[mh->GetModeChar() - 65] = on;
+ modes[mh->GetId()] = on;
}
-void Channel::SetModeParam(char mode, const std::string& parameter)
+void Channel::SetTopic(User* u, const std::string& ntopic, time_t topicts, const std::string* setter)
{
- CustomModeList::iterator n = custom_mode_params.find(mode);
- // always erase, even if changing, so that the map gets the new value
- if (n != custom_mode_params.end())
- custom_mode_params.erase(n);
- if (parameter.empty())
- {
- modes[mode-65] = false;
- }
- else
+ // Send a TOPIC message to the channel only if the new topic text differs
+ if (this->topic != ntopic)
{
- custom_mode_params[mode] = parameter;
- modes[mode-65] = true;
+ this->topic = ntopic;
+ ClientProtocol::Messages::Topic topicmsg(u, this, this->topic);
+ Write(ServerInstance->GetRFCEvents().topic, topicmsg);
}
-}
-void Channel::SetModeParam(ModeHandler* mode, const std::string& parameter)
-{
- SetModeParam(mode->GetModeChar(), parameter);
-}
+ // Always update setter and set time
+ if (!setter)
+ setter = ServerInstance->Config->FullHostInTopic ? &u->GetFullHost() : &u->nick;
+ this->setby.assign(*setter, 0, ServerInstance->Config->Limits.GetMaxMask());
+ this->topicset = topicts;
-std::string Channel::GetModeParameter(char mode)
-{
- CustomModeList::iterator n = custom_mode_params.find(mode);
- if (n != custom_mode_params.end())
- return n->second;
- return "";
+ FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
}
-std::string Channel::GetModeParameter(ModeHandler* mode)
-{
- CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
- if (n != custom_mode_params.end())
- return n->second;
- return "";
-}
-
-int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
+Membership* Channel::AddUser(User* user)
{
- if (!u)
- u = ServerInstance->FakeClient;
- if (IS_LOCAL(u) && !forceset)
- {
- ModResult res;
- FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
-
- if (res == MOD_RES_DENY)
- return CMD_FAILURE;
- if (res != MOD_RES_ALLOW)
- {
- if (!this->HasUser(u))
- {
- u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
- return CMD_FAILURE;
- }
- if (IsModeSet('t') && !ServerInstance->OnCheckExemption(u,this,"topiclock").check(GetPrefixValue(u) >= HALFOP_VALUE))
- {
- u->WriteNumeric(482, "%s %s :You do not have access to change the topic on this channel", u->nick.c_str(), this->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
-
- this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
- this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
- this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
-
- this->topicset = ServerInstance->Time();
-
- FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
+ std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
+ if (!ret.second)
+ return NULL;
- return CMD_SUCCESS;
+ Membership* memb = new(ret.first->second) Membership(user, this);
+ return memb;
}
-long Channel::GetUserCounter()
+void Channel::DelUser(User* user)
{
- return userlist.size();
+ MemberMap::iterator it = userlist.find(user);
+ if (it != userlist.end())
+ DelUser(it);
}
-Membership* Channel::AddUser(User* user)
+void Channel::CheckDestroy()
{
- Membership* memb = new Membership(user, this);
- userlist[user] = memb;
- return memb;
-}
-
-void Channel::DelUser(User* user)
-{
- UserMembIter a = userlist.find(user);
+ if (!userlist.empty())
+ return;
- if (a != userlist.end())
- {
- a->second->cull();
- delete a->second;
- userlist.erase(a);
- }
+ ModResult res;
+ FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
+ if (res == MOD_RES_DENY)
+ return;
- if (userlist.empty())
- {
- ModResult res;
- FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
- if (res == MOD_RES_DENY)
- return;
- chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
- /* kill the record */
- if (iter != ServerInstance->chanlist->end())
- {
- FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
- ServerInstance->chanlist->erase(iter);
- }
+ // If the channel isn't in chanlist then it is already in the cull list, don't add it again
+ chan_hash::iterator iter = ServerInstance->chanlist.find(this->name);
+ if ((iter == ServerInstance->chanlist.end()) || (iter->second != this))
+ return;
- ClearInvites();
- ServerInstance->GlobalCulls.AddItem(this);
- }
+ FOREACH_MOD(OnChannelDelete, (this));
+ ServerInstance->chanlist.erase(iter);
+ ServerInstance->GlobalCulls.AddItem(this);
}
-bool Channel::HasUser(User* user)
+void Channel::DelUser(const MemberMap::iterator& membiter)
{
- return (userlist.find(user) != userlist.end());
+ Membership* memb = membiter->second;
+ memb->cull();
+ memb->~Membership();
+ userlist.erase(membiter);
+
+ // If this channel became empty then it should be removed
+ CheckDestroy();
}
Membership* Channel::GetUser(User* user)
{
- UserMembIter i = userlist.find(user);
+ MemberMap::iterator i = userlist.find(user);
if (i == userlist.end())
return NULL;
return i->second;
}
-const UserMembList* Channel::GetUsers()
-{
- return &userlist;
-}
-
void Channel::SetDefaultModes()
{
- ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s",
+ ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s",
ServerInstance->Config->DefaultModes.c_str());
irc::spacesepstream list(ServerInstance->Config->DefaultModes);
std::string modeseq;
@@ -201,7 +133,10 @@ void Channel::SetDefaultModes()
ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
if (mode)
{
- if (mode->GetNumParams(true))
+ if (mode->IsPrefixMode())
+ continue;
+
+ if (mode->NeedsParam(true))
{
list.GetToken(parameter);
// If the parameter begins with a ':' then it's invalid
@@ -211,7 +146,7 @@ void Channel::SetDefaultModes()
else
parameter.clear();
- if ((mode->GetNumParams(true)) && (parameter.empty()))
+ if ((mode->NeedsParam(true)) && (parameter.empty()))
continue;
mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
@@ -223,204 +158,141 @@ void Channel::SetDefaultModes()
* add a channel to a user, creating the record for it if needed and linking
* it to the user record
*/
-Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
+Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, const std::string& key)
{
- // Fix: unregistered users could be joined using /SAJOIN
- if (!user || !cn || user->registered != REG_ALL)
+ if (user->registered != REG_ALL)
+ {
+ ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join unregistered user " + user->uuid + " to channel " + cname);
return NULL;
-
- std::string privs;
- Channel *Ptr;
+ }
/*
* We don't restrict the number of channels that remote users or users that are override-joining may be in.
- * We restrict local users to MaxChans channels.
- * We restrict local operators to OperMaxChans channels.
+ * We restrict local users to <connect:maxchans> channels.
+ * We restrict local operators to <oper:maxchans> channels.
* This is a lot more logical than how it was formerly. -- w00t
*/
- if (IS_LOCAL(user) && !override)
+ if (!override)
{
- if (user->HasPrivPermission("channels/high-join-limit"))
+ unsigned int maxchans = user->GetClass()->maxchans;
+ if (user->IsOper())
{
- if (user->chans.size() >= ServerInstance->Config->OperMaxChans)
- {
- user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
- return NULL;
- }
+ unsigned int opermaxchans = ConvToInt(user->oper->getConfig("maxchans"));
+ // If not set, use 2.0's <channels:opers>, if that's not set either, use limit from CC
+ if (!opermaxchans && user->HasPrivPermission("channels/high-join-limit"))
+ opermaxchans = ServerInstance->Config->OperMaxChans;
+ if (opermaxchans)
+ maxchans = opermaxchans;
}
- else
+ if (user->chans.size() >= maxchans)
{
- unsigned int maxchans = user->GetClass()->maxchans;
- if (!maxchans)
- maxchans = ServerInstance->Config->MaxChans;
- if (user->chans.size() >= maxchans)
- {
- user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
- return NULL;
- }
+ user->WriteNumeric(ERR_TOOMANYCHANNELS, cname, "You are on too many channels");
+ return NULL;
}
}
- std::string cname;
- cname.assign(std::string(cn), 0, ServerInstance->Config->Limits.ChanMax);
- Ptr = ServerInstance->FindChan(cname);
- bool created_by_local = false;
+ // Crop channel name if it's too long
+ if (cname.length() > ServerInstance->Config->Limits.ChanMax)
+ cname.resize(ServerInstance->Config->Limits.ChanMax);
- if (!Ptr)
+ Channel* chan = ServerInstance->FindChan(cname);
+ bool created_by_local = (chan == NULL); // Flag that will be passed to modules in the OnUserJoin() hook later
+ std::string privs; // Prefix mode(letter)s to give to the joining user
+
+ if (!chan)
{
- /*
- * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
- */
- if (!IS_LOCAL(user))
- {
- if (!TS)
- ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
- }
- else
- {
- privs = "o";
- created_by_local = true;
- }
+ privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' '));
- if (IS_LOCAL(user) && override == false)
+ if (override == false)
{
+ // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname.c_str(), privs, key ? key : ""));
+ FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
if (MOD_RESULT == MOD_RES_DENY)
- return NULL;
+ return NULL; // A module wasn't happy with the join, abort
}
- Ptr = new Channel(cname, TS);
+ chan = new Channel(cname, ServerInstance->Time());
+ // Set the default modes on the channel (<options:defaultmodes>)
+ chan->SetDefaultModes();
}
else
{
/* Already on the channel */
- if (Ptr->HasUser(user))
+ if (chan->HasUser(user))
return NULL;
- /*
- * remote users are allowed us to bypass channel modes
- * and bans (used by servers)
- */
- if (IS_LOCAL(user) && override == false)
+ if (override == false)
{
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname.c_str(), privs, key ? key : ""));
+ FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key));
+
+ // A module explicitly denied the join and (hopefully) generated a message
+ // describing the situation, so we may stop here without sending anything
if (MOD_RESULT == MOD_RES_DENY)
- {
return NULL;
- }
- else if (MOD_RESULT == MOD_RES_PASSTHRU)
- {
- std::string ckey = Ptr->GetModeParameter('k');
- bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
- bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
-
- if (!ckey.empty())
- {
- FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key ? key : ""));
- if (!MOD_RESULT.check((key && ckey == key) || can_bypass))
- {
- // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
- user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
- return NULL;
- }
- }
-
- if (Ptr->IsModeSet('i'))
- {
- FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
- if (!MOD_RESULT.check(invited))
- {
- user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
- return NULL;
- }
- }
-
- std::string limit = Ptr->GetModeParameter('l');
- if (!limit.empty())
- {
- FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
- if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
- {
- user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
- return NULL;
- }
- }
- if (Ptr->IsBanned(user) && !can_bypass)
+ // If no module returned MOD_RES_DENY or MOD_RES_ALLOW (which is the case
+ // most of the time) then proceed to check channel bans.
+ //
+ // If a module explicitly allowed the join (by returning MOD_RES_ALLOW),
+ // then this entire section is skipped
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if (chan->IsBanned(user))
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (You're banned)");
return NULL;
}
-
- /*
- * If the user has invites for this channel, remove them now
- * after a successful join so they don't build up.
- */
- if (invited)
- {
- IS_LOCAL(user)->RemoveInvite(Ptr->name.c_str());
- }
}
}
}
- if (created_by_local)
- {
- /* As spotted by jilles, dont bother to set this on remote users */
- Ptr->SetDefaultModes();
- }
-
- return Channel::ForceChan(Ptr, user, privs, bursting, created_by_local);
+ // We figured that this join is allowed and also created the
+ // channel if it didn't exist before, now do the actual join
+ chan->ForceJoin(user, &privs, false, created_by_local);
+ return chan;
}
-Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created)
+Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
{
- std::string nick = user->nick;
+ if (IS_SERVER(user))
+ {
+ ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
+ return NULL;
+ }
+
+ Membership* memb = this->AddUser(user);
+ if (!memb)
+ return NULL; // Already on the channel
- Membership* memb = Ptr->AddUser(user);
- user->chans.insert(Ptr);
+ user->chans.push_front(memb);
- for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
+ if (privs)
{
- const char status = *x;
- ModeHandler* mh = ServerInstance->Modes->FindMode(status, MODETYPE_CHANNEL);
- if (mh)
+ // If the user was granted prefix modes (in the OnUserPreJoin hook, or he's a
+ // remote user and his own server set the modes), then set them internally now
+ for (std::string::const_iterator i = privs->begin(); i != privs->end(); ++i)
{
- /* Set, and make sure that the mode handler knows this mode was now set */
- Ptr->SetPrefix(user, mh->GetModeChar(), true);
- mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, Ptr, nick, true);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+ if (mh)
+ {
+ std::string nick = user->nick;
+ // Set the mode on the user
+ mh->OnModeChange(ServerInstance->FakeClient, NULL, this, nick, true);
+ }
}
}
+ // Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to
CUList except_list;
- FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created, except_list));
+ FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
- Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
-
- /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
- if ((Ptr->GetUserCounter() > 1) && (!memb->modes.empty()))
- {
- std::string ms = memb->modes;
- for(unsigned int i=0; i < memb->modes.length(); i++)
- ms.append(" ").append(user->nick);
-
- except_list.insert(user);
- Ptr->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
- }
+ ClientProtocol::Events::Join joinevent(memb);
+ this->Write(joinevent, 0, except_list);
- if (IS_LOCAL(user))
- {
- if (Ptr->topicset)
- {
- user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
- user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
- }
- Ptr->UserList(user);
- }
- FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
- return Ptr;
+ FOREACH_MOD(OnPostJoin, (memb));
+ return memb;
}
bool Channel::IsBanned(User* user)
@@ -431,10 +303,18 @@ bool Channel::IsBanned(User* user)
if (result != MOD_RES_PASSTHRU)
return (result == MOD_RES_DENY);
- for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+ ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+ if (!banlm)
+ return false;
+
+ const ListModeBase::ModeList* bans = banlm->GetList(this);
+ if (bans)
{
- if (CheckBan(user, i->data))
- return true;
+ for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); it++)
+ {
+ if (CheckBan(user, it->mask))
+ return true;
+ }
}
return false;
}
@@ -454,14 +334,13 @@ bool Channel::CheckBan(User* user, const std::string& mask)
if (at == std::string::npos)
return false;
- char tomatch[MAXBUF];
- snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
- std::string prefix = mask.substr(0, at);
- if (InspIRCd::Match(tomatch, prefix, NULL))
+ const std::string nickIdent = user->nick + "!" + user->ident;
+ std::string prefix(mask, 0, at);
+ if (InspIRCd::Match(nickIdent, prefix, NULL))
{
- std::string suffix = mask.substr(at + 1);
- if (InspIRCd::Match(user->host, suffix, NULL) ||
- InspIRCd::Match(user->dhost, suffix, NULL) ||
+ std::string suffix(mask, at + 1);
+ if (InspIRCd::Match(user->GetRealHost(), suffix, NULL) ||
+ InspIRCd::Match(user->GetDisplayedHost(), suffix, NULL) ||
InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
return true;
}
@@ -474,12 +353,20 @@ ModResult Channel::GetExtBanStatus(User *user, char type)
FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
if (rv != MOD_RES_PASSTHRU)
return rv;
- for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+
+ ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+ if (!banlm)
+ return MOD_RES_PASSTHRU;
+
+ const ListModeBase::ModeList* bans = banlm->GetList(this);
+ if (bans)
{
- if (i->data.length() > 2 && i->data[0] == type && i->data[1] == ':')
+ for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); ++it)
{
- std::string val = i->data.substr(2);
- if (CheckBan(user, val))
+ if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
+ continue;
+
+ if (CheckBan(user, it->mask.substr(2)))
return MOD_RES_DENY;
}
}
@@ -487,407 +374,124 @@ ModResult Channel::GetExtBanStatus(User *user, char type)
}
/* Channel::PartUser
- * remove a channel from a users record, and return the number of users left.
- * Therefore, if this function returns 0 the caller should delete the Channel.
+ * Remove a channel from a users record, remove the reference to the Membership object
+ * from the channel and destroy it.
*/
-void Channel::PartUser(User *user, std::string &reason)
-{
- if (!user)
- return;
-
- Membership* memb = GetUser(user);
-
- if (memb)
- {
- CUList except_list;
- FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
-
- WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
-
- user->chans.erase(this);
- this->RemoveAllPrefixes(user);
- }
-
- this->DelUser(user);
-}
-
-void Channel::KickUser(User *src, User *user, const char* reason)
-{
- if (!src || !user || !reason)
- return;
-
- Membership* memb = GetUser(user);
- if (IS_LOCAL(src))
- {
- if (!memb)
- {
- src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
- return;
- }
- if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
- {
- src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
- return;
- }
-
- ModResult res;
- if (ServerInstance->ULine(src->server))
- res = MOD_RES_ALLOW;
- else
- FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
-
- if (res == MOD_RES_DENY)
- return;
-
- if (res == MOD_RES_PASSTHRU)
- {
- unsigned int them = this->GetPrefixValue(src);
- unsigned int req = HALFOP_VALUE;
- for (std::string::size_type i = 0; i < memb->modes.length(); i++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
- if (mh && mh->GetLevelRequired() > req)
- req = mh->GetLevelRequired();
- }
-
- if (them < req)
- {
- src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
- src->nick.c_str(), this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
- return;
- }
- }
- }
-
- if (memb)
- {
- CUList except_list;
- FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
-
- WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
-
- user->chans.erase(this);
- this->RemoveAllPrefixes(user);
- }
-
- this->DelUser(user);
-}
-
-void Channel::WriteChannel(User* user, const char* text, ...)
-{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (!user || !text)
- return;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteChannel(user, std::string(textbuffer));
-}
-
-void Channel::WriteChannel(User* user, const std::string &text)
-{
- char tb[MAXBUF];
-
- if (!user)
- return;
-
- snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
- std::string out = tb;
-
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
- {
- if (IS_LOCAL(i->first))
- i->first->Write(out);
- }
-}
-
-void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
-{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (!text)
- return;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteChannelWithServ(ServName, std::string(textbuffer));
-}
-
-void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
-{
- char tb[MAXBUF];
-
- snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
- std::string out = tb;
-
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
- {
- if (IS_LOCAL(i->first))
- i->first->Write(out);
- }
-}
-
-/* write formatted text from a source user to all users on a channel except
- * for the sender (for privmsg etc) */
-void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
+bool Channel::PartUser(User* user, std::string& reason)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
+ MemberMap::iterator membiter = userlist.find(user);
- if (!text)
- return;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
-}
-
-void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
-{
- char textbuffer[MAXBUF];
- va_list argsPtr;
+ if (membiter == userlist.end())
+ return false;
- if (!text)
- return;
+ Membership* memb = membiter->second;
+ CUList except_list;
+ FOREACH_MOD(OnUserPart, (memb, reason, except_list));
- int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
+ ClientProtocol::Messages::Part partmsg(memb, reason);
+ Write(ServerInstance->GetRFCEvents().part, partmsg, 0, except_list);
- va_start(argsPtr, text);
- vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
- va_end(argsPtr);
+ // Remove this channel from the user's chanlist
+ user->chans.erase(memb);
+ // Remove the Membership from this channel's userlist and destroy it
+ this->DelUser(membiter);
- this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
+ return true;
}
-void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
+void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason)
{
- char tb[MAXBUF];
+ Membership* memb = victimiter->second;
+ CUList except_list;
+ FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
- snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
+ ClientProtocol::Messages::Kick kickmsg(src, memb, reason);
+ Write(ServerInstance->GetRFCEvents().kick, kickmsg, 0, except_list);
- this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
+ memb->user->chans.erase(memb);
+ this->DelUser(victimiter);
}
-void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
+void Channel::Write(ClientProtocol::Event& protoev, char status, const CUList& except_list)
{
unsigned int minrank = 0;
if (status)
{
- ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
if (mh)
minrank = mh->GetPrefixRank();
}
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
- if (IS_LOCAL(i->first) && (except_list.find(i->first) == except_list.end()))
+ LocalUser* user = IS_LOCAL(i->first);
+ if ((user) && (!except_list.count(user)))
{
/* User doesn't have the status we're after */
if (minrank && i->second->getRank() < minrank)
continue;
- i->first->Write(out);
+ user->Send(protoev);
}
}
}
-void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
-{
- CUList except_list;
- except_list.insert(user);
- this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
-}
-
-/*
- * return a count of the users on a specific channel accounting for
- * invisible users who won't increase the count. e.g. for /LIST
- */
-int Channel::CountInvisible()
-{
- int count = 0;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
- {
- if (!i->first->quitting && !i->first->IsModeSet('i'))
- count++;
- }
-
- return count;
-}
-
-char* Channel::ChanModes(bool showkey)
+const char* Channel::ChanModes(bool showkey)
{
- static char scratch[MAXBUF];
- static char sparam[MAXBUF];
- char* offset = scratch;
- std::string extparam;
+ static std::string scratch;
+ std::string sparam;
- *scratch = '\0';
- *sparam = '\0';
+ scratch.clear();
/* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
for(int n = 0; n < 64; n++)
{
- if(this->modes[n])
+ ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_CHANNEL);
+ if (mh && IsModeSet(mh))
{
- *offset++ = n + 65;
- extparam.clear();
+ scratch.push_back(n + 65);
+
+ ParamModeBase* pm = mh->IsParameterMode();
+ if (!pm)
+ continue;
+
if (n == 'k' - 65 && !showkey)
{
- extparam = "<key>";
+ sparam += " <key>";
}
else
{
- extparam = this->GetModeParameter(n + 65);
- }
- if (!extparam.empty())
- {
- charlcat(sparam,' ',MAXBUF);
- strlcat(sparam,extparam.c_str(),MAXBUF);
+ sparam += ' ';
+ pm->GetParameter(this, sparam);
}
}
}
- /* Null terminate scratch */
- *offset = '\0';
- strlcat(scratch,sparam,MAXBUF);
- return scratch;
-}
-
-/* compile a userlist of a channel into a string, each nick seperated by
- * spaces and op, voice etc status shown as @ and +, and send it to 'user'
- */
-void Channel::UserList(User *user)
-{
- if (!IS_LOCAL(user))
- return;
-
- bool has_privs = user->HasPrivPermission("channels/auspex");
-
- if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
- return;
- }
-
- std::string list = user->nick;
- list.push_back(' ');
- list.push_back(this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=');
- list.push_back(' ');
- list.append(this->name).append(" :");
- std::string::size_type pos = list.size();
-
- bool has_one = false;
-
- /* Improvement by Brain - this doesnt change in value, so why was it inside
- * the loop?
- */
- bool has_user = this->HasUser(user);
-
- const size_t maxlen = MAXBUF - 10 - ServerInstance->Config->ServerName.size();
- std::string prefixlist;
- std::string nick;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
- {
- if (i->first->quitting)
- continue;
- if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
- {
- /*
- * user is +i, and source not on the channel, does not show
- * nick in NAMES list
- */
- continue;
- }
-
- prefixlist = this->GetPrefixChar(i->first);
- nick = i->first->nick;
-
- FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
-
- /* Nick was nuked, a module wants us to skip it */
- if (nick.empty())
- continue;
-
- if (list.size() + prefixlist.length() + nick.length() + 1 > maxlen)
- {
- /* list overflowed into multiple numerics */
- user->WriteNumeric(RPL_NAMREPLY, list);
-
- // Erase all nicks, keep the constant part
- list.erase(pos);
- has_one = false;
- }
-
- list.append(prefixlist).append(nick).push_back(' ');
-
- has_one = true;
- }
-
- /* if whats left in the list isnt empty, send it */
- if (has_one)
- {
- user->WriteNumeric(RPL_NAMREPLY, list);
- }
-
- user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
-}
-
-long Channel::GetMaxBans()
-{
- /* Return the cached value if there is one */
- if (this->maxbans)
- return this->maxbans;
-
- /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
- for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
- {
- if (InspIRCd::Match(this->name, n->first, NULL))
- {
- this->maxbans = n->second;
- return n->second;
- }
- }
-
- /* Screw it, just return the default of 64 */
- this->maxbans = 64;
- return this->maxbans;
+ scratch += sparam;
+ return scratch.c_str();
}
-void Channel::ResetMaxBans()
+void Channel::WriteNotice(const std::string& text)
{
- this->maxbans = 0;
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this, text, MSG_NOTICE);
+ Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
/* returns the status character for a given user on a channel, e.g. @ for op,
* % for halfop etc. If the user has several modes set, the highest mode
* the user has must be returned.
*/
-const char* Channel::GetPrefixChar(User *user)
+char Membership::GetPrefixChar() const
{
- static char pf[2] = {0, 0};
- *pf = 0;
+ char pf = 0;
unsigned int bestrank = 0;
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
+ for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
{
- for(unsigned int i=0; i < m->second->modes.length(); i++)
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+ if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
{
- char mchar = m->second->modes[i];
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
- if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
- {
- bestrank = mh->GetPrefixRank();
- pf[0] = mh->GetPrefix();
- }
+ bestrank = mh->GetPrefixRank();
+ pf = mh->GetPrefix();
}
}
return pf;
@@ -899,160 +503,61 @@ unsigned int Membership::getRank()
unsigned int rv = 0;
if (mchar)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
if (mh)
rv = mh->GetPrefixRank();
}
return rv;
}
-const char* Channel::GetAllPrefixChars(User* user)
+std::string Membership::GetAllPrefixChars() const
{
- static char prefix[64];
- int ctr = 0;
-
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
+ std::string ret;
+ for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
{
- for(unsigned int i=0; i < m->second->modes.length(); i++)
- {
- char mchar = m->second->modes[i];
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
- if (mh && mh->GetPrefix())
- prefix[ctr++] = mh->GetPrefix();
- }
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+ if (mh && mh->GetPrefix())
+ ret.push_back(mh->GetPrefix());
}
- prefix[ctr] = 0;
- return prefix;
+ return ret;
}
unsigned int Channel::GetPrefixValue(User* user)
{
- UserMembIter m = userlist.find(user);
+ MemberMap::iterator m = userlist.find(user);
if (m == userlist.end())
return 0;
return m->second->getRank();
}
-bool Channel::SetPrefix(User* user, char prefix, bool adding)
+bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
{
- ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
- if (!delta_mh)
- return false;
- UserMembIter m = userlist.find(user);
- if (m == userlist.end())
- return false;
- for(unsigned int i=0; i < m->second->modes.length(); i++)
+ char prefix = delta_mh->GetModeChar();
+ for (unsigned int i = 0; i < modes.length(); i++)
{
- char mchar = m->second->modes[i];
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
+ char mchar = modes[i];
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
{
- m->second->modes =
- m->second->modes.substr(0,i) +
+ modes = modes.substr(0,i) +
(adding ? std::string(1, prefix) : "") +
- m->second->modes.substr(mchar == prefix ? i+1 : i);
+ modes.substr(mchar == prefix ? i+1 : i);
return adding != (mchar == prefix);
}
}
if (adding)
- m->second->modes += std::string(1, prefix);
+ modes.push_back(prefix);
return adding;
}
-void Channel::RemoveAllPrefixes(User* user)
-{
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
- {
- m->second->modes.clear();
- }
-}
-void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
+void Membership::WriteNotice(const std::string& text) const
{
- if ((timeout != 0) && (ServerInstance->Time() >= timeout))
- // Expired, don't bother
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (!localuser)
return;
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
-
- Invitation* inv = Invitation::Find(c, u, false);
- if (inv)
- {
- if ((inv->expiry == 0) || (inv->expiry > timeout))
- return;
- inv->expiry = timeout;
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
- }
- else
- {
- inv = new Invitation(c, u, timeout);
- c->invites.push_back(inv);
- u->invites.push_back(inv);
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
- }
-}
-
-Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
-{
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
- if (!u || u->invites.empty())
- return NULL;
-
- InviteList locallist;
- locallist.swap(u->invites);
-
- Invitation* result = NULL;
- for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
- {
- Invitation* inv = *i;
- if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
- {
- /* Expired invite, remove it. */
- std::string expiration = ServerInstance->TimeString(inv->expiry);
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
- i = locallist.erase(i);
- inv->cull();
- delete inv;
- }
- else
- {
- /* Is it what we're searching for? */
- if (inv->chan == c)
- {
- result = inv;
- break;
- }
- ++i;
- }
- }
-
- locallist.swap(u->invites);
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
- return result;
-}
-
-Invitation::~Invitation()
-{
- // Remove this entry from both lists
- InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
- if (it != chan->invites.end())
- chan->invites.erase(it);
- it = std::find(user->invites.begin(), user->invites.end(), this);
- if (it != user->invites.end())
- user->invites.erase(it);
-}
-
-void InviteBase::ClearInvites()
-{
- ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
- InviteList locallist;
- locallist.swap(invites);
- for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
- {
- (*i)->cull();
- delete *i;
- }
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this->chan, text, MSG_NOTICE);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
diff --git a/src/cidr.cpp b/src/cidr.cpp
index b245a1552..250ad9c69 100644
--- a/src/cidr.cpp
+++ b/src/cidr.cpp
@@ -19,8 +19,6 @@
*/
-/* $Core */
-
#include "inspircd.h"
/* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32
@@ -55,8 +53,8 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr
}
else
{
- address_copy = address.substr(username_addr_pos + 1);
- cidr_copy = cidr_mask.substr(username_mask_pos + 1);
+ address_copy.assign(address, username_addr_pos + 1, std::string::npos);
+ cidr_copy.assign(cidr_mask, username_mask_pos + 1, std::string::npos);
}
}
else
@@ -82,5 +80,3 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr
return mask == mask2;
}
-
-
diff --git a/src/clientprotocol.cpp b/src/clientprotocol.cpp
new file mode 100644
index 000000000..a732855a5
--- /dev/null
+++ b/src/clientprotocol.cpp
@@ -0,0 +1,105 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+ClientProtocol::Serializer::Serializer(Module* mod, const char* Name)
+ : DataProvider(mod, std::string("serializer/") + Name)
+ , evprov(mod, "event/messagetag")
+{
+}
+
+bool ClientProtocol::Serializer::HandleTag(LocalUser* user, const std::string& tagname, std::string& tagvalue, TagMap& tags) const
+{
+ // Catch and block empty tags
+ if (tagname.empty())
+ return false;
+
+ const ::Events::ModuleEventProvider::SubscriberList& list = evprov.GetSubscribers();
+ for (::Events::ModuleEventProvider::SubscriberList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ MessageTagProvider* const tagprov = static_cast<MessageTagProvider*>(*i);
+ const ModResult res = tagprov->OnClientProtocolProcessTag(user, tagname, tagvalue);
+ if (res == MOD_RES_ALLOW)
+ return tags.insert(std::make_pair(tagname, MessageTagData(tagprov, tagvalue))).second;
+ else if (res == MOD_RES_DENY)
+ break;
+ }
+
+ // No module handles the tag but that's not an error
+ return true;
+}
+
+ClientProtocol::TagSelection ClientProtocol::Serializer::MakeTagWhitelist(LocalUser* user, const TagMap& tagmap) const
+{
+ TagSelection tagwl;
+ for (TagMap::const_iterator i = tagmap.begin(); i != tagmap.end(); ++i)
+ {
+ const MessageTagData& tagdata = i->second;
+ if (tagdata.tagprov->ShouldSendTag(user, tagdata))
+ tagwl.Select(tagmap, i);
+ }
+ return tagwl;
+}
+
+const ClientProtocol::SerializedMessage& ClientProtocol::Serializer::SerializeForUser(LocalUser* user, Message& msg)
+{
+ if (!msg.msginit_done)
+ {
+ msg.msginit_done = true;
+ FOREACH_MOD_CUSTOM(evprov, MessageTagProvider, OnClientProtocolPopulateTags, (msg));
+ }
+ return msg.GetSerialized(Message::SerializedInfo(this, MakeTagWhitelist(user, msg.GetTags())));
+}
+
+const ClientProtocol::SerializedMessage& ClientProtocol::Message::GetSerialized(const SerializedInfo& serializeinfo) const
+{
+ // First check if the serialized line they're asking for is in the cache
+ for (SerializedList::const_iterator i = serlist.begin(); i != serlist.end(); ++i)
+ {
+ const SerializedInfo& curr = i->first;
+ if (curr == serializeinfo)
+ return i->second;
+ }
+
+ // Not cached, generate it and put it in the cache for later use
+ serlist.push_back(std::make_pair(serializeinfo, serializeinfo.serializer->Serialize(*this, serializeinfo.tagwl)));
+ return serlist.back().second;
+}
+
+void ClientProtocol::Event::GetMessagesForUser(LocalUser* user, MessageList& messagelist)
+{
+ if (!eventinit_done)
+ {
+ eventinit_done = true;
+ FOREACH_MOD_CUSTOM(*event, EventHook, OnEventInit, (*this));
+ }
+
+ // Most of the time there's only a single message but in rare cases there are more
+ if (initialmsg)
+ messagelist.assign(1, initialmsg);
+ else
+ messagelist = *initialmsglist;
+
+ // Let modules modify the message list
+ ModResult res;
+ FIRST_MOD_RESULT_CUSTOM(*event, EventHook, OnPreEventSend, res, (user, *this, messagelist));
+ if (res == MOD_RES_DENY)
+ messagelist.clear();
+}
diff --git a/src/command_parse.cpp b/src/command_parse.cpp
index 53f19437b..d4dd7da21 100644
--- a/src/command_parse.cpp
+++ b/src/command_parse.cpp
@@ -23,117 +23,94 @@
#include "inspircd.h"
-int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
+bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype)
{
ModResult res;
FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype));
/* Module matched */
if (res == MOD_RES_ALLOW)
- return 0;
+ return true;
/* Module explicitly didnt match */
if (res == MOD_RES_DENY)
- return 1;
+ return false;
/* We dont handle any hash types except for plaintext - Thanks tra26 */
if (!hashtype.empty() && hashtype != "plaintext")
- /* See below. 1 because they dont match */
- return 1;
+ return false;
- return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else
+ return TimingSafeCompare(data, input);
}
-/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
- * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
- * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
- * the channel names and their keys as follows:
- * JOIN #chan1,#chan2,#chan3 key1,,key3
- * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
- * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
- * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
- * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
- */
-int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
+bool CommandParser::LoopCall(User* user, Command* handler, const CommandBase::Params& parameters, unsigned int splithere, int extra, bool usemax)
{
if (splithere >= parameters.size())
- return 0;
-
- if (extra >= (signed)parameters.size())
- extra = -1;
+ return false;
- /* First check if we have more than one item in the list, if we don't we return zero here and the handler
+ /* First check if we have more than one item in the list, if we don't we return false here and the handler
* which called us just carries on as it was.
*/
if (parameters[splithere].find(',') == std::string::npos)
- return 0;
+ return false;
/** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
* By using std::set (thanks for the idea w00t) we can cut this down a ton.
* ...VOOODOOOO!
+ *
+ * Only check for duplicates if there is one list (allow them in JOIN).
*/
- std::set<irc::string> dupes;
+ insp::flat_set<std::string, irc::insensitive_swo> dupes;
+ bool check_dupes = (extra < 0);
- /* Create two lists, one for channel names, one for keys
+ /* Create two sepstreams, if we have only one list, then initialize the second sepstream with
+ * an empty string. The second parameter of the constructor of the sepstream tells whether
+ * or not to allow empty tokens.
+ * We allow empty keys, so "JOIN #a,#b ,bkey" will be interpreted as "JOIN #a", "JOIN #b bkey"
*/
irc::commasepstream items1(parameters[splithere]);
- irc::commasepstream items2(extra >= 0 ? parameters[extra] : "");
- std::string extrastuff;
+ irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", true);
std::string item;
unsigned int max = 0;
+ LocalUser* localuser = IS_LOCAL(user);
- /* Attempt to iterate these lists and call the command objech
- * which called us, for every parameter pair until there are
- * no more left to parse.
+ /* Attempt to iterate these lists and call the command handler
+ * for every parameter or parameter pair until there are no more
+ * left to parse.
*/
while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
{
- if (dupes.find(item.c_str()) == dupes.end())
+ if ((!check_dupes) || (dupes.insert(item).second))
{
- std::vector<std::string> new_parameters(parameters);
-
- if (!items2.GetToken(extrastuff))
- extrastuff.clear();
-
+ CommandBase::Params new_parameters(parameters);
new_parameters[splithere] = item;
- if (extra >= 0)
- new_parameters[extra] = extrastuff;
-
- CommandObj->Handle(new_parameters, user);
-
- dupes.insert(item.c_str());
- }
- }
- return 1;
-}
-bool CommandParser::IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user)
-{
- Commandtable::iterator n = cmdlist.find(commandname);
-
- if (n != cmdlist.end())
- {
- if ((pcnt >= n->second->min_params))
- {
- if (IS_LOCAL(user) && n->second->flags_needed)
+ if (extra >= 0)
{
- if (user->IsModeSet(n->second->flags_needed))
- {
- return (user->HasPermission(commandname));
- }
+ // If we have two lists then get the next item from the second list.
+ // In case it runs out of elements then 'item' will be an empty string.
+ items2.GetToken(item);
+ new_parameters[extra] = item;
}
- else
+
+ CommandBase::Params params(new_parameters, parameters.GetTags());
+ CmdResult result = handler->Handle(user, params);
+ if (localuser)
{
- return true;
+ // Run the OnPostCommand hook with the last parameter being true to indicate
+ // that the event is being called in a loop.
+ item.clear();
+ FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, true));
}
}
}
- return false;
+
+ return true;
}
Command* CommandParser::GetHandler(const std::string &commandname)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
return n->second;
@@ -142,9 +119,9 @@ Command* CommandParser::GetHandler(const std::string &commandname)
// calls a handler function for a command
-CmdResult CommandParser::CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user)
+CmdResult CommandParser::CallHandler(const std::string& commandname, const CommandBase::Params& parameters, User* user, Command** cmd)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
{
@@ -174,49 +151,30 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std::
if (bOkay)
{
- return n->second->Handle(parameters,user);
+ if (cmd)
+ *cmd = n->second;
+
+ ClientProtocol::TagMap tags;
+ return n->second->Handle(user, CommandBase::Params(parameters, tags));
}
}
}
return CMD_INVALID;
}
-bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
+void CommandParser::ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& command_p)
{
- std::vector<std::string> command_p;
- irc::tokenstream tokens(cmd);
- std::string command, token;
- tokens.GetToken(command);
-
- /* A client sent a nick prefix on their command (ick)
- * rhapsody and some braindead bouncers do this --
- * the rfc says they shouldnt but also says the ircd should
- * discard it if they do.
- */
- if ((command.empty() || command[0] == ':') && !tokens.GetToken(command))
- {
- // Penalise the user to discourage them from spamming the server with trash.
- user->CommandFloodPenalty += 2000;
- return false;
- }
-
- while (tokens.GetToken(token) && (command_p.size() <= MAXPARAMETERS))
- command_p.push_back(token);
-
- std::transform(command.begin(), command.end(), command.begin(), ::toupper);
-
/* find the command, check it exists */
- Commandtable::iterator cm = cmdlist.find(command);
+ Command* handler = GetHandler(command);
// Penalty to give if the command fails before the handler is executed
unsigned int failpenalty = 0;
/* Modify the user's penalty regardless of whether or not the command exists */
- bool do_more = true;
if (!user->HasPrivPermission("users/flood/no-throttle"))
{
// If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap
- unsigned int penalty = (cm != cmdlist.end() ? cm->second->Penalty * 1000 : 2000);
+ unsigned int penalty = (handler ? handler->Penalty * 1000 : 2000);
user->CommandFloodPenalty += penalty;
// Increase their penalty later if we fail and the command has 0 penalty by default (i.e. in Command::Penalty) to
@@ -226,13 +184,12 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
failpenalty = 1000;
}
-
- if (cm == cmdlist.end())
+ if (!handler)
{
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
+ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false));
if (MOD_RESULT == MOD_RES_DENY)
- return true;
+ return;
/*
* This double lookup is in case a module (abbreviation) wishes to change a command.
@@ -241,17 +198,19 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
* Thanks dz for making me actually understand why this is necessary!
* -- w00t
*/
- cm = cmdlist.find(command);
- if (cm == cmdlist.end())
+ handler = GetHandler(command);
+ if (!handler)
{
if (user->registered == REG_ALL)
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
- ServerInstance->stats->statsUnknown++;
- return true;
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
+ ServerInstance->stats.Unknown++;
+ return;
}
}
- if (cm->second->max_params && command_p.size() > cm->second->max_params)
+ // If we were given more parameters than max_params then append the excess parameter(s)
+ // to command_p[maxparams-1], i.e. to the last param that is still allowed
+ if (handler->max_params && command_p.size() > handler->max_params)
{
/*
* command_p input (assuming max_params 1):
@@ -260,32 +219,21 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
* a
* test
*/
- std::string lparam;
- /*
- * The '-1' here is a clever trick, we'll go backwards throwing everything into a temporary param
- * and then just toss that into the array.
- * -- w00t
- */
- while (command_p.size() > (cm->second->max_params - 1))
- {
- // BE CAREFUL: .end() returns past the end of the vector, hence decrement.
- std::vector<std::string>::iterator it = command_p.end() - 1;
+ // Iterator to the last parameter that will be kept
+ const CommandBase::Params::iterator lastkeep = command_p.begin() + (handler->max_params - 1);
+ // Iterator to the first excess parameter
+ const CommandBase::Params::iterator firstexcess = lastkeep + 1;
- lparam.insert(0, " " + *(it));
- command_p.erase(it); // remove last element
+ // Append all excess parameter(s) to the last parameter, seperated by spaces
+ for (CommandBase::Params::const_iterator i = firstexcess; i != command_p.end(); ++i)
+ {
+ lastkeep->push_back(' ');
+ lastkeep->append(*i);
}
- /* we now have (each iteration):
- * ' test'
- * ' a test'
- * ' is a test' <-- final string
- * ...now remove the ' ' at the start...
- */
- lparam.erase(lparam.begin());
-
- /* param is now 'is a test', which is exactly what we wanted! */
- command_p.push_back(lparam);
+ // Erase the excess parameter(s)
+ command_p.erase(firstexcess, command_p.end());
}
/*
@@ -293,106 +241,146 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
* truncate to max_params if necessary. -- w00t
*/
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
+ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false));
if (MOD_RESULT == MOD_RES_DENY)
- return true;
+ return;
/* activity resets the ping pending timer */
user->nping = ServerInstance->Time() + user->MyClass->GetPingTime();
- if (cm->second->flags_needed)
+ if (handler->flags_needed)
{
- if (!user->IsModeSet(cm->second->flags_needed))
+ if (!user->IsModeSet(handler->flags_needed))
{
user->CommandFloodPenalty += failpenalty;
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
+ return;
}
+
if (!user->HasPermission(command))
{
user->CommandFloodPenalty += failpenalty;
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to command %s",
- user->nick.c_str(), user->oper->NameStr(), command.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to command %s",
+ user->oper->name.c_str(), command.c_str()));
+ return;
}
}
- if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
+
+ if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
{
/* command is disabled! */
user->CommandFloodPenalty += failpenalty;
if (ServerInstance->Config->DisabledDontExist)
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
}
else
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
- user->nick.c_str(), command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "This command has been disabled.");
}
ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
- command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- return do_more;
+ command.c_str(), user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
+ return;
}
- if ((!command_p.empty()) && (command_p.back().empty()) && (!cm->second->allow_empty_last_param))
+ if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param))
command_p.pop_back();
- if (command_p.size() < cm->second->min_params)
+ if (command_p.size() < handler->min_params)
{
user->CommandFloodPenalty += failpenalty;
- user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s %s :Not enough parameters.", user->nick.c_str(), command.c_str());
- if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
- user->WriteNumeric(RPL_SYNTAX, "%s :SYNTAX %s %s", user->nick.c_str(), cm->second->name.c_str(), cm->second->syntax.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NEEDMOREPARAMS, command, "Not enough parameters.");
+ if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
+ user->WriteNumeric(RPL_SYNTAX, handler->name, handler->syntax);
+ return;
}
- if ((user->registered != REG_ALL) && (!cm->second->WorksBeforeReg()))
+
+ if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
{
user->CommandFloodPenalty += failpenalty;
- user->WriteNumeric(ERR_NOTREGISTERED, "%s %s :You have not registered", user->nick.c_str(), command.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NOTREGISTERED, command, "You have not registered");
}
else
{
/* passed all checks.. first, do the (ugly) stats counters. */
- cm->second->use_count++;
- cm->second->total_bytes += cmd.length();
+ handler->use_count++;
/* module calls too */
- FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true, cmd));
+ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true));
if (MOD_RESULT == MOD_RES_DENY)
- return do_more;
+ return;
/*
* WARNING: be careful, the user may be deleted soon
*/
- CmdResult result = cm->second->Handle(command_p, user);
+ CmdResult result = handler->Handle(user, command_p);
- FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, user, result,cmd));
- return do_more;
+ FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, false));
}
}
void CommandParser::RemoveCommand(Command* x)
{
- Commandtable::iterator n = cmdlist.find(x->name);
+ CommandMap::iterator n = cmdlist.find(x->name);
if (n != cmdlist.end() && n->second == x)
cmdlist.erase(n);
}
+CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : ServiceProvider(mod, cmd, SERVICE_COMMAND)
+ , flags_needed(0)
+ , min_params(minpara)
+ , max_params(maxpara)
+ , use_count(0)
+ , disabled(false)
+ , works_before_reg(false)
+ , allow_empty_last_param(true)
+ , Penalty(1)
+{
+}
+
+CommandBase::~CommandBase()
+{
+}
+
+void CommandBase::EncodeParameter(std::string& parameter, unsigned int index)
+{
+}
+
+RouteDescriptor CommandBase::GetRouting(User* user, const Params& parameters)
+{
+ return ROUTE_LOCALONLY;
+}
+
+Command::Command(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : CommandBase(mod, cmd, minpara, maxpara)
+ , force_manual_route(false)
+{
+}
+
Command::~Command()
{
- ServerInstance->Parser->RemoveCommand(this);
+ ServerInstance->Parser.RemoveCommand(this);
}
-bool CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
+void Command::RegisterService()
{
- if (!user || buffer.empty())
- return true;
+ if (!ServerInstance->Parser.AddCommand(this))
+ throw ModuleException("Command already exists: " + name);
+}
+
+void CommandParser::ProcessBuffer(LocalUser* user, const std::string& buffer)
+{
+ ClientProtocol::ParseOutput parseoutput;
+ if (!user->serializer->Parse(user, buffer, parseoutput))
+ return;
+
+ std::string& command = parseoutput.cmd;
+ std::transform(command.begin(), command.end(), command.begin(), ::toupper);
- ServerInstance->Logs->Log("USERINPUT", RAWIO, "C[%s] I :%s %s",
- user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
- return ProcessCommand(user,buffer);
+ CommandBase::Params parameters(parseoutput.params, parseoutput.tags);
+ ProcessCommand(user, command, parameters);
}
bool CommandParser::AddCommand(Command *f)
@@ -410,88 +398,64 @@ CommandParser::CommandParser()
{
}
-int CommandParser::TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final, Command* custom_translator)
+std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const CommandBase::Params& source, bool prefix_final, CommandBase* custom_translator)
{
std::vector<TranslateType>::const_iterator types = to.begin();
- User* user = NULL;
- unsigned int i;
- int translations = 0;
- dest.clear();
+ std::string dest;
- for(i=0; i < source.size(); i++)
+ for (unsigned int i = 0; i < source.size(); i++)
{
- TranslateType t;
- std::string item = source[i];
-
- if (types == to.end())
- t = TR_TEXT;
- else
+ TranslateType t = TR_TEXT;
+ // They might supply less translation types than parameters,
+ // in that case pretend that all remaining types are TR_TEXT
+ if (types != to.end())
{
t = *types;
types++;
}
- if (prefix_final && i == source.size() - 1)
- dest.append(":");
+ bool last = (i == (source.size() - 1));
+ if (prefix_final && last)
+ dest.push_back(':');
- switch (t)
- {
- case TR_NICK:
- /* Translate single nickname */
- user = ServerInstance->FindNick(item);
- if (user)
- {
- dest.append(user->uuid);
- translations++;
- }
- else
- dest.append(item);
- break;
- case TR_CUSTOM:
- if (custom_translator)
- custom_translator->EncodeParameter(item, i);
- dest.append(item);
- break;
- case TR_END:
- case TR_TEXT:
- default:
- /* Do nothing */
- dest.append(item);
- break;
- }
- if (i != source.size() - 1)
- dest.append(" ");
+ TranslateSingleParam(t, source[i], dest, custom_translator, i);
+
+ if (!last)
+ dest.push_back(' ');
}
- return translations;
+ return dest;
}
-int CommandParser::TranslateUIDs(TranslateType to, const std::string &source, std::string &dest)
+void CommandParser::TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator, unsigned int paramnumber)
{
- User* user = NULL;
- int translations = 0;
- dest.clear();
-
switch (to)
{
case TR_NICK:
+ {
/* Translate single nickname */
- user = ServerInstance->FindNick(source);
+ User* user = ServerInstance->FindNick(item);
if (user)
+ dest.append(user->uuid);
+ else
+ dest.append(item);
+ break;
+ }
+ case TR_CUSTOM:
+ {
+ if (custom_translator)
{
- dest = user->uuid;
- translations++;
+ std::string translated = item;
+ custom_translator->EncodeParameter(translated, paramnumber);
+ dest.append(translated);
+ break;
}
- else
- dest = source;
- break;
- case TR_END:
+ // If no custom translator was given, fall through
+ }
case TR_TEXT:
default:
/* Do nothing */
- dest = source;
+ dest.append(item);
break;
}
-
- return translations;
}
diff --git a/src/commands.cpp b/src/commands.cpp
index 5bf2aeb03..c5b34c72f 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -21,128 +21,38 @@
*/
-/* $Core */
-
#include "inspircd.h"
-#include "xline.h"
-#include "command_parse.h"
-
-/* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */
-bool InspIRCd::HostMatchesEveryone(const std::string &mask, User* user)
+CmdResult SplitCommand::Handle(User* user, const Params& parameters)
{
- long matches = 0;
-
- ConfigTag* insane = Config->ConfValue("insane");
-
- if (insane->getBool("hostmasks"))
- return false;
-
- float itrigger = insane->getFloat("trigger", 95.5);
-
- for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
+ switch (user->usertype)
{
- if ((InspIRCd::MatchCIDR(u->second->MakeHost(), mask, ascii_case_insensitive_map)) ||
- (InspIRCd::MatchCIDR(u->second->MakeHostIP(), mask, ascii_case_insensitive_map)))
- {
- matches++;
- }
- }
+ case USERTYPE_LOCAL:
+ return HandleLocal(static_cast<LocalUser*>(user), parameters);
- if (!matches)
- return false;
+ case USERTYPE_REMOTE:
+ return HandleRemote(static_cast<RemoteUser*>(user), parameters);
- float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
- if (percent > itrigger)
- {
- SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),mask.c_str(),percent);
- return true;
+ case USERTYPE_SERVER:
+ return HandleServer(static_cast<FakeUser*>(user), parameters);
}
- return false;
-}
-bool InspIRCd::IPMatchesEveryone(const std::string &ip, User* user)
-{
- long matches = 0;
-
- ConfigTag* insane = Config->ConfValue("insane");
-
- if (insane->getBool("ipmasks"))
- return false;
-
- float itrigger = insane->getFloat("trigger", 95.5);
-
- for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
- {
- if (InspIRCd::MatchCIDR(u->second->GetIPString(), ip, ascii_case_insensitive_map))
- matches++;
- }
-
- if (!matches)
- return false;
-
- float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
- if (percent > itrigger)
- {
- SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),ip.c_str(),percent);
- return true;
- }
- return false;
-}
-
-bool InspIRCd::NickMatchesEveryone(const std::string &nick, User* user)
-{
- long matches = 0;
-
- ConfigTag* insane = Config->ConfValue("insane");
-
- if (insane->getBool("nickmasks"))
- return false;
-
- float itrigger = insane->getFloat("trigger", 95.5);
-
- for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
- {
- if (InspIRCd::Match(u->second->nick, nick))
- matches++;
- }
-
- if (!matches)
- return false;
-
- float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
- if (percent > itrigger)
- {
- SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),nick.c_str(),percent);
- return true;
- }
- return false;
-}
-
-CmdResult SplitCommand::Handle(const std::vector<std::string>& parms, User* u)
-{
- if (IS_LOCAL(u))
- return HandleLocal(parms, IS_LOCAL(u));
- if (IS_REMOTE(u))
- return HandleRemote(parms, IS_REMOTE(u));
- if (IS_SERVER(u))
- return HandleServer(parms, IS_SERVER(u));
- ServerInstance->Logs->Log("COMMAND", DEFAULT, "Unknown user type in command (uuid=%s)!", u->uuid.c_str());
+ ServerInstance->Logs->Log("COMMAND", LOG_DEFAULT, "Unknown user type %d in command (uuid=%s)!",
+ user->usertype, user->uuid.c_str());
return CMD_INVALID;
}
-CmdResult SplitCommand::HandleLocal(const std::vector<std::string>&, LocalUser*)
+CmdResult SplitCommand::HandleLocal(LocalUser* user, const Params& parameters)
{
return CMD_INVALID;
}
-CmdResult SplitCommand::HandleRemote(const std::vector<std::string>&, RemoteUser*)
+CmdResult SplitCommand::HandleRemote(RemoteUser* user, const Params& parameters)
{
return CMD_INVALID;
}
-CmdResult SplitCommand::HandleServer(const std::vector<std::string>&, FakeUser*)
+CmdResult SplitCommand::HandleServer(FakeUser* user, const Params& parameters)
{
return CMD_INVALID;
}
-
diff --git a/src/commands/cmd_admin.cpp b/src/commands/cmd_admin.cpp
deleted file mode 100644
index 0d6c235f0..000000000
--- a/src/commands/cmd_admin.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /ADMIN. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandAdmin : public Command
-{
- public:
- /** Constructor for admin.
- */
- CommandAdmin(Module* parent) : Command(parent,"ADMIN",0,0)
- {
- Penalty = 2;
- syntax = "[<servername>]";
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-/** Handle /ADMIN
- */
-CmdResult CommandAdmin::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
- user->SendText(":%s %03d %s :Administrative info for %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
- if (!ServerInstance->Config->AdminName.empty())
- user->SendText(":%s %03d %s :Name - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINLOC1, user->nick.c_str(), ServerInstance->Config->AdminName.c_str());
- user->SendText(":%s %03d %s :Nickname - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINLOC2, user->nick.c_str(), ServerInstance->Config->AdminNick.c_str());
- user->SendText(":%s %03d %s :E-Mail - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINEMAIL, user->nick.c_str(), ServerInstance->Config->AdminEmail.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandAdmin)
diff --git a/src/commands/cmd_away.cpp b/src/commands/cmd_away.cpp
deleted file mode 100644
index fa3f7fae9..000000000
--- a/src/commands/cmd_away.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /AWAY. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandAway : public Command
-{
- public:
- /** Constructor for away.
- */
- CommandAway ( Module* parent) : Command(parent,"AWAY",0,0) { syntax = "[<message>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /AWAY
- */
-CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ModResult MOD_RESULT;
-
- if ((parameters.size()) && (!parameters[0].empty()))
- {
- FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, parameters[0]));
-
- if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
- return CMD_FAILURE;
-
- user->awaytime = ServerInstance->Time();
- user->awaymsg.assign(parameters[0], 0, ServerInstance->Config->Limits.MaxAway);
-
- user->WriteNumeric(RPL_NOWAWAY, "%s :You have been marked as being away",user->nick.c_str());
- }
- else
- {
- FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, ""));
-
- if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
- return CMD_FAILURE;
-
- user->awaymsg.clear();
- user->WriteNumeric(RPL_UNAWAY, "%s :You are no longer marked as being away",user->nick.c_str());
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandAway)
diff --git a/src/commands/cmd_clearcache.cpp b/src/commands/cmd_clearcache.cpp
deleted file mode 100644
index 5914f9a8f..000000000
--- a/src/commands/cmd_clearcache.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /CLEARCACHE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandClearcache : public Command
-{
- public:
- /** Constructor for clearcache.
- */
- CommandClearcache ( Module* parent) : Command(parent,"CLEARCACHE",0) { flags_needed = 'o'; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /CLEARCACHE
- */
-CmdResult CommandClearcache::Handle (const std::vector<std::string>& parameters, User *user)
-{
- int n = ServerInstance->Res->ClearCache();
- user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick.c_str(), n);
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandClearcache)
diff --git a/src/commands/cmd_commands.cpp b/src/commands/cmd_commands.cpp
deleted file mode 100644
index 1555b4d04..000000000
--- a/src/commands/cmd_commands.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /COMMANDS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandCommands : public Command
-{
- public:
- /** Constructor for commands.
- */
- CommandCommands(Module* parent) : Command(parent,"COMMANDS",0,0)
- {
- Penalty = 3;
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /COMMANDS
- */
-CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user)
-{
- std::vector<std::string> list;
- list.reserve(ServerInstance->Parser->cmdlist.size());
- for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
- {
- // Don't show S2S commands to users
- if (i->second->flags_needed == FLAG_SERVERONLY)
- continue;
-
- Module* src = i->second->creator;
- char buffer[MAXBUF];
- snprintf(buffer, MAXBUF, ":%s %03d %s :%s %s %d %d",
- ServerInstance->Config->ServerName.c_str(), RPL_COMMANDS, user->nick.c_str(),
- i->second->name.c_str(), src->ModuleSourceFile.c_str(),
- i->second->min_params, i->second->Penalty);
- list.push_back(buffer);
- }
- sort(list.begin(), list.end());
- for(unsigned int i=0; i < list.size(); i++)
- user->Write(list[i]);
- user->WriteNumeric(RPL_COMMANDSEND, "%s :End of COMMANDS list",user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandCommands)
diff --git a/src/commands/cmd_connect.cpp b/src/commands/cmd_connect.cpp
deleted file mode 100644
index 8fb82fab4..000000000
--- a/src/commands/cmd_connect.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /CONNECT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandConnect : public Command
-{
- public:
- /** Constructor for connect.
- */
- CommandConnect ( Module* parent) : Command(parent,"CONNECT",1) { flags_needed = 'o'; syntax = "<servername>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/*
- * This is handled by the server linking module, if necessary. Do not remove this stub.
- */
-
-/** Handle /CONNECT
- */
-CmdResult CommandConnect::Handle (const std::vector<std::string>&, User *user)
-{
- user->WriteServ( "NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandConnect)
diff --git a/src/commands/cmd_die.cpp b/src/commands/cmd_die.cpp
deleted file mode 100644
index 1d6640213..000000000
--- a/src/commands/cmd_die.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /DIE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandDie : public Command
-{
- public:
- /** Constructor for die.
- */
- CommandDie ( Module* parent) : Command(parent,"DIE",1) { flags_needed = 'o'; syntax = "<password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-#include "exitcodes.h"
-
-/** Handle /DIE
- */
-CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (!ServerInstance->PassCompare(user, ServerInstance->Config->diepass, parameters[0].c_str(), ServerInstance->Config->powerhash))
- {
- {
- std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
- ServerInstance->Logs->Log("COMMAND",SPARSE, diebuf);
- ServerInstance->SendError(diebuf);
- }
-
- ServerInstance->Exit(EXIT_STATUS_DIE);
- }
- else
- {
- ServerInstance->Logs->Log("COMMAND",SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
- ServerInstance->SNO->WriteGlobalSno('a', "Failed DIE Command from %s.", user->GetFullRealHost().c_str());
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandDie)
diff --git a/src/commands/cmd_invite.cpp b/src/commands/cmd_invite.cpp
deleted file mode 100644
index c69e6bd1b..000000000
--- a/src/commands/cmd_invite.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /INVITE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandInvite : public Command
-{
- public:
- /** Constructor for invite.
- */
- CommandInvite ( Module* parent) : Command(parent,"INVITE", 0, 0) { Penalty = 4; syntax = "[<nick> <channel>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /INVITE
- */
-CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ModResult MOD_RESULT;
-
- if (parameters.size() == 2 || parameters.size() == 3)
- {
- User* u;
- if (IS_LOCAL(user))
- u = ServerInstance->FindNickOnly(parameters[0]);
- else
- u = ServerInstance->FindNick(parameters[0]);
-
- Channel* c = ServerInstance->FindChan(parameters[1]);
- time_t timeout = 0;
- if (parameters.size() == 3)
- {
- if (IS_LOCAL(user))
- timeout = ServerInstance->Time() + ServerInstance->Duration(parameters[2]);
- else
- timeout = ConvToInt(parameters[2]);
- }
-
- if ((!c) || (!u) || (u->registered != REG_ALL))
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), c ? parameters[0].c_str() : parameters[1].c_str());
- return CMD_FAILURE;
- }
-
- if ((IS_LOCAL(user)) && (!c->HasUser(user)))
- {
- user->WriteNumeric(ERR_NOTONCHANNEL, "%s %s :You're not on that channel!",user->nick.c_str(), c->name.c_str());
- return CMD_FAILURE;
- }
-
- if (c->HasUser(u))
- {
- user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
- return CMD_FAILURE;
- }
-
- FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout));
-
- if (MOD_RESULT == MOD_RES_DENY)
- {
- return CMD_FAILURE;
- }
- else if (MOD_RESULT == MOD_RES_PASSTHRU)
- {
- if (IS_LOCAL(user))
- {
- unsigned int rank = c->GetPrefixValue(user);
- if (rank < HALFOP_VALUE)
- {
- // Check whether halfop mode is available and phrase error message accordingly
- ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
- user->nick.c_str(), c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : ""));
- return CMD_FAILURE;
- }
- }
- }
-
- if (IS_LOCAL(u))
- IS_LOCAL(u)->InviteTo(c->name.c_str(), timeout);
- u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
- user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
- if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
- {
- char prefix;
- switch (ServerInstance->Config->AnnounceInvites)
- {
- case ServerConfig::INVITE_ANNOUNCE_OPS:
- {
- prefix = '@';
- break;
- }
- case ServerConfig::INVITE_ANNOUNCE_DYNAMIC:
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
- prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@');
- break;
- }
- default:
- {
- prefix = 0;
- break;
- }
- }
- c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
- }
- FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c,timeout));
- }
- else if (IS_LOCAL(user))
- {
- // pinched from ircu - invite with not enough parameters shows channels
- // youve been invited to but haven't joined yet.
- InviteList& il = IS_LOCAL(user)->GetInviteList();
- for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i)
- {
- user->WriteNumeric(RPL_INVITELIST, "%s :%s",user->nick.c_str(), (*i)->chan->name.c_str());
- }
- user->WriteNumeric(RPL_ENDOFINVITELIST, "%s :End of INVITE list",user->nick.c_str());
- }
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandInvite)
diff --git a/src/commands/cmd_ison.cpp b/src/commands/cmd_ison.cpp
deleted file mode 100644
index 01d12e13b..000000000
--- a/src/commands/cmd_ison.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /ISON. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandIson : public Command
-{
- public:
- /** Constructor for ison.
- */
- CommandIson ( Module* parent) : Command(parent,"ISON", 1) {
- syntax = "<nick> {nick}";
- }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /ISON
- */
-CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::map<User*,User*> ison_already;
- User *u;
- std::string reply = "303 " + user->nick + " :";
-
- for (unsigned int i = 0; i < parameters.size(); i++)
- {
- u = ServerInstance->FindNickOnly(parameters[i]);
- if (ison_already.find(u) != ison_already.end())
- continue;
-
- if ((u) && (u->registered == REG_ALL))
- {
- reply.append(u->nick).append(" ");
- if (reply.length() > 450)
- {
- user->WriteServ(reply);
- reply = "303 " + user->nick + " :";
- }
- ison_already[u] = u;
- }
- else
- {
- if ((i == parameters.size() - 1) && (parameters[i].find(' ') != std::string::npos))
- {
- /* Its a space seperated list of nicks (RFC1459 says to support this)
- */
- irc::spacesepstream list(parameters[i]);
- std::string item;
-
- while (list.GetToken(item))
- {
- u = ServerInstance->FindNickOnly(item);
- if (ison_already.find(u) != ison_already.end())
- continue;
-
- if ((u) && (u->registered == REG_ALL))
- {
- reply.append(u->nick).append(" ");
- if (reply.length() > 450)
- {
- user->WriteServ(reply);
- reply = "303 " + user->nick + " :";
- }
- ison_already[u] = u;
- }
- }
- }
- }
- }
-
- if (!reply.empty())
- user->WriteServ(reply);
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandIson)
diff --git a/src/commands/cmd_join.cpp b/src/commands/cmd_join.cpp
deleted file mode 100644
index 6124fcc1c..000000000
--- a/src/commands/cmd_join.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /JOIN. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandJoin : public Command
-{
- public:
- /** Constructor for join.
- */
- CommandJoin ( Module* parent) : Command(parent,"JOIN", 1, 2) { syntax = "<channel>{,<channel>} {<key>{,<key>}}"; Penalty = 2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /JOIN
- */
-CmdResult CommandJoin::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 1)
- {
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, 1, false))
- return CMD_SUCCESS;
-
- if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- Channel::JoinUser(user, parameters[0].c_str(), false, parameters[1].c_str(), false);
- return CMD_SUCCESS;
- }
- }
- else
- {
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, -1, false))
- return CMD_SUCCESS;
-
- if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
- return CMD_SUCCESS;
- }
- }
-
- user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s %s :Invalid channel name",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandJoin)
diff --git a/src/commands/cmd_kick.cpp b/src/commands/cmd_kick.cpp
deleted file mode 100644
index c497dd459..000000000
--- a/src/commands/cmd_kick.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /KICK. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKick : public Command
-{
- public:
- /** Constructor for kick.
- */
- CommandKick ( Module* parent) : Command(parent,"KICK",2,3) { syntax = "<channel> <nick>{,<nick>} [<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /KICK
- */
-CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string reason;
- Channel* c = ServerInstance->FindChan(parameters[0]);
- User* u;
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 1))
- return CMD_SUCCESS;
-
- if (IS_LOCAL(user))
- u = ServerInstance->FindNickOnly(parameters[1]);
- else
- u = ServerInstance->FindNick(parameters[1]);
-
- if ((!u) || (!c) || (u->registered != REG_ALL))
- {
- user->WriteServ( "401 %s %s :No such nick/channel", user->nick.c_str(), c ? parameters[1].c_str() : parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server)))
- {
- user->WriteServ( "442 %s %s :You're not on that channel!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- if (parameters.size() > 2)
- {
- reason.assign(parameters[2], 0, ServerInstance->Config->Limits.MaxKick);
- }
- else
- {
- reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick);
- }
-
- c->KickUser(user, u, reason.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandKick)
diff --git a/src/commands/cmd_kill.cpp b/src/commands/cmd_kill.cpp
deleted file mode 100644
index 7bdf32c74..000000000
--- a/src/commands/cmd_kill.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /KILL. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKill : public Command
-{
- public:
- /** Constructor for kill.
- */
- CommandKill ( Module* parent) : Command(parent,"KILL",2,2) {
- flags_needed = 'o';
- syntax = "<nickname> <reason>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
- }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- // local kills of remote users are routed via the OnRemoteKill hook
- if (IS_LOCAL(user))
- return ROUTE_LOCALONLY;
- return ROUTE_BROADCAST;
- }
-};
-
-/** Handle /KILL
- */
-CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
-{
- /* Allow comma seperated lists of users for /KILL (thanks w00t) */
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- User *u = ServerInstance->FindNick(parameters[0]);
- if ((u) && (!IS_SERVER(u)))
- {
- /*
- * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
- * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
- *
- * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
- * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
- */
-
- std::string killreason;
- if (IS_LOCAL(user))
- {
- /*
- * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
- * and the other half not. This would be a bad thing. ;p -- w00t
- */
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
-
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- killreason = "Killed (";
- if (!ServerInstance->Config->HideKillsServer.empty())
- {
- // hidekills is on, use it
- killreason += ServerInstance->Config->HideKillsServer;
- }
- else
- {
- // hidekills is off, do nothing
- killreason += user->nick;
- }
-
- killreason += " (" + parameters[1] + "))";
- }
- else
- {
- /* Leave it alone, remote server has already formatted it */
- killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
- }
-
- /*
- * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
- * No time to fix it right now, so left a note. -- w00t
- */
- if (!IS_LOCAL(u))
- {
- // remote kill
- if (!ServerInstance->Config->HideULineKills || !ServerInstance->ULine(user->server))
- ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killreason));
- }
- else
- {
- // local kill
- /*
- * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
- * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
- */
- if (!ServerInstance->Config->HideULineKills || !ServerInstance->ULine(user->server))
- {
- if (IS_LOCAL(user))
- ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- else
- ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- }
- ServerInstance->Logs->Log("KILL",DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
- /* Bug #419, make sure this message can only occur once even in the case of multiple KILL messages crossing the network, and change to show
- * hidekillsserver as source if possible
- */
- if (!u->quitting)
- {
- u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
- u->nick.c_str(),
- ServerInstance->Config->ServerName.c_str(),
- ServerInstance->Config->HideKillsServer.empty() ? user->dhost.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
- ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
- parameters[1].c_str());
- }
- }
-
- // send the quit out
- ServerInstance->Users->QuitUser(u, killreason);
- }
- else
- {
- user->WriteServ( "401 %s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandKill)
diff --git a/src/commands/cmd_links.cpp b/src/commands/cmd_links.cpp
deleted file mode 100644
index f4152ebc5..000000000
--- a/src/commands/cmd_links.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /LINKS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLinks : public Command
-{
- public:
- /** Constructor for links.
- */
- CommandLinks ( Module* parent) : Command(parent,"LINKS",0,0) { }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /LINKS
- */
-CmdResult CommandLinks::Handle (const std::vector<std::string>&, User *user)
-{
- user->WriteNumeric(364, "%s %s %s :0 %s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerDesc.c_str());
- user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandLinks)
diff --git a/src/commands/cmd_list.cpp b/src/commands/cmd_list.cpp
deleted file mode 100644
index eb28fb89c..000000000
--- a/src/commands/cmd_list.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /LIST. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandList : public Command
-{
- public:
- /** Constructor for list.
- */
- CommandList ( Module* parent) : Command(parent,"LIST", 0, 0) { Penalty = 5; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/** Handle /LIST
- */
-CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
-{
- int minusers = 0, maxusers = 0;
-
- user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
-
- if ((parameters.size() == 1) && (!parameters[0].empty()))
- {
- if (parameters[0][0] == '<')
- {
- maxusers = atoi((parameters[0].c_str())+1);
- }
- else if (parameters[0][0] == '>')
- {
- minusers = atoi((parameters[0].c_str())+1);
- }
- }
-
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
- {
- // attempt to match a glob pattern
- long users = i->second->GetUserCounter();
-
- bool too_few = (minusers && (users <= minusers));
- bool too_many = (maxusers && (users >= maxusers));
-
- if (too_many || too_few)
- continue;
-
- if (parameters.size() && !parameters[0].empty() && (parameters[0][0] != '<' && parameters[0][0] != '>'))
- {
- if (!InspIRCd::Match(i->second->name, parameters[0]) && !InspIRCd::Match(i->second->topic, parameters[0]))
- continue;
- }
-
- // if the channel is not private/secret, OR the user is on the channel anyway
- bool n = (i->second->HasUser(user) || user->HasPrivPermission("channels/auspex"));
-
- // If we're not in the channel and +s is set on it, we want to ignore it
- if (n || !i->second->IsModeSet('s'))
- {
- if (!n && i->second->IsModeSet('p'))
- {
- /* Channel is +p and user is outside/not privileged */
- user->WriteNumeric(322, "%s * %ld :",user->nick.c_str(), users);
- }
- else
- {
- /* User is in the channel/privileged, channel is not +s */
- user->WriteNumeric(322, "%s %s %ld :[+%s] %s",user->nick.c_str(),i->second->name.c_str(),users,i->second->ChanModes(n),i->second->topic.c_str());
- }
- }
- }
- user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandList)
diff --git a/src/commands/cmd_loadmodule.cpp b/src/commands/cmd_loadmodule.cpp
deleted file mode 100644
index 379e597e4..000000000
--- a/src/commands/cmd_loadmodule.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /LOADMODULE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLoadmodule : public Command
-{
- public:
- /** Constructor for loadmodule.
- */
- CommandLoadmodule ( Module* parent) : Command(parent,"LOADMODULE",1,1) { flags_needed='o'; syntax = "<modulename>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /LOADMODULE
- */
-CmdResult CommandLoadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (ServerInstance->Modules->Load(parameters[0]))
- {
- ServerInstance->SNO->WriteGlobalSno('a', "NEW MODULE: %s loaded %s",user->nick.c_str(), parameters[0].c_str());
- user->WriteNumeric(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
- return CMD_FAILURE;
- }
-}
-
-COMMAND_INIT(CommandLoadmodule)
diff --git a/src/commands/cmd_map.cpp b/src/commands/cmd_map.cpp
deleted file mode 100644
index 385a2c752..000000000
--- a/src/commands/cmd_map.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandMap : public Command
-{
- public:
- /** Constructor for map.
- */
- CommandMap ( Module* parent) : Command(parent,"MAP",0,0) { Penalty=2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /MAP
- */
-CmdResult CommandMap::Handle (const std::vector<std::string>&, User *user)
-{
- // as with /LUSERS this does nothing without a linking
- // module to override its behaviour and display something
- // better.
-
- if (IS_OPER(user))
- {
- user->WriteNumeric(006, "%s :%s [%s]", user->nick.c_str(), ServerInstance->Config->ServerName.c_str(), ServerInstance->Config->GetSID().c_str());
- user->WriteNumeric(007, "%s :End of /MAP", user->nick.c_str());
- return CMD_SUCCESS;
- }
- user->WriteNumeric(006, "%s :%s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
- user->WriteNumeric(007, "%s :End of /MAP",user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMap)
diff --git a/src/commands/cmd_mode.cpp b/src/commands/cmd_mode.cpp
deleted file mode 100644
index 17e21b182..000000000
--- a/src/commands/cmd_mode.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /MODE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandMode : public Command
-{
- public:
- /** Constructor for mode.
- */
- CommandMode ( Module* parent) : Command(parent,"MODE",1) { syntax = "<target> <modes> {<mode-parameters>}"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/** Handle /MODE
- */
-CmdResult CommandMode::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ServerInstance->Modes->Process(parameters, user, false);
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandMode)
diff --git a/src/commands/cmd_modules.cpp b/src/commands/cmd_modules.cpp
deleted file mode 100644
index 2a15b43ed..000000000
--- a/src/commands/cmd_modules.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /MODULES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandModules : public Command
-{
- public:
- /** Constructor for modules.
- */
- CommandModules(Module* parent) : Command(parent,"MODULES",0,0)
- {
- Penalty = 4;
- syntax = "[<servername>]";
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() >= 1)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-/** Handle /MODULES
- */
-CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() >= 1 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
-
- std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
-
- for (unsigned int i = 0; i < module_names.size(); i++)
- {
- Module* m = ServerInstance->Modules->Find(module_names[i]);
- Version V = m->GetVersion();
-
- if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
- {
- std::string flags("SvcC");
- int pos = 0;
- for (int mult = 1; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
- if (!(V.Flags & mult))
- flags[pos] = '-';
-
-#ifdef PURE_STATIC
- user->SendText(":%s 702 %s :%p %s %s :%s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), (void*)m, module_names[i].c_str(), flags.c_str(), V.description.c_str());
-#else
- std::string srcrev = m->ModuleDLLManager->GetVersion();
- user->SendText(":%s 702 %s :%p %s %s :%s - %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), (void*)m, module_names[i].c_str(), flags.c_str(), V.description.c_str(), srcrev.c_str());
-#endif
- }
- else
- {
- user->SendText(":%s 702 %s :%s %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), module_names[i].c_str(), V.description.c_str());
- }
- }
- user->SendText(":%s 703 %s :End of MODULES list", ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandModules)
diff --git a/src/commands/cmd_motd.cpp b/src/commands/cmd_motd.cpp
deleted file mode 100644
index 9ed5ff188..000000000
--- a/src/commands/cmd_motd.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /MOTD. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandMotd : public Command
-{
- public:
- /** Constructor for motd.
- */
- CommandMotd ( Module* parent) : Command(parent,"MOTD",0,1) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-/** Handle /MOTD
- */
-CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- {
- // Give extra penalty if a non-oper queries the /MOTD of a remote server
- LocalUser* localuser = IS_LOCAL(user);
- if ((localuser) && (!IS_OPER(user)))
- localuser->CommandFloodPenalty += 2000;
- return CMD_SUCCESS;
- }
-
- ConfigTag* tag = ServerInstance->Config->EmptyTag;
- if (IS_LOCAL(user))
- tag = user->GetClass()->config;
- std::string motd_name = tag->getString("motd", "motd");
- ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
- if (motd == ServerInstance->Config->Files.end())
- {
- user->SendText(":%s %03d %s :Message of the day file is missing.",
- ServerInstance->Config->ServerName.c_str(), ERR_NOMOTD, user->nick.c_str());
- return CMD_SUCCESS;
- }
-
- user->SendText(":%s %03d %s :%s message of the day", ServerInstance->Config->ServerName.c_str(),
- RPL_MOTDSTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
-
- for (file_cache::iterator i = motd->second.begin(); i != motd->second.end(); i++)
- user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_MOTD, user->nick.c_str(), i->c_str());
-
- user->SendText(":%s %03d %s :End of message of the day.", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFMOTD, user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMotd)
diff --git a/src/commands/cmd_names.cpp b/src/commands/cmd_names.cpp
deleted file mode 100644
index fd4a9cef5..000000000
--- a/src/commands/cmd_names.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /NAMES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNames : public Command
-{
- public:
- /** Constructor for names.
- */
- CommandNames ( Module* parent) : Command(parent,"NAMES",0,0) { syntax = "{<channel>{,<channel>}}"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /NAMES
- */
-CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user)
-{
- Channel* c;
-
- if (!parameters.size())
- {
- user->WriteNumeric(366, "%s * :End of /NAMES list.",user->nick.c_str());
- return CMD_SUCCESS;
- }
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- c = ServerInstance->FindChan(parameters[0]);
- if (c)
- {
- c->UserList(user);
- }
- else
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandNames)
diff --git a/src/commands/cmd_notice.cpp b/src/commands/cmd_notice.cpp
deleted file mode 100644
index d5ef7ba1d..000000000
--- a/src/commands/cmd_notice.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-/** Handle /NOTICE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNotice : public Command
-{
- public:
- /** Constructor for notice.
- */
- CommandNotice ( Module* parent) : Command(parent,"NOTICE",2,2) { syntax = "<target>{,<target>} <message>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (IS_LOCAL(user))
- // This is handled by the OnUserNotice hook to split the LoopCall pieces
- return ROUTE_LOCALONLY;
- else
- return ROUTE_MESSAGE(parameters[0]);
- }
-};
-
-
-CmdResult CommandNotice::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- Channel *chan;
-
- CUList exempt_list;
-
- user->idle_lastmsg = ServerInstance->Time();
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
- if (parameters[0][0] == '$')
- {
- if (!user->HasPrivPermission("users/mass-message"))
- return CMD_SUCCESS;
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, exempt_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
- const char* text = temp.c_str();
- const char* servermask = (parameters[0].c_str()) + 1;
-
- FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
- if (InspIRCd::Match(ServerInstance->Config->ServerName,servermask, NULL))
- {
- user->SendAll("NOTICE", "%s", text);
- }
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
- return CMD_SUCCESS;
- }
- char status = 0;
- const char* target = parameters[0].c_str();
-
- if (ServerInstance->Modes->FindPrefix(*target))
- {
- status = *target;
- target++;
- }
- if (*target == '#')
- {
- chan = ServerInstance->FindChan(target);
-
- exempt_list.insert(user);
-
- if (chan)
- {
- if (IS_LOCAL(user))
- {
- if ((chan->IsModeSet('n')) && (!chan->HasUser(user)))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- if ((chan->IsModeSet('m')) && (chan->GetPrefixValue(user) < VOICE_VALUE))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- if (chan->IsBanned(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status, exempt_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- if (temp.empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,exempt_list));
-
- if (status)
- {
- if (ServerInstance->Config->UndernetMsgPrefix)
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name.c_str(), status, text);
- }
- else
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name.c_str(), text);
- }
- }
- else
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,text,status,exempt_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), target);
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
- }
-
- const char* destnick = parameters[0].c_str();
-
- if (IS_LOCAL(user))
- {
- const char* targetserver = strchr(destnick, '@');
-
- if (targetserver)
- {
- std::string nickonly;
-
- nickonly.assign(destnick, 0, targetserver - destnick);
- dest = ServerInstance->FindNickOnly(nickonly);
- if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
- {
- /* Incorrect server for user */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
- else
- dest = ServerInstance->FindNickOnly(destnick);
- }
- else
- dest = ServerInstance->FindNick(destnick);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- if (parameters[1].empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,dest,TYPE_USER,temp,0,exempt_list));
- if (MOD_RESULT == MOD_RES_DENY) {
- return CMD_FAILURE;
- }
- const char* text = temp.c_str();
-
- FOREACH_MOD(I_OnText,OnText(user,dest,TYPE_USER,text,0,exempt_list));
-
- if (IS_LOCAL(dest))
- {
- // direct write, same server
- user->WriteTo(dest, "NOTICE %s :%s", dest->nick.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,text,0,exempt_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-
-}
-
-COMMAND_INIT(CommandNotice)
diff --git a/src/commands/cmd_oper.cpp b/src/commands/cmd_oper.cpp
deleted file mode 100644
index 95f6b98df..000000000
--- a/src/commands/cmd_oper.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-bool OneOfMatches(const char* host, const char* ip, const char* hostlist);
-
-/** Handle /OPER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandOper : public SplitCommand
-{
- public:
- /** Constructor for oper.
- */
- CommandOper ( Module* parent) : SplitCommand(parent,"OPER",2,2) { syntax = "<username> <password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
-
-CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
- char TheHost[MAXBUF];
- char TheIP[MAXBUF];
- bool match_login = false;
- bool match_pass = false;
- bool match_hosts = false;
-
- snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(),user->host.c_str());
- snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(),user->GetIPString());
-
- OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
- if ((i != ServerInstance->Config->oper_blocks.end()) && (i->second->oper_block))
- {
- OperInfo* ifo = i->second;
- ConfigTag* tag = ifo->oper_block;
- match_login = true;
- match_pass = !ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
- match_hosts = OneOfMatches(TheHost,TheIP,tag->getString("host"));
-
- if (match_pass && match_hosts)
- {
- /* found this oper's opertype */
- user->Oper(ifo);
- return CMD_SUCCESS;
- }
- }
-
- std::string fields;
- if (!match_login)
- fields.append("login ");
- if (!match_pass)
- fields.append("password ");
- if (!match_hosts)
- fields.append("hosts");
-
- // tell them they suck, and lag them up to help prevent brute-force attacks
- user->WriteNumeric(491, "%s :Invalid oper credentials",user->nick.c_str());
- user->CommandFloodPenalty += 10000;
-
- ServerInstance->SNO->WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': The following fields do not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
- ServerInstance->Logs->Log("OPER",DEFAULT,"OPER: Failed oper attempt by %s using login '%s': The following fields did not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandOper)
diff --git a/src/commands/cmd_part.cpp b/src/commands/cmd_part.cpp
deleted file mode 100644
index aadb42d90..000000000
--- a/src/commands/cmd_part.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PART. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPart : public Command
-{
- public:
- /** Constructor for part.
- */
- CommandPart (Module* parent) : Command(parent,"PART", 1, 2) { Penalty = 5; syntax = "<channel>{,<channel>} [<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string reason;
-
- if (IS_LOCAL(user))
- {
- if (!ServerInstance->Config->FixedPart.empty())
- reason = ServerInstance->Config->FixedPart;
- else if (parameters.size() > 1)
- reason = ServerInstance->Config->PrefixPart + parameters[1] + ServerInstance->Config->SuffixPart;
- }
- else
- {
- if (parameters.size() > 1)
- reason = parameters[1];
- }
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- Channel* c = ServerInstance->FindChan(parameters[0]);
-
- if (c)
- {
- c->PartUser(user, reason);
- }
- else
- {
- user->WriteServ( "401 %s %s :No such channel", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPart)
diff --git a/src/commands/cmd_pass.cpp b/src/commands/cmd_pass.cpp
deleted file mode 100644
index 9fc758874..000000000
--- a/src/commands/cmd_pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PASS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPass : public SplitCommand
-{
- public:
- /** Constructor for pass.
- */
- CommandPass (Module* parent) : SplitCommand(parent,"PASS",1,1) { works_before_reg = true; Penalty = 0; syntax = "<password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-
-CmdResult CommandPass::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
- // Check to make sure they haven't registered -- Fix by FCS
- if (user->registered == REG_ALL)
- {
- user->CommandFloodPenalty += 1000;
- user->WriteNumeric(ERR_ALREADYREGISTERED, "%s :You may not reregister",user->nick.c_str());
- return CMD_FAILURE;
- }
- user->password = parameters[0];
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPass)
diff --git a/src/commands/cmd_ping.cpp b/src/commands/cmd_ping.cpp
deleted file mode 100644
index dd14b52c8..000000000
--- a/src/commands/cmd_ping.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PING. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPing : public Command
-{
- public:
- /** Constructor for ping.
- */
- CommandPing ( Module* parent) : Command(parent,"PING", 1, 2) { syntax = "<servername> [:<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandPing::Handle (const std::vector<std::string>& parameters, User *user)
-{
- user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPing)
diff --git a/src/commands/cmd_pong.cpp b/src/commands/cmd_pong.cpp
deleted file mode 100644
index 3b6f17f3b..000000000
--- a/src/commands/cmd_pong.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PONG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPong : public Command
-{
- public:
- /** Constructor for pong.
- */
- CommandPong ( Module* parent) : Command(parent,"PONG", 0, 1) { Penalty = 0; syntax = "<ping-text>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandPong::Handle (const std::vector<std::string>&, User *user)
-{
- // set the user as alive so they survive to next ping
- LocalUser* localuser = IS_LOCAL(user);
- if (localuser)
- {
- // Increase penalty unless we've sent a PING and this is the reply
- if (localuser->lastping)
- localuser->CommandFloodPenalty += 1000;
- else
- localuser->lastping = 1;
- }
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPong)
diff --git a/src/commands/cmd_privmsg.cpp b/src/commands/cmd_privmsg.cpp
deleted file mode 100644
index cefdd4800..000000000
--- a/src/commands/cmd_privmsg.cpp
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PRIVMSG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPrivmsg : public Command
-{
- public:
- /** Constructor for privmsg.
- */
- CommandPrivmsg ( Module* parent) : Command(parent,"PRIVMSG",2,2) { syntax = "<target>{,<target>} <message>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (IS_LOCAL(user))
- // This is handled by the OnUserMessage hook to split the LoopCall pieces
- return ROUTE_LOCALONLY;
- else
- return ROUTE_MESSAGE(parameters[0]);
- }
-};
-
-CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- Channel *chan;
- CUList except_list;
-
- user->idle_lastmsg = ServerInstance->Time();
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- if (parameters[0][0] == '$')
- {
- if (!user->HasPrivPermission("users/mass-message"))
- return CMD_SUCCESS;
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
- const char* servermask = (parameters[0].c_str()) + 1;
-
- FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
- if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL))
- {
- user->SendAll("PRIVMSG", "%s", text);
- }
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
- return CMD_SUCCESS;
- }
- char status = 0;
- const char* target = parameters[0].c_str();
-
- if (ServerInstance->Modes->FindPrefix(*target))
- {
- status = *target;
- target++;
- }
- if (*target == '#')
- {
- chan = ServerInstance->FindChan(target);
-
- except_list.insert(user);
-
- if (chan)
- {
- if (IS_LOCAL(user) && chan->GetPrefixValue(user) < VOICE_VALUE)
- {
- if (chan->IsModeSet('n') && !chan->HasUser(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (chan->IsModeSet('m'))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- if (chan->IsBanned(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status,except_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- /* Check again, a module may have zapped the input string */
- if (temp.empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,except_list));
-
- if (status)
- {
- if (ServerInstance->Config->UndernetMsgPrefix)
- {
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name.c_str(), status, text);
- }
- else
- {
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name.c_str(), text);
- }
- }
- else
- {
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,text,status,except_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), target);
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
- }
-
- const char* destnick = parameters[0].c_str();
-
- if (IS_LOCAL(user))
- {
- const char* targetserver = strchr(destnick, '@');
-
- if (targetserver)
- {
- std::string nickonly;
-
- nickonly.assign(destnick, 0, targetserver - destnick);
- dest = ServerInstance->FindNickOnly(nickonly);
- if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
- {
- /* Incorrect server for user */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
- else
- dest = ServerInstance->FindNickOnly(destnick);
- }
- else
- dest = ServerInstance->FindNick(destnick);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- if (parameters[1].empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- if (IS_AWAY(dest))
- {
- /* auto respond with aweh msg */
- user->WriteNumeric(301, "%s %s :%s", user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
- }
-
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- FOREACH_MOD(I_OnText,OnText(user, dest, TYPE_USER, text, 0, except_list));
-
- if (IS_LOCAL(dest))
- {
- // direct write, same server
- user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, dest, TYPE_USER, text, 0, except_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPrivmsg)
diff --git a/src/commands/cmd_quit.cpp b/src/commands/cmd_quit.cpp
deleted file mode 100644
index 6a6b447e5..000000000
--- a/src/commands/cmd_quit.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /QUIT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandQuit : public Command
-{
- public:
- /** Constructor for quit.
- */
- CommandQuit ( Module* parent) : Command(parent,"QUIT",0,1) { works_before_reg = true; syntax = "[<message>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-CmdResult CommandQuit::Handle (const std::vector<std::string>& parameters, User *user)
-{
-
- std::string quitmsg;
-
- if (IS_LOCAL(user))
- {
- if (!ServerInstance->Config->FixedQuit.empty())
- quitmsg = ServerInstance->Config->FixedQuit;
- else
- quitmsg = parameters.size() ?
- ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit
- : "Client exited";
- }
- else
- quitmsg = parameters.size() ? parameters[0] : "Client exited";
-
- std::string* operquit = ServerInstance->OperQuit.get(user);
- if (operquit)
- {
- ServerInstance->Users->QuitUser(user, quitmsg, operquit->c_str());
- }
- else
- {
- ServerInstance->Users->QuitUser(user, quitmsg);
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandQuit)
diff --git a/src/commands/cmd_reloadmodule.cpp b/src/commands/cmd_reloadmodule.cpp
deleted file mode 100644
index eac364f0e..000000000
--- a/src/commands/cmd_reloadmodule.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandReloadmodule : public Command
-{
- public:
- /** Constructor for reloadmodule.
- */
- CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-class ReloadModuleWorker : public HandlerBase1<void, bool>
-{
- public:
- const std::string name;
- const std::string uid;
- ReloadModuleWorker(const std::string& uuid, const std::string& modn)
- : name(modn), uid(uuid) {}
- void Call(bool result)
- {
- ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded",
- name.c_str(), result ? "" : "un");
- User* user = ServerInstance->FindNick(uid);
- if (user)
- user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
- user->nick.c_str(), name.c_str(), result ? "" : "un");
- ServerInstance->GlobalCulls.AddItem(this);
- }
-};
-
-CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters[0] == "cmd_reloadmodule.so")
- {
- user->WriteNumeric(975, "%s %s :You cannot reload cmd_reloadmodule.so (unload and load it)",
- user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- Module* m = ServerInstance->Modules->Find(parameters[0]);
- if (m)
- {
- ServerInstance->Modules->Reload(m, (creator->dying ? NULL : new ReloadModuleWorker(user->uuid, parameters[0])));
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-}
-
-COMMAND_INIT(CommandReloadmodule)
diff --git a/src/commands/cmd_rules.cpp b/src/commands/cmd_rules.cpp
deleted file mode 100644
index 7aacf8c31..000000000
--- a/src/commands/cmd_rules.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /RULES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandRules : public Command
-{
- public:
- /** Constructor for rules.
- */
- CommandRules ( Module* parent) : Command(parent,"RULES",0,0) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-CmdResult CommandRules::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- {
- // Give extra penalty if a non-oper queries the /RULES of a remote server
- LocalUser* localuser = IS_LOCAL(user);
- if ((localuser) && (!IS_OPER(user)))
- localuser->CommandFloodPenalty += 2000;
- return CMD_SUCCESS;
- }
-
- ConfigTag* tag = ServerInstance->Config->EmptyTag;
- if (IS_LOCAL(user))
- tag = user->GetClass()->config;
- std::string rules_name = tag->getString("rules", "rules");
- ConfigFileCache::iterator rules = ServerInstance->Config->Files.find(rules_name);
- if (rules == ServerInstance->Config->Files.end())
- {
- user->SendText(":%s %03d %s :RULES file is missing.",
- ServerInstance->Config->ServerName.c_str(), ERR_NORULES, user->nick.c_str());
- return CMD_SUCCESS;
- }
- user->SendText(":%s %03d %s :%s server rules:", ServerInstance->Config->ServerName.c_str(),
- RPL_RULESTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
-
- for (file_cache::iterator i = rules->second.begin(); i != rules->second.end(); i++)
- user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_RULES, user->nick.c_str(),i->c_str());
-
- user->SendText(":%s %03d %s :End of RULES command.", ServerInstance->Config->ServerName.c_str(), RPL_RULESEND, user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandRules)
diff --git a/src/commands/cmd_server.cpp b/src/commands/cmd_server.cpp
deleted file mode 100644
index 05954f3e8..000000000
--- a/src/commands/cmd_server.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /SERVER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandServer : public Command
-{
- public:
- /** Constructor for server.
- */
- CommandServer ( Module* parent) : Command(parent,"SERVER") { works_before_reg = true;}
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandServer::Handle (const std::vector<std::string>&, User *user)
-{
- if (user->registered == REG_ALL)
- {
- user->WriteNumeric(ERR_ALREADYREGISTERED, "%s :You are already registered. (Perhaps your IRC client does not have a /SERVER command).",user->nick.c_str());
- }
- else
- {
- user->WriteNumeric(ERR_NOTREGISTERED, "%s %s :You may not register as a server (servers have separate ports from clients, change your config)", user->nick.c_str(), name.c_str());
- }
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandServer)
diff --git a/src/commands/cmd_squit.cpp b/src/commands/cmd_squit.cpp
deleted file mode 100644
index ce73ee05b..000000000
--- a/src/commands/cmd_squit.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /SQUIT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandSquit : public Command
-{
- public:
- /** Constructor for squit.
- */
- CommandSquit ( Module* parent) : Command(parent,"SQUIT",1,2) { flags_needed = 'o'; syntax = "<servername>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/*
- * This is handled by the server linking module, if necessary. Do not remove this stub.
- */
-
-
-CmdResult CommandSquit::Handle (const std::vector<std::string>&, User *user)
-{
- user->WriteServ( "NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandSquit)
diff --git a/src/commands/cmd_stats.cpp b/src/commands/cmd_stats.cpp
deleted file mode 100644
index aa5bf44cd..000000000
--- a/src/commands/cmd_stats.cpp
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-#include "commands/cmd_whowas.h"
-
-#ifdef _WIN32
-#include <psapi.h>
-#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
-#endif
-
-/** Handle /STATS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandStats : public Command
-{
- void DoStats(char statschar, User* user, string_list &results);
- public:
- /** Constructor for stats.
- */
- CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { allow_empty_last_param = false; syntax = "<stats-symbol> [<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 1)
- return ROUTE_UNICAST(parameters[1]);
- return ROUTE_LOCALONLY;
- }
-};
-
-void CommandStats::DoStats(char statschar, User* user, string_list &results)
-{
- std::string sn(ServerInstance->Config->ServerName);
-
- bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
- bool isRemoteOper = IS_REMOTE(user) && IS_OPER(user);
- bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
-
- if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
- {
- ServerInstance->SNO->WriteToSnoMask('t',
- "%s '%c' denied for %s (%s@%s)",
- (IS_LOCAL(user) ? "Stats" : "Remote stats"),
- statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- results.push_back(sn + " 481 " + user->nick + " :Permission denied - STATS " + statschar + " requires the servers/auspex priv.");
- return;
- }
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
- if (MOD_RESULT == MOD_RES_DENY)
- {
- results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
- ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
- (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- return;
- }
-
- switch (statschar)
- {
- /* stats p (show listening ports) */
- case 'p':
- {
- for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
- {
- ListenSocket* ls = *i;
- std::string ip = ls->bind_addr;
- if (ip.empty())
- ip.assign("*");
- std::string type = ls->bind_tag->getString("type", "clients");
- std::string hook = ls->bind_tag->getString("ssl", "plaintext");
-
- results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
- " (" + type + ", " + hook + ")");
- }
- }
- break;
-
- /* These stats symbols must be handled by a linking module */
- case 'n':
- case 'c':
- break;
-
- case 'i':
- {
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
- {
- ConnectClass* c = *i;
- std::stringstream res;
- res << sn << " 215 " << user->nick << " I " << c->name << ' ';
- if (c->type == CC_ALLOW)
- res << '+';
- if (c->type == CC_DENY)
- res << '-';
-
- if (c->type == CC_NAMED)
- res << '*';
- else
- res << c->host;
-
- res << ' ' << c->config->getString("port", "*") << ' ';
-
- res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
- << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
- if (c->fakelag)
- res << '*';
- results.push_back(res.str());
- }
- }
- break;
-
- case 'Y':
- {
- int idx = 0;
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
- {
- ConnectClass* c = *i;
- results.push_back(sn+" 215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : ServerInstance->SE->GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
- results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
- ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
- idx++;
- }
- }
- break;
-
- case 'U':
- {
- for(std::map<irc::string, bool>::iterator i = ServerInstance->Config->ulines.begin(); i != ServerInstance->Config->ulines.end(); ++i)
- {
- results.push_back(sn+" 248 "+user->nick+" U "+std::string(i->first.c_str()));
- }
- }
- break;
-
- case 'P':
- {
- unsigned int idx = 0;
- for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
- {
- User* oper = *i;
- if (!ServerInstance->ULine(oper->server))
- {
- results.push_back(sn+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
- (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
- idx++;
- }
- }
- results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
- }
- break;
-
- case 'k':
- ServerInstance->XLines->InvokeStats("K",216,user,results);
- break;
- case 'g':
- ServerInstance->XLines->InvokeStats("G",223,user,results);
- break;
- case 'q':
- ServerInstance->XLines->InvokeStats("Q",217,user,results);
- break;
- case 'Z':
- ServerInstance->XLines->InvokeStats("Z",223,user,results);
- break;
- case 'e':
- ServerInstance->XLines->InvokeStats("E",223,user,results);
- break;
- case 'E':
- results.push_back(sn+" 249 "+user->nick+" :Total events: "+ConvToStr(ServerInstance->SE->TotalEvents));
- results.push_back(sn+" 249 "+user->nick+" :Read events: "+ConvToStr(ServerInstance->SE->ReadEvents));
- results.push_back(sn+" 249 "+user->nick+" :Write events: "+ConvToStr(ServerInstance->SE->WriteEvents));
- results.push_back(sn+" 249 "+user->nick+" :Error events: "+ConvToStr(ServerInstance->SE->ErrorEvents));
- break;
-
- /* stats m (list number of times each command has been used, plus bytecount) */
- case 'm':
- for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
- {
- if (i->second->use_count)
- {
- /* RPL_STATSCOMMANDS */
- results.push_back(sn+" 212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
- }
- }
- break;
-
- /* stats z (debug and memory info) */
- case 'z':
- {
- results.push_back(sn+" 249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->clientlist->size()));
- results.push_back(sn+" 249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->chanlist->size()));
- results.push_back(sn+" 249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser->cmdlist.size()));
-
- if (ServerInstance->Config->WhoWasGroupSize && ServerInstance->Config->WhoWasMaxGroups)
- {
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_STATS);
- req.user = user;
- req.Send();
- results.push_back(sn+" 249 "+user->nick+" :"+req.value);
- }
- }
-
- float kbitpersec_in, kbitpersec_out, kbitpersec_total;
- char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
-
- ServerInstance->SE->GetStats(kbitpersec_in, kbitpersec_out, kbitpersec_total);
-
- snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
- snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
- snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
-
- results.push_back(sn+" 249 "+user->nick+" :Bandwidth total: "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
- results.push_back(sn+" 249 "+user->nick+" :Bandwidth out: "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
- results.push_back(sn+" 249 "+user->nick+" :Bandwidth in: "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
-
-#ifndef _WIN32
- /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
- * Also cuts out some identical code in both branches of the ifndef. -- Om
- */
- rusage R;
-
- /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
- if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
- {
- results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
- results.push_back(sn+" 249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals));
- results.push_back(sn+" 249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt));
- results.push_back(sn+" 249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap));
- results.push_back(sn+" 249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
-
- char percent[30];
-
- float n_elapsed = (ServerInstance->Time() - ServerInstance->stats->LastSampled.tv_sec) * 1000000
- + (ServerInstance->Time_ns() - ServerInstance->stats->LastSampled.tv_nsec) / 1000;
- float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats->LastCPU.tv_usec);
- float per = (n_eaten / n_elapsed) * 100;
-
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (now): "+percent);
-
- n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
- n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
- per = (n_eaten / n_elapsed) * 100;
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (total): "+percent);
- }
-#else
- PROCESS_MEMORY_COUNTERS MemCounters;
- if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
- {
- results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
- results.push_back(sn+" 249 "+user->nick+" :Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
- results.push_back(sn+" 249 "+user->nick+" :Page faults: "+ConvToStr(MemCounters.PageFaultCount));
- }
-
- FILETIME CreationTime;
- FILETIME ExitTime;
- FILETIME KernelTime;
- FILETIME UserTime;
- LARGE_INTEGER ThisSample;
- if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
- QueryPerformanceCounter(&ThisSample))
- {
- KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
- KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
- double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats->LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats->LastCPU.dwLowDateTime) )/100000;
- double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats->LastSampled.QuadPart) / ServerInstance->stats->QPFrequency.QuadPart;
- double per = (n_eaten/n_elapsed);
-
- char percent[30];
-
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (now): "+percent);
-
- n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
- n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
- per = (n_eaten / n_elapsed);
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (total): "+percent);
- }
-#endif
- }
- break;
-
- case 'T':
- {
- char buffer[MAXBUF];
- results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused));
- results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown));
- results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions));
- results.push_back(sn+" 249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad));
- results.push_back(sn+" 249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects));
- snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",
- user->nick.c_str(),ServerInstance->stats->statsSent / 1024.0,ServerInstance->stats->statsRecv / 1024.0);
- results.push_back(sn+buffer);
- }
- break;
-
- /* stats o */
- case 'o':
- {
- ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- results.push_back(sn+" 243 "+user->nick+" O "+tag->getString("host")+" * "+
- tag->getString("name") + " " + tag->getString("type")+" 0");
- }
- }
- break;
- case 'O':
- {
- for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
- {
- // just the types, not the actual oper blocks...
- if (i->first[0] != ' ')
- continue;
- OperInfo* tag = i->second;
- tag->init();
- std::string umodes;
- std::string cmodes;
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
- if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
- umodes.push_back(c);
- mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
- cmodes.push_back(c);
- }
- results.push_back(sn+" 243 "+user->nick+" O "+tag->NameStr() + " " + umodes + " " + cmodes);
- }
- }
- break;
-
- /* stats l (show user I/O stats) */
- case 'l':
- results.push_back(sn+" 211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open");
- for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++)
- {
- LocalUser* i = *n;
- results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->signon));
- }
- break;
-
- /* stats L (show user I/O stats with IP addresses) */
- case 'L':
- results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open");
- for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++)
- {
- LocalUser* i = *n;
- results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->signon));
- }
- break;
-
- /* stats u (show server uptime) */
- case 'u':
- {
- time_t current_time = 0;
- current_time = ServerInstance->Time();
- time_t server_uptime = current_time - ServerInstance->startup_time;
- struct tm* stime;
- stime = gmtime(&server_uptime);
- /* i dont know who the hell would have an ircd running for over a year nonstop, but
- * Craig suggested this, and it seemed a good idea so in it went */
- if (stime->tm_year > 70)
- {
- char buffer[MAXBUF];
- snprintf(buffer,MAXBUF," 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick.c_str(),(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
- results.push_back(sn+buffer);
- }
- else
- {
- char buffer[MAXBUF];
- snprintf(buffer,MAXBUF," 242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick.c_str(),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
- results.push_back(sn+buffer);
- }
- }
- break;
-
- default:
- break;
- }
-
- results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
- ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
- (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- return;
-}
-
-CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
- {
- // Give extra penalty if a non-oper does /STATS <remoteserver>
- LocalUser* localuser = IS_LOCAL(user);
- if ((localuser) && (!IS_OPER(user)))
- localuser->CommandFloodPenalty += 2000;
- return CMD_SUCCESS;
- }
- string_list values;
- char search = parameters[0][0];
- DoStats(search, user, values);
- for (size_t i = 0; i < values.size(); i++)
- user->SendText(":%s", values[i].c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandStats)
diff --git a/src/commands/cmd_time.cpp b/src/commands/cmd_time.cpp
deleted file mode 100644
index db452d381..000000000
--- a/src/commands/cmd_time.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /TIME. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandTime : public Command
-{
- public:
- /** Constructor for time.
- */
- CommandTime ( Module* parent) : Command(parent,"TIME",0,0) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-CmdResult CommandTime::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
- struct tm* timeinfo;
- time_t local = ServerInstance->Time();
-
- timeinfo = localtime(&local);
-
- char tms[26];
- snprintf(tms,26,"%s",asctime(timeinfo));
- tms[24] = 0;
-
- user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),tms);
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandTime)
diff --git a/src/commands/cmd_topic.cpp b/src/commands/cmd_topic.cpp
deleted file mode 100644
index 412ca1c06..000000000
--- a/src/commands/cmd_topic.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /TOPIC. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandTopic : public Command
-{
- public:
- /** Constructor for topic.
- */
- CommandTopic ( Module* parent) : Command(parent,"TOPIC",1, 2) { syntax = "<channel> [<topic>]"; Penalty = 2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User *user)
-{
- Channel* c;
-
- c = ServerInstance->FindChan(parameters[0]);
- if (!c)
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- if (parameters.size() == 1)
- {
- if (c)
- {
- if ((c->IsModeSet('s')) && (!c->HasUser(user)))
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), c->name.c_str());
- return CMD_FAILURE;
- }
-
- if (c->topic.length())
- {
- user->WriteNumeric(332, "%s %s :%s", user->nick.c_str(), c->name.c_str(), c->topic.c_str());
- user->WriteNumeric(333, "%s %s %s %lu", user->nick.c_str(), c->name.c_str(), c->setby.c_str(), (unsigned long)c->topicset);
- }
- else
- {
- user->WriteNumeric(RPL_NOTOPICSET, "%s %s :No topic is set.", user->nick.c_str(), c->name.c_str());
- }
- }
- return CMD_SUCCESS;
- }
- else if (parameters.size()>1)
- {
- std::string t = parameters[1]; // needed, in case a module wants to change it
- c->SetTopic(user, t);
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandTopic)
diff --git a/src/commands/cmd_unloadmodule.cpp b/src/commands/cmd_unloadmodule.cpp
deleted file mode 100644
index 6d0f5f41c..000000000
--- a/src/commands/cmd_unloadmodule.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /UNLOADMODULE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUnloadmodule : public Command
-{
- public:
- /** Constructor for unloadmodule.
- */
- CommandUnloadmodule ( Module* parent) : Command(parent,"UNLOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandUnloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters[0] == "cmd_unloadmodule.so" || parameters[0] == "cmd_loadmodule.so")
- {
- user->WriteNumeric(972, "%s %s :You cannot unload module loading commands!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- Module* m = ServerInstance->Modules->Find(parameters[0]);
- if (m && ServerInstance->Modules->Unload(m))
- {
- ServerInstance->SNO->WriteGlobalSno('a', "MODULE UNLOADED: %s unloaded %s", user->nick.c_str(), parameters[0].c_str());
- user->WriteNumeric(973, "%s %s :Module successfully unloaded.",user->nick.c_str(), parameters[0].c_str());
- }
- else
- {
- user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(),
- m ? ServerInstance->Modules->LastError().c_str() : "No such module");
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUnloadmodule)
diff --git a/src/commands/cmd_user.cpp b/src/commands/cmd_user.cpp
deleted file mode 100644
index 09e1e3319..000000000
--- a/src/commands/cmd_user.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /USER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUser : public SplitCommand
-{
- public:
- /** Constructor for user.
- */
- CommandUser ( Module* parent) : SplitCommand(parent,"USER",4,4) { works_before_reg = true; Penalty = 0; syntax = "<username> <localhost> <remotehost> <GECOS>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
- /* A user may only send the USER command once */
- if (!(user->registered & REG_USER))
- {
- if (!ServerInstance->IsIdent(parameters[0].c_str()))
- {
- /*
- * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
- * -- Craig, and then w00t.
- */
- user->WriteNumeric(461, "%s USER :Your username is not valid",user->nick.c_str());
- return CMD_FAILURE;
- }
- else
- {
- /*
- * The ident field is IDENTMAX+2 in size to account for +1 for the optional
- * ~ character, and +1 for null termination, therefore we can safely use up to
- * IDENTMAX here.
- */
- user->ChangeIdent(parameters[0].c_str());
- user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
- user->registered = (user->registered | REG_USER);
- }
- }
- else
- {
- user->CommandFloodPenalty += 1000;
- user->WriteNumeric(462, "%s :You may not reregister", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- /* parameters 2 and 3 are local and remote hosts, and are ignored */
- if (user->registered == REG_NICKUSER)
- {
- ModResult MOD_RESULT;
-
- /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
- FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUser)
diff --git a/src/commands/cmd_version.cpp b/src/commands/cmd_version.cpp
deleted file mode 100644
index 7620197fd..000000000
--- a/src/commands/cmd_version.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /VERSION. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandVersion : public Command
-{
- public:
- /** Constructor for version.
- */
- CommandVersion ( Module* parent) : Command(parent,"VERSION",0,0) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandVersion::Handle (const std::vector<std::string>&, User *user)
-{
- std::string version = ServerInstance->GetVersionString(IS_OPER(user));
- user->WriteNumeric(RPL_VERSION, "%s :%s", user->nick.c_str(), version.c_str());
- ServerInstance->Config->Send005(user);
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandVersion)
diff --git a/src/commands/cmd_wallops.cpp b/src/commands/cmd_wallops.cpp
deleted file mode 100644
index 198997a95..000000000
--- a/src/commands/cmd_wallops.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WALLOPS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWallops : public Command
-{
- public:
- /** Constructor for wallops.
- */
- CommandWallops ( Module* parent) : Command(parent,"WALLOPS",1,1) { flags_needed = 'o'; syntax = "<any-text>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string wallop("WALLOPS :");
- wallop.append(parameters[0]);
-
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
- {
- User* t = *i;
- if (t->IsModeSet('w'))
- user->WriteTo(t,wallop);
- }
-
- FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0]));
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandWallops)
diff --git a/src/commands/cmd_who.cpp b/src/commands/cmd_who.cpp
deleted file mode 100644
index 1a18ab410..000000000
--- a/src/commands/cmd_who.cpp
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WHO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWho : public Command
-{
- bool CanView(Channel* chan, User* user);
- bool opt_viewopersonly;
- bool opt_showrealhost;
- bool opt_realname;
- bool opt_mode;
- bool opt_ident;
- bool opt_metadata;
- bool opt_port;
- bool opt_away;
- bool opt_local;
- bool opt_far;
- bool opt_time;
-
- public:
- /** Constructor for who.
- */
- CommandWho ( Module* parent) : Command(parent,"WHO", 1) {
- syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [afhilMmoprt]";
- }
- void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults);
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- bool whomatch(User* cuser, User* user, const char* matchtext);
-};
-
-
-static Channel* get_first_visible_channel(User *source, User *u)
-{
- UCListIter i = u->chans.begin();
- while (i != u->chans.end())
- {
- Channel* c = *i++;
-
- /* XXX move the +I check into m_hidechans */
- if (source == u || !(c->IsModeSet('s') || c->IsModeSet('p') || u->IsModeSet('I')) || c->HasUser(source))
- return c;
- }
- return NULL;
-}
-
-bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
-{
- bool match = false;
- bool positive = false;
-
- if (user->registered != REG_ALL)
- return false;
-
- if (opt_local && !IS_LOCAL(user))
- return false;
- else if (opt_far && IS_LOCAL(user))
- return false;
-
- if (opt_mode)
- {
- for (const char* n = matchtext; *n; n++)
- {
- if (*n == '+')
- {
- positive = true;
- continue;
- }
- else if (*n == '-')
- {
- positive = false;
- continue;
- }
- if (user->IsModeSet(*n) != positive)
- return false;
- }
- return true;
- }
- else
- {
- /*
- * This was previously one awesome pile of ugly nested if, when really, it didn't need
- * to be, since only one condition was ever checked, a chained if works just fine.
- * -- w00t
- */
- if (opt_metadata)
- {
- match = false;
- const Extensible::ExtensibleStore& list = user->GetExtList();
- for(Extensible::ExtensibleStore::const_iterator i = list.begin(); i != list.end(); ++i)
- if (InspIRCd::Match(i->first->name, matchtext))
- match = true;
- }
- else if (opt_realname)
- match = InspIRCd::Match(user->fullname, matchtext);
- else if (opt_showrealhost)
- match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
- else if (opt_ident)
- match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
- else if (opt_port)
- {
- irc::portparser portrange(matchtext, false);
- long portno = -1;
- while ((portno = portrange.GetToken()))
- if (IS_LOCAL(user) && portno == IS_LOCAL(user)->GetServerPort())
- {
- match = true;
- break;
- }
- }
- else if (opt_away)
- match = InspIRCd::Match(user->awaymsg, matchtext);
- else if (opt_time)
- {
- long seconds = ServerInstance->Duration(matchtext);
-
- // Okay, so time matching, we want all users connected `seconds' ago
- if (user->signon >= ServerInstance->Time() - seconds)
- match = true;
- }
-
- /*
- * Once the conditionals have been checked, only check dhost/nick/server
- * if they didn't match this user -- and only match if we don't find a match.
- *
- * This should make things minutely faster, and again, less ugly.
- * -- w00t
- */
- if (!match)
- match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
-
- if (!match)
- match = InspIRCd::Match(user->nick, matchtext);
-
- /* Don't allow server name matches if HideWhoisServer is enabled, unless the command user has the priv */
- if (!match && (ServerInstance->Config->HideWhoisServer.empty() || cuser->HasPrivPermission("users/auspex")))
- match = InspIRCd::Match(user->server, matchtext);
-
- return match;
- }
-}
-
-bool CommandWho::CanView(Channel* chan, User* user)
-{
- if (!user || !chan)
- return false;
-
- /* Bug #383 - moved higher up the list, because if we are in the channel
- * we can see all its users
- */
- if (chan->HasUser(user))
- return true;
- /* Opers see all */
- if (user->HasPrivPermission("users/auspex"))
- return true;
- /* Cant see inside a +s or a +p channel unless we are a member (see above) */
- else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
- return true;
-
- return false;
-}
-
-void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults)
-{
- if (!ch)
- ch = get_first_visible_channel(user, u);
-
- std::string wholine = initial + (ch ? ch->name : "*") + " " + u->ident + " " +
- (opt_showrealhost ? u->host : u->dhost) + " ";
- if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- wholine.append(ServerInstance->Config->HideWhoisServer);
- else
- wholine.append(u->server);
-
- wholine.append(" " + u->nick + " ");
-
- /* away? */
- if (IS_AWAY(u))
- {
- wholine.append("G");
- }
- else
- {
- wholine.append("H");
- }
-
- /* oper? */
- if (IS_OPER(u))
- {
- wholine.push_back('*');
- }
-
- if (ch)
- wholine.append(ch->GetPrefixChar(u));
-
- wholine.append(" :0 " + u->fullname);
-
- FOREACH_MOD(I_OnSendWhoLine, OnSendWhoLine(user, parms, u, wholine));
-
- if (!wholine.empty())
- whoresults.push_back(wholine);
-}
-
-CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
-{
- /*
- * XXX - RFC says:
- * The <name> passed to WHO is matched against users' host, server, real
- * name and nickname
- * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
- */
-
- /* WHO options */
- opt_viewopersonly = false;
- opt_showrealhost = false;
- opt_realname = false;
- opt_mode = false;
- opt_ident = false;
- opt_metadata = false;
- opt_port = false;
- opt_away = false;
- opt_local = false;
- opt_far = false;
- opt_time = false;
-
- Channel *ch = NULL;
- std::vector<std::string> whoresults;
- std::string initial = "352 " + user->nick + " ";
-
- char matchtext[MAXBUF];
- bool usingwildcards = false;
-
- /* Change '0' into '*' so the wildcard matcher can grok it */
- if (parameters[0] == "0")
- strlcpy(matchtext, "*", MAXBUF);
- else
- strlcpy(matchtext, parameters[0].c_str(), MAXBUF);
-
- for (const char* check = matchtext; *check; check++)
- {
- if (*check == '*' || *check == '?' || *check == '.')
- {
- usingwildcards = true;
- break;
- }
- }
-
- if (parameters.size() > 1)
- {
- /* Fix for bug #444, WHO flags count as a wildcard */
- usingwildcards = true;
-
- for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
- {
- switch (*iter)
- {
- case 'o':
- opt_viewopersonly = true;
- break;
- case 'h':
- if (user->HasPrivPermission("users/auspex"))
- opt_showrealhost = true;
- break;
- case 'r':
- opt_realname = true;
- break;
- case 'm':
- if (user->HasPrivPermission("users/auspex"))
- opt_mode = true;
- break;
- case 'M':
- if (user->HasPrivPermission("users/auspex"))
- opt_metadata = true;
- break;
- case 'i':
- opt_ident = true;
- break;
- case 'p':
- if (user->HasPrivPermission("users/auspex"))
- opt_port = true;
- break;
- case 'a':
- opt_away = true;
- break;
- case 'l':
- if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
- opt_local = true;
- break;
- case 'f':
- if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
- opt_far = true;
- break;
- case 't':
- opt_time = true;
- break;
- }
- }
- }
-
-
- /* who on a channel? */
- ch = ServerInstance->FindChan(matchtext);
-
- if (ch)
- {
- if (CanView(ch,user))
- {
- bool inside = ch->HasUser(user);
-
- /* who on a channel. */
- const UserMembList *cu = ch->GetUsers();
-
- for (UserMembCIter i = cu->begin(); i != cu->end(); i++)
- {
- {
- /* opers only, please */
- if (opt_viewopersonly && !IS_OPER(i->first))
- continue;
-
- /* If we're not inside the channel, hide +i users */
- if (!inside && user != i->first && i->first->IsModeSet('i') && !user->HasPrivPermission("users/auspex"))
- continue;
- }
-
- SendWhoLine(user, parameters, initial, ch, i->first, whoresults);
- }
- }
- }
- else
- {
- /* Match against wildcard of nick, server or host */
- if (opt_viewopersonly)
- {
- /* Showing only opers */
- for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
- {
- User* oper = *i;
-
- if (whomatch(user, oper, matchtext))
- {
- if (!user->SharesChannelWith(oper))
- {
- if (usingwildcards && (oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
- continue;
- }
-
- SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
- }
- }
- }
- else
- {
- for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
- {
- if (whomatch(user, i->second, matchtext))
- {
- if (!user->SharesChannelWith(i->second))
- {
- if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
- continue;
- }
-
- SendWhoLine(user, parameters, initial, NULL, i->second, whoresults);
- }
- }
- }
- }
- /* Send the results out */
- for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
- user->WriteServ(*n);
- user->WriteNumeric(315, "%s %s :End of /WHO list.",user->nick.c_str(), *parameters[0].c_str() ? parameters[0].c_str() : "*");
-
- // Penalize the user a bit for large queries
- // (add one unit of penalty per 200 results)
- if (IS_LOCAL(user))
- IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandWho)
diff --git a/src/commands/cmd_whois.cpp b/src/commands/cmd_whois.cpp
deleted file mode 100644
index ab0b82fff..000000000
--- a/src/commands/cmd_whois.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WHOIS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWhois : public Command
-{
- public:
- /** Constructor for whois.
- */
- CommandWhois ( Module* parent) : Command(parent,"WHOIS",1) { Penalty = 2; syntax = "<nick>{,<nick>}"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- int userindex = 0;
- unsigned long idle = 0, signon = 0;
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
-
- /*
- * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
- * does, and use the second one, otherwise, use the only paramter. -- djGrrr
- */
- if (parameters.size() > 1)
- userindex = 1;
-
- if (IS_LOCAL(user))
- dest = ServerInstance->FindNickOnly(parameters[userindex]);
- else
- dest = ServerInstance->FindNick(parameters[userindex]);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- /*
- * Okay. Umpteenth attempt at doing this, so let's re-comment...
- * For local users (/w localuser), we show idletime if hidewhois is disabled
- * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
- * For remote users (/w remoteuser), we do NOT show idletime
- * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
- * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
- */
- if (IS_LOCAL(dest) && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
- {
- idle = labs((long)((dest->idle_lastmsg)-ServerInstance->Time()));
- signon = dest->signon;
- }
-
- ServerInstance->DoWhois(user,dest,signon,idle,parameters[userindex].c_str());
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
- user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-
-
-COMMAND_INIT(CommandWhois)
diff --git a/src/commands/cmd_whowas.cpp b/src/commands/cmd_whowas.cpp
deleted file mode 100644
index 3a6444b45..000000000
--- a/src/commands/cmd_whowas.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "commands/cmd_whowas.h"
-
-WhoWasMaintainTimer * timer;
-
-CommandWhowas::CommandWhowas( Module* parent) : Command(parent, "WHOWAS", 1)
-{
- syntax = "<nick>{,<nick>}";
- Penalty = 2;
- timer = new WhoWasMaintainTimer(3600);
- ServerInstance->Timers->AddTimer(timer);
-}
-
-CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
-{
- /* if whowas disabled in config */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
- {
- user->WriteNumeric(421, "%s %s :This command has been disabled.",user->nick.c_str(),name.c_str());
- return CMD_FAILURE;
- }
-
- whowas_users::iterator i = whowas.find(assign(parameters[0]));
-
- if (i == whowas.end())
- {
- user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
- }
- else
- {
- whowas_set* grp = i->second;
- if (grp->size())
- {
- for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
- {
- WhoWasGroup* u = *ux;
-
- user->WriteNumeric(314, "%s %s %s %s * :%s",user->nick.c_str(),parameters[0].c_str(),
- u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
-
- if (user->HasPrivPermission("users/auspex"))
- user->WriteNumeric(379, "%s %s :was connecting from *@%s",
- user->nick.c_str(), parameters[0].c_str(), u->host.c_str());
-
- std::string signon = ServerInstance->TimeString(u->signon);
- if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), ServerInstance->Config->HideWhoisServer.c_str(), signon.c_str());
- else
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), u->server.c_str(), signon.c_str());
- }
- }
- else
- {
- user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
-
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_SUCCESS;
-}
-
-std::string CommandWhowas::GetStats()
-{
- int whowas_size = 0;
- int whowas_bytes = 0;
- whowas_users_fifo::iterator iter;
- for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
- {
- whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
- if (n->size())
- {
- whowas_size += n->size();
- whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
- }
- }
- return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
-}
-
-void CommandWhowas::AddToWhoWas(User* user)
-{
- /* if whowas disabled */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
- {
- return;
- }
-
- whowas_users::iterator iter = whowas.find(irc::string(user->nick.c_str()));
-
- if (iter == whowas.end())
- {
- whowas_set* n = new whowas_set;
- WhoWasGroup *a = new WhoWasGroup(user);
- n->push_back(a);
- whowas[user->nick.c_str()] = n;
- whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick.c_str()));
-
- if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
- {
- whowas_users::iterator iter2 = whowas.find(whowas_fifo[0].second);
- if (iter2 != whowas.end())
- {
- whowas_set* n2 = (whowas_set*)iter2->second;
-
- if (n2->size())
- {
- while (n2->begin() != n2->end())
- {
- WhoWasGroup *a2 = *(n2->begin());
- delete a2;
- n2->pop_front();
- }
- }
-
- delete n2;
- whowas.erase(iter2);
- }
- whowas_fifo.pop_front();
- }
- }
- else
- {
- whowas_set* group = (whowas_set*)iter->second;
- WhoWasGroup *a = new WhoWasGroup(user);
- group->push_back(a);
-
- if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
- {
- WhoWasGroup *a2 = (WhoWasGroup*)*(group->begin());
- delete a2;
- group->pop_front();
- }
- }
-}
-
-/* on rehash, refactor maps according to new conf values */
-void CommandWhowas::PruneWhoWas(time_t t)
-{
- /* config values */
- int groupsize = ServerInstance->Config->WhoWasGroupSize;
- int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
- int maxkeep = ServerInstance->Config->WhoWasMaxKeep;
-
- /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
- {
- if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
- {
- iter = whowas.find(whowas_fifo[0].second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (1)");
- return;
- }
-
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
-
- delete n;
- whowas.erase(iter);
- whowas_fifo.pop_front();
- }
- else
- break;
- }
-
- /* Then cut the whowas sets to new size (groupsize) */
- fifosize = (int)whowas_fifo.size();
- for (int i = 0; i < fifosize; i++)
- {
- iter = whowas.find(whowas_fifo[0].second);
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (2)");
- return;
- }
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
- {
- int nickcount = n->size();
- while (n->begin() != n->end() && nickcount > groupsize)
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- nickcount--;
- }
- }
- }
-}
-
-/* call maintain once an hour to remove expired nicks */
-void CommandWhowas::MaintainWhoWas(time_t t)
-{
- for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
- {
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
- {
- while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->erase(n->begin());
- }
- }
- }
-}
-
-CommandWhowas::~CommandWhowas()
-{
- if (timer)
- {
- ServerInstance->Timers->DelTimer(timer);
- }
-
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
- {
- iter = whowas.find(whowas_fifo[0].second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (3)");
- return;
- }
-
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
-
- delete n;
- whowas.erase(iter);
- whowas_fifo.pop_front();
- }
-}
-
-WhoWasGroup::WhoWasGroup(User* user) : host(user->host), dhost(user->dhost), ident(user->ident),
- server(user->server), gecos(user->fullname), signon(user->signon)
-{
-}
-
-WhoWasGroup::~WhoWasGroup()
-{
-}
-
-/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
-void WhoWasMaintainTimer::Tick(time_t)
-{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest(whowas, whowas, WhowasRequest::WHOWAS_MAINTAIN).Send();
- }
-}
-
-class ModuleWhoWas : public Module
-{
- CommandWhowas cmd;
- public:
- ModuleWhoWas() : cmd(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- void OnRequest(Request& request)
- {
- WhowasRequest& req = static_cast<WhowasRequest&>(request);
- switch (req.type)
- {
- case WhowasRequest::WHOWAS_ADD:
- cmd.AddToWhoWas(req.user);
- break;
- case WhowasRequest::WHOWAS_STATS:
- req.value = cmd.GetStats();
- break;
- case WhowasRequest::WHOWAS_PRUNE:
- cmd.PruneWhoWas(ServerInstance->Time());
- break;
- case WhowasRequest::WHOWAS_MAINTAIN:
- cmd.MaintainWhoWas(ServerInstance->Time());
- break;
- }
- }
-
- Version GetVersion()
- {
- return Version("WHOWAS Command", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleWhoWas)
diff --git a/src/configparser.cpp b/src/configparser.cpp
index 409ebdd39..825053044 100644
--- a/src/configparser.cpp
+++ b/src/configparser.cpp
@@ -91,7 +91,7 @@ struct Parser
unget(ch);
}
- bool kv(std::vector<KeyVal>* items, std::set<std::string>& seen)
+ bool kv(ConfigItems* items)
{
std::string key;
nextword(key);
@@ -119,13 +119,13 @@ struct Parser
while (1)
{
ch = next();
- if (ch == '&' && (flags & FLAG_USE_XML))
+ if (ch == '&' && !(flags & FLAG_USE_COMPAT))
{
std::string varname;
while (1)
{
ch = next();
- if (isalnum(ch))
+ if (isalnum(ch) || (varname.empty() && ch == '#'))
varname.push_back(ch);
else if (ch == ';')
break;
@@ -136,12 +136,32 @@ struct Parser
throw CoreException("Parse error");
}
}
- std::map<std::string, std::string>::iterator var = stack.vars.find(varname);
- if (var == stack.vars.end())
- throw CoreException("Undefined XML entity reference '&" + varname + ";'");
- value.append(var->second);
+ if (varname.empty())
+ throw CoreException("Empty XML entity reference");
+ else if (varname[0] == '#' && (varname.size() == 1 || (varname.size() == 2 && varname[1] == 'x')))
+ throw CoreException("Empty numeric character reference");
+ else if (varname[0] == '#')
+ {
+ const char* cvarname = varname.c_str();
+ char* endptr;
+ unsigned long lvalue;
+ if (cvarname[1] == 'x')
+ lvalue = strtoul(cvarname + 2, &endptr, 16);
+ else
+ lvalue = strtoul(cvarname + 1, &endptr, 10);
+ if (*endptr != '\0' || lvalue > 255)
+ throw CoreException("Invalid numeric character reference '&" + varname + ";'");
+ value.push_back(static_cast<char>(lvalue));
+ }
+ else
+ {
+ insp::flat_map<std::string, std::string>::iterator var = stack.vars.find(varname);
+ if (var == stack.vars.end())
+ throw CoreException("Undefined XML entity reference '&" + varname + ";'");
+ value.append(var->second);
+ }
}
- else if (ch == '\\' && !(flags & FLAG_USE_XML))
+ else if (ch == '\\' && (flags & FLAG_USE_COMPAT))
{
int esc = next();
if (esc == 'n')
@@ -153,14 +173,14 @@ struct Parser
}
else if (ch == '"')
break;
- else
+ else if (ch != '\r')
value.push_back(ch);
}
- if (!seen.insert(key).second)
+ if (items->find(key) != items->end())
throw CoreException("Duplicate key '" + key + "' found");
- items->push_back(KeyVal(key, value));
+ (*items)[key] = value;
return true;
}
@@ -179,11 +199,13 @@ struct Parser
if (name.empty())
throw CoreException("Empty tag name");
- std::vector<KeyVal>* items;
- std::set<std::string> seen;
+ ConfigItems* items;
tag = ConfigTag::create(name, current.filename, current.line, items);
- while (kv(items, seen));
+ while (kv(items))
+ {
+ // Do nothing here (silences a GCC warning).
+ }
if (name == mandatory_tag)
{
@@ -191,27 +213,27 @@ struct Parser
mandatory_tag.clear();
}
- if (name == "include")
+ if (stdalgo::string::equalsci(name, "include"))
{
stack.DoInclude(tag, flags);
}
- else if (name == "files")
+ else if (stdalgo::string::equalsci(name, "files"))
{
- for(std::vector<KeyVal>::iterator i = items->begin(); i != items->end(); i++)
+ for(ConfigItems::iterator i = items->begin(); i != items->end(); i++)
{
stack.DoReadFile(i->first, i->second, flags, false);
}
}
- else if (name == "execfiles")
+ else if (stdalgo::string::equalsci(name, "execfiles"))
{
- for(std::vector<KeyVal>::iterator i = items->begin(); i != items->end(); i++)
+ for(ConfigItems::iterator i = items->begin(); i != items->end(); i++)
{
stack.DoReadFile(i->first, i->second, flags, true);
}
}
- else if (name == "define")
+ else if (stdalgo::string::equalsci(name, "define"))
{
- if (!(flags & FLAG_USE_XML))
+ if (flags & FLAG_USE_COMPAT)
throw CoreException("<define> tags may only be used in XML-style config (add <config format=\"xml\">)");
std::string varname = tag->getString("name");
std::string value = tag->getString("value");
@@ -219,13 +241,13 @@ struct Parser
throw CoreException("Variable definition must include variable name");
stack.vars[varname] = value;
}
- else if (name == "config")
+ else if (stdalgo::string::equalsci(name, "config"))
{
std::string format = tag->getString("format");
- if (format == "xml")
- flags |= FLAG_USE_XML;
- else if (format == "compat")
- flags &= ~FLAG_USE_XML;
+ if (stdalgo::string::equalsci(format, "xml"))
+ flags &= ~FLAG_USE_COMPAT;
+ else if (stdalgo::string::equalsci(format, "compat"))
+ flags |= FLAG_USE_COMPAT;
else if (!format.empty())
throw CoreException("Unknown configuration format " + format);
}
@@ -297,7 +319,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
flags |= FLAG_NO_INC;
if (tag->getBool("noexec", false))
flags |= FLAG_NO_EXEC;
- if (!ParseFile(name, flags, mandatorytag))
+ if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
throw CoreException("Included");
}
else if (tag->readString("executable", name))
@@ -308,7 +330,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
flags |= FLAG_NO_INC;
if (tag->getBool("noexec", true))
flags |= FLAG_NO_EXEC;
- if (!ParseExec(name, flags, mandatorytag))
+ if (!ParseFile(name, flags, mandatorytag, true))
throw CoreException("Included");
}
}
@@ -320,14 +342,15 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
if (exec && (flags & FLAG_NO_EXEC))
throw CoreException("Invalid <execfiles> tag in file included with noexec=\"yes\"");
- FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(name.c_str(), "r"), exec);
+ std::string path = ServerInstance->Config->Paths.PrependConfig(name);
+ FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(path.c_str(), "r"), exec);
if (!file)
- throw CoreException("Could not read \"" + name + "\" for \"" + key + "\" file");
+ throw CoreException("Could not read \"" + path + "\" for \"" + key + "\" file");
file_cache& cache = FilesOutput[key];
cache.clear();
- char linebuf[MAXBUF*10];
+ char linebuf[5120];
while (fgets(linebuf, sizeof(linebuf), file))
{
size_t len = strlen(linebuf);
@@ -340,76 +363,35 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
}
}
-bool ParseStack::ParseFile(const std::string& name, int flags, const std::string& mandatory_tag)
-{
- ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading file %s", name.c_str());
- for (unsigned int t = 0; t < reading.size(); t++)
- {
- if (std::string(name) == reading[t])
- {
- throw CoreException("File " + name + " is included recursively (looped inclusion)");
- }
- }
-
- /* It's not already included, add it to the list of files we've loaded */
-
- FileWrapper file(fopen(name.c_str(), "r"));
- if (!file)
- throw CoreException("Could not read \"" + name + "\" for include");
-
- reading.push_back(name);
- Parser p(*this, flags, file, name, mandatory_tag);
- bool ok = p.outer_parse();
- reading.pop_back();
- return ok;
-}
-
-bool ParseStack::ParseExec(const std::string& name, int flags, const std::string& mandatory_tag)
+bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec)
{
- ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading executable %s", name.c_str());
- for (unsigned int t = 0; t < reading.size(); t++)
- {
- if (std::string(name) == reading[t])
- {
- throw CoreException("Executable " + name + " is included recursively (looped inclusion)");
- }
- }
+ ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading (isexec=%d) %s", isexec, path.c_str());
+ if (stdalgo::isin(reading, path))
+ throw CoreException((isexec ? "Executable " : "File ") + path + " is included recursively (looped inclusion)");
/* It's not already included, add it to the list of files we've loaded */
- FileWrapper file(popen(name.c_str(), "r"), true);
+ FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
if (!file)
- throw CoreException("Could not open executable \"" + name + "\" for include");
+ throw CoreException("Could not read \"" + path + "\" for include");
- reading.push_back(name);
- Parser p(*this, flags, file, name, mandatory_tag);
+ reading.push_back(path);
+ Parser p(*this, flags, file, path, mandatory_tag);
bool ok = p.outer_parse();
reading.pop_back();
return ok;
}
-#ifdef __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wunknown-pragmas"
-# pragma clang diagnostic ignored "-Wundefined-bool-conversion"
-#elif defined __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wpragmas"
-# pragma GCC diagnostic ignored "-Wnonnull-compare"
-#endif
bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
{
- // TODO: this is undefined behaviour but changing the API is too risky for 2.0.
- if (!this)
- return false;
- for(std::vector<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
+ for(ConfigItems::iterator j = items.begin(); j != items.end(); ++j)
{
if(j->first != key)
continue;
value = j->second;
if (!allow_lf && (value.find('\n') != std::string::npos))
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
" contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
for (std::string::iterator n = value.begin(); n != value.end(); n++)
if (*n == '\n')
@@ -419,20 +401,100 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo
}
return false;
}
-#ifdef __clang__
-# pragma clang diagnostic pop
-#elif defined __GNUC__
-# pragma GCC diagnostic pop
-#endif
-std::string ConfigTag::getString(const std::string& key, const std::string& def)
+std::string ConfigTag::getString(const std::string& key, const std::string& def, const TR1NS::function<bool(const std::string&)>& validator)
+{
+ std::string res;
+ if (!readString(key, res))
+ return def;
+
+ if (!validator(res))
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: The value of <%s:%s> is not valid; value set to %s.",
+ tag.c_str(), key.c_str(), def.c_str());
+ return def;
+ }
+ return res;
+}
+
+std::string ConfigTag::getString(const std::string& key, const std::string& def, size_t minlen, size_t maxlen)
{
- std::string res = def;
- readString(key, res);
+ std::string res;
+ if (!readString(key, res))
+ return def;
+
+ if (res.length() < minlen || res.length() > maxlen)
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: The length of <%s:%s> is not between %ld and %ld; value set to %s.",
+ tag.c_str(), key.c_str(), minlen, maxlen, def.c_str());
+ return def;
+ }
return res;
}
-long ConfigTag::getInt(const std::string &key, long def)
+namespace
+{
+ /** Check for an invalid magnitude specifier. If one is found a warning is logged and the
+ * value is corrected (set to \p def).
+ * @param tag The tag name; used in the warning message.
+ * @param key The key name; used in the warning message.
+ * @param val The full value set in the config as a string.
+ * @param num The value to verify and modify if needed.
+ * @param def The default value, \p res will be set to this if \p tail does not contain a.
+ * valid magnitude specifier.
+ * @param tail The location in the config value at which the magnifier is located.
+ */
+ template <typename Numeric>
+ void CheckMagnitude(const std::string& tag, const std::string& key, const std::string& val, Numeric& num, Numeric def, const char* tail)
+ {
+ // If this is NULL then no magnitude specifier was given.
+ if (!*tail)
+ return;
+
+ switch (toupper(*tail))
+ {
+ case 'K':
+ num *= 1024;
+ return;
+
+ case 'M':
+ num *= 1024 * 1024;
+ return;
+
+ case 'G':
+ num *= 1024 * 1024 * 1024;
+ return;
+ }
+
+ const std::string message = "WARNING: <" + tag + ":" + key + "> value of " + val + " contains an invalid magnitude specifier '"
+ + tail + "'; value set to " + ConvToStr(def) + ".";
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, message);
+ num = def;
+ }
+
+ /** Check for an out of range value. If the value falls outside the boundaries a warning is
+ * logged and the value is corrected (set to \p def).
+ * @param tag The tag name; used in the warning message.
+ * @param key The key name; used in the warning message.
+ * @param num The value to verify and modify if needed.
+ * @param def The default value, \p res will be set to this if (min <= res <= max) doesn't hold true.
+ * @param min Minimum accepted value for \p res.
+ * @param max Maximum accepted value for \p res.
+ */
+ template <typename Numeric>
+ void CheckRange(const std::string& tag, const std::string& key, Numeric& num, Numeric def, Numeric min, Numeric max)
+ {
+ if (num >= min && num <= max)
+ return;
+
+ const std::string message = "WARNING: <" + tag + ":" + key + "> value of " + ConvToStr(num) + " is not between "
+ + ConvToStr(min) + " and " + ConvToStr(max) + "; value set to " + ConvToStr(def) + ".";
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, message);
+ num = def;
+ }
+}
+
+long ConfigTag::getInt(const std::string &key, long def, long min, long max)
{
std::string result;
if(!readString(key, result))
@@ -443,27 +505,49 @@ long ConfigTag::getInt(const std::string &key, long def)
long res = strtol(res_cstr, &res_tail, 0);
if (res_tail == res_cstr)
return def;
- switch (toupper(*res_tail))
- {
- case 'K':
- res= res* 1024;
- break;
- case 'M':
- res= res* 1024 * 1024;
- break;
- case 'G':
- res= res* 1024 * 1024 * 1024;
- break;
- }
+
+ CheckMagnitude(tag, key, result, res, def, res_tail);
+ CheckRange(tag, key, res, def, min, max);
return res;
}
-double ConfigTag::getFloat(const std::string &key, double def)
+unsigned long ConfigTag::getUInt(const std::string& key, unsigned long def, unsigned long min, unsigned long max)
{
std::string result;
if (!readString(key, result))
return def;
- return strtod(result.c_str(), NULL);
+
+ const char* res_cstr = result.c_str();
+ char* res_tail = NULL;
+ unsigned long res = strtoul(res_cstr, &res_tail, 0);
+ if (res_tail == res_cstr)
+ return def;
+
+ CheckMagnitude(tag, key, result, res, def, res_tail);
+ CheckRange(tag, key, res, def, min, max);
+ return res;
+}
+
+unsigned long ConfigTag::getDuration(const std::string& key, unsigned long def, unsigned long min, unsigned long max)
+{
+ std::string duration;
+ if (!readString(key, duration))
+ return def;
+
+ unsigned long ret = InspIRCd::Duration(duration);
+ CheckRange(tag, key, ret, def, min, max);
+ return ret;
+}
+
+double ConfigTag::getFloat(const std::string& key, double def, double min, double max)
+{
+ std::string result;
+ if (!readString(key, result))
+ return def;
+
+ double res = strtod(result.c_str(), NULL);
+ CheckRange(tag, key, res, def, min, max);
+ return res;
}
bool ConfigTag::getBool(const std::string &key, bool def)
@@ -477,7 +561,7 @@ bool ConfigTag::getBool(const std::string &key, bool def)
if (result == "no" || result == "false" || result == "0" || result == "off")
return false;
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
" is not valid, ignoring");
return def;
}
@@ -487,7 +571,7 @@ std::string ConfigTag::getTagLocation()
return src_name + ":" + ConvToStr(src_line);
}
-ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, std::vector<KeyVal>*& Items)
+ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, ConfigItems*& Items)
{
ConfigTag* rv = new ConfigTag(Tag, file, line);
Items = &rv->items;
@@ -499,6 +583,11 @@ ConfigTag::ConfigTag(const std::string& Tag, const std::string& file, int line)
{
}
+OperInfo::OperInfo(const std::string& Name)
+ : name(Name)
+{
+}
+
std::string OperInfo::getConfig(const std::string& key)
{
std::string rv;
diff --git a/src/configreader.cpp b/src/configreader.cpp
index 301db14e8..a8e94586d 100644
--- a/src/configreader.cpp
+++ b/src/configreader.cpp
@@ -23,193 +23,78 @@
#include "inspircd.h"
-#include <fstream>
#include "xline.h"
+#include "listmode.h"
#include "exitcodes.h"
-#include "commands/cmd_whowas.h"
#include "configparser.h"
#include <iostream>
-#ifdef _WIN32
-#include <Iphlpapi.h>
-#pragma comment(lib, "Iphlpapi.lib")
-#endif
-ServerConfig::ServerConfig()
- : NoSnoticeStack(false)
-{
- WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
- RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false;
- WildcardIPv6 = CycleHosts = InvBypassModes = true;
- dns_timeout = 5;
- MaxTargets = 20;
- NetBufferSize = 10240;
- SoftLimit = ServerInstance->SE->GetMaxFds();
- MaxConn = SOMAXCONN;
- MaxChans = 20;
- OperMaxChans = 30;
- c_ipv4_range = 32;
- c_ipv6_range = 128;
-
- std::vector<KeyVal>* items;
- EmptyTag = ConfigTag::create("empty", "<auto>", 0, items);
-}
-
-ServerConfig::~ServerConfig()
+ServerLimits::ServerLimits(ConfigTag* tag)
+ : NickMax(tag->getUInt("maxnick", 30))
+ , ChanMax(tag->getUInt("maxchan", 64))
+ , MaxModes(tag->getUInt("maxmodes", 20))
+ , IdentMax(tag->getUInt("maxident", 10))
+ , MaxQuit(tag->getUInt("maxquit", 255))
+ , MaxTopic(tag->getUInt("maxtopic", 307))
+ , MaxKick(tag->getUInt("maxkick", 255))
+ , MaxReal(tag->getUInt("maxreal", tag->getUInt("maxgecos", 128)))
+ , MaxAway(tag->getUInt("maxaway", 200))
+ , MaxLine(tag->getUInt("maxline", 512))
+ , MaxHost(tag->getUInt("maxhost", 64))
{
- delete EmptyTag;
-}
-
-void ServerConfig::Update005()
-{
- std::stringstream out(data005);
- std::vector<std::string> data;
- std::string token;
- while (out >> token)
- data.push_back(token);
- sort(data.begin(), data.end());
-
- std::string line5;
- isupport.clear();
- for(unsigned int i=0; i < data.size(); i++)
- {
- token = data[i];
- line5 = line5 + token + " ";
- if (i % 13 == 12)
- {
- line5.append(":are supported by this server");
- isupport.push_back(line5);
- line5.clear();
- }
- }
- if (!line5.empty())
- {
- line5.append(":are supported by this server");
- isupport.push_back(line5);
- }
}
-void ServerConfig::Send005(User* user)
+ServerConfig::ServerPaths::ServerPaths(ConfigTag* tag)
+ : Config(tag->getString("configdir", INSPIRCD_CONFIG_PATH))
+ , Data(tag->getString("datadir", INSPIRCD_DATA_PATH))
+ , Log(tag->getString("logdir", INSPIRCD_LOG_PATH))
+ , Module(tag->getString("moduledir", INSPIRCD_MODULE_PATH))
{
- for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
- user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
}
-template<typename T, typename V>
-static void range(T& value, V min, V max, V def, const char* msg)
+static ConfigTag* CreateEmptyTag()
{
- if (value >= (T)min && value <= (T)max)
- return;
- ServerInstance->Logs->Log("CONFIG", DEFAULT,
- "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
- msg, (long)value, (long)min, (long)max, (long)def);
- value = def;
+ ConfigItems* items;
+ return ConfigTag::create("empty", "<auto>", 0, items);
}
-
-static void ValidIP(const std::string& ip, const std::string& key)
+ServerConfig::ServerConfig()
+ : EmptyTag(CreateEmptyTag())
+ , Limits(EmptyTag)
+ , Paths(EmptyTag)
+ , RawLog(false)
+ , NoSnoticeStack(false)
{
- irc::sockets::sockaddrs dummy;
- if (!irc::sockets::aptosa(ip, 0, dummy))
- throw CoreException("The value of "+key+" is not an IP address");
}
-static void ValidHost(const std::string& p, const std::string& msg)
+ServerConfig::~ServerConfig()
{
- int num_dots = 0;
- if (p.empty() || p[0] == '.')
- throw CoreException("The value of "+msg+" is not a valid hostname");
- for (unsigned int i=0;i < p.length();i++)
- {
- switch (p[i])
- {
- case ' ':
- throw CoreException("The value of "+msg+" is not a valid hostname");
- case '.':
- num_dots++;
- break;
- }
- }
- if (num_dots == 0)
- throw CoreException("The value of "+msg+" is not a valid hostname");
+ delete EmptyTag;
}
-bool ServerConfig::ApplyDisabledCommands(const std::string& data)
+bool ServerConfig::ApplyDisabledCommands()
{
- std::stringstream dcmds(data);
- std::string thiscmd;
-
- /* Enable everything first */
- for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
+ // Enable everything first.
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
x->second->Disable(false);
- /* Now disable all the ones which the user wants disabled */
- while (dcmds >> thiscmd)
- {
- Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
- if (cm != ServerInstance->Parser->cmdlist.end())
- {
- cm->second->Disable(true);
- }
- }
- return true;
-}
-
-static void FindDNS(std::string& server)
-{
- if (!server.empty())
- return;
-#ifdef _WIN32
- // attempt to look up their nameserver from the system
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
-
- PFIXED_INFO pFixedInfo;
- DWORD dwBufferSize = sizeof(FIXED_INFO);
- pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
-
- if(pFixedInfo)
+ // Now disable the commands specified in the config.
+ std::string command;
+ irc::spacesepstream commandlist(ConfValue("disabled")->getString("commands"));
+ while (commandlist.GetToken(command))
{
- if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) {
- HeapFree(GetProcessHeap(), 0, pFixedInfo);
- pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
- }
-
- if(pFixedInfo) {
- if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
- server = pFixedInfo->DnsServerList.IpAddress.String;
-
- HeapFree(GetProcessHeap(), 0, pFixedInfo);
- }
-
- if(!server.empty())
+ Command* handler = ServerInstance->Parser.GetHandler(command);
+ if (!handler)
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str());
- return;
+ ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Unable to disable the %s command as it does not exist!", command.c_str());
+ continue;
}
- }
-
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
-#else
- // attempt to look up their nameserver from /etc/resolv.conf
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
-
- std::ifstream resolv("/etc/resolv.conf");
- while (resolv >> server)
- {
- if (server == "nameserver")
- {
- resolv >> server;
- if (server.find_first_not_of("0123456789.") == std::string::npos)
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
- return;
- }
- }
+ ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "The %s command has been disabled", command.c_str());
+ handler->Disable(true);
}
-
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
-#endif
- server = "127.0.0.1";
+ return true;
}
static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
@@ -223,6 +108,7 @@ static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::str
throw CoreException("<"+tag+":"+key+"> missing at " + ctag->getTagLocation());
std::string reason = ctag->getString("reason", "<Config>");
XLine* xl = make->Generate(ServerInstance->Time(), 0, "<Config>", reason, mask);
+ xl->from_config = true;
if (!ServerInstance->XLines->AddLine(xl, NULL))
delete xl;
}
@@ -250,14 +136,11 @@ void ServerConfig::CrossCheckOperClassType()
std::string name = tag->getString("name");
if (name.empty())
throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
- if (!ServerInstance->IsNick(name.c_str(), Limits.NickMax))
- throw CoreException("<type:name> is invalid (value '" + name + "')");
- if (oper_blocks.find(" " + name) != oper_blocks.end())
+ if (OperTypes.find(name) != OperTypes.end())
throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
- OperInfo* ifo = new OperInfo;
- oper_blocks[" " + name] = ifo;
- ifo->name = name;
+ OperInfo* ifo = new OperInfo(name);
+ OperTypes[name] = ifo;
ifo->type_block = tag;
std::string classname;
@@ -281,14 +164,13 @@ void ServerConfig::CrossCheckOperClassType()
throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
std::string type = tag->getString("type");
- OperIndex::iterator tblk = oper_blocks.find(" " + type);
- if (tblk == oper_blocks.end())
+ OperIndex::iterator tblk = OperTypes.find(type);
+ if (tblk == OperTypes.end())
throw CoreException("Oper block " + name + " has missing type " + type);
if (oper_blocks.find(name) != oper_blocks.end())
throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
- OperInfo* ifo = new OperInfo;
- ifo->name = type;
+ OperInfo* ifo = new OperInfo(type);
ifo->oper_block = tag;
ifo->type_block = tblk->second->type_block;
ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
@@ -305,7 +187,7 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
{
ConnectClass* c = *i;
- if (c->name.substr(0, 8) != "unnamed-")
+ if (c->name.compare(0, 8, "unnamed-", 8))
{
oldBlocksByMask["n" + c->name] = c;
}
@@ -318,26 +200,26 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
}
}
- int blk_count = config_data.count("connect");
+ size_t blk_count = config_data.count("connect");
if (blk_count == 0)
{
// No connect blocks found; make a trivial default block
- std::vector<KeyVal>* items;
+ ConfigItems* items;
ConfigTag* tag = ConfigTag::create("connect", "<auto>", 0, items);
- items->push_back(std::make_pair("allow", "*"));
+ (*items)["allow"] = "*";
config_data.insert(std::make_pair("connect", tag));
blk_count = 1;
}
Classes.resize(blk_count);
- std::map<std::string, int> names;
+ std::map<std::string, size_t> names;
bool try_again = true;
- for(int tries=0; try_again; tries++)
+ for(size_t tries = 0; try_again; tries++)
{
try_again = false;
ConfigTagList tags = ConfTags("connect");
- int i=0;
+ size_t i = 0;
for(ConfigIter it = tags.first; it != tags.second; ++it, ++i)
{
ConfigTag* tag = it->second;
@@ -348,7 +230,7 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
std::string parentName = tag->getString("parent");
if (!parentName.empty())
{
- std::map<std::string,int>::iterator parentIter = names.find(parentName);
+ std::map<std::string, size_t>::const_iterator parentIter = names.find(parentName);
if (parentIter == names.end())
{
try_again = true;
@@ -404,30 +286,39 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
me->name = name;
- me->registration_timeout = tag->getInt("timeout", me->registration_timeout);
- me->pingtime = tag->getInt("pingfreq", me->pingtime);
+ me->registration_timeout = tag->getDuration("timeout", me->registration_timeout);
+ me->pingtime = tag->getDuration("pingfreq", me->pingtime);
std::string sendq;
if (tag->readString("sendq", sendq))
{
// attempt to guess a good hard/soft sendq from a single value
- long value = atol(sendq.c_str());
+ unsigned long value = strtoul(sendq.c_str(), NULL, 10);
if (value > 16384)
me->softsendqmax = value / 16;
else
me->softsendqmax = value;
me->hardsendqmax = value * 8;
}
- me->softsendqmax = tag->getInt("softsendq", me->softsendqmax);
- me->hardsendqmax = tag->getInt("hardsendq", me->hardsendqmax);
- me->recvqmax = tag->getInt("recvq", me->recvqmax);
- me->penaltythreshold = tag->getInt("threshold", me->penaltythreshold);
- me->commandrate = tag->getInt("commandrate", me->commandrate);
+ me->softsendqmax = tag->getUInt("softsendq", me->softsendqmax);
+ me->hardsendqmax = tag->getUInt("hardsendq", me->hardsendqmax);
+ me->recvqmax = tag->getUInt("recvq", me->recvqmax);
+ me->penaltythreshold = tag->getUInt("threshold", me->penaltythreshold);
+ me->commandrate = tag->getUInt("commandrate", me->commandrate);
me->fakelag = tag->getBool("fakelag", me->fakelag);
- me->maxlocal = tag->getInt("localmax", me->maxlocal);
- me->maxglobal = tag->getInt("globalmax", me->maxglobal);
- me->maxchans = tag->getInt("maxchans", me->maxchans);
+ me->maxlocal = tag->getUInt("localmax", me->maxlocal);
+ me->maxglobal = tag->getUInt("globalmax", me->maxglobal);
+ me->maxchans = tag->getUInt("maxchans", me->maxchans);
me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
- me->limit = tag->getInt("limit", me->limit);
+ me->limit = tag->getUInt("limit", me->limit);
+ me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
+
+ std::string ports = tag->getString("port");
+ if (!ports.empty())
+ {
+ irc::portparser portrange(ports, false);
+ while (int port = portrange.GetToken())
+ me->ports.insert(port);
+ }
ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
if (oldMask != oldBlocksByMask.end())
@@ -445,141 +336,104 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
/** Represents a deprecated configuration tag.
*/
-struct Deprecated
+struct DeprecatedConfig
{
- /** Tag name
- */
- const char* tag;
- /** Tag value
- */
- const char* value;
- /** Reason for deprecation
- */
- const char* reason;
+ /** Tag name. */
+ std::string tag;
+
+ /** Attribute key. */
+ std::string key;
+
+ /** Attribute value. */
+ std::string value;
+
+ /** Reason for deprecation. */
+ std::string reason;
};
-static const Deprecated ChangedConfig[] = {
- {"options", "hidelinks", "has been moved to <security:hidelinks> as of 1.2a3"},
- {"options", "hidewhois", "has been moved to <security:hidewhois> as of 1.2a3"},
- {"options", "userstats", "has been moved to <security:userstats> as of 1.2a3"},
- {"options", "customversion", "has been moved to <security:customversion> as of 1.2a3"},
- {"options", "hidesplits", "has been moved to <security:hidesplits> as of 1.2a3"},
- {"options", "hidebans", "has been moved to <security:hidebans> as of 1.2a3"},
- {"options", "hidekills", "has been moved to <security:hidekills> as of 1.2a3"},
- {"options", "operspywhois", "has been moved to <security:operspywhois> as of 1.2a3"},
- {"options", "announceinvites", "has been moved to <security:announceinvites> as of 1.2a3"},
- {"options", "hidemodes", "has been moved to <security:hidemodes> as of 1.2a3"},
- {"options", "maxtargets", "has been moved to <security:maxtargets> as of 1.2a3"},
- {"options", "nouserdns", "has been moved to <performance:nouserdns> as of 1.2a3"},
- {"options", "maxwho", "has been moved to <performance:maxwho> as of 1.2a3"},
- {"options", "softlimit", "has been moved to <performance:softlimit> as of 1.2a3"},
- {"options", "somaxconn", "has been moved to <performance:somaxconn> as of 1.2a3"},
- {"options", "netbuffersize", "has been moved to <performance:netbuffersize> as of 1.2a3"},
- {"options", "maxwho", "has been moved to <performance:maxwho> as of 1.2a3"},
- {"options", "loglevel", "1.2+ does not use the loglevel value. Please define <log> tags instead."},
- {"die", "value", "you need to reread your config"},
- {"bind", "transport", "has been moved to <bind:ssl> as of 2.0a1"},
- {"link", "transport", "has been moved to <link:ssl> as of 2.0a1"},
- {"link", "autoconnect", "2.0+ does not use the autoconnect value. Please define <autoconnect> tags instead."},
+static const DeprecatedConfig ChangedConfig[] = {
+ { "bind", "transport", "", "has been moved to <bind:ssl> as of 2.0" },
+ { "die", "value", "", "you need to reread your config" },
+ { "gnutls", "starttls", "", "has been replaced with m_starttls as of 3.0" },
+ { "link", "autoconnect", "", "2.0+ does not use this attribute - define <autoconnect> tags instead" },
+ { "link", "transport", "", "has been moved to <link:ssl> as of 2.0" },
+ { "module", "name", "m_chanprotect.so", "has been replaced with m_customprefix as of 3.0" },
+ { "module", "name", "m_halfop.so", "has been replaced with m_customprefix as of 3.0" },
+ { "options", "cyclehosts", "", "has been replaced with m_hostcycle as of 3.0" },
+ { "performance", "nouserdns", "", "has been moved to <connect:resolvehostnames> as of 3.0" }
};
void ServerConfig::Fill()
{
ConfigTag* options = ConfValue("options");
ConfigTag* security = ConfValue("security");
+ ConfigTag* server = ConfValue("server");
if (sid.empty())
{
- ServerName = ConfValue("server")->getString("name");
- sid = ConfValue("server")->getString("id");
- ValidHost(ServerName, "<server:name>");
- if (!sid.empty() && !ServerInstance->IsSID(sid))
+ ServerName = server->getString("name", "irc.example.com", InspIRCd::IsHost);
+
+ sid = server->getString("id");
+ if (!sid.empty() && !InspIRCd::IsSID(sid))
throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
+
+ CaseMapping = options->getString("casemapping", "rfc1459");
+ if (CaseMapping == "ascii")
+ national_case_insensitive_map = ascii_case_insensitive_map;
+ else if (CaseMapping == "rfc1459")
+ national_case_insensitive_map = rfc_case_insensitive_map;
+ else
+ throw CoreException("<options:casemapping> must be set to 'ascii', or 'rfc1459'");
}
else
{
- if (ServerName != ConfValue("server")->getString("name"))
- throw CoreException("You must restart to change the server name or SID");
- std::string nsid = ConfValue("server")->getString("id");
+ std::string name = server->getString("name");
+ if (!name.empty() && name != ServerName)
+ throw CoreException("You must restart to change the server name");
+
+ std::string nsid = server->getString("id");
if (!nsid.empty() && nsid != sid)
- throw CoreException("You must restart to change the server name or SID");
+ throw CoreException("You must restart to change the server id");
+
+ std::string casemapping = options->getString("casemapping");
+ if (!casemapping.empty() && casemapping != CaseMapping)
+ throw CoreException("You must restart to change the server casemapping");
+
}
- diepass = ConfValue("power")->getString("diepass");
- restartpass = ConfValue("power")->getString("restartpass");
- powerhash = ConfValue("power")->getString("hash");
- PrefixQuit = options->getString("prefixquit");
- SuffixQuit = options->getString("suffixquit");
- FixedQuit = options->getString("fixedquit");
- PrefixPart = options->getString("prefixpart");
- SuffixPart = options->getString("suffixpart");
- FixedPart = options->getString("fixedpart");
- SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
- MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
- MoronBanner = options->getString("moronbanner", "You're banned!");
- ServerDesc = ConfValue("server")->getString("description", "Configure Me");
- Network = ConfValue("server")->getString("network", "Network");
- AdminName = ConfValue("admin")->getString("name", "");
- AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
- AdminNick = ConfValue("admin")->getString("nick", "admin");
- ModPath = ConfValue("path")->getString("moduledir", MOD_PATH);
- NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
- dns_timeout = ConfValue("dns")->getInt("timeout", 5);
- DisabledCommands = ConfValue("disabled")->getString("commands", "");
+ SoftLimit = ConfValue("performance")->getUInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
+ CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
+ MaxConn = ConfValue("performance")->getUInt("somaxconn", SOMAXCONN);
+ XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
+ ServerDesc = server->getString("description", "Configure Me");
+ Network = server->getString("network", "Network");
+ NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
UserStats = security->getString("userstats");
- CustomVersion = security->getString("customversion", Network + " IRCd");
+ CustomVersion = security->getString("customversion");
HideSplits = security->getBool("hidesplits");
HideBans = security->getBool("hidebans");
- HideWhoisServer = security->getString("hidewhois");
+ HideServer = security->getString("hideserver", security->getString("hidewhois"));
HideKillsServer = security->getString("hidekills");
HideULineKills = security->getBool("hideulinekills");
- RestrictBannedUsers = security->getBool("restrictbannedusers", true);
GenericOper = security->getBool("genericoper");
- NoUserDns = ConfValue("performance")->getBool("nouserdns");
SyntaxHints = options->getBool("syntaxhints");
- CycleHosts = options->getBool("cyclehosts");
- CycleHostsFromUser = options->getBool("cyclehostsfromuser");
- UndernetMsgPrefix = options->getBool("ircumsgprefix");
FullHostInTopic = options->getBool("hostintopic");
- MaxTargets = security->getInt("maxtargets", 20);
- DefaultModes = options->getString("defaultmodes", "nt");
+ MaxTargets = security->getUInt("maxtargets", 20, 1, 31);
+ DefaultModes = options->getString("defaultmodes", "not");
PID = ConfValue("pid")->getString("file");
- WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
- WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
- WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
- MaxChans = ConfValue("channels")->getInt("users", 20);
- OperMaxChans = ConfValue("channels")->getInt("opers", 60);
- c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
- c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
- Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
- Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
- Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
- Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
- Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
- Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
- Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
- Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
- Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
- InvBypassModes = options->getBool("invitebypassmodes", true);
+ MaxChans = ConfValue("channels")->getUInt("users", 20);
+ OperMaxChans = ConfValue("channels")->getUInt("opers", 0);
+ c_ipv4_range = ConfValue("cidr")->getUInt("ipv4clone", 32, 1, 32);
+ c_ipv6_range = ConfValue("cidr")->getUInt("ipv6clone", 128, 1, 128);
+ Limits = ServerLimits(ConfValue("limits"));
+ Paths = ServerPaths(ConfValue("path"));
NoSnoticeStack = options->getBool("nosnoticestack", false);
- WelcomeNotice = options->getBool("welcomenotice", true);
-
- range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
- if (ConfValue("performance")->getBool("limitsomaxconn", true))
- range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
- range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
- range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
- range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
- range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
- range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
-
- ValidIP(DNSServer, "<dns:server>");
std::string defbind = options->getString("defaultbind");
- if (assign(defbind) == "ipv4")
+ if (stdalgo::string::equalsci(defbind, "ipv4"))
{
WildcardIPv6 = false;
}
- else if (assign(defbind) == "ipv6")
+ else if (stdalgo::string::equalsci(defbind, "ipv6"))
{
WildcardIPv6 = true;
}
@@ -590,75 +444,43 @@ void ServerConfig::Fill()
if (socktest < 0)
WildcardIPv6 = false;
else
- ServerInstance->SE->Close(socktest);
- }
- ConfigTagList tags = ConfTags("uline");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string server;
- if (!tag->readString("server", server))
- throw CoreException("<uline> tag missing server at " + tag->getTagLocation());
- ulines[assign(server)] = tag->getBool("silent");
- }
-
- tags = ConfTags("banlist");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string chan;
- if (!tag->readString("chan", chan))
- throw CoreException("<banlist> tag missing chan at " + tag->getTagLocation());
- maxbans[chan] = tag->getInt("limit");
+ SocketEngine::Close(socktest);
}
+ ServerInstance->XLines->ClearConfigLines();
ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
- memset(DisabledUModes, 0, sizeof(DisabledUModes));
+ const std::string restrictbannedusers = options->getString("restrictbannedusers", "yes");
+ if (stdalgo::string::equalsci(restrictbannedusers, "no"))
+ RestrictBannedUsers = ServerConfig::BUT_NORMAL;
+ else if (stdalgo::string::equalsci(restrictbannedusers, "silent"))
+ RestrictBannedUsers = ServerConfig::BUT_RESTRICT_SILENT;
+ else if (stdalgo::string::equalsci(restrictbannedusers, "yes"))
+ RestrictBannedUsers = ServerConfig::BUT_RESTRICT_NOTIFY;
+ else
+ throw CoreException(restrictbannedusers + " is an invalid <options:restrictbannedusers> value, at " + options->getTagLocation());
+
+ DisabledUModes.reset();
std::string modes = ConfValue("disabled")->getString("usermodes");
for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
{
- // Complain when the character is not a-z or A-Z
- if ((*p < 'A') || (*p > 'z') || ((*p < 'a') && (*p > 'Z')))
+ // Complain when the character is not a valid mode character.
+ if (!ModeParser::IsModeChar(*p))
throw CoreException("Invalid usermode " + std::string(1, *p) + " was found.");
- DisabledUModes[*p - 'A'] = 1;
+ DisabledUModes.set(*p - 'A');
}
- memset(DisabledCModes, 0, sizeof(DisabledCModes));
+ DisabledCModes.reset();
modes = ConfValue("disabled")->getString("chanmodes");
for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
{
- if ((*p < 'A') || (*p > 'z') || ((*p < 'a') && (*p > 'Z')))
+ if (!ModeParser::IsModeChar(*p))
throw CoreException("Invalid chanmode " + std::string(1, *p) + " was found.");
- DisabledCModes[*p - 'A'] = 1;
+ DisabledCModes.set(*p - 'A');
}
-
- memset(HideModeLists, 0, sizeof(HideModeLists));
- modes = ConfValue("security")->getString("hidemodes");
- for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
- HideModeLists[(unsigned char) *p] = true;
-
- std::string v = security->getString("announceinvites");
-
- if (v == "ops")
- AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
- else if (v == "all")
- AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
- else if (v == "dynamic")
- AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
- else
- AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
-
- v = security->getString("operspywhois");
- if (v == "splitmsg")
- OperSpyWhois = SPYWHOIS_SPLITMSG;
- else if (v == "on" || v == "yes")
- OperSpyWhois = SPYWHOIS_SINGLEMSG;
- else
- OperSpyWhois = SPYWHOIS_NONE;
}
// WARNING: it is not safe to use most of the codebase in this function, as it
@@ -675,12 +497,7 @@ void ServerConfig::Read()
catch (CoreException& err)
{
valid = false;
- errstr << err.GetReason();
- }
- if (valid)
- {
- DNSServer = ConfValue("dns")->getString("server");
- FindDNS(DNSServer);
+ errstr << err.GetReason() << std::endl;
}
}
@@ -692,6 +509,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
/*
* These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
*/
+ this->CaseMapping = old->CaseMapping;
this->ServerName = old->ServerName;
this->sid = old->sid;
this->cmdline = old->cmdline;
@@ -700,16 +518,26 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
/* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
try
{
- for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
+ for (unsigned long index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++)
{
- std::string dummy;
- ConfigTagList tags = ConfTags(ChangedConfig[Index].tag);
+ std::string value;
+ ConfigTagList tags = ConfTags(ChangedConfig[index].tag);
for(ConfigIter i = tags.first; i != tags.second; ++i)
{
- if (i->second->readString(ChangedConfig[Index].value, dummy, true))
- errstr << "Your configuration contains a deprecated value: <"
- << ChangedConfig[Index].tag << ":" << ChangedConfig[Index].value << "> - " << ChangedConfig[Index].reason
- << " (at " << i->second->getTagLocation() << ")\n";
+ if (i->second->readString(ChangedConfig[index].key, value, true)
+ && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value))
+ {
+ errstr << "Your configuration contains a deprecated value: <" << ChangedConfig[index].tag;
+ if (ChangedConfig[index].value.empty())
+ {
+ errstr << ':' << ChangedConfig[index].key;
+ }
+ else
+ {
+ errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\"";
+ }
+ errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")" << std::endl;
+ }
}
}
@@ -721,7 +549,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
}
catch (CoreException &ce)
{
- errstr << ce.GetReason();
+ errstr << ce.GetReason() << std::endl;
}
// Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances.
@@ -731,6 +559,11 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
if (valid)
ServerInstance->WritePID(this->PID, !old);
+ ConfigTagList binds = ConfTags("bind");
+ if (binds.first == binds.second)
+ errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
+ << "You will need to do this if you want clients to be able to connect!" << std::endl;
+
if (old && valid)
{
// On first run, ports are bound later on
@@ -738,14 +571,13 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
ServerInstance->BindPorts(pl);
if (pl.size())
{
- errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
+ errstr << "Not all your client ports could be bound." << std::endl
+ << "The following port(s) failed to bind:" << std::endl;
int j = 1;
for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
{
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "%d. Address: %s Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
- errstr << buf;
+ errstr << j << ".\tAddress: " << i->first.str() << "\tReason: " << strerror(i->second) << std::endl;
}
}
}
@@ -754,7 +586,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
if (!valid)
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
Classes.clear();
}
@@ -769,7 +601,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
std::cout << line << std::endl;
// If a user is rehashing, tell them directly
if (user)
- user->SendText(":%s NOTICE %s :*** %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), line.c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** %s", line.c_str()));
// Also tell opers
ServerInstance->SNO->WriteGlobalSno('a', line);
}
@@ -781,17 +613,10 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
for (ClassVector::const_iterator it = this->Classes.begin(), it_end = this->Classes.end(); it != it_end; ++it)
{
ConfigTag *tag = (*it)->config;
- // Make sure our connection class allows motd colors
- if(!tag->getBool("allowmotdcolors"))
- continue;
ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
if (file != this->Files.end())
- InspIRCd::ProcessColors(file->second);
-
- file = this->Files.find(tag->getString("rules", "rules"));
- if (file != this->Files.end())
- InspIRCd::ProcessColors(file->second);
+ InspIRCd::ProcessColors(file->second);
}
/* No old configuration -> initial boot, nothing more to do here */
@@ -813,20 +638,14 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
ApplyModules(user);
if (user)
- user->SendText(":%s NOTICE %s :*** Successfully rehashed server.",
- ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
+ user->WriteRemoteNotice("*** Successfully rehashed server.");
ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
}
void ServerConfig::ApplyModules(User* user)
{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
-
- const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
std::vector<std::string> added_modules;
- std::set<std::string> removed_modules(v.begin(), v.end());
+ ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
ConfigTagList tags = ConfTags("module");
for(ConfigIter i = tags.first; i != tags.second; ++i)
@@ -835,6 +654,7 @@ void ServerConfig::ApplyModules(User* user)
std::string name;
if (tag->readString("name", name))
{
+ name = ModuleManager::ExpandModName(name);
// if this module is already loaded, the erase will succeed, so we need do nothing
// otherwise, we need to add the module (which will be done later)
if (removed_modules.erase(name) == 0)
@@ -842,58 +662,54 @@ void ServerConfig::ApplyModules(User* user)
}
}
- if (ConfValue("options")->getBool("allowhalfop") && removed_modules.erase("m_halfop.so") == 0)
- added_modules.push_back("m_halfop.so");
-
- for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
+ for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
{
- // Don't remove cmd_*.so, just remove m_*.so
- if (removing->c_str()[0] == 'c')
+ const std::string& modname = i->first;
+ // Don't remove core_*.so, just remove m_*.so
+ if (InspIRCd::Match(modname, "core_*.so", ascii_case_insensitive_map))
continue;
- Module* m = ServerInstance->Modules->Find(*removing);
- if (m && ServerInstance->Modules->Unload(m))
+ if (ServerInstance->Modules->Unload(i->second))
{
- ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
if (user)
- user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
+ user->WriteNumeric(RPL_UNLOADEDMODULE, modname, InspIRCd::Format("Module %s successfully unloaded.", modname.c_str()));
else
- ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
}
else
{
if (user)
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s %s :Failed to unload module %s: %s",user->nick.c_str(), removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, modname, InspIRCd::Format("Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str()));
else
- ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
}
}
for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
{
- if (ServerInstance->Modules->Load(adding->c_str()))
+ // Skip modules which are already loaded.
+ if (ServerInstance->Modules->Find(*adding))
+ continue;
+
+ if (ServerInstance->Modules->Load(*adding))
{
ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
if (user)
- user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, *adding, InspIRCd::Format("Module %s successfully loaded.", adding->c_str()));
else
ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
}
else
{
if (user)
- user->WriteNumeric(ERR_CANTLOADMODULE, "%s %s :Failed to load module %s: %s",user->nick.c_str(), adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, *adding, InspIRCd::Format("Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str()));
else
ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
}
}
}
-bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
-{
- return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
-}
-
ConfigTag* ServerConfig::ConfValue(const std::string &tag)
{
ConfigTagList found = config_data.equal_range(tag);
@@ -902,7 +718,7 @@ ConfigTag* ServerConfig::ConfValue(const std::string &tag)
ConfigTag* rv = found.first->second;
found.first++;
if (found.first != found.second)
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
"(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
return rv;
}
@@ -912,35 +728,28 @@ ConfigTagList ServerConfig::ConfTags(const std::string& tag)
return config_data.equal_range(tag);
}
-bool ServerConfig::FileExists(const char* file)
+std::string ServerConfig::Escape(const std::string& str, bool xml)
{
- struct stat sb;
- if (stat(file, &sb) == -1)
- return false;
-
- if ((sb.st_mode & S_IFDIR) > 0)
- return false;
-
- FILE *input = fopen(file, "r");
- if (input == NULL)
- return false;
- else
+ std::string escaped;
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
{
- fclose(input);
- return true;
+ switch (*it)
+ {
+ case '"':
+ escaped += xml ? "&quot;" : "\"";
+ break;
+ case '&':
+ escaped += xml ? "&amp;" : "&";
+ break;
+ case '\\':
+ escaped += xml ? "\\" : "\\\\";
+ break;
+ default:
+ escaped += *it;
+ break;
+ }
}
-}
-
-const char* ServerConfig::CleanFilename(const char* name)
-{
- const char* p = name + strlen(name);
- while ((p != name) && (*p != '/') && (*p != '\\')) p--;
- return (p != name ? ++p : p);
-}
-
-const std::string& ServerConfig::GetSID()
-{
- return sid;
+ return escaped;
}
void ConfigReaderThread::Run()
@@ -952,7 +761,7 @@ void ConfigReaderThread::Run()
void ConfigReaderThread::Finish()
{
ServerConfig* old = ServerInstance->Config;
- ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
+ ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
ServerInstance->Config = this->Config;
Config->Apply(old, TheUserUID);
@@ -964,15 +773,33 @@ void ConfigReaderThread::Finish()
* XXX: The order of these is IMPORTANT, do not reorder them without testing
* thoroughly!!!
*/
- ServerInstance->Users->RehashCloneCounts();
+ ServerInstance->Users.RehashCloneCounts();
ServerInstance->XLines->CheckELines();
ServerInstance->XLines->ApplyLines();
- ServerInstance->Res->Rehash();
- ServerInstance->ResetMaxBans();
- Config->ApplyDisabledCommands(Config->DisabledCommands);
+ Config->ApplyDisabledCommands();
User* user = ServerInstance->FindNick(TheUserUID);
- FOREACH_MOD(I_OnRehash, OnRehash(user));
- ServerInstance->BuildISupport();
+
+ ConfigStatus status(user);
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+ {
+ try
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Rehashing " + i->first);
+ i->second->ReadConfig(status);
+ }
+ catch (CoreException& modex)
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modex.GetReason());
+ if (user)
+ user->WriteNotice(i->first + ": " + modex.GetReason());
+ }
+ }
+
+ // The description of this server may have changed - update it for WHOIS etc.
+ ServerInstance->FakeClient->server->description = Config->ServerDesc;
+
+ ServerInstance->ISupport.Build();
ServerInstance->Logs->CloseLogs();
ServerInstance->Logs->OpenFileLogs();
diff --git a/src/coremods/core_channel/cmd_invite.cpp b/src/coremods/core_channel/cmd_invite.cpp
new file mode 100644
index 000000000..1b480aa20
--- /dev/null
+++ b/src/coremods/core_channel/cmd_invite.cpp
@@ -0,0 +1,185 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+#include "invite.h"
+
+CommandInvite::CommandInvite(Module* parent, Invite::APIImpl& invapiimpl)
+ : Command(parent, "INVITE", 0, 0)
+ , invapi(invapiimpl)
+{
+ Penalty = 4;
+ syntax = "[<nick> <channel>]";
+}
+
+/** Handle /INVITE
+ */
+CmdResult CommandInvite::Handle(User* user, const Params& parameters)
+{
+ ModResult MOD_RESULT;
+
+ if (parameters.size() >= 2)
+ {
+ User* u;
+ if (IS_LOCAL(user))
+ u = ServerInstance->FindNickOnly(parameters[0]);
+ else
+ u = ServerInstance->FindNick(parameters[0]);
+
+ Channel* c = ServerInstance->FindChan(parameters[1]);
+ time_t timeout = 0;
+ if (parameters.size() >= 3)
+ {
+ if (IS_LOCAL(user))
+ timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[2]);
+ else if (parameters.size() > 3)
+ timeout = ConvToInt(parameters[3]);
+ }
+
+ if (!c)
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[1]));
+ return CMD_FAILURE;
+ }
+ if ((!u) || (u->registered != REG_ALL))
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
+ }
+
+ // Verify channel timestamp if the INVITE is coming from a remote server
+ if (!IS_LOCAL(user))
+ {
+ // Remote INVITE commands must carry a channel timestamp
+ if (parameters.size() < 3)
+ return CMD_INVALID;
+
+ // Drop the invite if our channel TS is lower
+ time_t RemoteTS = ConvToInt(parameters[2]);
+ if (c->age < RemoteTS)
+ return CMD_FAILURE;
+ }
+
+ if ((IS_LOCAL(user)) && (!c->HasUser(user)))
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel!");
+ return CMD_FAILURE;
+ }
+
+ if (c->HasUser(u))
+ {
+ user->WriteNumeric(ERR_USERONCHANNEL, u->nick, c->name, "is already on channel");
+ return CMD_FAILURE;
+ }
+
+ FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout));
+
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ return CMD_FAILURE;
+ }
+ else if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if (IS_LOCAL(user))
+ {
+ unsigned int rank = c->GetPrefixValue(user);
+ if (rank < HALFOP_VALUE)
+ {
+ // Check whether halfop mode is available and phrase error message accordingly
+ ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator",
+ (mh && mh->name == "halfop" ? "half-" : "")));
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ LocalUser* const localtargetuser = IS_LOCAL(u);
+ if (localtargetuser)
+ {
+ invapi.Create(localtargetuser, c, timeout);
+ ClientProtocol::Messages::Invite invitemsg(user, localtargetuser, c);
+ localtargetuser->Send(ServerInstance->GetRFCEvents().invite, invitemsg);
+ }
+
+ if (IS_LOCAL(user))
+ {
+ user->WriteNumeric(RPL_INVITING, u->nick, c->name);
+ if (u->IsAway())
+ user->WriteNumeric(RPL_AWAY, u->nick, u->awaymsg);
+ }
+
+ char prefix = 0;
+ unsigned int minrank = 0;
+ switch (announceinvites)
+ {
+ case Invite::ANNOUNCE_OPS:
+ {
+ prefix = '@';
+ minrank = OP_VALUE;
+ break;
+ }
+ case Invite::ANNOUNCE_DYNAMIC:
+ {
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+ if ((mh) && (mh->name == "halfop"))
+ {
+ prefix = mh->GetPrefix();
+ minrank = mh->GetPrefixRank();
+ }
+ break;
+ }
+ default:
+ {
+ }
+ }
+
+ CUList excepts;
+ FOREACH_MOD(OnUserInvite, (user, u, c, timeout, minrank, excepts));
+
+ if (announceinvites != Invite::ANNOUNCE_NONE)
+ {
+ excepts.insert(user);
+ ClientProtocol::Messages::Privmsg privmsg(ServerInstance->FakeClient, c, InspIRCd::Format("*** %s invited %s into the channel", user->nick.c_str(), u->nick.c_str()), MSG_NOTICE);
+ c->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, prefix, excepts);
+ }
+ }
+ else if (IS_LOCAL(user))
+ {
+ // pinched from ircu - invite with not enough parameters shows channels
+ // youve been invited to but haven't joined yet.
+ const Invite::List* list = invapi.GetList(IS_LOCAL(user));
+ if (list)
+ {
+ for (Invite::List::const_iterator i = list->begin(); i != list->end(); ++i)
+ user->WriteNumeric(RPL_INVITELIST, (*i)->chan->name);
+ }
+ user->WriteNumeric(RPL_ENDOFINVITELIST, "End of INVITE list");
+ }
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandInvite::GetRouting(User* user, const Params& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_channel/cmd_join.cpp b/src/coremods/core_channel/cmd_join.cpp
new file mode 100644
index 000000000..2caed9dc6
--- /dev/null
+++ b/src/coremods/core_channel/cmd_join.cpp
@@ -0,0 +1,60 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandJoin::CommandJoin(Module* parent)
+ : SplitCommand(parent, "JOIN", 1, 2)
+{
+ syntax = "<channel>{,<channel>} {<key>{,<key>}}";
+ Penalty = 2;
+}
+
+/** Handle /JOIN
+ */
+CmdResult CommandJoin::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ if (parameters.size() > 1)
+ {
+ if (CommandParser::LoopCall(user, this, parameters, 0, 1, false))
+ return CMD_SUCCESS;
+
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ Channel::JoinUser(user, parameters[0], false, parameters[1]);
+ return CMD_SUCCESS;
+ }
+ }
+ else
+ {
+ if (CommandParser::LoopCall(user, this, parameters, 0, -1, false))
+ return CMD_SUCCESS;
+
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ Channel::JoinUser(user, parameters[0]);
+ return CMD_SUCCESS;
+ }
+ }
+
+ user->WriteNumeric(ERR_BADCHANMASK, parameters[0], "Invalid channel name");
+ return CMD_FAILURE;
+}
diff --git a/src/coremods/core_channel/cmd_kick.cpp b/src/coremods/core_channel/cmd_kick.cpp
new file mode 100644
index 000000000..755c6613b
--- /dev/null
+++ b/src/coremods/core_channel/cmd_kick.cpp
@@ -0,0 +1,133 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandKick::CommandKick(Module* parent)
+ : Command(parent, "KICK", 2, 3)
+{
+ syntax = "<channel> <nick>{,<nick>} [<reason>]";
+}
+
+/** Handle /KICK
+ */
+CmdResult CommandKick::Handle(User* user, const Params& parameters)
+{
+ Channel* c = ServerInstance->FindChan(parameters[0]);
+ User* u;
+
+ if (CommandParser::LoopCall(user, this, parameters, 1))
+ return CMD_SUCCESS;
+
+ if (IS_LOCAL(user))
+ u = ServerInstance->FindNickOnly(parameters[1]);
+ else
+ u = ServerInstance->FindNick(parameters[1]);
+
+ if (!c)
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+ }
+ if ((!u) || (u->registered != REG_ALL))
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[1]));
+ return CMD_FAILURE;
+ }
+
+ Membership* srcmemb = NULL;
+ if (IS_LOCAL(user))
+ {
+ srcmemb = c->GetUser(user);
+ if (!srcmemb)
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, parameters[0], "You're not on that channel!");
+ return CMD_FAILURE;
+ }
+
+ if (u->server->IsULine())
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, "You may not kick a u-lined client");
+ return CMD_FAILURE;
+ }
+ }
+
+ const Channel::MemberMap::iterator victimiter = c->userlist.find(u);
+ if (victimiter == c->userlist.end())
+ {
+ user->WriteNumeric(ERR_USERNOTINCHANNEL, u->nick, c->name, "They are not on that channel");
+ return CMD_FAILURE;
+ }
+ Membership* const memb = victimiter->second;
+
+ // KICKs coming from servers can carry a membership id
+ if ((!IS_LOCAL(user)) && (parameters.size() > 3))
+ {
+ // If the current membership id is not equal to the one in the message then the user rejoined
+ if (memb->id != Membership::IdFromString(parameters[2]))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Dropped KICK due to membership id mismatch: " + ConvToStr(memb->id) + " != " + parameters[2]);
+ return CMD_FAILURE;
+ }
+ }
+
+ const bool has_reason = (parameters.size() > 2);
+ const std::string reason((has_reason ? parameters.back() : user->nick), 0, ServerInstance->Config->Limits.MaxKick);
+
+ // Do the following checks only if the KICK is done by a local user;
+ // each server enforces its own rules.
+ if (srcmemb)
+ {
+ // Modules are allowed to explicitly allow or deny kicks done by local users
+ ModResult res;
+ FIRST_MOD_RESULT(OnUserPreKick, res, (user, memb, reason));
+ if (res == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ if (res == MOD_RES_PASSTHRU)
+ {
+ unsigned int them = srcmemb->getRank();
+ unsigned int req = HALFOP_VALUE;
+ for (std::string::size_type i = 0; i < memb->modes.length(); i++)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
+ if (mh && mh->GetLevelRequired(true) > req)
+ req = mh->GetLevelRequired(true);
+ }
+
+ if (them < req)
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator",
+ req > HALFOP_VALUE ? "" : "half-"));
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ c->KickUser(user, victimiter, reason);
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKick::GetRouting(User* user, const Params& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_channel/cmd_names.cpp b/src/coremods/core_channel/cmd_names.cpp
new file mode 100644
index 000000000..28273c903
--- /dev/null
+++ b/src/coremods/core_channel/cmd_names.cpp
@@ -0,0 +1,114 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandNames::CommandNames(Module* parent)
+ : SplitCommand(parent, "NAMES", 0, 0)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , invisiblemode(parent, "invisible")
+{
+ syntax = "{<channel>{,<channel>}}";
+}
+
+/** Handle /NAMES
+ */
+CmdResult CommandNames::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ Channel* c;
+
+ if (parameters.empty())
+ {
+ user->WriteNumeric(RPL_ENDOFNAMES, '*', "End of /NAMES list.");
+ return CMD_SUCCESS;
+ }
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ c = ServerInstance->FindChan(parameters[0]);
+ if (c)
+ {
+ // Show the NAMES list if one of the following is true:
+ // - the channel is not secret
+ // - the user doing the /NAMES is inside the channel
+ // - the user doing the /NAMES has the channels/auspex privilege
+
+ // If the user is inside the channel or has privs, instruct SendNames() to show invisible (+i) members
+ bool show_invisible = ((c->HasUser(user)) || (user->HasPrivPermission("channels/auspex")));
+ if ((show_invisible) || (!c->IsModeSet(secretmode)))
+ {
+ SendNames(user, c, show_invisible);
+ return CMD_SUCCESS;
+ }
+ }
+
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+}
+
+void CommandNames::SendNames(LocalUser* user, Channel* chan, bool show_invisible)
+{
+ Numeric::Builder<' '> reply(user, RPL_NAMREPLY, false, chan->name.size() + 3);
+ Numeric::Numeric& numeric = reply.GetNumeric();
+ if (chan->IsModeSet(secretmode))
+ numeric.push(std::string(1, '@'));
+ else if (chan->IsModeSet(privatemode))
+ numeric.push(std::string(1, '*'));
+ else
+ numeric.push(std::string(1, '='));
+
+ numeric.push(chan->name);
+ numeric.push(std::string());
+
+ std::string prefixlist;
+ std::string nick;
+ const Channel::MemberMap& members = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = members.begin(); i != members.end(); ++i)
+ {
+ if ((!show_invisible) && (i->first->IsModeSet(invisiblemode)))
+ {
+ // Member is invisible and we are not supposed to show them
+ continue;
+ }
+
+ Membership* const memb = i->second;
+
+ prefixlist.clear();
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ prefixlist.push_back(prefix);
+ nick = i->first->nick;
+
+ ModResult res;
+ FIRST_MOD_RESULT(OnNamesListItem, res, (user, memb, prefixlist, nick));
+
+ // See if a module wants us to exclude this user from NAMES
+ if (res == MOD_RES_DENY)
+ continue;
+
+ reply.Add(prefixlist, nick);
+ }
+
+ reply.Flush();
+ user->WriteNumeric(RPL_ENDOFNAMES, chan->name, "End of /NAMES list.");
+}
diff --git a/src/coremods/core_channel/cmd_topic.cpp b/src/coremods/core_channel/cmd_topic.cpp
new file mode 100644
index 000000000..0417f1a0c
--- /dev/null
+++ b/src/coremods/core_channel/cmd_topic.cpp
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandTopic::CommandTopic(Module* parent)
+ : SplitCommand(parent, "TOPIC", 1, 2)
+ , exemptionprov(parent)
+ , secretmode(parent, "secret")
+ , topiclockmode(parent, "topiclock")
+{
+ syntax = "<channel> [<topic>]";
+ Penalty = 2;
+}
+
+CmdResult CommandTopic::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ Channel* c = ServerInstance->FindChan(parameters[0]);
+ if (!c)
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+ }
+
+ if (parameters.size() == 1)
+ {
+ if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(c->name));
+ return CMD_FAILURE;
+ }
+
+ if (c->topic.length())
+ {
+ Topic::ShowTopic(user, c);
+ }
+ else
+ {
+ user->WriteNumeric(RPL_NOTOPICSET, c->name, "No topic is set.");
+ }
+ return CMD_SUCCESS;
+ }
+
+ std::string t = parameters[1]; // needed, in case a module wants to change it
+ ModResult res;
+ FIRST_MOD_RESULT(OnPreTopicChange, res, (user,c,t));
+
+ if (res == MOD_RES_DENY)
+ return CMD_FAILURE;
+ if (res != MOD_RES_ALLOW)
+ {
+ if (!c->HasUser(user))
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel!");
+ return CMD_FAILURE;
+ }
+ if (c->IsModeSet(topiclockmode))
+ {
+ ModResult MOD_RESULT = CheckExemption::Call(exemptionprov, user, c, "topiclock");
+ if (!MOD_RESULT.check(c->GetPrefixValue(user) >= HALFOP_VALUE))
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, "You do not have access to change the topic on this channel");
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ // Make sure the topic is not longer than the limit in the config
+ if (t.length() > ServerInstance->Config->Limits.MaxTopic)
+ t.erase(ServerInstance->Config->Limits.MaxTopic);
+
+ // Only change if the new topic is different than the current one
+ if (c->topic != t)
+ c->SetTopic(user, t, ServerInstance->Time());
+ return CMD_SUCCESS;
+}
+
+void Topic::ShowTopic(LocalUser* user, Channel* chan)
+{
+ user->WriteNumeric(RPL_TOPIC, chan->name, chan->topic);
+ user->WriteNumeric(RPL_TOPICTIME, chan->name, chan->setby, (unsigned long)chan->topicset);
+}
diff --git a/src/modes/cmode_k.cpp b/src/coremods/core_channel/cmode_k.cpp
index 400333fce..4fc29e04c 100644
--- a/src/modes/cmode_k.cpp
+++ b/src/coremods/core_channel/cmode_k.cpp
@@ -21,79 +21,69 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_k.h"
+#include "core_channel.h"
-ModeChannelKey::ModeChannelKey() : ModeHandler(NULL, "key", 'k', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-}
-
-void ModeChannelKey::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- /** +k needs a parameter when being removed,
- * so we have a special-case RemoveMode here for it
- */
-
- if (channel->IsModeSet('k'))
- {
- if (stack)
- {
- stack->Push('k', channel->GetModeParameter('k'));
- }
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-k");
- parameters.push_back(channel->GetModeParameter('k'));
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
+const std::string::size_type ModeChannelKey::maxkeylen = 32;
-void ModeChannelKey::RemoveMode(User*, irc::modestacker* stack)
+ModeChannelKey::ModeChannelKey(Module* Creator)
+ : ParamMode<ModeChannelKey, LocalStringExt>(Creator, "key", 'k', PARAM_ALWAYS)
{
}
ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
{
- bool exists = channel->IsModeSet('k');
+ const std::string* key = ext.get(channel);
+ bool exists = (key != NULL);
if (IS_LOCAL(source))
{
if (exists == adding)
return MODEACTION_DENY;
- if (exists && (parameter != channel->GetModeParameter('k')))
+ if (exists && (parameter != *key))
{
/* Key is currently set and the correct key wasnt given */
return MODEACTION_DENY;
}
} else {
- if (exists && adding && parameter == channel->GetModeParameter('k'))
+ if (exists && adding && parameter == *key)
{
/* no-op, don't show */
return MODEACTION_DENY;
}
}
- /* invalid keys */
- if (!parameter.length())
- return MODEACTION_DENY;
-
- if (parameter.rfind(' ') != std::string::npos)
- return MODEACTION_DENY;
-
if (adding)
{
- std::string ckey;
- ckey.assign(parameter, 0, 32);
- parameter = ckey;
- channel->SetModeParam('k', parameter);
+ // When joining a channel multiple keys are delimited with a comma so we strip
+ // them out here to avoid creating channels that are unjoinable.
+ size_t commapos;
+ while ((commapos = parameter.find(',')) != std::string::npos)
+ parameter.erase(commapos, 1);
+
+ // Truncate the parameter to the maximum key length.
+ if (parameter.length() > maxkeylen)
+ parameter.erase(maxkeylen);
+
+ // If the password is empty here then it only consisted of commas. This is not
+ // acceptable so we reject the mode change.
+ if (parameter.empty())
+ return MODEACTION_DENY;
+
+ ext.set(channel, parameter);
}
else
- {
- channel->SetModeParam('k', "");
- }
+ ext.unset(channel);
+
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
+
+void ModeChannelKey::SerializeParam(Channel* chan, const std::string* key, std::string& out)
+{
+ out += *key;
+}
+
+ModeAction ModeChannelKey::OnSet(User* source, Channel* chan, std::string& param)
+{
+ // Dummy function, never called
+ return MODEACTION_DENY;
+}
diff --git a/src/modes/cmode_l.cpp b/src/coremods/core_channel/cmode_l.cpp
index 001d058bb..e71eb500e 100644
--- a/src/modes/cmode_l.cpp
+++ b/src/coremods/core_channel/cmode_l.cpp
@@ -20,12 +20,11 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_l.h"
+#include "core_channel.h"
-ModeChannelLimit::ModeChannelLimit() : ParamChannelModeHandler(NULL, "limit", 'l')
+ModeChannelLimit::ModeChannelLimit(Module* Creator)
+ : ParamMode<ModeChannelLimit, LocalIntExt>(Creator, "limit", 'l')
+ , minlimit(0)
{
}
@@ -35,13 +34,17 @@ bool ModeChannelLimit::ResolveModeConflict(std::string &their_param, const std::
return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
}
-bool ModeChannelLimit::ParamValidate(std::string &parameter)
+ModeAction ModeChannelLimit::OnSet(User* user, Channel* chan, std::string& parameter)
{
- int limit = atoi(parameter.c_str());
+ size_t limit = ConvToNum<size_t>(parameter);
+ if (limit < minlimit)
+ return MODEACTION_DENY;
- if (limit < 0)
- return false;
+ ext.set(chan, limit);
+ return MODEACTION_ALLOW;
+}
- parameter = ConvToStr(limit);
- return true;
+void ModeChannelLimit::SerializeParam(Channel* chan, intptr_t n, std::string& out)
+{
+ out += ConvToStr(n);
}
diff --git a/src/coremods/core_channel/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp
new file mode 100644
index 000000000..4e49ba2b4
--- /dev/null
+++ b/src/coremods/core_channel/core_channel.cpp
@@ -0,0 +1,352 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+#include "invite.h"
+#include "listmode.h"
+
+namespace
+{
+/** Hook that sends a MODE after a JOIN if the user in the JOIN has some modes prefix set.
+ * This happens e.g. when modules such as operprefix explicitly set prefix modes on the joining
+ * user, or when a member with prefix modes does a host cycle.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+ ClientProtocol::Messages::Mode modemsg;
+ Modes::ChangeList modechangelist;
+ const User* joininguser;
+
+ public:
+ /** If true, MODE changes after JOIN will be sourced from the user, rather than the server
+ */
+ bool modefromuser;
+
+ JoinHook(Module* mod)
+ : ClientProtocol::EventHook(mod, "JOIN")
+ {
+ }
+
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
+ {
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ const Membership& memb = *join.GetMember();
+
+ modechangelist.clear();
+ for (std::string::const_iterator i = memb.modes.begin(); i != memb.modes.end(); ++i)
+ {
+ PrefixMode* const pm = ServerInstance->Modes.FindPrefixMode(*i);
+ if (!pm)
+ continue; // Shouldn't happen
+ modechangelist.push_add(pm, memb.user->nick);
+ }
+
+ if (modechangelist.empty())
+ {
+ // Member got no modes on join
+ joininguser = NULL;
+ return;
+ }
+
+ joininguser = memb.user;
+
+ // Prepare a mode protocol event that we can append to the message list in OnPreEventSend()
+ modemsg.SetParams(memb.chan, NULL, modechangelist);
+ if (modefromuser)
+ modemsg.SetSource(join);
+ else
+ modemsg.SetSourceUser(ServerInstance->FakeClient);
+ }
+
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+ {
+ // If joininguser is NULL then they didn't get any modes on join, skip.
+ // Also don't show their own modes to them, they get that in the NAMES list not via MODE.
+ if ((joininguser) && (user != joininguser))
+ messagelist.push_back(&modemsg);
+ return MOD_RES_PASSTHRU;
+ }
+};
+
+}
+
+class CoreModChannel : public Module, public CheckExemption::EventListener
+{
+ Invite::APIImpl invapi;
+ CommandInvite cmdinvite;
+ CommandJoin cmdjoin;
+ CommandKick cmdkick;
+ CommandNames cmdnames;
+ CommandTopic cmdtopic;
+ Events::ModuleEventProvider evprov;
+ JoinHook joinhook;
+
+ ModeChannelBan banmode;
+ SimpleChannelModeHandler inviteonlymode;
+ ModeChannelKey keymode;
+ ModeChannelLimit limitmode;
+ SimpleChannelModeHandler moderatedmode;
+ SimpleChannelModeHandler noextmsgmode;
+ ModeChannelOp opmode;
+ SimpleChannelModeHandler privatemode;
+ SimpleChannelModeHandler secretmode;
+ SimpleChannelModeHandler topiclockmode;
+ ModeChannelVoice voicemode;
+
+ insp::flat_map<std::string, char> exemptions;
+
+ ModResult IsInvited(User* user, Channel* chan)
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (invapi.IsInvited(localuser, chan)))
+ return MOD_RES_ALLOW;
+ return MOD_RES_PASSTHRU;
+ }
+
+ public:
+ CoreModChannel()
+ : CheckExemption::EventListener(this)
+ , invapi(this)
+ , cmdinvite(this, invapi)
+ , cmdjoin(this)
+ , cmdkick(this)
+ , cmdnames(this)
+ , cmdtopic(this)
+ , evprov(this, "event/channel")
+ , joinhook(this)
+ , banmode(this)
+ , inviteonlymode(this, "inviteonly", 'i')
+ , keymode(this)
+ , limitmode(this)
+ , moderatedmode(this, "moderated", 'm')
+ , noextmsgmode(this, "noextmsg", 'n')
+ , opmode(this)
+ , privatemode(this, "private", 'p')
+ , secretmode(this, "secret", 's')
+ , topiclockmode(this, "topiclock", 't')
+ , voicemode(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
+ Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan };
+ if (optionstag->getBool("invitebypassmodes", true))
+ ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation));
+ else
+ {
+ for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++)
+ ServerInstance->Modules.Detach(events[i], this);
+ }
+
+ joinhook.modefromuser = optionstag->getBool("cyclehostsfromuser");
+
+ std::string current;
+ irc::spacesepstream defaultstream(optionstag->getString("exemptchanops"));
+ insp::flat_map<std::string, char> exempts;
+ while (defaultstream.GetToken(current))
+ {
+ std::string::size_type pos = current.find(':');
+ if (pos == std::string::npos || (pos + 2) > current.size())
+ throw ModuleException("Invalid exemptchanops value '" + current + "' at " + optionstag->getTagLocation());
+
+ const std::string restriction = current.substr(0, pos);
+ const char prefix = current[pos + 1];
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Exempting prefix %c from %s", prefix, restriction.c_str());
+ exempts[restriction] = prefix;
+ }
+ exemptions.swap(exempts);
+
+ ConfigTag* securitytag = ServerInstance->Config->ConfValue("security");
+ const std::string announceinvites = securitytag->getString("announceinvites", "dynamic");
+ if (stdalgo::string::equalsci(announceinvites, "none"))
+ cmdinvite.announceinvites = Invite::ANNOUNCE_NONE;
+ else if (stdalgo::string::equalsci(announceinvites, "all"))
+ cmdinvite.announceinvites = Invite::ANNOUNCE_ALL;
+ else if (stdalgo::string::equalsci(announceinvites, "ops"))
+ cmdinvite.announceinvites = Invite::ANNOUNCE_OPS;
+ else if (stdalgo::string::equalsci(announceinvites, "dynamic"))
+ cmdinvite.announceinvites = Invite::ANNOUNCE_DYNAMIC;
+ else
+ throw ModuleException(announceinvites + " is an invalid <security:announceinvites> value, at " + securitytag->getTagLocation());
+
+ // In 2.0 we allowed limits of 0 to be set. This is non-standard behaviour
+ // and will be removed in the next major release.
+ limitmode.minlimit = optionstag->getBool("allowzerolimit", true) ? 0 : 1;
+
+ banmode.DoRehash();
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["KEYLEN"] = ConvToStr(ModeChannelKey::maxkeylen);
+
+ // Build a map of limits to their mode character.
+ insp::flat_map<int, std::string> limits;
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator iter = listmodes.begin(); iter != listmodes.end(); ++iter)
+ {
+ const unsigned int limit = (*iter)->GetLowerLimit();
+ limits[limit].push_back((*iter)->GetModeChar());
+ }
+
+ // Generate the MAXLIST token from the limits map.
+ std::string& buffer = tokens["MAXLIST"];
+ for (insp::flat_map<int, std::string>::const_iterator iter = limits.begin(); iter != limits.end(); ++iter)
+ {
+ if (!buffer.empty())
+ buffer.push_back(',');
+
+ buffer.append(iter->second);
+ buffer.push_back(':');
+ buffer.append(ConvToStr(iter->first));
+ }
+ }
+
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string&, std::string&, const std::string& keygiven) CXX11_OVERRIDE
+ {
+ if (!chan)
+ return MOD_RES_PASSTHRU;
+
+ // Check whether the channel key is correct.
+ const std::string ckey = chan->GetModeParameter(&keymode);
+ if (!ckey.empty())
+ {
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, keygiven));
+ if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, keygiven)))
+ {
+ // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
+ user->WriteNumeric(ERR_BADCHANNELKEY, chan->name, "Cannot join channel (Incorrect channel key)");
+ return MOD_RES_DENY;
+ }
+ }
+
+ // Check whether the invite only mode is set.
+ if (chan->IsModeSet(inviteonlymode))
+ {
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
+ if (MOD_RESULT != MOD_RES_ALLOW)
+ {
+ user->WriteNumeric(ERR_INVITEONLYCHAN, chan->name, "Cannot join channel (Invite only)");
+ return MOD_RES_DENY;
+ }
+ }
+
+ // Check whether the limit would be exceeded by this user joining.
+ if (chan->IsModeSet(limitmode))
+ {
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
+ if (!MOD_RESULT.check(chan->GetUserCounter() < static_cast<size_t>(limitmode.ext.get(chan))))
+ {
+ user->WriteNumeric(ERR_CHANNELISFULL, chan->name, "Cannot join channel (Channel is full)");
+ return MOD_RES_DENY;
+ }
+ }
+
+ // Everything looks okay.
+ return MOD_RES_PASSTHRU;
+ }
+
+ void OnPostJoin(Membership* memb) CXX11_OVERRIDE
+ {
+ Channel* const chan = memb->chan;
+ LocalUser* const localuser = IS_LOCAL(memb->user);
+ if (localuser)
+ {
+ // Remove existing invite, if any
+ invapi.Remove(localuser, chan);
+
+ if (chan->topic.length())
+ Topic::ShowTopic(localuser, chan);
+
+ // Show all members of the channel, including invisible (+i) users
+ cmdnames.SendNames(localuser, chan, true);
+ }
+ }
+
+ ModResult OnCheckKey(User* user, Channel* chan, const std::string& keygiven) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckLimit(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook always runs
+ return IsInvited(user, chan);
+ }
+
+ void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ invapi.RemoveAll(user);
+ }
+
+ void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
+ {
+ // Make sure the channel won't appear in invite lists from now on, don't wait for cull to unset the ext
+ invapi.RemoveAll(chan);
+ }
+
+ ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) CXX11_OVERRIDE
+ {
+ if (!exemptions.count(restriction))
+ return MOD_RES_PASSTHRU;
+
+ unsigned int mypfx = chan->GetPrefixValue(user);
+ char minmode = exemptions[restriction];
+
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(minmode);
+ if (mh && mypfx >= mh->GetPrefixRank())
+ return MOD_RES_ALLOW;
+ if (mh || minmode == '*')
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ ServerInstance->Modules.SetPriority(this, I_OnPostJoin, PRIORITY_FIRST);
+ ServerInstance->Modules.SetPriority(this, I_OnUserPreJoin, PRIORITY_LAST);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the INVITE, JOIN, KICK, NAMES, and TOPIC commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModChannel)
diff --git a/src/coremods/core_channel/core_channel.h b/src/coremods/core_channel/core_channel.h
new file mode 100644
index 000000000..59a417790
--- /dev/null
+++ b/src/coremods/core_channel/core_channel.h
@@ -0,0 +1,219 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+#include "listmode.h"
+#include "modules/exemption.h"
+
+namespace Topic
+{
+ void ShowTopic(LocalUser* user, Channel* chan);
+}
+
+namespace Invite
+{
+ class APIImpl;
+
+ /** Used to indicate who we announce invites to on a channel. */
+ enum AnnounceState
+ {
+ /** Don't send invite announcements. */
+ ANNOUNCE_NONE,
+
+ /** Send invite announcements to all users. */
+ ANNOUNCE_ALL,
+
+ /** Send invite announcements to channel operators and higher. */
+ ANNOUNCE_OPS,
+
+ /** Send invite announcements to channel half-operators (if available) and higher. */
+ ANNOUNCE_DYNAMIC
+ };
+}
+
+/** Handle /INVITE.
+ */
+class CommandInvite : public Command
+{
+ Invite::APIImpl& invapi;
+
+ public:
+ Invite::AnnounceState announceinvites;
+
+ /** Constructor for invite.
+ */
+ CommandInvite(Module* parent, Invite::APIImpl& invapiimpl);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /JOIN.
+ */
+class CommandJoin : public SplitCommand
+{
+ public:
+ /** Constructor for join.
+ */
+ CommandJoin(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /TOPIC.
+ */
+class CommandTopic : public SplitCommand
+{
+ CheckExemption::EventProvider exemptionprov;
+ ChanModeReference secretmode;
+ ChanModeReference topiclockmode;
+
+ public:
+ /** Constructor for topic.
+ */
+ CommandTopic(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /NAMES.
+ */
+class CommandNames : public SplitCommand
+{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference invisiblemode;
+
+ public:
+ /** Constructor for names.
+ */
+ CommandNames(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+
+ /** Spool the NAMES list for a given channel to the given user
+ * @param user User to spool the NAMES list to
+ * @param chan Channel whose nicklist to send
+ * @param show_invisible True to show invisible (+i) members to the user, false to omit them from the list
+ */
+ void SendNames(LocalUser* user, Channel* chan, bool show_invisible);
+};
+
+/** Handle /KICK.
+ */
+class CommandKick : public Command
+{
+ public:
+ /** Constructor for kick.
+ */
+ CommandKick(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Channel mode +b
+ */
+class ModeChannelBan : public ListModeBase
+{
+ public:
+ ModeChannelBan(Module* Creator)
+ : ListModeBase(Creator, "ban", 'b', "End of channel ban list", 367, 368, true, "maxbans")
+ {
+ }
+};
+
+/** Channel mode +k
+ */
+class ModeChannelKey : public ParamMode<ModeChannelKey, LocalStringExt>
+{
+ public:
+ static const std::string::size_type maxkeylen;
+ ModeChannelKey(Module* Creator);
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
+ void SerializeParam(Channel* chan, const std::string* key, std::string& out) ;
+ ModeAction OnSet(User* source, Channel* chan, std::string& param) CXX11_OVERRIDE;
+};
+
+/** Channel mode +l
+ */
+class ModeChannelLimit : public ParamMode<ModeChannelLimit, LocalIntExt>
+{
+ public:
+ size_t minlimit;
+ ModeChannelLimit(Module* Creator);
+ bool ResolveModeConflict(std::string& their_param, const std::string& our_param, Channel* channel) CXX11_OVERRIDE;
+ void SerializeParam(Channel* chan, intptr_t n, std::string& out);
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE;
+};
+
+/** Channel mode +o
+ */
+class ModeChannelOp : public PrefixMode
+{
+ public:
+ ModeChannelOp(Module* Creator)
+ : PrefixMode(Creator, "op", 'o', OP_VALUE, '@')
+ {
+ ranktoset = ranktounset = OP_VALUE;
+ }
+};
+
+/** Channel mode +v
+ */
+class ModeChannelVoice : public PrefixMode
+{
+ public:
+ ModeChannelVoice(Module* Creator)
+ : PrefixMode(Creator, "voice", 'v', VOICE_VALUE, '+')
+ {
+ selfremove = false;
+ ranktoset = ranktounset = HALFOP_VALUE;
+ }
+};
diff --git a/src/coremods/core_channel/invite.cpp b/src/coremods/core_channel/invite.cpp
new file mode 100644
index 000000000..7ac662edc
--- /dev/null
+++ b/src/coremods/core_channel/invite.cpp
@@ -0,0 +1,208 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012, 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "invite.h"
+
+class InviteExpireTimer : public Timer
+{
+ Invite::Invite* const inv;
+
+ bool Tick(time_t currtime) CXX11_OVERRIDE;
+
+ public:
+ InviteExpireTimer(Invite::Invite* invite, time_t timeout);
+};
+
+static Invite::APIImpl* apiimpl;
+
+void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan)
+{
+ apiimpl->Destruct(inv, remove_user, remove_chan);
+}
+
+void UnserializeInvite(LocalUser* user, const std::string& str)
+{
+ apiimpl->Unserialize(user, str);
+}
+
+Invite::APIBase::APIBase(Module* parent)
+ : DataProvider(parent, "core_channel_invite")
+{
+}
+
+Invite::APIImpl::APIImpl(Module* parent)
+ : APIBase(parent)
+ , userext(parent, "invite_user")
+ , chanext(parent, "invite_chan")
+{
+ apiimpl = this;
+}
+
+void Invite::APIImpl::Destruct(Invite* inv, bool remove_user, bool remove_chan)
+{
+ Store<LocalUser>* ustore = userext.get(inv->user);
+ if (ustore)
+ {
+ ustore->invites.erase(inv);
+ if ((remove_user) && (ustore->invites.empty()))
+ userext.unset(inv->user);
+ }
+
+ Store<Channel>* cstore = chanext.get(inv->chan);
+ if (cstore)
+ {
+ cstore->invites.erase(inv);
+ if ((remove_chan) && (cstore->invites.empty()))
+ chanext.unset(inv->chan);
+ }
+
+ delete inv;
+}
+
+bool Invite::APIImpl::Remove(LocalUser* user, Channel* chan)
+{
+ Invite* inv = Find(user, chan);
+ if (inv)
+ {
+ Destruct(inv);
+ return true;
+ }
+ return false;
+}
+
+void Invite::APIImpl::Create(LocalUser* user, Channel* chan, time_t timeout)
+{
+ if ((timeout != 0) && (ServerInstance->Time() >= timeout))
+ // Expired, don't bother
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): user=%s chan=%s timeout=%lu", user->uuid.c_str(), chan->name.c_str(), (unsigned long)timeout);
+
+ Invite* inv = Find(user, chan);
+ if (inv)
+ {
+ // We only ever extend invites, so nothing to do if the existing one is not timed
+ if (!inv->IsTimed())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): changing expiration in %p", (void*) inv);
+ if (timeout == 0)
+ {
+ // Convert timed invite to non-expiring
+ delete inv->expiretimer;
+ inv->expiretimer = NULL;
+ }
+ else if (inv->expiretimer->GetTrigger() >= ServerInstance->Time() + timeout)
+ {
+ // New expiration time is further than the current, extend the expiration
+ inv->expiretimer->SetInterval(timeout - ServerInstance->Time());
+ }
+ }
+ else
+ {
+ inv = new Invite(user, chan);
+ if (timeout)
+ {
+ inv->expiretimer = new InviteExpireTimer(inv, timeout - ServerInstance->Time());
+ ServerInstance->Timers.AddTimer(inv->expiretimer);
+ }
+
+ userext.get(user, true)->invites.push_front(inv);
+ chanext.get(chan, true)->invites.push_front(inv);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): created new Invite %p", (void*) inv);
+ }
+}
+
+Invite::Invite* Invite::APIImpl::Find(LocalUser* user, Channel* chan)
+{
+ const List* list = APIImpl::GetList(user);
+ if (!list)
+ return NULL;
+
+ for (List::iterator i = list->begin(); i != list->end(); ++i)
+ {
+ Invite* inv = *i;
+ if (inv->chan == chan)
+ return inv;
+ }
+
+ return NULL;
+}
+
+const Invite::List* Invite::APIImpl::GetList(LocalUser* user)
+{
+ Store<LocalUser>* list = userext.get(user);
+ if (list)
+ return &list->invites;
+ return NULL;
+}
+
+void Invite::APIImpl::Unserialize(LocalUser* user, const std::string& value)
+{
+ irc::spacesepstream ss(value);
+ for (std::string channame, exptime; (ss.GetToken(channame) && ss.GetToken(exptime)); )
+ {
+ Channel* chan = ServerInstance->FindChan(channame);
+ if (chan)
+ Create(user, chan, ConvToInt(exptime));
+ }
+}
+
+Invite::Invite::Invite(LocalUser* u, Channel* c)
+ : user(u)
+ , chan(c)
+ , expiretimer(NULL)
+{
+}
+
+Invite::Invite::~Invite()
+{
+ delete expiretimer;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::~ %p", (void*) this);
+}
+
+void Invite::Invite::Serialize(SerializeFormat format, bool show_chans, std::string& out)
+{
+ if (show_chans)
+ out.append(this->chan->name);
+ else
+ out.append((format == FORMAT_USER) ? user->nick : user->uuid);
+ out.push_back(' ');
+
+ if (expiretimer)
+ out.append(ConvToStr(expiretimer->GetTrigger()));
+ else
+ out.push_back('0');
+ out.push_back(' ');
+}
+
+InviteExpireTimer::InviteExpireTimer(Invite::Invite* invite, time_t timeout)
+ : Timer(timeout)
+ , inv(invite)
+{
+}
+
+bool InviteExpireTimer::Tick(time_t currtime)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "InviteExpireTimer::Tick(): expired %p", (void*) inv);
+ apiimpl->Destruct(inv);
+ return false;
+}
diff --git a/src/coremods/core_channel/invite.h b/src/coremods/core_channel/invite.h
new file mode 100644
index 000000000..19e3861f8
--- /dev/null
+++ b/src/coremods/core_channel/invite.h
@@ -0,0 +1,127 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "modules/invite.h"
+
+namespace Invite
+{
+ template<typename T>
+ struct Store
+ {
+ typedef insp::intrusive_list<Invite, T> List;
+
+ /** List of pending Invites
+ */
+ List invites;
+ };
+
+ template<typename T, ExtensionItem::ExtensibleType ExtType>
+ class ExtItem;
+
+ class APIImpl;
+}
+
+extern void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan);
+extern void UnserializeInvite(LocalUser* user, const std::string& value);
+
+template<typename T, ExtensionItem::ExtensibleType ExtType>
+class Invite::ExtItem : public ExtensionItem
+{
+ public:
+ ExtItem(Module* owner, const char* extname)
+ : ExtensionItem(extname, ExtType, owner)
+ {
+ }
+
+ Store<T>* get(Extensible* ext, bool create = false)
+ {
+ Store<T>* store = static_cast<Store<T>*>(get_raw(ext));
+ if ((create) && (!store))
+ {
+ store = new Store<T>;
+ set_raw(ext, store);
+ }
+ return store;
+ }
+
+ void unset(Extensible* ext)
+ {
+ void* store = unset_raw(ext);
+ if (store)
+ free(ext, store);
+ }
+
+ void free(Extensible* container, void* item) CXX11_OVERRIDE
+ {
+ Store<T>* store = static_cast<Store<T>*>(item);
+ for (typename Store<T>::List::iterator i = store->invites.begin(); i != store->invites.end(); )
+ {
+ Invite* inv = *i;
+ // Destructing the Invite invalidates the iterator, so move it now
+ ++i;
+ RemoveInvite(inv, (ExtType != ExtensionItem::EXT_USER), (ExtType == ExtensionItem::EXT_USER));
+ }
+
+ delete store;
+ }
+
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
+ {
+ if (format == FORMAT_NETWORK)
+ return std::string();
+
+ std::string ret;
+ Store<T>* store = static_cast<Store<T>*>(item);
+ for (typename insp::intrusive_list<Invite, T>::iterator i = store->invites.begin(); i != store->invites.end(); ++i)
+ {
+ Invite* inv = *i;
+ inv->Serialize(format, (ExtType == ExtensionItem::EXT_USER), ret);
+ }
+ if (!ret.empty())
+ ret.erase(ret.length()-1);
+ return ret;
+ }
+
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
+ {
+ if ((ExtType != ExtensionItem::EXT_CHANNEL) && (format != FORMAT_NETWORK))
+ UnserializeInvite(static_cast<LocalUser*>(container), value);
+ }
+};
+
+class Invite::APIImpl : public APIBase
+{
+ ExtItem<LocalUser, ExtensionItem::EXT_USER> userext;
+ ExtItem<Channel, ExtensionItem::EXT_CHANNEL> chanext;
+
+ public:
+ APIImpl(Module* owner);
+
+ void Create(LocalUser* user, Channel* chan, time_t timeout) CXX11_OVERRIDE;
+ Invite* Find(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+ bool Remove(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+ const List* GetList(LocalUser* user) CXX11_OVERRIDE;
+
+ void RemoveAll(LocalUser* user) { userext.unset(user); }
+ void RemoveAll(Channel* chan) { chanext.unset(chan); }
+ void Destruct(Invite* inv, bool remove_chan = true, bool remove_user = true);
+ void Unserialize(LocalUser* user, const std::string& value);
+};
diff --git a/src/coremods/core_dns.cpp b/src/coremods/core_dns.cpp
new file mode 100644
index 000000000..002686bc2
--- /dev/null
+++ b/src/coremods/core_dns.cpp
@@ -0,0 +1,850 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2013 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "modules/dns.h"
+#include <iostream>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Iphlpapi.h>
+#pragma comment(lib, "Iphlpapi.lib")
+#endif
+
+namespace DNS
+{
+ /** Maximum value of a dns request id, 16 bits wide, 0xFFFF.
+ */
+ const unsigned int MAX_REQUEST_ID = 0xFFFF;
+}
+
+using namespace DNS;
+
+/** A full packet sent or recieved to/from the nameserver
+ */
+class Packet : public Query
+{
+ void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
+ {
+ if (pos + name.length() + 2 > output_size)
+ throw Exception("Unable to pack name");
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Packing name " + name);
+
+ irc::sepstream sep(name, '.');
+ std::string token;
+
+ while (sep.GetToken(token))
+ {
+ output[pos++] = token.length();
+ memcpy(&output[pos], token.data(), token.length());
+ pos += token.length();
+ }
+
+ output[pos++] = 0;
+ }
+
+ std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ std::string name;
+ unsigned short pos_ptr = pos, lowest_ptr = input_size;
+ bool compressed = false;
+
+ if (pos_ptr >= input_size)
+ throw Exception("Unable to unpack name - no input");
+
+ while (input[pos_ptr] > 0)
+ {
+ unsigned short offset = input[pos_ptr];
+
+ if (offset & POINTER)
+ {
+ if ((offset & POINTER) != POINTER)
+ throw Exception("Unable to unpack name - bogus compression header");
+ if (pos_ptr + 1 >= input_size)
+ throw Exception("Unable to unpack name - bogus compression header");
+
+ /* Place pos at the second byte of the first (farthest) compression pointer */
+ if (compressed == false)
+ {
+ ++pos;
+ compressed = true;
+ }
+
+ pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
+
+ /* Pointers can only go back */
+ if (pos_ptr >= lowest_ptr)
+ throw Exception("Unable to unpack name - bogus compression pointer");
+ lowest_ptr = pos_ptr;
+ }
+ else
+ {
+ if (pos_ptr + offset + 1 >= input_size)
+ throw Exception("Unable to unpack name - offset too large");
+ if (!name.empty())
+ name += ".";
+ for (unsigned i = 1; i <= offset; ++i)
+ name += input[pos_ptr + i];
+
+ pos_ptr += offset + 1;
+ if (compressed == false)
+ /* Move up pos */
+ pos = pos_ptr;
+ }
+ }
+
+ /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
+ ++pos;
+
+ if (name.empty())
+ throw Exception("Unable to unpack name - no name");
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unpack name " + name);
+
+ return name;
+ }
+
+ Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ Question q;
+
+ q.name = this->UnpackName(input, input_size, pos);
+
+ if (pos + 4 > input_size)
+ throw Exception("Unable to unpack question");
+
+ q.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
+ pos += 2;
+
+ // Skip over query class code
+ pos += 2;
+
+ return q;
+ }
+
+ ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
+
+ if (pos + 6 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
+ pos += 4;
+
+ uint16_t rdlength = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ switch (record.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 4 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ irc::sockets::sockaddrs addrs;
+ memset(&addrs, 0, sizeof(addrs));
+
+ addrs.in4.sin_family = AF_INET;
+ addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
+ pos += 4;
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 16 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ irc::sockets::sockaddrs addrs;
+ memset(&addrs, 0, sizeof(addrs));
+
+ addrs.in6.sin6_family = AF_INET6;
+ for (int j = 0; j < 16; ++j)
+ addrs.in6.sin6_addr.s6_addr[j] = input[pos + j];
+ pos += 16;
+
+ record.rdata = addrs.addr();
+
+ break;
+ }
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ record.rdata = this->UnpackName(input, input_size, pos);
+ if (!InspIRCd::IsHost(record.rdata))
+ throw Exception("Invalid name"); // XXX: Causes the request to time out
+
+ break;
+ }
+ case QUERY_TXT:
+ {
+ if (pos + rdlength > input_size)
+ throw Exception("Unable to unpack txt resource record");
+
+ record.rdata = std::string(reinterpret_cast<const char *>(input + pos), rdlength);
+ pos += rdlength;
+
+ if (record.rdata.find_first_of("\r\n\0", 0, 3) != std::string::npos)
+ throw Exception("Invalid character in txt record");
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!record.name.empty() && !record.rdata.empty())
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, record.name + " -> " + record.rdata);
+
+ return record;
+ }
+
+ public:
+ static const int POINTER = 0xC0;
+ static const int LABEL = 0x3F;
+ static const int HEADER_LENGTH = 12;
+
+ /* ID for this packet */
+ RequestId id;
+ /* Flags on the packet */
+ unsigned short flags;
+
+ Packet() : id(0), flags(0)
+ {
+ }
+
+ void Fill(const unsigned char* input, const unsigned short len)
+ {
+ if (len < HEADER_LENGTH)
+ throw Exception("Unable to fill packet");
+
+ unsigned short packet_pos = 0;
+
+ this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
+
+ if (qdcount != 1)
+ throw Exception("Question count != 1 in incoming packet");
+
+ this->question = this->UnpackQuestion(input, len, packet_pos);
+
+ for (unsigned i = 0; i < ancount; ++i)
+ this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
+ }
+
+ unsigned short Pack(unsigned char* output, unsigned short output_size)
+ {
+ if (output_size < HEADER_LENGTH)
+ throw Exception("Unable to pack packet");
+
+ unsigned short pos = 0;
+
+ output[pos++] = this->id >> 8;
+ output[pos++] = this->id & 0xFF;
+ output[pos++] = this->flags >> 8;
+ output[pos++] = this->flags & 0xFF;
+ output[pos++] = 0; // Question count, high byte
+ output[pos++] = 1; // Question count, low byte
+ output[pos++] = 0; // Answer count, high byte
+ output[pos++] = 0; // Answer count, low byte
+ output[pos++] = 0;
+ output[pos++] = 0;
+ output[pos++] = 0;
+ output[pos++] = 0;
+
+ {
+ Question& q = this->question;
+
+ if (q.type == QUERY_PTR)
+ {
+ irc::sockets::sockaddrs ip;
+ irc::sockets::aptosa(q.name, 0, ip);
+
+ if (q.name.find(':') != std::string::npos)
+ {
+ static const char* const hex = "0123456789abcdef";
+ char reverse_ip[128];
+ unsigned reverse_ip_count = 0;
+ for (int j = 15; j >= 0; --j)
+ {
+ reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF];
+ reverse_ip[reverse_ip_count++] = '.';
+ reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4];
+ reverse_ip[reverse_ip_count++] = '.';
+ }
+ reverse_ip[reverse_ip_count++] = 0;
+
+ q.name = reverse_ip;
+ q.name += "ip6.arpa";
+ }
+ else
+ {
+ unsigned long forward = ip.in4.sin_addr.s_addr;
+ ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
+
+ q.name = ip.addr() + ".in-addr.arpa";
+ }
+ }
+
+ this->PackName(output, output_size, pos, q.name);
+
+ if (pos + 4 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ short s = htons(q.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ // Query class, always IN
+ output[pos++] = 0;
+ output[pos++] = 1;
+ }
+
+ return pos;
+ }
+};
+
+class MyManager : public Manager, public Timer, public EventHandler
+{
+ typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
+ cache_map cache;
+
+ irc::sockets::sockaddrs myserver;
+ bool unloading;
+
+ /** Maximum number of entries in cache
+ */
+ static const unsigned int MAX_CACHE_SIZE = 1000;
+
+ static bool IsExpired(const Query& record, time_t now = ServerInstance->Time())
+ {
+ const ResourceRecord& req = record.answers[0];
+ return (req.created + static_cast<time_t>(req.ttl) < now);
+ }
+
+ /** Check the DNS cache to see if request can be handled by a cached result
+ * @return true if a cached result was found.
+ */
+ bool CheckCache(DNS::Request* req, const DNS::Question& question)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "cache: Checking cache for " + question.name);
+
+ cache_map::iterator it = this->cache.find(question);
+ if (it == this->cache.end())
+ return false;
+
+ Query& record = it->second;
+ if (IsExpired(record))
+ {
+ this->cache.erase(it);
+ return false;
+ }
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: Using cached result for " + question.name);
+ record.cached = true;
+ req->OnLookupComplete(&record);
+ return true;
+ }
+
+ /** Add a record to the dns cache
+ * @param r The record
+ */
+ void AddCache(Query& r)
+ {
+ if (cache.size() >= MAX_CACHE_SIZE)
+ cache.clear();
+
+ // Determine the lowest TTL value and use that as the TTL of the cache entry
+ unsigned int cachettl = UINT_MAX;
+ for (std::vector<ResourceRecord>::const_iterator i = r.answers.begin(); i != r.answers.end(); ++i)
+ {
+ const ResourceRecord& rr = *i;
+ if (rr.ttl < cachettl)
+ cachettl = rr.ttl;
+ }
+
+ cachettl = std::min(cachettl, (unsigned int)5*60);
+ ResourceRecord& rr = r.answers.front();
+ // Set TTL to what we've determined to be the lowest
+ rr.ttl = cachettl;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
+ this->cache[r.question] = r;
+ }
+
+ public:
+ DNS::Request* requests[MAX_REQUEST_ID+1];
+
+ MyManager(Module* c) : Manager(c), Timer(5*60, true)
+ , unloading(false)
+ {
+ for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+ requests[i] = NULL;
+ ServerInstance->Timers.AddTimer(this);
+ }
+
+ ~MyManager()
+ {
+ // Ensure Process() will fail for new requests
+ unloading = true;
+
+ for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+ {
+ DNS::Request* request = requests[i];
+ if (!request)
+ continue;
+
+ Query rr(request->question);
+ rr.error = ERROR_UNKNOWN;
+ request->OnError(&rr);
+
+ delete request;
+ }
+ }
+
+ void Process(DNS::Request* req) CXX11_OVERRIDE
+ {
+ if ((unloading) || (req->creator->dying))
+ throw Exception("Module is being unloaded");
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing request to lookup " + req->question.name + " of type " + ConvToStr(req->question.type) + " to " + this->myserver.addr());
+
+ /* Create an id */
+ unsigned int tries = 0;
+ int id;
+ do
+ {
+ id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID+1);
+
+ if (++tries == DNS::MAX_REQUEST_ID*5)
+ {
+ // If we couldn't find an empty slot this many times, do a sequential scan as a last
+ // resort. If an empty slot is found that way, go on, otherwise throw an exception
+ id = -1;
+ for (unsigned int i = 0; i <= DNS::MAX_REQUEST_ID; i++)
+ {
+ if (!this->requests[i])
+ {
+ id = i;
+ break;
+ }
+ }
+
+ if (id == -1)
+ throw Exception("DNS: All ids are in use");
+
+ break;
+ }
+ }
+ while (this->requests[id]);
+
+ req->id = id;
+ this->requests[req->id] = req;
+
+ Packet p;
+ p.flags = QUERYFLAGS_RD;
+ p.id = req->id;
+ p.question = req->question;
+
+ unsigned char buffer[524];
+ unsigned short len = p.Pack(buffer, sizeof(buffer));
+
+ /* Note that calling Pack() above can actually change the contents of p.question.name, if the query is a PTR,
+ * to contain the value that would be in the DNS cache, which is why this is here.
+ */
+ if (req->use_cache && this->CheckCache(req, p.question))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Using cached result");
+ delete req;
+ return;
+ }
+
+ // Update name in the original request so question checking works for PTR queries
+ req->question.name = p.question.name;
+
+ if (SocketEngine::SendTo(this, buffer, len, 0, this->myserver) != len)
+ throw Exception("DNS: Unable to send query");
+
+ // Add timer for timeout
+ ServerInstance->Timers.AddTimer(req);
+ }
+
+ void RemoveRequest(DNS::Request* req) CXX11_OVERRIDE
+ {
+ if (requests[req->id] == req)
+ requests[req->id] = NULL;
+ }
+
+ std::string GetErrorStr(Error e) CXX11_OVERRIDE
+ {
+ switch (e)
+ {
+ case ERROR_UNLOADED:
+ return "Module is unloading";
+ case ERROR_TIMEDOUT:
+ return "Request timed out";
+ case ERROR_NOT_AN_ANSWER:
+ case ERROR_NONSTANDARD_QUERY:
+ case ERROR_FORMAT_ERROR:
+ case ERROR_MALFORMED:
+ return "Malformed answer";
+ case ERROR_SERVER_FAILURE:
+ case ERROR_NOT_IMPLEMENTED:
+ case ERROR_REFUSED:
+ case ERROR_INVALIDTYPE:
+ return "Nameserver failure";
+ case ERROR_DOMAIN_NOT_FOUND:
+ case ERROR_NO_RECORDS:
+ return "Domain not found";
+ case ERROR_NONE:
+ case ERROR_UNKNOWN:
+ default:
+ return "Unknown error";
+ }
+ }
+
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "UDP socket got an error event");
+ }
+
+ void OnEventHandlerRead() CXX11_OVERRIDE
+ {
+ unsigned char buffer[524];
+ irc::sockets::sockaddrs from;
+ socklen_t x = sizeof(from);
+
+ int length = SocketEngine::RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x);
+
+ if (length < Packet::HEADER_LENGTH)
+ return;
+
+ if (myserver != from)
+ {
+ std::string server1 = from.str();
+ std::string server2 = myserver.str();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
+ server1.c_str(), server2.c_str());
+ return;
+ }
+
+ Packet recv_packet;
+ bool valid = false;
+
+ try
+ {
+ recv_packet.Fill(buffer, length);
+ valid = true;
+ }
+ catch (Exception& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
+ }
+
+ // recv_packet.id must be filled in here
+ DNS::Request* request = this->requests[recv_packet.id];
+ if (request == NULL)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer for something we didn't request");
+ return;
+ }
+
+ if (request->question != recv_packet.question)
+ {
+ // This can happen under high latency, drop it silently, do not fail the request
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer that isn't for a question we asked");
+ return;
+ }
+
+ if (!valid)
+ {
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = ERROR_MALFORMED;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.flags & QUERYFLAGS_OPCODE)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received a nonstandard query");
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = ERROR_NONSTANDARD_QUERY;
+ request->OnError(&recv_packet);
+ }
+ else if (!(recv_packet.flags & QUERYFLAGS_QR) || (recv_packet.flags & QUERYFLAGS_RCODE))
+ {
+ Error error = ERROR_UNKNOWN;
+
+ switch (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ case 1:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "format error");
+ error = ERROR_FORMAT_ERROR;
+ break;
+ case 2:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "server error");
+ error = ERROR_SERVER_FAILURE;
+ break;
+ case 3:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "domain not found");
+ error = ERROR_DOMAIN_NOT_FOUND;
+ break;
+ case 4:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "not implemented");
+ error = ERROR_NOT_IMPLEMENTED;
+ break;
+ case 5:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "refused");
+ error = ERROR_REFUSED;
+ break;
+ default:
+ break;
+ }
+
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = error;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.answers.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "No resource records returned");
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = ERROR_NO_RECORDS;
+ request->OnError(&recv_packet);
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Lookup complete for " + request->question.name);
+ ServerInstance->stats.DnsGood++;
+ request->OnLookupComplete(&recv_packet);
+ this->AddCache(recv_packet);
+ }
+
+ ServerInstance->stats.Dns++;
+
+ /* Request's destructor removes it from the request map */
+ delete request;
+ }
+
+ bool Tick(time_t now) CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: purging DNS cache");
+
+ for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
+ {
+ const Query& query = it->second;
+ if (IsExpired(query, now))
+ this->cache.erase(it++);
+ else
+ ++it;
+ }
+ return true;
+ }
+
+ void Rehash(const std::string& dnsserver, std::string sourceaddr, unsigned int sourceport)
+ {
+ if (this->GetFd() > -1)
+ {
+ SocketEngine::Shutdown(this, 2);
+ SocketEngine::Close(this);
+
+ /* Remove expired entries from the cache */
+ this->Tick(ServerInstance->Time());
+ }
+
+ irc::sockets::aptosa(dnsserver, DNS::PORT, myserver);
+
+ /* Initialize mastersocket */
+ int s = socket(myserver.family(), SOCK_DGRAM, 0);
+ this->SetFd(s);
+
+ /* Have we got a socket? */
+ if (this->GetFd() != -1)
+ {
+ SocketEngine::SetReuse(s);
+ SocketEngine::NonBlocking(s);
+
+ irc::sockets::sockaddrs bindto;
+ if (sourceaddr.empty())
+ {
+ // set a sourceaddr for irc::sockets::aptosa() based on the servers af type
+ if (myserver.family() == AF_INET)
+ sourceaddr = "0.0.0.0";
+ else if (myserver.family() == AF_INET6)
+ sourceaddr = "::";
+ }
+ irc::sockets::aptosa(sourceaddr, sourceport, bindto);
+
+ if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
+ {
+ /* Failed to bind */
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error binding dns socket - hostnames will NOT resolve");
+ SocketEngine::Close(this->GetFd());
+ this->SetFd(-1);
+ }
+ else if (!SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Internal error starting DNS - hostnames will NOT resolve.");
+ SocketEngine::Close(this->GetFd());
+ this->SetFd(-1);
+ }
+
+ if (bindto.family() != myserver.family())
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Nameserver address family differs from source address family - hostnames might not resolve");
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
+ }
+ }
+};
+
+class ModuleDNS : public Module
+{
+ MyManager manager;
+ std::string DNSServer;
+ std::string SourceIP;
+ unsigned int SourcePort;
+
+ void FindDNSServer()
+ {
+#ifdef _WIN32
+ // attempt to look up their nameserver from the system
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
+
+ PFIXED_INFO pFixedInfo;
+ DWORD dwBufferSize = sizeof(FIXED_INFO);
+ pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
+
+ if (pFixedInfo)
+ {
+ if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW)
+ {
+ HeapFree(GetProcessHeap(), 0, pFixedInfo);
+ pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
+ }
+
+ if (pFixedInfo)
+ {
+ if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
+ DNSServer = pFixedInfo->DnsServerList.IpAddress.String;
+
+ HeapFree(GetProcessHeap(), 0, pFixedInfo);
+ }
+
+ if (!DNSServer.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
+ return;
+ }
+ }
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
+#else
+ // attempt to look up their nameserver from /etc/resolv.conf
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+
+ std::ifstream resolv("/etc/resolv.conf");
+
+ while (resolv >> DNSServer)
+ {
+ if (DNSServer == "nameserver")
+ {
+ resolv >> DNSServer;
+ if (DNSServer.find_first_not_of("0123456789.") == std::string::npos || DNSServer.find_first_not_of("0123456789ABCDEFabcdef:") == std::string::npos)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
+ return;
+ }
+ }
+ }
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
+#endif
+ DNSServer = "127.0.0.1";
+ }
+
+ public:
+ ModuleDNS() : manager(this)
+ , SourcePort(0)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ std::string oldserver = DNSServer;
+ const std::string oldip = SourceIP;
+ const unsigned int oldport = SourcePort;
+
+ ConfigTag* tag = ServerInstance->Config->ConfValue("dns");
+ DNSServer = tag->getString("server");
+ SourceIP = tag->getString("sourceip");
+ SourcePort = tag->getUInt("sourceport", 0, 0, UINT16_MAX);
+
+ if (DNSServer.empty())
+ FindDNSServer();
+
+ if (oldserver != DNSServer || oldip != SourceIP || oldport != SourcePort)
+ this->manager.Rehash(DNSServer, SourceIP, SourcePort);
+ }
+
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+ {
+ for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+ {
+ DNS::Request* req = this->manager.requests[i];
+ if (!req)
+ continue;
+
+ if (req->creator == mod)
+ {
+ Query rr(req->question);
+ rr.error = ERROR_UNLOADED;
+ req->OnError(&rr);
+
+ delete req;
+ }
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("DNS support", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleDNS)
+
diff --git a/src/coremods/core_hostname_lookup.cpp b/src/coremods/core_hostname_lookup.cpp
new file mode 100644
index 000000000..ec93732b1
--- /dev/null
+++ b/src/coremods/core_hostname_lookup.cpp
@@ -0,0 +1,239 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013-2016 Adam <Adam@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/dns.h"
+
+namespace
+{
+ LocalIntExt* dl;
+ LocalStringExt* ph;
+}
+
+/** Derived from Resolver, and performs user forward/reverse lookups.
+ */
+class UserResolver : public DNS::Request
+{
+ /** UUID we are looking up */
+ const std::string uuid;
+
+ /** True if the lookup is forward, false if is a reverse lookup
+ */
+ const bool fwd;
+
+ public:
+ /** Create a resolver.
+ * @param mgr DNS Manager
+ * @param me this module
+ * @param user The user to begin lookup on
+ * @param to_resolve The IP or host to resolve
+ * @param qt The query type
+ */
+ UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
+ : DNS::Request(mgr, me, to_resolve, qt)
+ , uuid(user->uuid)
+ , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA)
+ {
+ }
+
+ /** Called on successful lookup
+ * if a previous result has already come back.
+ * @param r The finished query
+ */
+ void OnLookupComplete(const DNS::Query* r) CXX11_OVERRIDE
+ {
+ LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+ if (!bound_user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
+ return;
+ }
+
+ const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
+ if (ans_record == NULL)
+ {
+ OnError(r);
+ return;
+ }
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record->name.c_str(), ans_record->rdata.c_str());
+
+ if (!fwd)
+ {
+ // first half of resolution is done. We now need to verify that the host matches.
+ ph->set(bound_user, ans_record->rdata);
+
+ UserResolver* res_forward;
+ if (bound_user->client_sa.family() == AF_INET6)
+ {
+ /* IPV6 forward lookup */
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
+ }
+ else
+ {
+ /* IPV4 lookup */
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
+ }
+ try
+ {
+ this->manager->Process(res_forward);
+ }
+ catch (DNS::Exception& e)
+ {
+ delete res_forward;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
+
+ bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ dl->set(bound_user, 0);
+ }
+ }
+ else
+ {
+ /* Both lookups completed */
+
+ irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
+ bool rev_match = false;
+ if (user_ip->family() == AF_INET6)
+ {
+ struct in6_addr res_bin;
+ if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
+ {
+ rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
+ }
+ }
+ else
+ {
+ struct in_addr res_bin;
+ if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
+ {
+ rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
+ }
+ }
+
+ dl->set(bound_user, 0);
+
+ if (rev_match)
+ {
+ std::string* hostname = ph->get(bound_user);
+
+ if (hostname == NULL)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
+ bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ return;
+ }
+ else if (hostname->length() <= ServerInstance->Config->Limits.MaxHost)
+ {
+ /* Hostnames starting with : are not a good thing (tm) */
+ if ((*hostname)[0] == ':')
+ hostname->insert(0, "0");
+
+ bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
+ bound_user->ChangeRealHost(hostname->substr(0, ServerInstance->Config->Limits.MaxHost), true);
+ }
+ else
+ {
+ bound_user->WriteNotice("*** Your hostname is longer than the maximum of " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ }
+
+ ph->unset(bound_user);
+ }
+ else
+ {
+ bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ }
+ }
+ }
+
+ /** Called on failed lookup
+ * @param query The errored query
+ */
+ void OnError(const DNS::Query* query) CXX11_OVERRIDE
+ {
+ LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+ if (bound_user)
+ {
+ bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
+ dl->set(bound_user, 0);
+ }
+ }
+};
+
+class ModuleHostnameLookup : public Module
+{
+ LocalIntExt dnsLookup;
+ LocalStringExt ptrHosts;
+ dynamic_reference<DNS::Manager> DNS;
+
+ public:
+ ModuleHostnameLookup()
+ : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
+ , ptrHosts("ptrHosts", ExtensionItem::EXT_USER, this)
+ , DNS(this, "DNS")
+ {
+ dl = &dnsLookup;
+ ph = &ptrHosts;
+ }
+
+ void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
+ {
+ if (!DNS || !user->MyClass->resolvehostnames)
+ {
+ user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
+ return;
+ }
+
+ // Clients can't have a DNS hostname if they aren't connected via IPv4 or IPv6.
+ if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
+ {
+ user->WriteNotice("*** Skipping host resolution (connected via a non-IP socket)");
+ return;
+ }
+
+ user->WriteNotice("*** Looking up your hostname...");
+
+ UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
+ try
+ {
+ /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
+ * before Process() completes, which is why dnsLookup.set() is here, before Process()
+ */
+ this->dnsLookup.set(user, 1);
+ this->DNS->Process(res_reverse);
+ }
+ catch (DNS::Exception& e)
+ {
+ this->dnsLookup.set(user, 0);
+ delete res_reverse;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
+ }
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHostnameLookup)
diff --git a/src/coremods/core_info/cmd_admin.cpp b/src/coremods/core_info/cmd_admin.cpp
new file mode 100644
index 000000000..44827555d
--- /dev/null
+++ b/src/coremods/core_info/cmd_admin.cpp
@@ -0,0 +1,43 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandAdmin::CommandAdmin(Module* parent)
+ : ServerTargetCommand(parent, "ADMIN")
+{
+ Penalty = 2;
+ syntax = "[<servername>]";
+}
+
+/** Handle /ADMIN
+ */
+CmdResult CommandAdmin::Handle(User* user, const Params& parameters)
+{
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
+ user->WriteRemoteNumeric(RPL_ADMINME, InspIRCd::Format("Administrative info for %s", ServerInstance->Config->ServerName.c_str()));
+ if (!AdminName.empty())
+ user->WriteRemoteNumeric(RPL_ADMINLOC1, InspIRCd::Format("Name - %s", AdminName.c_str()));
+ user->WriteRemoteNumeric(RPL_ADMINLOC2, InspIRCd::Format("Nickname - %s", AdminNick.c_str()));
+ user->WriteRemoteNumeric(RPL_ADMINEMAIL, InspIRCd::Format("E-Mail - %s", AdminEmail.c_str()));
+ return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_commands.cpp b/src/coremods/core_info/cmd_commands.cpp
new file mode 100644
index 000000000..0a3efdf23
--- /dev/null
+++ b/src/coremods/core_info/cmd_commands.cpp
@@ -0,0 +1,59 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+enum
+{
+ // InspIRCd-specific.
+ RPL_COMMANDS = 700,
+ RPL_COMMANDSEND = 701
+};
+
+CommandCommands::CommandCommands(Module* parent)
+ : Command(parent, "COMMANDS", 0, 0)
+{
+ Penalty = 3;
+}
+
+/** Handle /COMMANDS
+ */
+CmdResult CommandCommands::Handle(User* user, const Params& parameters)
+{
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ std::vector<std::string> list;
+ list.reserve(commands.size());
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ // Don't show S2S commands to users
+ if (i->second->flags_needed == FLAG_SERVERONLY)
+ continue;
+
+ Module* src = i->second->creator;
+ list.push_back(InspIRCd::Format("%s %s %d %d", i->second->name.c_str(), src->ModuleSourceFile.c_str(),
+ i->second->min_params, i->second->Penalty));
+ }
+ std::sort(list.begin(), list.end());
+ for(unsigned int i=0; i < list.size(); i++)
+ user->WriteNumeric(RPL_COMMANDS, list[i]);
+ user->WriteNumeric(RPL_COMMANDSEND, "End of COMMANDS list");
+ return CMD_SUCCESS;
+}
diff --git a/src/commands/cmd_info.cpp b/src/coremods/core_info/cmd_info.cpp
index 76e414b19..54aa7e16e 100644
--- a/src/commands/cmd_info.cpp
+++ b/src/coremods/core_info/cmd_info.cpp
@@ -3,7 +3,7 @@
*
* Copyright (C) 2011 Jackmcbarn <jackmcbarn@jackmcbarn.no-ip.org>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007-2015 Robin Burchell <robin+git@viroteck.net>
* Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
@@ -21,46 +21,24 @@
#include "inspircd.h"
+#include "core_info.h"
-/** Handle /INFO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandInfo : public Command
+CommandInfo::CommandInfo(Module* parent)
+ : ServerTargetCommand(parent, "INFO")
{
- public:
- /** Constructor for info.
- */
- CommandInfo(Module* parent) : Command(parent,"INFO")
- {
- Penalty = 4;
- syntax = "[<servername>]";
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
+ Penalty = 4;
+ syntax = "[<servername>]";
+}
static const char* const lines[] = {
" -/\\- \2InspIRCd\2 -\\/-",
" November 2002 - Present",
" ",
"\2Core Developers\2:",
- " Craig Edwards, Brain, <brain@inspircd.org>",
- " Craig McLure, Craig, <craig@inspircd.org>",
- " Robin Burchell, w00t, <w00t@inspircd.org>",
+ " Attila Molnar, Attila, <attilamolnar@hush.com>",
+ " Peter Powell, SaberUK, <petpow@saberuk.com>",
+ " ",
+ "\2Former Developers\2:",
" Oliver Lupton, Om, <om@inspircd.org>",
" John Brooks, Special, <special@inspircd.org>",
" Dennis Friis, peavey, <peavey@inspircd.org>",
@@ -68,14 +46,14 @@ static const char* const lines[] = {
" Uli Schlachter, psychon, <psychon@inspircd.org>",
" Matt Smith, dz, <dz@inspircd.org>",
" Daniel De Graaf, danieldg, <danieldg@inspircd.org>",
- " jackmcbarn, <jackmcbarn@inspircd.org>",
- " Attila Molnar, Attila, <attilamolnar@hush.com>",
" ",
- "\2Regular Contributors\2:",
- " Adam SaberUK",
+ "\2Founding Developers\2:",
+ " Craig Edwards, Brain, <brain@inspircd.org>",
+ " Craig McLure, Craig, <craig@inspircd.org>",
+ " Robin Burchell, w00t, <w00t@inspircd.org>",
" ",
- "\2Other Contributors\2:",
- " ChrisTX Shawn Shutter",
+ "\2Active Contributors\2:",
+ " Adam",
" ",
"\2Former Contributors\2:",
" dmb Zaba skenmy GreenReaper",
@@ -87,9 +65,11 @@ static const char* const lines[] = {
" Stskeeps ThaPrince BuildSmart Thunderhacker",
" Skip LeaChim Majic MacGyver",
" Namegduf Ankit Phoenix Taros",
+ " jackmcbarn ChrisTX Shawn Shutter",
" ",
"\2Thanks To\2:",
- " searchirc.com irc-junkie.org Brik fraggeln",
+ " Asmo Brik fraggeln genius3000",
+ " Sheogorath",
" ",
" Best experienced with: \2An IRC client\2",
NULL
@@ -97,17 +77,15 @@ static const char* const lines[] = {
/** Handle /INFO
*/
-CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandInfo::Handle(User* user, const Params& parameters)
{
if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
return CMD_SUCCESS;
int i=0;
while (lines[i])
- user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_INFO, user->nick.c_str(), lines[i++]);
- FOREACH_MOD(I_OnInfo,OnInfo(user));
- user->SendText(":%s %03d %s :End of /INFO list", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFINFO, user->nick.c_str());
+ user->WriteRemoteNumeric(RPL_INFO, lines[i++]);
+
+ user->WriteRemoteNumeric(RPL_ENDOFINFO, "End of /INFO list");
return CMD_SUCCESS;
}
-
-COMMAND_INIT(CommandInfo)
diff --git a/src/coremods/core_info/cmd_modules.cpp b/src/coremods/core_info/cmd_modules.cpp
new file mode 100644
index 000000000..7212e9525
--- /dev/null
+++ b/src/coremods/core_info/cmd_modules.cpp
@@ -0,0 +1,85 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+enum
+{
+ // From ircd-ratbox with an InspIRCd-specific format.
+ RPL_MODLIST = 702,
+ RPL_ENDOFMODLIST = 703
+};
+
+CommandModules::CommandModules(Module* parent)
+ : ServerTargetCommand(parent, "MODULES")
+{
+ Penalty = 4;
+ syntax = "[<servername>]";
+}
+
+/** Handle /MODULES
+ */
+CmdResult CommandModules::Handle(User* user, const Params& parameters)
+{
+ // Don't ask remote servers about their modules unless the local user asking is an oper
+ // 2.0 asks anyway, so let's handle that the same way
+ bool for_us = (parameters.empty() || parameters[0] == ServerInstance->Config->ServerName);
+ if ((!for_us) || (!IS_LOCAL(user)))
+ {
+ if (!user->IsOper())
+ {
+ user->WriteNotice("*** You cannot check what modules other servers have loaded.");
+ return CMD_FAILURE;
+ }
+
+ // From an oper and not for us, forward
+ if (!for_us)
+ return CMD_SUCCESS;
+ }
+
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+ {
+ Module* m = i->second;
+ Version V = m->GetVersion();
+
+ if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
+ {
+ std::string flags("VCO");
+ size_t pos = 0;
+ for (int mult = 2; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
+ if (!(V.Flags & mult))
+ flags[pos] = '-';
+
+ std::string srcrev = m->ModuleDLLManager->GetVersion();
+ user->WriteRemoteNumeric(RPL_MODLIST, m->ModuleSourceFile, srcrev.empty() ? "*" : srcrev, flags, V.description);
+ }
+ else
+ {
+ user->WriteRemoteNumeric(RPL_MODLIST, m->ModuleSourceFile, '*', '*', V.description);
+ }
+ }
+ user->WriteRemoteNumeric(RPL_ENDOFMODLIST, "End of MODULES list");
+
+ return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_motd.cpp b/src/coremods/core_info/cmd_motd.cpp
new file mode 100644
index 000000000..cc770b157
--- /dev/null
+++ b/src/coremods/core_info/cmd_motd.cpp
@@ -0,0 +1,63 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandMotd::CommandMotd(Module* parent)
+ : ServerTargetCommand(parent, "MOTD")
+{
+ syntax = "[<servername>]";
+}
+
+/** Handle /MOTD
+ */
+CmdResult CommandMotd::Handle(User* user, const Params& parameters)
+{
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ {
+ // Give extra penalty if a non-oper queries the /MOTD of a remote server
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (!user->IsOper()))
+ localuser->CommandFloodPenalty += 2000;
+ return CMD_SUCCESS;
+ }
+
+ ConfigTag* tag = ServerInstance->Config->EmptyTag;
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ tag = localuser->GetClass()->config;
+ std::string motd_name = tag->getString("motd", "motd");
+ ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
+ if (motd == ServerInstance->Config->Files.end())
+ {
+ user->WriteRemoteNumeric(ERR_NOMOTD, "Message of the day file is missing.");
+ return CMD_SUCCESS;
+ }
+
+ user->WriteRemoteNumeric(RPL_MOTDSTART, InspIRCd::Format("%s message of the day", ServerInstance->Config->ServerName.c_str()));
+
+ for (file_cache::iterator i = motd->second.begin(); i != motd->second.end(); i++)
+ user->WriteRemoteNumeric(RPL_MOTD, InspIRCd::Format("- %s", i->c_str()));
+
+ user->WriteRemoteNumeric(RPL_ENDOFMOTD, "End of message of the day.");
+
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/operquit.cpp b/src/coremods/core_info/cmd_time.cpp
index af2e04ebc..73987cf01 100644
--- a/src/modules/m_spanningtree/operquit.cpp
+++ b/src/coremods/core_info/cmd_time.cpp
@@ -1,7 +1,8 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -18,28 +19,20 @@
#include "inspircd.h"
-#include "xline.h"
-
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "core_info.h"
+CommandTime::CommandTime(Module* parent)
+ : ServerTargetCommand(parent, "TIME")
+{
+ syntax = "[<servername>]";
+}
-bool TreeSocket::OperQuit(const std::string &prefix, parameterlist &params)
+CmdResult CommandTime::Handle(User* user, const Params& parameters)
{
- if (params.size() < 1)
- return true;
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
- User* u = ServerInstance->FindUUID(prefix);
+ user->WriteRemoteNumeric(RPL_TIME, ServerInstance->Config->ServerName, InspIRCd::TimeString(ServerInstance->Time()));
- if ((u) && (!IS_SERVER(u)))
- {
- ServerInstance->OperQuit.set(u, params[0]);
- params[0] = ":" + params[0];
- Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix);
- }
- return true;
+ return CMD_SUCCESS;
}
-
diff --git a/src/modules/spanningtree.h b/src/coremods/core_info/cmd_version.cpp
index 212f35ff3..29d96169e 100644
--- a/src/modules/spanningtree.h
+++ b/src/coremods/core_info/cmd_version.cpp
@@ -2,6 +2,7 @@
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -17,27 +18,23 @@
*/
-#ifndef SPANNINGTREE_H
-#define SPANNINGTREE_H
+#include "inspircd.h"
+#include "core_info.h"
-struct AddServerEvent : public Event
+CommandVersion::CommandVersion(Module* parent)
+ : Command(parent, "VERSION", 0, 0)
{
- const std::string servername;
- AddServerEvent(Module* me, const std::string& name)
- : Event(me, "new_server"), servername(name)
- {
- Send();
- }
-};
+ syntax = "[<servername>]";
+}
-struct DelServerEvent : public Event
+CmdResult CommandVersion::Handle(User* user, const Params& parameters)
{
- const std::string servername;
- DelServerEvent(Module* me, const std::string& name)
- : Event(me, "lost_server"), servername(name)
+ std::string version = ServerInstance->GetVersionString((user->IsOper()));
+ user->WriteNumeric(RPL_VERSION, version);
+ LocalUser *lu = IS_LOCAL(user);
+ if (lu != NULL)
{
- Send();
+ ServerInstance->ISupport.SendTo(lu);
}
-};
-
-#endif
+ return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/core_info.cpp b/src/coremods/core_info/core_info.cpp
new file mode 100644
index 000000000..2d7a89475
--- /dev/null
+++ b/src/coremods/core_info/core_info.cpp
@@ -0,0 +1,159 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+enum
+{
+ // From RFC 2812.
+ RPL_WELCOME = 1,
+ RPL_YOURHOST = 2,
+ RPL_CREATED = 3,
+ RPL_MYINFO = 4
+};
+
+RouteDescriptor ServerTargetCommand::GetRouting(User* user, const Params& parameters)
+{
+ // Parameter must be a server name, not a nickname or uuid
+ if ((!parameters.empty()) && (parameters[0].find('.') != std::string::npos))
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
+
+class CoreModInfo : public Module
+{
+ CommandAdmin cmdadmin;
+ CommandCommands cmdcommands;
+ CommandInfo cmdinfo;
+ CommandModules cmdmodules;
+ CommandMotd cmdmotd;
+ CommandTime cmdtime;
+ CommandVersion cmdversion;
+ Numeric::Numeric numeric004;
+
+ /** Returns a list of user or channel mode characters.
+ * Used for constructing the parts of the mode list in the 004 numeric.
+ * @param mt Controls whether to list user modes or channel modes
+ * @param needparam Return modes only if they require a parameter to be set
+ * @return The available mode letters that satisfy the given conditions
+ */
+ static std::string CreateModeList(ModeType mt, bool needparam = false)
+ {
+ std::string modestr;
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ ModeHandler* mh = ServerInstance->Modes.FindMode(mode, mt);
+ if ((mh) && ((!needparam) || (mh->NeedsParam(true))))
+ modestr.push_back(mode);
+ }
+ return modestr;
+ }
+
+ void OnServiceChange(const ServiceProvider& prov)
+ {
+ if (prov.service != SERVICE_MODE)
+ return;
+
+ std::vector<std::string>& params = numeric004.GetParams();
+ params.erase(params.begin()+2, params.end());
+
+ // Create lists of modes
+ // 1. User modes
+ // 2. Channel modes
+ // 3. Channel modes that require a parameter when set
+ numeric004.push(CreateModeList(MODETYPE_USER));
+ numeric004.push(CreateModeList(MODETYPE_CHANNEL));
+ numeric004.push(CreateModeList(MODETYPE_CHANNEL, true));
+ }
+ public:
+ CoreModInfo()
+ : cmdadmin(this)
+ , cmdcommands(this)
+ , cmdinfo(this)
+ , cmdmodules(this)
+ , cmdmotd(this)
+ , cmdtime(this)
+ , cmdversion(this)
+ , numeric004(RPL_MYINFO)
+ {
+ numeric004.push(ServerInstance->Config->ServerName);
+ numeric004.push(INSPIRCD_BRANCH);
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("admin");
+ cmdadmin.AdminName = tag->getString("name");
+ cmdadmin.AdminEmail = tag->getString("email", "null@example.com");
+ cmdadmin.AdminNick = tag->getString("nick", "admin");
+ }
+
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ user->WriteNumeric(RPL_WELCOME, InspIRCd::Format("Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), user->GetFullRealHost().c_str()));
+ user->WriteNumeric(RPL_YOURHOST, InspIRCd::Format("Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH));
+ user->WriteNumeric(RPL_CREATED, InspIRCd::TimeString(ServerInstance->startup_time, "This server was created %H:%M:%S %b %d %Y"));
+ user->WriteNumeric(numeric004);
+
+ ServerInstance->ISupport.SendTo(user);
+
+ /* Trigger MOTD and LUSERS output, give modules a chance too */
+ ModResult MOD_RESULT;
+ std::string command("LUSERS");
+ CommandBase::Params parameters;
+ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, user, true));
+ if (!MOD_RESULT)
+ ServerInstance->Parser.CallHandler(command, parameters, user);
+
+ MOD_RESULT = MOD_RES_PASSTHRU;
+ command = "MOTD";
+ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, user, true));
+ if (!MOD_RESULT)
+ ServerInstance->Parser.CallHandler(command, parameters, user);
+
+ if (ServerInstance->Config->RawLog)
+ {
+ ClientProtocol::Messages::Privmsg rawlogmsg(ServerInstance->FakeClient, user, "*** Raw I/O logging is enabled on user server. All messages, passwords, and commands are being recorded.");
+ user->Send(ServerInstance->GetRFCEvents().privmsg, rawlogmsg);
+ }
+ }
+
+ void OnServiceAdd(ServiceProvider& service) CXX11_OVERRIDE
+ {
+ OnServiceChange(service);
+ }
+
+ void OnServiceDel(ServiceProvider& service) CXX11_OVERRIDE
+ {
+ OnServiceChange(service);
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ ServerInstance->Modules.SetPriority(this, I_OnUserConnect, PRIORITY_FIRST);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the ADMIN, COMMANDS, INFO, MODULES, MOTD, TIME and VERSION commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModInfo)
diff --git a/src/coremods/core_info/core_info.h b/src/coremods/core_info/core_info.h
new file mode 100644
index 000000000..127041b46
--- /dev/null
+++ b/src/coremods/core_info/core_info.h
@@ -0,0 +1,169 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+/** These commands require no parameters, but if there is a parameter it is a server name where the command will be routed to.
+ */
+class ServerTargetCommand : public Command
+{
+ public:
+ ServerTargetCommand(Module* mod, const std::string& Name)
+ : Command(mod, Name)
+ {
+ }
+
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /ADMIN.
+ */
+class CommandAdmin : public ServerTargetCommand
+{
+ public:
+ /** Holds the admin's name, for output in
+ * the /ADMIN command.
+ */
+ std::string AdminName;
+
+ /** Holds the email address of the admin,
+ * for output in the /ADMIN command.
+ */
+ std::string AdminEmail;
+
+ /** Holds the admin's nickname, for output
+ * in the /ADMIN command
+ */
+ std::string AdminNick;
+
+ /** Constructor for admin.
+ */
+ CommandAdmin(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /COMMANDS.
+ */
+class CommandCommands : public Command
+{
+ public:
+ /** Constructor for commands.
+ */
+ CommandCommands(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /INFO.
+ */
+class CommandInfo : public ServerTargetCommand
+{
+ public:
+ /** Constructor for info.
+ */
+ CommandInfo(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /MODULES.
+ */
+class CommandModules : public ServerTargetCommand
+{
+ public:
+ /** Constructor for modules.
+ */
+ CommandModules(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /MOTD.
+ */
+class CommandMotd : public ServerTargetCommand
+{
+ public:
+ /** Constructor for motd.
+ */
+ CommandMotd(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /TIME.
+ */
+class CommandTime : public ServerTargetCommand
+{
+ public:
+ /** Constructor for time.
+ */
+ CommandTime(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /VERSION.
+ */
+class CommandVersion : public Command
+{
+ public:
+ /** Constructor for version.
+ */
+ CommandVersion(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
diff --git a/src/coremods/core_ison.cpp b/src/coremods/core_ison.cpp
new file mode 100644
index 000000000..3f6b1ac74
--- /dev/null
+++ b/src/coremods/core_ison.cpp
@@ -0,0 +1,97 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /ISON.
+ */
+class CommandIson : public SplitCommand
+{
+ public:
+ /** Constructor for ison.
+ */
+ CommandIson(Module* parent)
+ : SplitCommand(parent, "ISON", 1)
+ {
+ syntax = "<nick> {nick}";
+ }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+class IsonReplyBuilder : public Numeric::Builder<' ', true>
+{
+ public:
+ IsonReplyBuilder(LocalUser* user)
+ : Numeric::Builder<' ', true>(user, RPL_ISON)
+ {
+ }
+
+ void AddNick(const std::string& nickname)
+ {
+ User* const user = ServerInstance->FindNickOnly(nickname);
+ if ((user) && (user->registered == REG_ALL))
+ Add(user->nick);
+ }
+};
+
+/** Handle /ISON
+ */
+CmdResult CommandIson::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ IsonReplyBuilder reply(user);
+
+ for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end()-1; ++i)
+ {
+ const std::string& targetstr = *i;
+ reply.AddNick(targetstr);
+ }
+
+ // Last parameter can be a space separated list
+ irc::spacesepstream ss(parameters.back());
+ for (std::string token; ss.GetToken(token); )
+ reply.AddNick(token);
+
+ reply.Flush();
+ return CMD_SUCCESS;
+}
+
+class CoreModIson : public Module
+{
+ private:
+ CommandIson cmd;
+
+ public:
+ CoreModIson()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the ISON command", VF_CORE | VF_VENDOR);
+ }
+};
+
+MODULE_INIT(CoreModIson)
diff --git a/src/coremods/core_list.cpp b/src/coremods/core_list.cpp
new file mode 100644
index 000000000..f03fbbe5e
--- /dev/null
+++ b/src/coremods/core_list.cpp
@@ -0,0 +1,215 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /LIST.
+ */
+class CommandList : public Command
+{
+ private:
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+
+ /** Parses the creation time or topic set time out of a LIST parameter.
+ * @param value The parameter containing a minute count.
+ * @return The UNIX time at \p value minutes ago.
+ */
+ time_t ParseMinutes(const std::string& value)
+ {
+ time_t minutes = ConvToNum<time_t>(value.c_str() + 2);
+ if (!minutes)
+ return 0;
+ return ServerInstance->Time() - (minutes * 60);
+ }
+
+ public:
+ /** Constructor for list.
+ */
+ CommandList(Module* parent)
+ : Command(parent,"LIST", 0, 0)
+ , secretmode(creator, "secret")
+ , privatemode(creator, "private")
+ {
+ Penalty = 5;
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+
+/** Handle /LIST
+ */
+CmdResult CommandList::Handle(User* user, const Params& parameters)
+{
+ // C: Searching based on creation time, via the "C<val" and "C>val" modifiers
+ // to search for a channel creation time that is lower or higher than val
+ // respectively.
+ time_t mincreationtime = 0;
+ time_t maxcreationtime = 0;
+
+ // M: Searching based on mask.
+ // N: Searching based on !mask.
+ bool match_name_topic = false;
+ bool match_inverted = false;
+ const char* match = NULL;
+
+ // T: Searching based on topic time, via the "T<val" and "T>val" modifiers to
+ // search for a topic time that is lower or higher than val respectively.
+ time_t mintopictime = 0;
+ time_t maxtopictime = 0;
+
+ // U: Searching based on user count within the channel, via the "<val" and
+ // ">val" modifiers to search for a channel that has less than or more than
+ // val users respectively.
+ size_t minusers = 0;
+ size_t maxusers = 0;
+
+ if ((parameters.size() == 1) && (!parameters[0].empty()))
+ {
+ if (parameters[0][0] == '<')
+ {
+ maxusers = ConvToNum<size_t>(parameters[0].c_str() + 1);
+ }
+ else if (parameters[0][0] == '>')
+ {
+ minusers = ConvToNum<size_t>(parameters[0].c_str() + 1);
+ }
+ else if (!parameters[0].compare(0, 2, "C<", 2))
+ {
+ mincreationtime = ParseMinutes(parameters[0]);
+ }
+ else if (!parameters[0].compare(0, 2, "C>", 2))
+ {
+ maxcreationtime = ParseMinutes(parameters[0]);
+ }
+ else if (!parameters[0].compare(0, 2, "T<", 2))
+ {
+ mintopictime = ParseMinutes(parameters[0]);
+ }
+ else if (!parameters[0].compare(0, 2, "T>", 2))
+ {
+ maxtopictime = ParseMinutes(parameters[0]);
+ }
+ else
+ {
+ // If the glob is prefixed with ! it is inverted.
+ match = parameters[0].c_str();
+ if (match[0] == '!')
+ {
+ match_inverted = true;
+ match += 1;
+ }
+
+ // Ensure that the user didn't just run "LIST !".
+ if (match[0])
+ match_name_topic = true;
+ }
+ }
+
+ const bool has_privs = user->HasPrivPermission("channels/auspex");
+
+ user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ Channel* const chan = i->second;
+
+ // Check the user count if a search has been specified.
+ const size_t users = chan->GetUserCounter();
+ if ((minusers && users <= minusers) || (maxusers && users >= maxusers))
+ continue;
+
+ // Check the creation ts if a search has been specified.
+ const time_t creationtime = chan->age;
+ if ((mincreationtime && creationtime <= mincreationtime) || (maxcreationtime && creationtime >= maxcreationtime))
+ continue;
+
+ // Check the topic ts if a search has been specified.
+ const time_t topictime = chan->topicset;
+ if ((mintopictime && (!topictime || topictime <= mintopictime)) || (maxtopictime && (!topictime || topictime >= maxtopictime)))
+ continue;
+
+ // Attempt to match a glob pattern.
+ if (match_name_topic)
+ {
+ bool matches = InspIRCd::Match(chan->name, match) || InspIRCd::Match(chan->topic, match);
+
+ // The user specified an match that we did not match.
+ if (!matches && !match_inverted)
+ continue;
+
+ // The user specified an inverted match that we did match.
+ if (matches && match_inverted)
+ continue;
+ }
+
+ // if the channel is not private/secret, OR the user is on the channel anyway
+ bool n = (has_privs || chan->HasUser(user));
+
+ // If we're not in the channel and +s is set on it, we want to ignore it
+ if ((n) || (!chan->IsModeSet(secretmode)))
+ {
+ if ((!n) && (chan->IsModeSet(privatemode)))
+ {
+ // Channel is private (+p) and user is outside/not privileged
+ user->WriteNumeric(RPL_LIST, '*', users, "");
+ }
+ else
+ {
+ /* User is in the channel/privileged, channel is not +s */
+ user->WriteNumeric(RPL_LIST, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
+ }
+ }
+ }
+ user->WriteNumeric(RPL_LISTEND, "End of channel list.");
+
+ return CMD_SUCCESS;
+}
+
+class CoreModList : public Module
+{
+ private:
+ CommandList cmd;
+
+ public:
+ CoreModList()
+ : cmd(this)
+ {
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["ELIST"] = "CMNTU";
+ tokens["SAFELIST"];
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the LIST command", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModList)
diff --git a/src/coremods/core_loadmodule.cpp b/src/coremods/core_loadmodule.cpp
new file mode 100644
index 000000000..69c6bbcef
--- /dev/null
+++ b/src/coremods/core_loadmodule.cpp
@@ -0,0 +1,125 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /LOADMODULE.
+ */
+class CommandLoadmodule : public Command
+{
+ public:
+ /** Constructor for loadmodule.
+ */
+ CommandLoadmodule ( Module* parent) : Command(parent,"LOADMODULE",1,1) { flags_needed='o'; syntax = "<modulename>"; }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /LOADMODULE
+ */
+CmdResult CommandLoadmodule::Handle(User* user, const Params& parameters)
+{
+ if (ServerInstance->Modules->Load(parameters[0]))
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "NEW MODULE: %s loaded %s",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Module successfully loaded.");
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteNumeric(ERR_CANTLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
+ return CMD_FAILURE;
+ }
+}
+
+/** Handle /UNLOADMODULE.
+ */
+class CommandUnloadmodule : public Command
+{
+ public:
+ /** Constructor for unloadmodule.
+ */
+ CommandUnloadmodule(Module* parent)
+ : Command(parent,"UNLOADMODULE", 1)
+ {
+ flags_needed = 'o';
+ syntax = "<modulename>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+CmdResult CommandUnloadmodule::Handle(User* user, const Params& parameters)
+{
+ if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
+ InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload core commands!");
+ return CMD_FAILURE;
+ }
+
+ Module* m = ServerInstance->Modules->Find(parameters[0]);
+ if (m == creator)
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload module loading commands!");
+ return CMD_FAILURE;
+ }
+
+ if (m && ServerInstance->Modules->Unload(m))
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "MODULE UNLOADED: %s unloaded %s", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_UNLOADEDMODULE, parameters[0], "Module successfully unloaded.");
+ }
+ else
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], (m ? ServerInstance->Modules->LastError() : "No such module"));
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+class CoreModLoadModule : public Module
+{
+ CommandLoadmodule cmdloadmod;
+ CommandUnloadmodule cmdunloadmod;
+
+ public:
+ CoreModLoadModule()
+ : cmdloadmod(this), cmdunloadmod(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the LOADMODULE and UNLOADMODULE commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModLoadModule)
diff --git a/src/commands/cmd_lusers.cpp b/src/coremods/core_lusers.cpp
index 91a718090..39a5dac7f 100644
--- a/src/commands/cmd_lusers.cpp
+++ b/src/coremods/core_lusers.cpp
@@ -26,10 +26,10 @@ struct LusersCounters
unsigned int max_global;
unsigned int invisible;
- LusersCounters()
+ LusersCounters(unsigned int inv)
: max_local(ServerInstance->Users->LocalUserCount())
, max_global(ServerInstance->Users->RegisteredUserCount())
- , invisible(ServerInstance->Users->ModeCount('i'))
+ , invisible(inv)
{
}
@@ -45,10 +45,7 @@ struct LusersCounters
}
};
-/** 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.
+/** Handle /LUSERS.
*/
class CommandLusers : public Command
{
@@ -60,24 +57,23 @@ class CommandLusers : public Command
: Command(parent,"LUSERS",0,0), counters(Counters)
{ }
/** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
+ * @param parameters The parameters to the command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
};
/** Handle /LUSERS
*/
-CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
+CmdResult CommandLusers::Handle(User* user, const Params& parameters)
{
unsigned int n_users = ServerInstance->Users->RegisteredUserCount();
- ProtoServerList serverlist;
+ ProtocolInterface::ServerList serverlist;
ServerInstance->PI->GetServerList(serverlist);
unsigned int n_serv = serverlist.size();
unsigned int n_local_servs = 0;
- for(ProtoServerList::iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+ for (ProtocolInterface::ServerList::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
{
if (i->parentname == ServerInstance->Config->ServerName)
n_local_servs++;
@@ -88,19 +84,19 @@ CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
counters.UpdateMaxUsers();
- user->WriteNumeric(251, "%s :There are %d users and %d invisible on %d servers",user->nick.c_str(),
- n_users - counters.invisible, counters.invisible, n_serv);
+ user->WriteNumeric(RPL_LUSERCLIENT, InspIRCd::Format("There are %d users and %d invisible on %d servers",
+ n_users - counters.invisible, counters.invisible, n_serv));
if (ServerInstance->Users->OperCount())
- user->WriteNumeric(252, "%s %d :operator(s) online",user->nick.c_str(),ServerInstance->Users->OperCount());
+ user->WriteNumeric(RPL_LUSEROP, ServerInstance->Users.OperCount(), "operator(s) online");
if (ServerInstance->Users->UnregisteredUserCount())
- user->WriteNumeric(253, "%s %d :unknown connections",user->nick.c_str(),ServerInstance->Users->UnregisteredUserCount());
+ user->WriteNumeric(RPL_LUSERUNKNOWN, ServerInstance->Users.UnregisteredUserCount(), "unknown connections");
- user->WriteNumeric(254, "%s %ld :channels formed",user->nick.c_str(),ServerInstance->ChannelCount());
- user->WriteNumeric(255, "%s :I have %d clients and %d servers",user->nick.c_str(),ServerInstance->Users->LocalUserCount(),n_local_servs);
- user->WriteNumeric(265, "%s :Current Local Users: %d Max: %d", user->nick.c_str(), ServerInstance->Users->LocalUserCount(), counters.max_local);
- user->WriteNumeric(266, "%s :Current Global Users: %d Max: %d", user->nick.c_str(), n_users, counters.max_global);
+ user->WriteNumeric(RPL_LUSERCHANNELS, ServerInstance->GetChans().size(), "channels formed");
+ user->WriteNumeric(RPL_LUSERME, InspIRCd::Format("I have %d clients and %d servers", ServerInstance->Users.LocalUserCount(), n_local_servs));
+ user->WriteNumeric(RPL_LOCALUSERS, InspIRCd::Format("Current local users: %d Max: %d", ServerInstance->Users.LocalUserCount(), counters.max_local));
+ user->WriteNumeric(RPL_GLOBALUSERS, InspIRCd::Format("Current global users: %d Max: %d", n_users, counters.max_global));
return CMD_SUCCESS;
}
@@ -110,11 +106,11 @@ class InvisibleWatcher : public ModeWatcher
unsigned int& invisible;
public:
InvisibleWatcher(Module* mod, unsigned int& Invisible)
- : ModeWatcher(mod, 'i', MODETYPE_USER), invisible(Invisible)
+ : ModeWatcher(mod, "invisible", MODETYPE_USER), invisible(Invisible)
{
}
- void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
+ void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding) CXX11_OVERRIDE
{
if (dest->registered != REG_ALL)
return;
@@ -128,43 +124,47 @@ public:
class ModuleLusers : public Module
{
+ UserModeReference invisiblemode;
LusersCounters counters;
CommandLusers cmd;
InvisibleWatcher mw;
- public:
- ModuleLusers()
- : cmd(this, counters), mw(this, counters.invisible)
+ unsigned int CountInvisible()
{
+ unsigned int c = 0;
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* u = i->second;
+ if (u->IsModeSet(invisiblemode))
+ c++;
+ }
+ return c;
}
- void init()
+ public:
+ ModuleLusers()
+ : invisiblemode(this, "invisible")
+ , counters(CountInvisible())
+ , cmd(this, counters)
+ , mw(this, counters.invisible)
{
- ServerInstance->Modules->AddService(cmd);
- Implementation events[] = { I_OnPostConnect, I_OnUserQuit };
- ServerInstance->Modules->Attach(events, this, sizeof(events)/sizeof(Implementation));
- ServerInstance->Modes->AddModeWatcher(&mw);
}
- void OnPostConnect(User* user)
+ void OnPostConnect(User* user) CXX11_OVERRIDE
{
counters.UpdateMaxUsers();
- if (user->IsModeSet('i'))
+ if (user->IsModeSet(invisiblemode))
counters.invisible++;
}
- void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
{
- if (user->IsModeSet('i'))
+ if (user->IsModeSet(invisiblemode))
counters.invisible--;
}
- ~ModuleLusers()
- {
- ServerInstance->Modes->DelModeWatcher(&mw);
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("LUSERS", VF_VENDOR | VF_CORE);
}
diff --git a/src/coremods/core_oper/cmd_die.cpp b/src/coremods/core_oper/cmd_die.cpp
new file mode 100644
index 000000000..b25fe2407
--- /dev/null
+++ b/src/coremods/core_oper/cmd_die.cpp
@@ -0,0 +1,82 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "exitcodes.h"
+#include "core_oper.h"
+
+CommandDie::CommandDie(Module* parent)
+ : Command(parent, "DIE", 1)
+{
+ flags_needed = 'o';
+ syntax = "<server>";
+}
+
+static void QuitAll()
+{
+ const std::string quitmsg = "Server shutdown";
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ while (!list.empty())
+ ServerInstance->Users.QuitUser(list.front(), quitmsg);
+}
+
+void DieRestart::SendError(const std::string& message)
+{
+ ClientProtocol::Messages::Error errormsg(message);
+ ClientProtocol::Event errorevent(ServerInstance->GetRFCEvents().error, errormsg);
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNotice(message);
+ }
+ else
+ {
+ // Unregistered connections receive ERROR, not a NOTICE
+ user->Send(errorevent);
+ }
+ }
+}
+
+/** Handle /DIE
+ */
+CmdResult CommandDie::Handle(User* user, const Params& parameters)
+{
+ if (DieRestart::CheckPass(user, parameters[0], "diepass"))
+ {
+ {
+ std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, diebuf);
+ DieRestart::SendError(diebuf);
+ }
+
+ QuitAll();
+ ServerInstance->Exit(EXIT_STATUS_DIE);
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed DIE Command from %s.", user->GetFullRealHost().c_str());
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_oper/cmd_kill.cpp b/src/coremods/core_oper/cmd_kill.cpp
new file mode 100644
index 000000000..5572e5789
--- /dev/null
+++ b/src/coremods/core_oper/cmd_kill.cpp
@@ -0,0 +1,154 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandKill::CommandKill(Module* parent)
+ : Command(parent, "KILL", 2, 2)
+ , protoev(parent, name)
+{
+ flags_needed = 'o';
+ syntax = "<nickname> <reason>";
+ TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
+}
+
+class KillMessage : public ClientProtocol::Message
+{
+ public:
+ KillMessage(ClientProtocol::EventProvider& protoev, User* user, LocalUser* target, const std::string& text)
+ : ClientProtocol::Message("KILL", NULL)
+ {
+ if (ServerInstance->Config->HideKillsServer.empty())
+ SetSourceUser(user);
+ else
+ SetSource(ServerInstance->Config->HideKillsServer);
+
+ PushParamRef(target->nick);
+ PushParamRef(text);
+ }
+};
+
+/** Handle /KILL
+ */
+CmdResult CommandKill::Handle(User* user, const Params& parameters)
+{
+ /* Allow comma seperated lists of users for /KILL (thanks w00t) */
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ {
+ // If we got a colon delimited list of nicks then the handler ran for each nick,
+ // and KILL commands were broadcast for remote targets.
+ return CMD_FAILURE;
+ }
+
+ User* target = ServerInstance->FindNick(parameters[0]);
+ if (!target)
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
+ }
+
+ /*
+ * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
+ * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
+ *
+ * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
+ * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
+ */
+
+ if (IS_LOCAL(user))
+ {
+ /*
+ * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
+ * and the other half not. This would be a bad thing. ;p -- w00t
+ */
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, target, parameters[1]));
+
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ killreason = "Killed (";
+ if (!ServerInstance->Config->HideKillsServer.empty())
+ {
+ // hidekills is on, use it
+ killreason += ServerInstance->Config->HideKillsServer;
+ }
+ else
+ {
+ // hidekills is off, do nothing
+ killreason += user->nick;
+ }
+
+ killreason += " (" + parameters[1] + "))";
+ }
+ else
+ {
+ /* Leave it alone, remote server has already formatted it */
+ killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
+ }
+
+ if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
+ {
+ if (IS_LOCAL(user) && IS_LOCAL(target))
+ ServerInstance->SNO->WriteGlobalSno('k', "Local kill by %s: %s (%s)", user->nick.c_str(), target->GetFullRealHost().c_str(), parameters[1].c_str());
+ else
+ ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), target->GetFullRealHost().c_str(), parameters[1].c_str());
+ }
+
+ if (IS_LOCAL(target))
+ {
+ LocalUser* localu = IS_LOCAL(target);
+ KillMessage msg(protoev, user, localu, killreason);
+ ClientProtocol::Event killevent(protoev, msg);
+ localu->Send(killevent);
+
+ this->lastuuid.clear();
+ }
+ else
+ {
+ this->lastuuid = target->uuid;
+ }
+
+ // send the quit out
+ ServerInstance->Users->QuitUser(target, killreason);
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKill::GetRouting(User* user, const Params& parameters)
+{
+ // FindNick() doesn't work here because we quit the target user in Handle() which
+ // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
+ // was for a local user, otherwise it contains the uuid of the user who was killed.
+ if (lastuuid.empty())
+ return ROUTE_LOCALONLY;
+ return ROUTE_BROADCAST;
+}
+
+
+void CommandKill::EncodeParameter(std::string& param, unsigned int index)
+{
+ // Manually translate the nick -> uuid (see above), and also the reason (params[1])
+ // because we decorate it if the oper is local and want remote servers to see the
+ // decorated reason not the original.
+ param = ((index == 0) ? lastuuid : killreason);
+}
diff --git a/src/coremods/core_oper/cmd_oper.cpp b/src/coremods/core_oper/cmd_oper.cpp
new file mode 100644
index 000000000..8c3c86adc
--- /dev/null
+++ b/src/coremods/core_oper/cmd_oper.cpp
@@ -0,0 +1,71 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandOper::CommandOper(Module* parent)
+ : SplitCommand(parent, "OPER", 2, 2)
+{
+ syntax = "<username> <password>";
+}
+
+CmdResult CommandOper::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ bool match_login = false;
+ bool match_pass = false;
+ bool match_hosts = false;
+
+ const std::string userHost = user->ident + "@" + user->GetRealHost();
+ const std::string userIP = user->ident + "@" + user->GetIPString();
+
+ ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+ if (i != ServerInstance->Config->oper_blocks.end())
+ {
+ OperInfo* ifo = i->second;
+ ConfigTag* tag = ifo->oper_block;
+ match_login = true;
+ match_pass = ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
+ match_hosts = InspIRCd::MatchMask(tag->getString("host"), userHost, userIP);
+
+ if (match_pass && match_hosts)
+ {
+ /* found this oper's opertype */
+ user->Oper(ifo);
+ return CMD_SUCCESS;
+ }
+ }
+
+ std::string fields;
+ if (!match_login)
+ fields.append("login ");
+ if (!match_pass)
+ fields.append("password ");
+ if (!match_hosts)
+ fields.append("hosts");
+
+ // tell them they suck, and lag them up to help prevent brute-force attacks
+ user->WriteNumeric(ERR_NOOPERHOST, "Invalid oper credentials");
+ user->CommandFloodPenalty += 10000;
+
+ ServerInstance->SNO->WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': The following fields do not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
+ return CMD_FAILURE;
+}
diff --git a/src/commands/cmd_rehash.cpp b/src/coremods/core_oper/cmd_rehash.cpp
index abf0b7876..e234e54b4 100644
--- a/src/commands/cmd_rehash.cpp
+++ b/src/coremods/core_oper/cmd_rehash.cpp
@@ -20,32 +20,21 @@
#include "inspircd.h"
+#include "core_oper.h"
-/** Handle /REHASH. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandRehash : public Command
+CommandRehash::CommandRehash(Module* parent)
+ : Command(parent, "REHASH", 0)
{
- public:
- /** Constructor for rehash.
- */
- CommandRehash ( Module* parent) : Command(parent,"REHASH",0) { flags_needed = 'o'; Penalty = 2; syntax = "[<servermask>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
+ flags_needed = 'o';
+ Penalty = 2;
+ syntax = "[<servermask>]";
+}
-CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandRehash::Handle(User* user, const Params& parameters)
{
std::string param = parameters.size() ? parameters[0] : "";
- FOREACH_MOD(I_OnPreRehash,OnPreRehash(user, param));
+ FOREACH_MOD(OnPreRehash, (user, param));
if (param.empty())
{
@@ -66,34 +55,27 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
// the leading "-" is optional; remove it if present.
if (param[0] == '-')
- param = param.substr(1);
+ param.erase(param.begin());
- FOREACH_MOD(I_OnModuleRehash,OnModuleRehash(user, param));
+ FOREACH_MOD(OnModuleRehash, (user, param));
return CMD_SUCCESS;
}
// Rehash for me. Try to start the rehash thread
if (!ServerInstance->ConfigThread)
{
- std::string m = user->nick + " is rehashing config file " + ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()) + " on " + ServerInstance->Config->ServerName;
+ std::string m = user->nick + " is rehashing config file " + FileSystem::GetFileName(ServerInstance->ConfigFileName) + " on " + ServerInstance->Config->ServerName;
ServerInstance->SNO->WriteGlobalSno('a', m);
if (IS_LOCAL(user))
- user->WriteNumeric(RPL_REHASHING, "%s %s :Rehashing",
- user->nick.c_str(),ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()));
+ user->WriteNumeric(RPL_REHASHING, FileSystem::GetFileName(ServerInstance->ConfigFileName), "Rehashing");
else
- ServerInstance->PI->SendUserNotice(user, std::string("*** Rehashing server ") +
- ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()));
+ ServerInstance->PI->SendUserNotice(user, "*** Rehashing server " + FileSystem::GetFileName(ServerInstance->ConfigFileName));
/* Don't do anything with the logs here -- logs are restarted
* after the config thread has completed.
*/
- ServerInstance->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
-
-
- ServerInstance->ConfigThread = new ConfigReaderThread(user->uuid);
- ServerInstance->Threads->Start(ServerInstance->ConfigThread);
+ ServerInstance->Rehash(user->uuid);
}
else
{
@@ -102,7 +84,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
* XXX, todo: we should find some way to kill runaway rehashes that are blocking, this is a major problem for unrealircd users
*/
if (IS_LOCAL(user))
- user->WriteServ("NOTICE %s :*** Could not rehash: A rehash is already in progress.", user->nick.c_str());
+ user->WriteNotice("*** Could not rehash: A rehash is already in progress.");
else
ServerInstance->PI->SendUserNotice(user, "*** Could not rehash: A rehash is already in progress.");
}
@@ -110,6 +92,3 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
// Always return success so spanningtree forwards an incoming REHASH even if we failed
return CMD_SUCCESS;
}
-
-
-COMMAND_INIT(CommandRehash)
diff --git a/src/commands/cmd_restart.cpp b/src/coremods/core_oper/cmd_restart.cpp
index 48f902d1e..936348f95 100644
--- a/src/commands/cmd_restart.cpp
+++ b/src/coremods/core_oper/cmd_restart.cpp
@@ -19,32 +19,23 @@
#include "inspircd.h"
+#include "core_oper.h"
-/** Handle /RESTART
- */
-class CommandRestart : public Command
+CommandRestart::CommandRestart(Module* parent)
+ : Command(parent, "RESTART", 1, 1)
{
- public:
- /** Constructor for restart.
- */
- CommandRestart(Module* parent) : Command(parent,"RESTART",1,1) { flags_needed = 'o'; syntax = "<password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
+ flags_needed = 'o';
+ syntax = "<server>";
+}
-CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandRestart::Handle(User* user, const Params& parameters)
{
- ServerInstance->Logs->Log("COMMAND",DEFAULT,"Restart: %s",user->nick.c_str());
- if (!ServerInstance->PassCompare(user, ServerInstance->Config->restartpass, parameters[0].c_str(), ServerInstance->Config->powerhash))
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Restart: %s", user->nick.c_str());
+ if (DieRestart::CheckPass(user, parameters[0], "restartpass"))
{
ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
- ServerInstance->SendError("Server restarting.");
+ DieRestart::SendError("Server restarting.");
#ifndef _WIN32
/* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execvp() below succeeds.
@@ -71,6 +62,3 @@ CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, Us
}
return CMD_FAILURE;
}
-
-
-COMMAND_INIT(CommandRestart)
diff --git a/src/coremods/core_oper/core_oper.cpp b/src/coremods/core_oper/core_oper.cpp
new file mode 100644
index 000000000..a6b2abd81
--- /dev/null
+++ b/src/coremods/core_oper/core_oper.cpp
@@ -0,0 +1,55 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+namespace DieRestart
+{
+ bool CheckPass(User* user, const std::string& inputpass, const char* confentry)
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("power");
+ // The hash method for *BOTH* the die and restart passwords
+ const std::string hash = tag->getString("hash");
+ const std::string correctpass = tag->getString(confentry, ServerInstance->Config->ServerName);
+ return ServerInstance->PassCompare(user, correctpass, inputpass, hash);
+ }
+}
+
+class CoreModOper : public Module
+{
+ CommandDie cmddie;
+ CommandKill cmdkill;
+ CommandOper cmdoper;
+ CommandRehash cmdrehash;
+ CommandRestart cmdrestart;
+
+ public:
+ CoreModOper()
+ : cmddie(this), cmdkill(this), cmdoper(this), cmdrehash(this), cmdrestart(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the DIE, KILL, OPER, REHASH, and RESTART commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModOper)
diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h
new file mode 100644
index 000000000..db8c4161c
--- /dev/null
+++ b/src/coremods/core_oper/core_oper.h
@@ -0,0 +1,130 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+namespace DieRestart
+{
+ /** Checks a die or restart password
+ * @param user The user executing /DIE or /RESTART
+ * @param inputpass The password given by the user
+ * @param confkey The name of the key in the power tag containing the correct password
+ * @return True if the given password was correct, false if it was not
+ */
+ bool CheckPass(User* user, const std::string& inputpass, const char* confkey);
+
+ /** Send an ERROR to unregistered users and a NOTICE to all registered local users
+ * @param message Message to send
+ */
+ void SendError(const std::string& message);
+}
+
+/** Handle /DIE.
+ */
+class CommandDie : public Command
+{
+ public:
+ /** Constructor for die.
+ */
+ CommandDie(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /KILL.
+ */
+class CommandKill : public Command
+{
+ std::string lastuuid;
+ std::string killreason;
+ ClientProtocol::EventProvider protoev;
+
+ public:
+ /** Constructor for kill.
+ */
+ CommandKill(Module* parent);
+
+ /** Handle command.
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+ void EncodeParameter(std::string& param, unsigned int index) CXX11_OVERRIDE;
+};
+
+/** Handle /OPER.
+ */
+class CommandOper : public SplitCommand
+{
+ public:
+ /** Constructor for oper.
+ */
+ CommandOper(Module* parent);
+
+ /** Handle command.
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /REHASH.
+ */
+class CommandRehash : public Command
+{
+ public:
+ /** Constructor for rehash.
+ */
+ CommandRehash(Module* parent);
+
+ /** Handle command.
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /RESTART
+ */
+class CommandRestart : public Command
+{
+ public:
+ /** Constructor for restart.
+ */
+ CommandRestart(Module* parent);
+
+ /** Handle command.
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
diff --git a/src/coremods/core_privmsg.cpp b/src/coremods/core_privmsg.cpp
new file mode 100644
index 000000000..dae770abe
--- /dev/null
+++ b/src/coremods/core_privmsg.cpp
@@ -0,0 +1,376 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class MessageDetailsImpl : public MessageDetails
+{
+public:
+ MessageDetailsImpl(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags)
+ : MessageDetails(mt, msg, tags)
+ {
+ }
+
+ bool IsCTCP(std::string& name, std::string& body) const CXX11_OVERRIDE
+ {
+ if (!this->IsCTCP())
+ return false;
+
+ size_t end_of_name = text.find(' ', 2);
+ size_t end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
+ if (end_of_name == std::string::npos)
+ {
+ // The CTCP only contains a name.
+ name.assign(text, 1, text.length() - 1 - end_of_ctcp);
+ body.clear();
+ return true;
+ }
+
+ // The CTCP contains a name and a body.
+ name.assign(text, 1, end_of_name - 1);
+
+ size_t start_of_body = text.find_first_not_of(' ', end_of_name + 1);
+ if (start_of_body == std::string::npos)
+ {
+ // The CTCP body is provided but empty.
+ body.clear();
+ return true;
+ }
+
+ // The CTCP body provided was non-empty.
+ body.assign(text, start_of_body, text.length() - start_of_body - end_of_ctcp);
+ return true;
+ }
+
+ bool IsCTCP(std::string& name) const CXX11_OVERRIDE
+ {
+ if (!this->IsCTCP())
+ return false;
+
+ size_t end_of_name = text.find(' ', 2);
+ if (end_of_name == std::string::npos)
+ {
+ // The CTCP only contains a name.
+ size_t end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
+ name.assign(text, 1, text.length() - 1 - end_of_ctcp);
+ return true;
+ }
+
+ // The CTCP contains a name and a body.
+ name.assign(text, 1, end_of_name - 1);
+ return true;
+ }
+
+ bool IsCTCP() const CXX11_OVERRIDE
+ {
+ // According to draft-oakley-irc-ctcp-02 a valid CTCP must begin with SOH and
+ // contain at least one octet which is not NUL, SOH, CR, LF, or SPACE. As most
+ // of these are restricted at the protocol level we only need to check for SOH
+ // and SPACE.
+ return (text.length() >= 2) && (text[0] == '\x1') && (text[1] != '\x1') && (text[1] != ' ');
+ }
+};
+
+class MessageCommandBase : public Command
+{
+ ChanModeReference moderatedmode;
+ ChanModeReference noextmsgmode;
+
+ /** Send a PRIVMSG or NOTICE message to all local users from the given user
+ * @param user User sending the message
+ * @param msg The message to send
+ * @param mt Type of the message (MSG_PRIVMSG or MSG_NOTICE)
+ * @param tags Message tags to include in the outgoing protocol message
+ */
+ static void SendAll(User* user, const std::string& msg, MessageType mt, const ClientProtocol::TagMap& tags);
+
+ public:
+ MessageCommandBase(Module* parent, MessageType mt)
+ : Command(parent, ClientProtocol::Messages::Privmsg::CommandStrFromMsgType(mt), 2, 2)
+ , moderatedmode(parent, "moderated")
+ , noextmsgmode(parent, "noextmsg")
+ {
+ syntax = "<target>{,<target>} <message>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleMessage(User* user, const Params& parameters, MessageType mt);
+
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if (IS_LOCAL(user))
+ // This is handled by the OnUserPostMessage hook to split the LoopCall pieces
+ return ROUTE_LOCALONLY;
+ else
+ return ROUTE_MESSAGE(parameters[0]);
+ }
+};
+
+void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt, const ClientProtocol::TagMap& tags)
+{
+ ClientProtocol::Messages::Privmsg message(ClientProtocol::Messages::Privmsg::nocopy, user, "$*", msg, mt);
+ message.AddTags(tags);
+ message.SetSideEffect(true);
+ ClientProtocol::Event messageevent(ServerInstance->GetRFCEvents().privmsg, message);
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ if ((*i)->registered == REG_ALL)
+ (*i)->Send(messageevent);
+ }
+}
+
+CmdResult MessageCommandBase::HandleMessage(User* user, const Params& parameters, MessageType mt)
+{
+ User *dest;
+ Channel *chan;
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ if (parameters[0][0] == '$')
+ {
+ if (!user->HasPrivPermission("users/mass-message"))
+ return CMD_SUCCESS;
+
+ std::string servername(parameters[0], 1);
+ MessageTarget msgtarget(&servername);
+ MessageDetailsImpl msgdetails(mt, parameters[1], parameters.GetTags());
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
+ return CMD_FAILURE;
+ }
+
+ FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
+ if (InspIRCd::Match(ServerInstance->Config->ServerName, servername, NULL))
+ {
+ SendAll(user, msgdetails.text, mt, msgdetails.tags_out);
+ }
+ FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
+ return CMD_SUCCESS;
+ }
+
+ char status = 0;
+ const char* target = parameters[0].c_str();
+
+ if (ServerInstance->Modes->FindPrefix(*target))
+ {
+ status = *target;
+ target++;
+ }
+ if (*target == '#')
+ {
+ chan = ServerInstance->FindChan(target);
+
+ if (chan)
+ {
+ if (IS_LOCAL(user) && chan->GetPrefixValue(user) < VOICE_VALUE)
+ {
+ if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
+ return CMD_FAILURE;
+ }
+
+ if (chan->IsModeSet(moderatedmode))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m)");
+ return CMD_FAILURE;
+ }
+
+ if (ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL)
+ {
+ if (chan->IsBanned(user))
+ {
+ if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ MessageTarget msgtarget(chan, status);
+ MessageDetailsImpl msgdetails(mt, parameters[1], parameters.GetTags());
+ msgdetails.exemptions.insert(user);
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
+ return CMD_FAILURE;
+ }
+
+ /* Check again, a module may have zapped the input string */
+ if (msgdetails.text.empty())
+ {
+ user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
+ return CMD_FAILURE;
+ }
+
+ FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
+
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, chan, msgdetails.text, msgdetails.type, msgtarget.status);
+ privmsg.AddTags(msgdetails.tags_out);
+ privmsg.SetSideEffect(true);
+ chan->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, msgtarget.status, msgdetails.exemptions);
+
+ FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
+ }
+ else
+ {
+ /* channel does not exist */
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+ }
+
+ const char* destnick = parameters[0].c_str();
+
+ if (IS_LOCAL(user))
+ {
+ const char* targetserver = strchr(destnick, '@');
+
+ if (targetserver)
+ {
+ std::string nickonly;
+
+ nickonly.assign(destnick, 0, targetserver - destnick);
+ dest = ServerInstance->FindNickOnly(nickonly);
+ if (dest && strcasecmp(dest->server->GetName().c_str(), targetserver + 1))
+ {
+ /* Incorrect server for user */
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
+ }
+ }
+ else
+ dest = ServerInstance->FindNickOnly(destnick);
+ }
+ else
+ dest = ServerInstance->FindNick(destnick);
+
+ if ((dest) && (dest->registered == REG_ALL))
+ {
+ if (parameters[1].empty())
+ {
+ user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
+ return CMD_FAILURE;
+ }
+
+ if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
+ {
+ /* auto respond with aweh msg */
+ user->WriteNumeric(RPL_AWAY, dest->nick, dest->awaymsg);
+ }
+
+ MessageTarget msgtarget(dest);
+ MessageDetailsImpl msgdetails(mt, parameters[1], parameters.GetTags());
+
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
+ return CMD_FAILURE;
+ }
+
+ FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
+
+ LocalUser* const localtarget = IS_LOCAL(dest);
+ if (localtarget)
+ {
+ // direct write, same server
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, localtarget->nick, msgdetails.text, mt);
+ privmsg.AddTags(msgdetails.tags_out);
+ privmsg.SetSideEffect(true);
+ localtarget->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+ }
+
+ FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+}
+
+template<MessageType MT>
+class CommandMessage : public MessageCommandBase
+{
+ public:
+ CommandMessage(Module* parent)
+ : MessageCommandBase(parent, MT)
+ {
+ }
+
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ return HandleMessage(user, parameters, MT);
+ }
+};
+
+class ModuleCoreMessage : public Module
+{
+ CommandMessage<MSG_PRIVMSG> CommandPrivmsg;
+ CommandMessage<MSG_NOTICE> CommandNotice;
+
+ public:
+ ModuleCoreMessage()
+ : CommandPrivmsg(this), CommandNotice(this)
+ {
+ }
+
+ void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+ {
+ // We only handle the idle times of local users.
+ LocalUser* luser = IS_LOCAL(user);
+ if (!luser)
+ return;
+
+ // We don't update the idle time when a CTCP reply is sent.
+ if (details.type == MSG_NOTICE && details.IsCTCP())
+ return;
+
+ luser->idle_lastmsg = ServerInstance->Time();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleCoreMessage)
diff --git a/src/coremods/core_reloadmodule.cpp b/src/coremods/core_reloadmodule.cpp
new file mode 100644
index 000000000..ea5d40abd
--- /dev/null
+++ b/src/coremods/core_reloadmodule.cpp
@@ -0,0 +1,785 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "listmode.h"
+#include "modules/reload.h"
+
+static Events::ModuleEventProvider* reloadevprov;
+static ClientProtocol::Serializer* dummyserializer;
+
+class DummySerializer : public ClientProtocol::Serializer
+{
+ bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE
+ {
+ return false;
+ }
+
+ ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE
+ {
+ return ClientProtocol::SerializedMessage();
+ }
+
+ public:
+ DummySerializer(Module* mod)
+ : ClientProtocol::Serializer(mod, "dummy")
+ {
+ }
+};
+
+class CommandReloadmodule : public Command
+{
+ Events::ModuleEventProvider evprov;
+ DummySerializer dummyser;
+
+ public:
+ /** Constructor for reloadmodule.
+ */
+ CommandReloadmodule(Module* parent)
+ : Command(parent, "RELOADMODULE", 1)
+ , evprov(parent, "event/reloadmodule")
+ , dummyser(parent)
+ {
+ reloadevprov = &evprov;
+ dummyserializer = &dummyser;
+ flags_needed = 'o';
+ syntax = "<modulename>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+namespace ReloadModule
+{
+
+class DataKeeper
+{
+ /** Data we save for each mode and extension provided by the module
+ */
+ struct ProviderInfo
+ {
+ std::string itemname;
+ union
+ {
+ ModeHandler* mh;
+ ExtensionItem* extitem;
+ ClientProtocol::Serializer* serializer;
+ };
+
+ ProviderInfo(ModeHandler* mode)
+ : itemname(mode->name)
+ , mh(mode)
+ {
+ }
+
+ ProviderInfo(ExtensionItem* ei)
+ : itemname(ei->name)
+ , extitem(ei)
+ {
+ }
+
+ ProviderInfo(ClientProtocol::Serializer* ser)
+ : itemname(ser->name)
+ , serializer(ser)
+ {
+ }
+ };
+
+ struct InstanceData
+ {
+ /** Position of the ModeHandler or ExtensionItem that the serialized data belongs to
+ */
+ size_t index;
+
+ /** Serialized data
+ */
+ std::string serialized;
+
+ InstanceData(size_t Index, const std::string& Serialized)
+ : index(Index)
+ , serialized(Serialized)
+ {
+ }
+ };
+
+ struct ModesExts
+ {
+ /** Mode data for the object, one entry per mode set by the module being reloaded
+ */
+ std::vector<InstanceData> modelist;
+
+ /** Extensions for the object, one entry per extension set by the module being reloaded
+ */
+ std::vector<InstanceData> extlist;
+
+ bool empty() const { return ((modelist.empty()) && (extlist.empty())); }
+
+ void swap(ModesExts& other)
+ {
+ modelist.swap(other.modelist);
+ extlist.swap(other.extlist);
+ }
+ };
+
+ struct OwnedModesExts : public ModesExts
+ {
+ /** User uuid or channel name
+ */
+ std::string owner;
+
+ OwnedModesExts(const std::string& Owner)
+ : owner(Owner)
+ {
+ }
+ };
+
+ // Data saved for each channel
+ struct ChanData : public OwnedModesExts
+ {
+ /** Type of data stored for each member who has any affected modes or extensions set
+ */
+ typedef OwnedModesExts MemberData;
+
+ /** List of data (modes and extensions) about each member
+ */
+ std::vector<MemberData> memberdatalist;
+
+ ChanData(Channel* chan)
+ : OwnedModesExts(chan->name)
+ {
+ }
+ };
+
+ // Data saved for each user
+ struct UserData : public OwnedModesExts
+ {
+ static const size_t UNUSED_INDEX = (size_t)-1;
+ size_t serializerindex;
+
+ UserData(User* user, size_t serializeridx)
+ : OwnedModesExts(user->uuid)
+ , serializerindex(serializeridx)
+ {
+ }
+ };
+
+ /** Module being reloaded
+ */
+ Module* mod;
+
+ /** Stores all user and channel modes provided by the module
+ */
+ std::vector<ProviderInfo> handledmodes[2];
+
+ /** Stores all extensions provided by the module
+ */
+ std::vector<ProviderInfo> handledexts;
+
+ /** Stores all serializers provided by the module
+ */
+ std::vector<ProviderInfo> handledserializers;
+
+ /** Stores all of the module data related to users
+ */
+ std::vector<UserData> userdatalist;
+
+ /** Stores all of the module data related to channels and memberships
+ */
+ std::vector<ChanData> chandatalist;
+
+ /** Data attached by modules
+ */
+ ReloadModule::CustomData moddata;
+
+ void SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdatalist);
+ void SaveMemberData(Channel* chan, std::vector<ChanData::MemberData>& memberdatalist);
+ static void SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata);
+ size_t SaveSerializer(User* user);
+
+ /** Get the index of a ProviderInfo representing the serializer in the handledserializers list.
+ * If the serializer is not already in the list it is added.
+ * @param serializer Serializer to get an index to.
+ * @return Index of the ProviderInfo representing the serializer.
+ */
+ size_t GetSerializerIndex(ClientProtocol::Serializer* serializer);
+
+ void CreateModeList(ModeType modetype);
+ void DoSaveUsers();
+ void DoSaveChans();
+
+ /** Link previously saved extension names to currently available ExtensionItems
+ */
+ void LinkExtensions();
+
+ /** Link previously saved mode names to currently available ModeHandlers
+ * @param modetype Type of the modes to look for
+ */
+ void LinkModes(ModeType modetype);
+
+ /** Link previously saved serializer names to currently available Serializers
+ */
+ void LinkSerializers();
+
+ void DoRestoreUsers();
+ void DoRestoreChans();
+ void DoRestoreModules();
+
+ /** Restore previously saved modes and extensions on an Extensible.
+ * The extensions are set directly on the extensible, the modes are added into the provided mode change list.
+ * @param data Data to unserialize from
+ * @param extensible Object to restore
+ * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+ * (for Channels and Memberships).
+ * @param modechange Mode change to populate with the modes
+ */
+ void RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange);
+
+ /** Restore all previously saved extensions on an Extensible
+ * @param list List of extensions and their serialized data to restore
+ * @param extensible Target Extensible
+ */
+ void RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible);
+
+ /** Restore all previously saved modes on a User, Channel or Membership
+ * @param list List of modes to restore
+ * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+ * @param modechange Mode change to populate with the modes
+ */
+ void RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange);
+
+ /** Restore previously saved serializer on a User.
+ * Quit the user if the serializer cannot be restored.
+ * @param serializerindex Saved serializer index to restore.
+ * @param user User whose serializer to restore. If not local then calling this method is a no-op.
+ * @return True if the serializer didn't need restoring or was restored successfully.
+ * False if the serializer should have been restored but the required serializer is unavailable and the user was quit.
+ */
+ bool RestoreSerializer(size_t serializerindex, User* user);
+
+ /** Restore all modes and extensions of all members on a channel
+ * @param chan Channel whose members are being restored
+ * @param memberdata Data to restore
+ * @param modechange Mode change to populate with prefix modes
+ */
+ void RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange);
+
+ /** Verify that a service which had its data saved is available and owned by the module that owned it previously
+ * @param service Service descriptor
+ * @param type Human-readable type of the service for log messages
+ */
+ void VerifyServiceProvider(const ProviderInfo& service, const char* type);
+
+ public:
+ /** Save module state
+ * @param currmod Module whose data to save
+ */
+ void Save(Module* currmod);
+
+ /** Restore module state
+ * @param newmod Newly loaded instance of the module which had its data saved
+ */
+ void Restore(Module* newmod);
+
+ /** Handle reload failure
+ */
+ void Fail();
+};
+
+void DataKeeper::DoSaveUsers()
+{
+ ModesExts currdata;
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* const user = i->second;
+
+ // Serialize user modes
+ for (size_t j = 0; j < handledmodes[MODETYPE_USER].size(); j++)
+ {
+ ModeHandler* mh = handledmodes[MODETYPE_USER][j].mh;
+ if (user->IsModeSet(mh))
+ currdata.modelist.push_back(InstanceData(j, mh->GetUserParameter(user)));
+ }
+
+ // Serialize all extensions attached to the User
+ SaveExtensions(user, currdata.extlist);
+
+ // Save serializer name if applicable and get an index to it
+ size_t serializerindex = SaveSerializer(user);
+
+ // Add to list if the user has any modes or extensions set that we are interested in, otherwise we don't
+ // have to do anything with this user when restoring
+ if ((!currdata.empty()) || (serializerindex != UserData::UNUSED_INDEX))
+ {
+ userdatalist.push_back(UserData(user, serializerindex));
+ userdatalist.back().swap(currdata);
+ }
+ }
+}
+
+size_t DataKeeper::GetSerializerIndex(ClientProtocol::Serializer* serializer)
+{
+ for (size_t i = 0; i < handledserializers.size(); i++)
+ {
+ if (handledserializers[i].serializer == serializer)
+ return i;
+ }
+
+ handledserializers.push_back(ProviderInfo(serializer));
+ return handledserializers.size()-1;
+}
+
+size_t DataKeeper::SaveSerializer(User* user)
+{
+ LocalUser* const localuser = IS_LOCAL(user);
+ if ((!localuser) || (!localuser->serializer))
+ return UserData::UNUSED_INDEX;
+ if (localuser->serializer->creator != mod)
+ return UserData::UNUSED_INDEX;
+
+ const size_t serializerindex = GetSerializerIndex(localuser->serializer);
+ localuser->serializer = dummyserializer;
+ return serializerindex;
+}
+
+void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdata)
+{
+ const Extensible::ExtensibleStore& setexts = extensible->GetExtList();
+
+ // Position of the extension saved in the handledexts list
+ size_t index = 0;
+ for (std::vector<ProviderInfo>::const_iterator i = handledexts.begin(); i != handledexts.end(); ++i, index++)
+ {
+ ExtensionItem* const item = i->extitem;
+ Extensible::ExtensibleStore::const_iterator it = setexts.find(item);
+ if (it == setexts.end())
+ continue;
+
+ std::string value = item->serialize(FORMAT_INTERNAL, extensible, it->second);
+ // If the serialized value is empty the extension won't be saved and restored
+ if (!value.empty())
+ extdata.push_back(InstanceData(index, value));
+ }
+}
+
+void DataKeeper::SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata)
+{
+ const ListModeBase::ModeList* list = lm->GetList(chan);
+ if (!list)
+ return;
+
+ for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ const ListModeBase::ListItem& listitem = *i;
+ currdata.modelist.push_back(InstanceData(index, listitem.mask));
+ }
+}
+
+void DataKeeper::DoSaveChans()
+{
+ ModesExts currdata;
+ std::vector<OwnedModesExts> currmemberdata;
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ Channel* const chan = i->second;
+
+ // Serialize channel modes
+ for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+ {
+ ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+ ListModeBase* lm = mh->IsListModeBase();
+ if (lm)
+ SaveListModes(chan, lm, j, currdata);
+ else if (chan->IsModeSet(mh))
+ currdata.modelist.push_back(InstanceData(j, chan->GetModeParameter(mh)));
+ }
+
+ // Serialize all extensions attached to the Channel
+ SaveExtensions(chan, currdata.extlist);
+
+ // Serialize all extensions attached to and all modes set on all members of the channel
+ SaveMemberData(chan, currmemberdata);
+
+ // Same logic as in DoSaveUsers() plus we consider the modes and extensions of all members
+ if ((!currdata.empty()) || (!currmemberdata.empty()))
+ {
+ chandatalist.push_back(ChanData(chan));
+ chandatalist.back().swap(currdata);
+ chandatalist.back().memberdatalist.swap(currmemberdata);
+ }
+ }
+}
+
+void DataKeeper::SaveMemberData(Channel* chan, std::vector<OwnedModesExts>& memberdatalist)
+{
+ ModesExts currdata;
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ Membership* const memb = i->second;
+
+ for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+ {
+ ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+ const PrefixMode* const pm = mh->IsPrefixMode();
+ if ((pm) && (memb->HasMode(pm)))
+ currdata.modelist.push_back(InstanceData(j, memb->user->uuid)); // Need to pass the user's uuid to the mode parser to set the mode later
+ }
+
+ SaveExtensions(memb, currdata.extlist);
+
+ // Same logic as in DoSaveUsers()
+ if (!currdata.empty())
+ {
+ memberdatalist.push_back(OwnedModesExts(memb->user->uuid));
+ memberdatalist.back().swap(currdata);
+ }
+ }
+}
+
+void DataKeeper::RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange)
+{
+ for (std::vector<ChanData::MemberData>::const_iterator i = memberdatalist.begin(); i != memberdatalist.end(); ++i)
+ {
+ const ChanData::MemberData& md = *i;
+ User* const user = ServerInstance->FindUUID(md.owner);
+ if (!user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone (while processing %s)", md.owner.c_str(), chan->name.c_str());
+ continue;
+ }
+
+ Membership* const memb = chan->GetUser(user);
+ if (!memb)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Member %s is no longer on channel %s", md.owner.c_str(), chan->name.c_str());
+ continue;
+ }
+
+ RestoreObj(md, memb, MODETYPE_CHANNEL, modechange);
+ }
+}
+
+void DataKeeper::CreateModeList(ModeType modetype)
+{
+ const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes->GetModes(modetype);
+ for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
+ {
+ ModeHandler* mh = i->second;
+ if (mh->creator == mod)
+ handledmodes[modetype].push_back(ProviderInfo(mh));
+ }
+}
+
+void DataKeeper::Save(Module* currmod)
+{
+ this->mod = currmod;
+
+ const ExtensionManager::ExtMap& allexts = ServerInstance->Extensions.GetExts();
+ for (ExtensionManager::ExtMap::const_iterator i = allexts.begin(); i != allexts.end(); ++i)
+ {
+ ExtensionItem* ext = i->second;
+ if (ext->creator == mod)
+ handledexts.push_back(ProviderInfo(ext));
+ }
+
+ CreateModeList(MODETYPE_USER);
+ DoSaveUsers();
+
+ CreateModeList(MODETYPE_CHANNEL);
+ DoSaveChans();
+
+ FOREACH_MOD_CUSTOM(*reloadevprov, ReloadModule::EventListener, OnReloadModuleSave, (mod, this->moddata));
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Saved data about %lu users %lu chans %lu modules", (unsigned long)userdatalist.size(), (unsigned long)chandatalist.size(), (unsigned long)moddata.list.size());
+}
+
+void DataKeeper::VerifyServiceProvider(const ProviderInfo& service, const char* type)
+{
+ const ServiceProvider* sp = service.extitem;
+ if (!sp)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is no longer available", type, service.itemname.c_str());
+ else if (sp->creator != mod)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is now handled by %s", type, service.itemname.c_str(), (sp->creator ? sp->creator->ModuleSourceFile.c_str() : "<core>"));
+}
+
+void DataKeeper::LinkModes(ModeType modetype)
+{
+ std::vector<ProviderInfo>& list = handledmodes[modetype];
+ for (std::vector<ProviderInfo>::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ ProviderInfo& item = *i;
+ item.mh = ServerInstance->Modes->FindMode(item.itemname, modetype);
+ VerifyServiceProvider(item, (modetype == MODETYPE_USER ? "User mode" : "Channel mode"));
+ }
+}
+
+void DataKeeper::LinkExtensions()
+{
+ for (std::vector<ProviderInfo>::iterator i = handledexts.begin(); i != handledexts.end(); ++i)
+ {
+ ProviderInfo& item = *i;
+ item.extitem = ServerInstance->Extensions.GetItem(item.itemname);
+ VerifyServiceProvider(item.extitem, "Extension");
+ }
+}
+
+void DataKeeper::LinkSerializers()
+{
+ for (std::vector<ProviderInfo>::iterator i = handledserializers.begin(); i != handledserializers.end(); ++i)
+ {
+ ProviderInfo& item = *i;
+ item.serializer = ServerInstance->Modules.FindDataService<ClientProtocol::Serializer>(item.itemname);
+ VerifyServiceProvider(item.serializer, "Serializer");
+ }
+}
+
+void DataKeeper::Restore(Module* newmod)
+{
+ this->mod = newmod;
+
+ // Find the new extension items
+ LinkExtensions();
+ LinkModes(MODETYPE_USER);
+ LinkModes(MODETYPE_CHANNEL);
+ LinkSerializers();
+
+ // Restore
+ DoRestoreUsers();
+ DoRestoreChans();
+ DoRestoreModules();
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore finished");
+}
+
+void DataKeeper::Fail()
+{
+ this->mod = NULL;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore failed, notifying modules");
+ DoRestoreModules();
+}
+
+void DataKeeper::RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange)
+{
+ RestoreExtensions(data.extlist, extensible);
+ RestoreModes(data.modelist, modetype, modechange);
+}
+
+void DataKeeper::RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible)
+{
+ for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const InstanceData& id = *i;
+ handledexts[id.index].extitem->unserialize(FORMAT_INTERNAL, extensible, id.serialized);
+ }
+}
+
+void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange)
+{
+ for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const InstanceData& id = *i;
+ modechange.push_add(handledmodes[modetype][id.index].mh, id.serialized);
+ }
+}
+
+bool DataKeeper::RestoreSerializer(size_t serializerindex, User* user)
+{
+ if (serializerindex == UserData::UNUSED_INDEX)
+ return true;
+
+ // The following checks are redundant
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (!localuser)
+ return true;
+ if (localuser->serializer != dummyserializer)
+ return true;
+
+ const ProviderInfo& provinfo = handledserializers[serializerindex];
+ if (!provinfo.serializer)
+ {
+ // Users cannot exist without a serializer
+ ServerInstance->Users.QuitUser(user, "Serializer lost in reload");
+ return false;
+ }
+
+ localuser->serializer = provinfo.serializer;
+ return true;
+}
+
+void DataKeeper::DoRestoreUsers()
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
+ Modes::ChangeList modechange;
+
+ for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
+ {
+ const UserData& userdata = *i;
+ User* const user = ServerInstance->FindUUID(userdata.owner);
+ if (!user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
+ continue;
+ }
+
+ // Attempt to restore serializer first, if it fails it's a fatal error and RestoreSerializer() quits them
+ if (!RestoreSerializer(userdata.serializerindex, user))
+ continue;
+
+ RestoreObj(userdata, user, MODETYPE_USER, modechange);
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
+ modechange.clear();
+ }
+}
+
+void DataKeeper::DoRestoreChans()
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring channel data");
+ Modes::ChangeList modechange;
+
+ for (std::vector<ChanData>::const_iterator i = chandatalist.begin(); i != chandatalist.end(); ++i)
+ {
+ const ChanData& chandata = *i;
+ Channel* const chan = ServerInstance->FindChan(chandata.owner);
+ if (!chan)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel %s not found", chandata.owner.c_str());
+ continue;
+ }
+
+ RestoreObj(chandata, chan, MODETYPE_CHANNEL, modechange);
+ // Process the mode change before applying any prefix modes
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+ modechange.clear();
+
+ // Restore all member data
+ RestoreMemberData(chan, chandata.memberdatalist, modechange);
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+ modechange.clear();
+ }
+}
+
+void DataKeeper::DoRestoreModules()
+{
+ for (ReloadModule::CustomData::List::iterator i = moddata.list.begin(); i != moddata.list.end(); ++i)
+ {
+ ReloadModule::CustomData::Data& data = *i;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Calling module data handler %p", (void*)data.handler);
+ data.handler->OnReloadModuleRestore(mod, data.data);
+ }
+}
+
+} // namespace ReloadModule
+
+class ReloadAction : public ActionBase
+{
+ Module* const mod;
+ const std::string uuid;
+ const std::string passedname;
+
+ public:
+ ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
+ : mod(m)
+ , uuid(uid)
+ , passedname(passedmodname)
+ {
+ }
+
+ void Call() CXX11_OVERRIDE
+ {
+ ReloadModule::DataKeeper datakeeper;
+ datakeeper.Save(mod);
+
+ DLLManager* dll = mod->ModuleDLLManager;
+ std::string name = mod->ModuleSourceFile;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ delete dll;
+ bool result = ServerInstance->Modules->Load(name);
+
+ if (result)
+ {
+ Module* newmod = ServerInstance->Modules->Find(name);
+ datakeeper.Restore(newmod);
+ }
+ else
+ datakeeper.Fail();
+
+ ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded", passedname.c_str(), result ? "" : "un");
+ User* user = ServerInstance->FindUUID(uuid);
+ if (user)
+ user->WriteNumeric(RPL_LOADEDMODULE, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
+
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+};
+
+CmdResult CommandReloadmodule::Handle(User* user, const Params& parameters)
+{
+ Module* m = ServerInstance->Modules->Find(parameters[0]);
+ if (m == creator)
+ {
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "You cannot reload core_reloadmodule (unload and load it)");
+ return CMD_FAILURE;
+ }
+
+ if (creator->dying)
+ return CMD_FAILURE;
+
+ if ((m) && (ServerInstance->Modules.CanUnload(m)))
+ {
+ ServerInstance->AtomicActions.AddAction(new ReloadAction(m, user->uuid, parameters[0]));
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
+ return CMD_FAILURE;
+ }
+}
+
+class CoreModReloadmodule : public Module
+{
+ private:
+ CommandReloadmodule cmd;
+
+ public:
+ CoreModReloadmodule()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the RELOADMODULE command", VF_CORE | VF_VENDOR);
+ }
+};
+
+MODULE_INIT(CoreModReloadmodule)
diff --git a/src/coremods/core_serialize_rfc.cpp b/src/coremods/core_serialize_rfc.cpp
new file mode 100644
index 000000000..1ba330146
--- /dev/null
+++ b/src/coremods/core_serialize_rfc.cpp
@@ -0,0 +1,222 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class RFCSerializer : public ClientProtocol::Serializer
+{
+ /** Maximum size of the message tags portion of the message, including the `@` and the trailing space characters.
+ */
+ static const std::string::size_type MAX_MESSAGE_TAG_LENGTH = 512;
+
+ static void SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line);
+
+ public:
+ RFCSerializer(Module* mod)
+ : ClientProtocol::Serializer(mod, "rfc")
+ {
+ }
+
+ bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE;
+ ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE;
+};
+
+bool RFCSerializer::Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput)
+{
+ size_t start = line.find_first_not_of(" ");
+ if (start == std::string::npos)
+ {
+ // Discourage the user from flooding the server.
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+
+ ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), line.c_str());
+
+ irc::tokenstream tokens(line, start);
+ std::string token;
+
+ // This will always exist because of the check at the start of the function.
+ tokens.GetMiddle(token);
+ if (token[0] == '@')
+ {
+ // Line begins with message tags, parse them.
+ std::string tagval;
+ irc::sepstream ss(token.substr(1), ';');
+ while (ss.GetToken(token))
+ {
+ // Two or more tags with the same key must not be sent, but if a client violates that we accept
+ // the first occurence of duplicate tags and ignore all later occurences.
+ //
+ // Another option is to reject the message entirely but there is no standard way of doing that.
+ const std::string::size_type p = token.find('=');
+ if (p != std::string::npos)
+ {
+ // Tag has a value
+ tagval.assign(token, p+1, std::string::npos);
+ token.erase(p);
+ }
+ else
+ tagval.clear();
+
+ HandleTag(user, token, tagval, parseoutput.tags);
+ }
+
+
+ // Try to read the prefix or command name.
+ if (!tokens.GetMiddle(token))
+ {
+ // Discourage the user from flooding the server.
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+ }
+
+ if (token[0] == ':')
+ {
+ // If this exists then the client sent a prefix as part of their
+ // message. Section 2.3 of RFC 1459 technically says we should only
+ // allow the nick of the client here but in practise everyone just
+ // ignores it so we will copy them.
+
+ // Try to read the command name.
+ if (!tokens.GetMiddle(token))
+ {
+ // Discourage the user from flooding the server.
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+ }
+
+ parseoutput.cmd.assign(token);
+
+ // Build the parameter map. We intentionally do not respect the RFC 1459
+ // thirteen parameter limit here.
+ while (tokens.GetTrailing(token))
+ parseoutput.params.push_back(token);
+
+ return true;
+}
+
+void RFCSerializer::SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line)
+{
+ char prefix = '@'; // First tag name is prefixed with a '@'
+ for (ClientProtocol::TagMap::const_iterator i = tags.begin(); i != tags.end(); ++i)
+ {
+ if (!tagwl.IsSelected(tags, i))
+ continue;
+
+ const std::string::size_type prevsize = line.size();
+ line.push_back(prefix);
+ prefix = ';'; // Remaining tags are prefixed with ';'
+ line.append(i->first);
+ const std::string& val = i->second.value;
+ if (!val.empty())
+ {
+ line.push_back('=');
+ line.append(val);
+ }
+
+ // The tags part of the message mustn't grow longer than what is allowed by the spec. If it does,
+ // remove last tag and stop adding more tags.
+ //
+ // One is subtracted from the limit before comparing because there must be a ' ' char after the last tag
+ // which also counts towards the limit.
+ if (line.size() > MAX_MESSAGE_TAG_LENGTH-1)
+ {
+ line.erase(prevsize);
+ break;
+ }
+ }
+
+ if (!line.empty())
+ line.push_back(' ');
+}
+
+ClientProtocol::SerializedMessage RFCSerializer::Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const
+{
+ std::string line;
+ SerializeTags(msg.GetTags(), tagwl, line);
+
+ // Save position for length calculation later
+ const std::string::size_type rfcmsg_begin = line.size();
+
+ if (msg.GetSource())
+ {
+ line.push_back(':');
+ line.append(*msg.GetSource());
+ line.push_back(' ');
+ }
+ line.append(msg.GetCommand());
+
+ const ClientProtocol::Message::ParamList& params = msg.GetParams();
+ if (!params.empty())
+ {
+ for (ClientProtocol::Message::ParamList::const_iterator i = params.begin(); i != params.end()-1; ++i)
+ {
+ const std::string& param = *i;
+ line.push_back(' ');
+ line.append(param);
+ }
+
+ line.append(" :", 2).append(params.back());
+ }
+
+ // Truncate if too long
+ std::string::size_type maxline = ServerInstance->Config->Limits.MaxLine - 2;
+ if (line.length() - rfcmsg_begin > maxline)
+ line.erase(rfcmsg_begin + maxline);
+
+ line.append("\r\n", 2);
+ return line;
+}
+
+class ModuleCoreRFCSerializer : public Module
+{
+ RFCSerializer rfcserializer;
+
+ public:
+ ModuleCoreRFCSerializer()
+ : rfcserializer(this)
+ {
+ }
+
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+ {
+ if (type != ExtensionItem::EXT_USER)
+ return;
+
+ LocalUser* const user = IS_LOCAL(static_cast<User*>(item));
+ if ((user) && (user->serializer == &rfcserializer))
+ ServerInstance->Users.QuitUser(user, "Protocol serializer module unloading");
+ }
+
+ void OnUserInit(LocalUser* user) CXX11_OVERRIDE
+ {
+ if (!user->serializer)
+ user->serializer = &rfcserializer;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("RFC client protocol serializer and unserializer", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleCoreRFCSerializer)
diff --git a/src/coremods/core_stats.cpp b/src/coremods/core_stats.cpp
new file mode 100644
index 000000000..06602b288
--- /dev/null
+++ b/src/coremods/core_stats.cpp
@@ -0,0 +1,417 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "modules/stats.h"
+
+#ifdef _WIN32
+#include <psapi.h>
+#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
+#endif
+
+/** Handle /STATS.
+ */
+class CommandStats : public Command
+{
+ Events::ModuleEventProvider statsevprov;
+ void DoStats(Stats::Context& stats);
+
+ public:
+ /** Constructor for stats.
+ */
+ CommandStats(Module* Creator)
+ : Command(Creator, "STATS", 1, 2)
+ , statsevprov(Creator, "event/stats")
+ {
+ allow_empty_last_param = false;
+ syntax = "<stats-symbol> [<servername>]";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
+ return ROUTE_UNICAST(parameters[1]);
+ return ROUTE_LOCALONLY;
+ }
+};
+
+static void GenerateStatsLl(Stats::Context& stats)
+{
+ stats.AddRow(211, InspIRCd::Format("nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", (stats.GetSymbol() == 'l' ? "host" : "ip")));
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* u = *i;
+ stats.AddRow(211, u->nick+"["+u->ident+"@"+(stats.GetSymbol() == 'l' ? u->GetDisplayedHost() : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->signon));
+ }
+}
+
+void CommandStats::DoStats(Stats::Context& stats)
+{
+ User* const user = stats.GetSource();
+ const char statschar = stats.GetSymbol();
+
+ bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
+ bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
+ bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
+
+ if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
+ {
+ ServerInstance->SNO->WriteToSnoMask('t',
+ "%s '%c' denied for %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"),
+ statschar, user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
+ stats.AddRow(481, (std::string("Permission Denied - STATS ") + statschar + " requires the servers/auspex priv."));
+ return;
+ }
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT_CUSTOM(statsevprov, Stats::EventListener, OnStats, MOD_RESULT, (stats));
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ stats.AddRow(219, statschar, "End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
+ return;
+ }
+
+ switch (statschar)
+ {
+ /* stats p (show listening ports) */
+ case 'p':
+ {
+ for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+ {
+ ListenSocket* ls = *i;
+ std::string type = ls->bind_tag->getString("type", "clients");
+ std::string hook = ls->bind_tag->getString("ssl", "plaintext");
+
+ stats.AddRow(249, ls->bind_sa.str() + " (" + type + ", " + hook + ")");
+ }
+ }
+ break;
+
+ /* These stats symbols must be handled by a linking module */
+ case 'n':
+ case 'c':
+ break;
+
+ case 'i':
+ {
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
+ {
+ ConnectClass* c = *i;
+ Stats::Row row(215);
+ row.push("I").push(c->name);
+
+ std::string param;
+ if (c->type == CC_ALLOW)
+ param.push_back('+');
+ if (c->type == CC_DENY)
+ param.push_back('-');
+
+ if (c->type == CC_NAMED)
+ param.push_back('*');
+ else
+ param.append(c->host);
+
+ row.push(param).push(c->config->getString("port", "*"));
+ row.push(ConvToStr(c->GetRecvqMax())).push(ConvToStr(c->GetSendqSoftMax())).push(ConvToStr(c->GetSendqHardMax())).push(ConvToStr(c->GetCommandRate()));
+
+ param = ConvToStr(c->GetPenaltyThreshold());
+ if (c->fakelag)
+ param.push_back('*');
+ row.push(param);
+
+ stats.AddRow(row);
+ }
+ }
+ break;
+
+ case 'Y':
+ {
+ int idx = 0;
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ ConnectClass* c = *i;
+ stats.AddRow(215, 'i', "NOMATCH", '*', c->GetHost(), (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
+ stats.AddRow(218, 'Y', idx, c->GetPingTime(), '0', c->GetSendqHardMax(), ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
+ idx++;
+ }
+ }
+ break;
+
+ case 'P':
+ {
+ unsigned int idx = 0;
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* oper = *i;
+ if (!oper->server->IsULine())
+ {
+ LocalUser* lu = IS_LOCAL(oper);
+ stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->GetDisplayedHost() + ") Idle: " +
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
+ idx++;
+ }
+ }
+ stats.AddRow(249, ConvToStr(idx)+" OPER(s)");
+ }
+ break;
+
+ case 'k':
+ ServerInstance->XLines->InvokeStats("K",216,stats);
+ break;
+ case 'g':
+ ServerInstance->XLines->InvokeStats("G",223,stats);
+ break;
+ case 'q':
+ ServerInstance->XLines->InvokeStats("Q",217,stats);
+ break;
+ case 'Z':
+ ServerInstance->XLines->InvokeStats("Z",223,stats);
+ break;
+ case 'e':
+ ServerInstance->XLines->InvokeStats("E",223,stats);
+ break;
+ case 'E':
+ {
+ const SocketEngine::Statistics& sestats = SocketEngine::GetStats();
+ stats.AddRow(249, "Total events: "+ConvToStr(sestats.TotalEvents));
+ stats.AddRow(249, "Read events: "+ConvToStr(sestats.ReadEvents));
+ stats.AddRow(249, "Write events: "+ConvToStr(sestats.WriteEvents));
+ stats.AddRow(249, "Error events: "+ConvToStr(sestats.ErrorEvents));
+ break;
+ }
+
+ /* stats m (list number of times each command has been used, plus bytecount) */
+ case 'm':
+ {
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ if (i->second->use_count)
+ {
+ /* RPL_STATSCOMMANDS */
+ stats.AddRow(212, i->second->name, i->second->use_count);
+ }
+ }
+ }
+ break;
+
+ /* stats z (debug and memory info) */
+ case 'z':
+ {
+ stats.AddRow(249, "Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
+ stats.AddRow(249, "Channels: "+ConvToStr(ServerInstance->GetChans().size()));
+ stats.AddRow(249, "Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
+
+ float kbitpersec_in, kbitpersec_out, kbitpersec_total;
+ SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
+
+ stats.AddRow(249, InspIRCd::Format("Bandwidth total: %03.5f kilobits/sec", kbitpersec_total));
+ stats.AddRow(249, InspIRCd::Format("Bandwidth out: %03.5f kilobits/sec", kbitpersec_out));
+ stats.AddRow(249, InspIRCd::Format("Bandwidth in: %03.5f kilobits/sec", kbitpersec_in));
+
+#ifndef _WIN32
+ /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
+ * Also cuts out some identical code in both branches of the ifndef. -- Om
+ */
+ rusage R;
+
+ /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
+ if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
+ {
+#ifndef __HAIKU__
+ stats.AddRow(249, "Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
+ stats.AddRow(249, "Signals: "+ConvToStr(R.ru_nsignals));
+ stats.AddRow(249, "Page faults: "+ConvToStr(R.ru_majflt));
+ stats.AddRow(249, "Swaps: "+ConvToStr(R.ru_nswap));
+ stats.AddRow(249, "Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
+#endif
+ float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
+ + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
+ float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
+ float per = (n_eaten / n_elapsed) * 100;
+
+ stats.AddRow(249, InspIRCd::Format("CPU Use (now): %03.5f%%", per));
+
+ n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
+ n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
+ per = (n_eaten / n_elapsed) * 100;
+
+ stats.AddRow(249, InspIRCd::Format("CPU Use (total): %03.5f%%", per));
+ }
+#else
+ PROCESS_MEMORY_COUNTERS MemCounters;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
+ {
+ stats.AddRow(249, "Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
+ stats.AddRow(249, "Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
+ stats.AddRow(249, "Page faults: "+ConvToStr(MemCounters.PageFaultCount));
+ }
+
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+ LARGE_INTEGER ThisSample;
+ if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
+ QueryPerformanceCounter(&ThisSample))
+ {
+ KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
+ KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
+ double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats.LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats.LastCPU.dwLowDateTime) )/100000;
+ double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats.LastSampled.QuadPart) / ServerInstance->stats.QPFrequency.QuadPart;
+ double per = (n_eaten/n_elapsed);
+
+ stats.AddRow(249, InspIRCd::Format("CPU Use (now): %03.5f%%", per));
+
+ n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
+ n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
+ per = (n_eaten / n_elapsed);
+
+ stats.AddRow(249, InspIRCd::Format("CPU Use (total): %03.5f%%", per));
+ }
+#endif
+ }
+ break;
+
+ case 'T':
+ {
+ stats.AddRow(249, "accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
+ stats.AddRow(249, "unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
+ stats.AddRow(249, "nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
+ stats.AddRow(249, "dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
+ stats.AddRow(249, "connection count "+ConvToStr(ServerInstance->stats.Connects));
+ stats.AddRow(249, InspIRCd::Format("bytes sent %5.2fK recv %5.2fK",
+ ServerInstance->stats.Sent / 1024.0, ServerInstance->stats.Recv / 1024.0));
+ }
+ break;
+
+ /* stats o */
+ case 'o':
+ {
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
+ {
+ OperInfo* ifo = i->second;
+ ConfigTag* tag = ifo->oper_block;
+ stats.AddRow(243, 'O', tag->getString("host"), '*', tag->getString("name"), tag->getString("type"), '0');
+ }
+ }
+ break;
+ case 'O':
+ {
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
+ {
+ OperInfo* tag = i->second;
+ tag->init();
+ std::string umodes;
+ std::string cmodes;
+ for(char c='A'; c <= 'z'; c++)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
+ if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
+ umodes.push_back(c);
+ mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
+ if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
+ cmodes.push_back(c);
+ }
+ stats.AddRow(243, 'O', tag->name, umodes, cmodes);
+ }
+ }
+ break;
+
+ /* stats l (show user I/O stats) */
+ case 'l':
+ /* stats L (show user I/O stats with IP addresses) */
+ case 'L':
+ GenerateStatsLl(stats);
+ break;
+
+ /* stats u (show server uptime) */
+ case 'u':
+ {
+ unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
+ stats.AddRow(242, InspIRCd::Format("Server up %u days, %.2u:%.2u:%.2u",
+ up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ stats.AddRow(219, statschar, "End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
+ return;
+}
+
+CmdResult CommandStats::Handle(User* user, const Params& parameters)
+{
+ if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
+ {
+ // Give extra penalty if a non-oper does /STATS <remoteserver>
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (!user->IsOper()))
+ localuser->CommandFloodPenalty += 2000;
+ return CMD_SUCCESS;
+ }
+ Stats::Context stats(user, parameters[0][0]);
+ DoStats(stats);
+ const std::vector<Stats::Row>& rows = stats.GetRows();
+ for (std::vector<Stats::Row>::const_iterator i = rows.begin(); i != rows.end(); ++i)
+ {
+ const Stats::Row& row = *i;
+ user->WriteRemoteNumeric(row);
+ }
+
+ return CMD_SUCCESS;
+}
+
+class CoreModStats : public Module
+{
+ private:
+ CommandStats cmd;
+
+ public:
+ CoreModStats()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the STATS command", VF_CORE | VF_VENDOR);
+ }
+};
+
+MODULE_INIT(CoreModStats)
diff --git a/src/coremods/core_stub.cpp b/src/coremods/core_stub.cpp
new file mode 100644
index 000000000..7ed3865e3
--- /dev/null
+++ b/src/coremods/core_stub.cpp
@@ -0,0 +1,156 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+
+/** Handle /CONNECT.
+ */
+class CommandConnect : public Command
+{
+ public:
+ /** Constructor for connect.
+ */
+ CommandConnect(Module* parent)
+ : Command(parent, "CONNECT", 1)
+ {
+ flags_needed = 'o';
+ syntax = "<servername>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ /*
+ * This is handled by the server linking module, if necessary. Do not remove this stub.
+ */
+ user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /LINKS.
+ */
+class CommandLinks : public Command
+{
+ public:
+ /** Constructor for links.
+ */
+ CommandLinks(Module* parent)
+ : Command(parent, "LINKS", 0, 0)
+ {
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ user->WriteNumeric(RPL_LINKS, ServerInstance->Config->ServerName, ServerInstance->Config->ServerName, InspIRCd::Format("0 %s", ServerInstance->Config->ServerDesc.c_str()));
+ user->WriteNumeric(RPL_ENDOFLINKS, '*', "End of /LINKS list.");
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /SERVER.
+ */
+class CommandServer : public Command
+{
+ public:
+ /** Constructor for server.
+ */
+ CommandServer(Module* parent)
+ : Command(parent, "SERVER")
+ {
+ works_before_reg = true;
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNumeric(ERR_ALREADYREGISTERED, "You are already registered. (Perhaps your IRC client does not have a /SERVER command).");
+ }
+ else
+ {
+ user->WriteNumeric(ERR_NOTREGISTERED, "SERVER", "You may not register as a server (servers have separate ports from clients, change your config)");
+ }
+ return CMD_FAILURE;
+ }
+};
+
+/** Handle /SQUIT.
+ */
+class CommandSquit : public Command
+{
+ public:
+ /** Constructor for squit.
+ */
+ CommandSquit(Module* parent)
+ : Command(parent, "SQUIT", 1, 2)
+ {
+ flags_needed = 'o';
+ syntax = "<servername>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
+ return CMD_FAILURE;
+ }
+};
+
+class CoreModStub : public Module
+{
+ CommandConnect cmdconnect;
+ CommandLinks cmdlinks;
+ CommandServer cmdserver;
+ CommandSquit cmdsquit;
+
+ public:
+ CoreModStub()
+ : cmdconnect(this), cmdlinks(this), cmdserver(this), cmdsquit(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the stub commands CONNECT, LINKS, SERVER and SQUIT", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModStub)
diff --git a/src/coremods/core_user/cmd_away.cpp b/src/coremods/core_user/cmd_away.cpp
new file mode 100644
index 000000000..50a586392
--- /dev/null
+++ b/src/coremods/core_user/cmd_away.cpp
@@ -0,0 +1,82 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+enum
+{
+ // From RFC 1459.
+ RPL_UNAWAY = 305,
+ RPL_NOWAWAY = 306
+};
+
+CommandAway::CommandAway(Module* parent)
+ : Command(parent, "AWAY", 0, 1)
+ , awayevprov(parent)
+{
+ allow_empty_last_param = false;
+ syntax = "[<message>]";
+}
+
+/** Handle /AWAY
+ */
+CmdResult CommandAway::Handle(User* user, const Params& parameters)
+{
+ LocalUser* luser = IS_LOCAL(user);
+ ModResult MOD_RESULT;
+
+ if (!parameters.empty())
+ {
+ std::string message(parameters[0]);
+ if (luser)
+ {
+ FIRST_MOD_RESULT_CUSTOM(awayevprov, Away::EventListener, OnUserPreAway, MOD_RESULT, (luser, message));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+ }
+
+ user->awaytime = ServerInstance->Time();
+ user->awaymsg.assign(message, 0, ServerInstance->Config->Limits.MaxAway);
+ user->WriteNumeric(RPL_NOWAWAY, "You have been marked as being away");
+ FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserAway, (user));
+ }
+ else
+ {
+ if (luser)
+ {
+ FIRST_MOD_RESULT_CUSTOM(awayevprov, Away::EventListener, OnUserPreBack, MOD_RESULT, (luser));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+ }
+
+ user->awaytime = 0;
+ user->awaymsg.clear();
+ user->WriteNumeric(RPL_UNAWAY, "You are no longer marked as being away");
+ FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserBack, (user));
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandAway::GetRouting(User* user, const Params& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_mode.cpp b/src/coremods/core_user/cmd_mode.cpp
new file mode 100644
index 000000000..101f748f1
--- /dev/null
+++ b/src/coremods/core_user/cmd_mode.cpp
@@ -0,0 +1,177 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandMode::CommandMode(Module* parent)
+ : Command(parent, "MODE", 1)
+ , seq(0)
+{
+ syntax = "<target> <modes> {<mode-parameters>}";
+ memset(&sent, 0, sizeof(sent));
+}
+
+CmdResult CommandMode::Handle(User* user, const Params& parameters)
+{
+ const std::string& target = parameters[0];
+ Channel* targetchannel = ServerInstance->FindChan(target);
+ User* targetuser = NULL;
+ if (!targetchannel)
+ {
+ if (IS_LOCAL(user))
+ targetuser = ServerInstance->FindNickOnly(target);
+ else
+ targetuser = ServerInstance->FindNick(target);
+ }
+
+ if ((!targetchannel) && (!targetuser))
+ {
+ if (target[0] == '#')
+ user->WriteNumeric(Numerics::NoSuchChannel(target));
+ else
+ user->WriteNumeric(Numerics::NoSuchNick(target));
+ return CMD_FAILURE;
+ }
+ if (parameters.size() == 1)
+ {
+ this->DisplayCurrentModes(user, targetuser, targetchannel);
+ return CMD_SUCCESS;
+ }
+
+ // Populate a temporary Modes::ChangeList with the parameters
+ Modes::ChangeList changelist;
+ ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
+ ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
+
+ ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
+ if (IS_LOCAL(user))
+ {
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if ((targetuser) && (user != targetuser))
+ {
+ // Local users may only change the modes of other users if a module explicitly allows it
+ user->WriteNumeric(ERR_USERSDONTMATCH, "Can't change mode for other users");
+ return CMD_FAILURE;
+ }
+
+ // This is a mode change by a local user and modules didn't explicitly allow/deny.
+ // Ensure access checks will happen for each mode being changed.
+ flags |= ModeParser::MODE_CHECKACCESS;
+ }
+ else if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE; // Entire mode change denied by a module
+ }
+ else
+ flags |= ModeParser::MODE_LOCALONLY;
+
+ if (IS_LOCAL(user))
+ ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
+ else
+ ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
+
+ if ((ServerInstance->Modes.GetLastChangeList().empty()) && (targetchannel) && (parameters.size() == 2))
+ {
+ /* Special case for displaying the list for listmodes,
+ * e.g. MODE #chan b, or MODE #chan +b without a parameter
+ */
+ this->DisplayListModes(user, targetchannel, parameters[1]);
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandMode::GetRouting(User* user, const Params& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
+
+void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
+{
+ seq++;
+
+ for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
+ {
+ unsigned char mletter = *i;
+ if (mletter == '+')
+ continue;
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
+ if (!mh || !mh->IsListMode())
+ return;
+
+ /* Ensure the user doesnt request the same mode twice,
+ * so they can't flood themselves off out of idiocy.
+ */
+ if (sent[mletter] == seq)
+ continue;
+
+ sent[mletter] = seq;
+ ServerInstance->Modes.ShowListModeList(user, chan, mh);
+ }
+}
+
+static std::string GetSnomasks(const User* user)
+{
+ ModeHandler* const snomask = ServerInstance->Modes.FindMode('s', MODETYPE_USER);
+ std::string snomaskstr = snomask->GetUserParameter(user);
+ // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
+ // In the former case output a "+", not an empty string.
+ if (snomaskstr.empty())
+ snomaskstr.push_back('+');
+ return snomaskstr;
+}
+
+void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
+{
+ if (targetchannel)
+ {
+ // Display channel's current mode string
+ user->WriteNumeric(RPL_CHANNELMODEIS, targetchannel->name, (std::string("+") + targetchannel->ChanModes(targetchannel->HasUser(user))));
+ user->WriteNumeric(RPL_CHANNELCREATED, targetchannel->name, (unsigned long)targetchannel->age);
+ }
+ else
+ {
+ if (targetuser == user)
+ {
+ // Display user's current mode string
+ user->WriteNumeric(RPL_UMODEIS, targetuser->GetModeLetters());
+ if (targetuser->IsOper())
+ user->WriteNumeric(RPL_SNOMASKIS, GetSnomasks(targetuser), "Server notice mask");
+ }
+ else if (user->HasPrivPermission("users/auspex"))
+ {
+ // Querying the modes of another user.
+ // We cannot use RPL_UMODEIS because that's only for showing the user's own modes.
+ user->WriteNumeric(RPL_OTHERUMODEIS, targetuser->nick, targetuser->GetModeLetters());
+ if (targetuser->IsOper())
+ user->WriteNumeric(RPL_OTHERSNOMASKIS, targetuser->nick, GetSnomasks(targetuser), "Server notice mask");
+ }
+ else
+ {
+ user->WriteNumeric(ERR_USERSDONTMATCH, "Can't view modes for other users");
+ }
+ }
+}
diff --git a/src/commands/cmd_nick.cpp b/src/coremods/core_user/cmd_nick.cpp
index a079e59d0..670931f7a 100644
--- a/src/commands/cmd_nick.cpp
+++ b/src/coremods/core_user/cmd_nick.cpp
@@ -21,44 +21,33 @@
#include "inspircd.h"
+#include "core_user.h"
-/** Handle /NICK. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNick : public Command
+CommandNick::CommandNick(Module* parent)
+ : SplitCommand(parent, "NICK", 1, 1)
{
- public:
- /** Constructor for nick.
- */
- CommandNick ( Module* parent) : Command(parent,"NICK", 1, 1) { works_before_reg = true; syntax = "<newnick>"; Penalty = 0; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
+ works_before_reg = true;
+ syntax = "<newnick>";
+ Penalty = 0;
+}
/** Handle nick changes from users.
* NOTE: If you are used to ircds based on ircd2.8, and are looking
* for the client introduction code in here, youre in the wrong place.
* You need to look in the spanningtree module for this!
*/
-CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandNick::HandleLocal(LocalUser* user, const Params& parameters)
{
std::string oldnick = user->nick;
std::string newnick = parameters[0];
// anything except the initial NICK gets a flood penalty
- if (user->registered == REG_ALL && IS_LOCAL(user))
- IS_LOCAL(user)->CommandFloodPenalty += 4000;
+ if (user->registered == REG_ALL)
+ user->CommandFloodPenalty += 4000;
if (newnick.empty())
{
- user->WriteNumeric(432, "%s * :Erroneous Nickname", oldnick.c_str());
+ user->WriteNumeric(ERR_NONICKNAMEGIVEN, "No nickname given");
return CMD_FAILURE;
}
@@ -66,33 +55,44 @@ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User
{
newnick = user->uuid;
}
- else if (!ServerInstance->IsNick(newnick.c_str(), ServerInstance->Config->Limits.NickMax))
+ else if (!ServerInstance->IsNick(newnick))
{
- user->WriteNumeric(432, "%s %s :Erroneous Nickname", user->nick.c_str(),newnick.c_str());
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, "Erroneous Nickname");
return CMD_FAILURE;
}
- if (!user->ChangeNick(newnick, false))
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (user, newnick));
+
+ // If a module denied the change, abort now
+ if (MOD_RESULT == MOD_RES_DENY)
return CMD_FAILURE;
- if (user->registered < REG_NICKUSER)
+ // Disallow the nick change if <security:restrictbannedusers> is on and there is a ban matching this user in
+ // one of the channels they are on
+ if (ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL)
{
- user->registered = (user->registered | REG_NICK);
- if (user->registered == REG_NICKUSER)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
{
- /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (IS_LOCAL(user)));
- if (MOD_RESULT == MOD_RES_DENY)
+ Channel* chan = (*i)->chan;
+ if (chan->GetPrefixValue(user) < VOICE_VALUE && chan->IsBanned(user))
+ {
+ if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
+ user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Cannot change nickname while on %s (you're banned)",
+ chan->name.c_str()));
return CMD_FAILURE;
-
- // return early to not penalize new users
- return CMD_SUCCESS;
+ }
}
}
- return CMD_SUCCESS;
-}
+ if (!user->ChangeNick(newnick))
+ return CMD_FAILURE;
+ if (user->registered < REG_NICKUSER)
+ {
+ user->registered = (user->registered | REG_NICK);
+ return CommandUser::CheckRegister(user);
+ }
-COMMAND_INIT(CommandNick)
+ return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_user/cmd_part.cpp b/src/coremods/core_user/cmd_part.cpp
new file mode 100644
index 000000000..2bc431ab3
--- /dev/null
+++ b/src/coremods/core_user/cmd_part.cpp
@@ -0,0 +1,65 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandPart::CommandPart(Module* parent)
+ : Command(parent, "PART", 1, 2)
+{
+ Penalty = 5;
+ syntax = "<channel>{,<channel>} [<reason>]";
+}
+
+CmdResult CommandPart::Handle(User* user, const Params& parameters)
+{
+ std::string reason;
+ if (parameters.size() > 1)
+ {
+ if (IS_LOCAL(user))
+ msgwrap.Wrap(parameters[1], reason);
+ else
+ reason = parameters[1];
+ }
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ Channel* c = ServerInstance->FindChan(parameters[0]);
+
+ if (!c)
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+ }
+
+ if (!c->PartUser(user, reason))
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel");
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandPart::GetRouting(User* user, const Params& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_quit.cpp b/src/coremods/core_user/cmd_quit.cpp
new file mode 100644
index 000000000..d919a5761
--- /dev/null
+++ b/src/coremods/core_user/cmd_quit.cpp
@@ -0,0 +1,51 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandQuit::CommandQuit(Module* parent)
+ : Command(parent, "QUIT", 0, 1)
+ , operquit("operquit", ExtensionItem::EXT_USER, parent)
+{
+ works_before_reg = true;
+ syntax = "[<message>]";
+}
+
+CmdResult CommandQuit::Handle(User* user, const Params& parameters)
+{
+ std::string quitmsg;
+ if (parameters.empty())
+ quitmsg = "Client exited";
+ else if (IS_LOCAL(user))
+ msgwrap.Wrap(parameters[0], quitmsg);
+ else
+ quitmsg = parameters[0];
+
+ std::string* operquitmsg = operquit.get(user);
+ ServerInstance->Users->QuitUser(user, quitmsg, operquitmsg);
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandQuit::GetRouting(User* user, const Params& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_user.cpp b/src/coremods/core_user/cmd_user.cpp
new file mode 100644
index 000000000..1a8b091f3
--- /dev/null
+++ b/src/coremods/core_user/cmd_user.cpp
@@ -0,0 +1,80 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+enum
+{
+ // From ircu.
+ ERR_INVALIDUSERNAME = 468
+};
+
+CommandUser::CommandUser(Module* parent)
+ : SplitCommand(parent, "USER", 4, 4)
+{
+ allow_empty_last_param = false;
+ works_before_reg = true;
+ Penalty = 0;
+ syntax = "<username> <localhost> <remotehost> <realname>";
+}
+
+CmdResult CommandUser::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ /* A user may only send the USER command once */
+ if (!(user->registered & REG_USER))
+ {
+ if (!ServerInstance->IsIdent(parameters[0]))
+ {
+ user->WriteNumeric(ERR_INVALIDUSERNAME, name, "Your username is not valid");
+ return CMD_FAILURE;
+ }
+ else
+ {
+ user->ChangeIdent(parameters[0]);
+ user->ChangeRealName(parameters[3]);
+ user->registered = (user->registered | REG_USER);
+ }
+ }
+ else
+ {
+ user->WriteNumeric(ERR_ALREADYREGISTERED, "You may not reregister");
+ user->CommandFloodPenalty += 1000;
+ return CMD_FAILURE;
+ }
+
+ /* parameters 2 and 3 are local and remote hosts, and are ignored */
+ return CheckRegister(user);
+}
+
+CmdResult CommandUser::CheckRegister(LocalUser* user)
+{
+ // If the user is registered, return CMD_SUCCESS/CMD_FAILURE depending on what modules say, otherwise just
+ // return CMD_SUCCESS without doing anything, knowing the other handler will call us again
+ if (user->registered == REG_NICKUSER)
+ {
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_user/core_user.cpp b/src/coremods/core_user/core_user.cpp
new file mode 100644
index 000000000..e4e7a5056
--- /dev/null
+++ b/src/coremods/core_user/core_user.cpp
@@ -0,0 +1,184 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+/** Handle /PASS.
+ */
+class CommandPass : public SplitCommand
+{
+ public:
+ /** Constructor for pass.
+ */
+ CommandPass(Module* parent)
+ : SplitCommand(parent, "PASS", 1, 1)
+ {
+ works_before_reg = true;
+ Penalty = 0;
+ syntax = "<password>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ // Check to make sure they haven't registered -- Fix by FCS
+ if (user->registered == REG_ALL)
+ {
+ user->CommandFloodPenalty += 1000;
+ user->WriteNumeric(ERR_ALREADYREGISTERED, "You may not reregister");
+ return CMD_FAILURE;
+ }
+ user->password = parameters[0];
+
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /PING.
+ */
+class CommandPing : public SplitCommand
+{
+ public:
+ /** Constructor for ping.
+ */
+ CommandPing(Module* parent)
+ : SplitCommand(parent, "PING", 1, 2)
+ {
+ syntax = "<servername> [:<servername>]";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ ClientProtocol::Messages::Pong pong(parameters[0]);
+ user->Send(ServerInstance->GetRFCEvents().pong, pong);
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /PONG.
+ */
+class CommandPong : public Command
+{
+ public:
+ /** Constructor for pong.
+ */
+ CommandPong(Module* parent)
+ : Command(parent, "PONG", 0, 1)
+ {
+ Penalty = 0;
+ syntax = "<ping-text>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ // set the user as alive so they survive to next ping
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ {
+ // Increase penalty unless we've sent a PING and this is the reply
+ if (localuser->lastping)
+ localuser->CommandFloodPenalty += 1000;
+ else
+ localuser->lastping = 1;
+ }
+ return CMD_SUCCESS;
+ }
+};
+
+void MessageWrapper::Wrap(const std::string& message, std::string& out)
+{
+ // If there is a fixed message, it is stored in prefix. Otherwise prefix contains
+ // only the prefix, so append the message and the suffix
+ out.assign(prefix);
+ if (!fixed)
+ out.append(message).append(suffix);
+}
+
+void MessageWrapper::ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname)
+{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("options");
+ prefix = tag->getString(fixedname);
+ fixed = (!prefix.empty());
+ if (!fixed)
+ {
+ prefix = tag->getString(prefixname);
+ suffix = tag->getString(suffixname);
+ }
+}
+
+class CoreModUser : public Module
+{
+ CommandAway cmdaway;
+ CommandMode cmdmode;
+ CommandNick cmdnick;
+ CommandPart cmdpart;
+ CommandPass cmdpass;
+ CommandPing cmdping;
+ CommandPong cmdpong;
+ CommandQuit cmdquit;
+ CommandUser cmduser;
+ SimpleUserModeHandler invisiblemode;
+ ModeUserOperator operatormode;
+ ModeUserServerNoticeMask snomaskmode;
+
+ public:
+ CoreModUser()
+ : cmdaway(this)
+ , cmdmode(this)
+ , cmdnick(this)
+ , cmdpart(this)
+ , cmdpass(this)
+ , cmdping(this)
+ , cmdpong(this)
+ , cmdquit(this)
+ , cmduser(this)
+ , invisiblemode(this, "invisible", 'i')
+ , operatormode(this)
+ , snomaskmode(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ cmdpart.msgwrap.ReadConfig("prefixpart", "suffixpart", "fixedpart");
+ cmdquit.msgwrap.ReadConfig("prefixquit", "suffixquit", "fixedquit");
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the AWAY, MODE, NICK, PART, PASS, PING, PONG, QUIT and USER commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModUser)
diff --git a/src/coremods/core_user/core_user.h b/src/coremods/core_user/core_user.h
new file mode 100644
index 000000000..1365cd048
--- /dev/null
+++ b/src/coremods/core_user/core_user.h
@@ -0,0 +1,226 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+#include "listmode.h"
+#include "modules/away.h"
+
+class MessageWrapper
+{
+ std::string prefix;
+ std::string suffix;
+ bool fixed;
+
+ public:
+ /**
+ * Wrap the given message according to the config rules
+ * @param message The message to wrap
+ * @param out String where the result is placed
+ */
+ void Wrap(const std::string& message, std::string& out);
+
+ /**
+ * Read the settings from the given config keys (options block)
+ * @param prefixname Name of the config key to read the prefix from
+ * @param suffixname Name of the config key to read the suffix from
+ * @param fixedname Name of the config key to read the fixed string string from.
+ * If this key has a non-empty value, all messages will be replaced with it.
+ */
+ void ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname);
+};
+
+/** Handle /AWAY.
+ */
+class CommandAway : public Command
+{
+ private:
+ Away::EventProvider awayevprov;
+
+ public:
+ /** Constructor for away.
+ */
+ CommandAway(Module* parent);
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandMode : public Command
+{
+ unsigned int sent[256];
+ unsigned int seq;
+
+ /** Show the list of one or more list modes to a user.
+ * @param user User to send to.
+ * @param chan Channel whose lists to show.
+ * @param mode_sequence Mode letters to show the lists of.
+ */
+ void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
+
+ /** Show the current modes of a channel or a user to a user.
+ * @param user User to show the modes to.
+ * @param targetuser User whose modes to show. NULL if showing the modes of a channel.
+ * @param targetchannel Channel whose modes to show. NULL if showing the modes of a user.
+ */
+ void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
+
+ public:
+ /** Constructor for mode.
+ */
+ CommandMode(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /NICK.
+ */
+class CommandNick : public SplitCommand
+{
+ public:
+ /** Constructor for nick.
+ */
+ CommandNick(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /PART.
+ */
+class CommandPart : public Command
+{
+ public:
+ MessageWrapper msgwrap;
+
+ /** Constructor for part.
+ */
+ CommandPart(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /QUIT.
+ */
+class CommandQuit : public Command
+{
+ private:
+ StringExtItem operquit;
+
+ public:
+ MessageWrapper msgwrap;
+
+ /** Constructor for quit.
+ */
+ CommandQuit(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /USER.
+ */
+class CommandUser : public SplitCommand
+{
+ public:
+ /** Constructor for user.
+ */
+ CommandUser(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+
+ /** Run the OnUserRegister hook if the user has sent both NICK and USER. Called after an unregistered user
+ * successfully executes the USER or the NICK command.
+ * @param user User to inspect and possibly pass to the OnUserRegister hook
+ * @return CMD_FAILURE if OnUserRegister was called and it returned MOD_RES_DENY, CMD_SUCCESS in every other case
+ * (i.e. if the hook wasn't fired because the user still needs to send NICK/USER or if it was fired and finished with
+ * a non-MOD_RES_DENY result).
+ */
+ static CmdResult CheckRegister(LocalUser* user);
+};
+
+/** User mode +s
+ */
+class ModeUserServerNoticeMask : public ModeHandler
+{
+ /** Process a snomask modifier string, e.g. +abc-de
+ * @param user The target user
+ * @param input A sequence of notice mask characters
+ * @return The cleaned mode sequence which can be output,
+ * e.g. in the above example if masks c and e are not
+ * valid, this function will return +ab-d
+ */
+ std::string ProcessNoticeMasks(User* user, const std::string& input);
+
+ public:
+ ModeUserServerNoticeMask(Module* Creator);
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE;
+ void OnParameterMissing(User* user, User* dest, Channel* channel) CXX11_OVERRIDE;
+
+ /** Create a displayable mode string of the snomasks set on a given user
+ * @param user The user whose notice masks to format
+ * @return The notice mask character sequence
+ */
+ std::string GetUserParameter(const User* user) const CXX11_OVERRIDE;
+};
+
+/** User mode +o
+ */
+class ModeUserOperator : public ModeHandler
+{
+ public:
+ ModeUserOperator(Module* Creator);
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE;
+};
diff --git a/src/modes/umode_o.cpp b/src/coremods/core_user/umode_o.cpp
index a5f590ba0..20668fdaa 100644
--- a/src/modes/umode_o.cpp
+++ b/src/coremods/core_user/umode_o.cpp
@@ -19,12 +19,10 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_o.h"
+#include "core_user.h"
-ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE, MODETYPE_USER)
+ModeUserOperator::ModeUserOperator(Module* Creator)
+ : ModeHandler(Creator, "oper", 'o', PARAM_NONE, MODETYPE_USER)
{
oper = true;
}
@@ -32,7 +30,7 @@ ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE
ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, std::string&, bool adding)
{
/* Only opers can execute this class at all */
- if (!ServerInstance->ULine(source->server) && !IS_OPER(source))
+ if (!source->server->IsULine() && !source->IsOper())
return MODEACTION_DENY;
/* Not even opers can GIVE the +o mode, only take it away */
@@ -40,14 +38,13 @@ ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, st
return MODEACTION_DENY;
/* Set the bitfields.
- * Note that oper status is only given in cmd_oper.cpp
+ * Note that oper status is only given in User::Oper()
* NOT here. It is impossible to directly set +o without
* verifying as an oper and getting an opertype assigned
* to your User!
*/
char snomask = IS_LOCAL(dest) ? 'o' : 'O';
- ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(),
- source->nick.empty() ? source->server.c_str() : source->nick.c_str());
+ ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(), source->nick.c_str());
dest->UnOper();
return MODEACTION_ALLOW;
diff --git a/src/coremods/core_user/umode_s.cpp b/src/coremods/core_user/umode_s.cpp
new file mode 100644
index 000000000..0122ebe3e
--- /dev/null
+++ b/src/coremods/core_user/umode_s.cpp
@@ -0,0 +1,145 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+ModeUserServerNoticeMask::ModeUserServerNoticeMask(Module* Creator)
+ : ModeHandler(Creator, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
+{
+ oper = true;
+}
+
+ModeAction ModeUserServerNoticeMask::OnModeChange(User* source, User* dest, Channel*, std::string &parameter, bool adding)
+{
+ if (adding)
+ {
+ dest->SetMode(this, true);
+ // Process the parameter (remove chars we don't understand, remove redundant chars, etc.)
+ parameter = ProcessNoticeMasks(dest, parameter);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ if (dest->IsModeSet(this))
+ {
+ dest->SetMode(this, false);
+ dest->snomasks.reset();
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ // Mode not set and trying to unset, deny
+ return MODEACTION_DENY;
+}
+
+std::string ModeUserServerNoticeMask::GetUserParameter(const User* user) const
+{
+ std::string ret;
+ if (!user->IsModeSet(this))
+ return ret;
+
+ ret.push_back('+');
+ for (unsigned char n = 0; n < 64; n++)
+ {
+ if (user->snomasks[n])
+ ret.push_back(n + 'A');
+ }
+ return ret;
+}
+
+void ModeUserServerNoticeMask::OnParameterMissing(User* user, User* dest, Channel* channel)
+{
+ user->WriteNotice("*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.");
+}
+
+std::string ModeUserServerNoticeMask::ProcessNoticeMasks(User* user, const std::string& input)
+{
+ bool adding = true;
+ std::bitset<64> curr = user->snomasks;
+
+ for (std::string::const_iterator i = input.begin(); i != input.end(); ++i)
+ {
+ switch (*i)
+ {
+ case '+':
+ adding = true;
+ break;
+ case '-':
+ adding = false;
+ break;
+ case '*':
+ for (size_t j = 0; j < 64; j++)
+ {
+ if (ServerInstance->SNO->IsSnomaskUsable(j+'A'))
+ curr[j] = adding;
+ }
+ break;
+ default:
+ // For local users check whether the given snomask is valid and enabled - IsSnomaskUsable() tests both.
+ // For remote users accept what we were told, unless the snomask char is not a letter.
+ if (IS_LOCAL(user))
+ {
+ if (!ServerInstance->SNO->IsSnomaskUsable(*i))
+ {
+ user->WriteNumeric(ERR_UNKNOWNSNOMASK, *i, "is unknown snomask char to me");
+ continue;
+ }
+ }
+ else if (!(((*i >= 'a') && (*i <= 'z')) || ((*i >= 'A') && (*i <= 'Z'))))
+ continue;
+
+ size_t index = ((*i) - 'A');
+ curr[index] = adding;
+ break;
+ }
+ }
+
+ std::string plus = "+";
+ std::string minus = "-";
+
+ // Apply changes and construct two strings consisting of the newly added and the removed snomask chars
+ for (size_t i = 0; i < 64; i++)
+ {
+ bool isset = curr[i];
+ if (user->snomasks[i] != isset)
+ {
+ user->snomasks[i] = isset;
+ std::string& appendhere = (isset ? plus : minus);
+ appendhere.push_back(i+'A');
+ }
+ }
+
+ // Create the final string that will be shown to the user and sent to servers
+ // Form: "+ABc-de"
+ std::string output;
+ if (plus.length() > 1)
+ output = plus;
+
+ if (minus.length() > 1)
+ output += minus;
+
+ // Unset the snomask usermode itself if every snomask was unset
+ if (user->snomasks.none())
+ user->SetMode(this, false);
+
+ return output;
+}
diff --git a/src/commands/cmd_userhost.cpp b/src/coremods/core_userhost.cpp
index 7e1efd637..542c1831d 100644
--- a/src/commands/cmd_userhost.cpp
+++ b/src/coremods/core_userhost.cpp
@@ -20,78 +20,83 @@
#include "inspircd.h"
-/** Handle /USERHOST. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
+/** Handle /USERHOST.
*/
class CommandUserhost : public Command
{
+ UserModeReference hideopermode;
+
public:
/** Constructor for userhost.
*/
- CommandUserhost ( Module* parent) : Command(parent,"USERHOST", 1) {
+ CommandUserhost(Module* parent)
+ : Command(parent,"USERHOST", 1)
+ , hideopermode(parent, "hideoper")
+ {
syntax = "<nick> [<nick> ...]";
}
/** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
+ * @param parameters The parameters to the command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
};
-CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandUserhost::Handle(User* user, const Params& parameters)
{
- std::string retbuf = "302 " + user->nick + " :";
+ const bool has_privs = user->HasPrivPermission("users/auspex");
+
+ std::string retbuf;
unsigned int max = parameters.size();
if (max > 5)
max = 5;
- bool has_privs = user->HasPrivPermission("users/auspex");
for (unsigned int i = 0; i < max; i++)
{
User *u = ServerInstance->FindNickOnly(parameters[i]);
if ((u) && (u->registered == REG_ALL))
{
- retbuf = retbuf + u->nick;
+ retbuf += u->nick;
- if (IS_OPER(u))
+ if (u->IsOper())
{
// XXX: +H hidden opers must not be shown as opers
- ModeHandler* mh = ServerInstance->Modes->FindMode('H', MODETYPE_USER);
- if ((u == user) || (has_privs) || (!mh) || (!u->IsModeSet('H')) || (mh->name != "hideoper"))
+ if ((u == user) || (has_privs) || (!u->IsModeSet(hideopermode)))
retbuf += '*';
}
- retbuf = retbuf + "=";
-
- if (IS_AWAY(u))
- retbuf += "-";
- else
- retbuf += "+";
-
- retbuf = retbuf + u->ident + "@";
-
- if (has_privs)
- {
- retbuf = retbuf + u->host;
- }
- else
- {
- retbuf = retbuf + u->dhost;
- }
-
- retbuf = retbuf + " ";
+ retbuf += '=';
+ retbuf += (u->IsAway() ? '-' : '+');
+ retbuf += u->ident;
+ retbuf += '@';
+ retbuf += u->GetHost(u == user || has_privs);
+ retbuf += ' ';
}
}
- user->WriteServ(retbuf);
+ user->WriteNumeric(RPL_USERHOST, retbuf);
return CMD_SUCCESS;
}
-COMMAND_INIT(CommandUserhost)
+class CoreModUserhost : public Module
+{
+ private:
+ CommandUserhost cmd;
+
+ public:
+ CoreModUserhost()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the USERHOST command", VF_CORE | VF_VENDOR);
+ }
+};
+
+MODULE_INIT(CoreModUserhost)
diff --git a/src/coremods/core_wallops.cpp b/src/coremods/core_wallops.cpp
new file mode 100644
index 000000000..6b055f874
--- /dev/null
+++ b/src/coremods/core_wallops.cpp
@@ -0,0 +1,89 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /WALLOPS.
+ */
+class CommandWallops : public Command
+{
+ SimpleUserModeHandler wallopsmode;
+ ClientProtocol::EventProvider protoevprov;
+
+ public:
+ /** Constructor for wallops.
+ */
+ CommandWallops(Module* parent)
+ : Command(parent, "WALLOPS", 1, 1)
+ , wallopsmode(parent, "wallops", 'w')
+ , protoevprov(parent, name)
+ {
+ flags_needed = 'o';
+ syntax = "<any-text>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ return ROUTE_BROADCAST;
+ }
+};
+
+CmdResult CommandWallops::Handle(User* user, const Params& parameters)
+{
+ ClientProtocol::Message msg("WALLOPS", user);
+ msg.PushParamRef(parameters[0]);
+ ClientProtocol::Event wallopsevent(protoevprov, msg);
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* curr = *i;
+ if (curr->IsModeSet(wallopsmode))
+ curr->Send(wallopsevent);
+ }
+
+ return CMD_SUCCESS;
+}
+
+class CoreModWallops : public Module
+{
+ private:
+ CommandWallops cmd;
+
+ public:
+ CoreModWallops()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the WALLOPS command", VF_CORE | VF_VENDOR);
+ }
+};
+
+MODULE_INIT(CoreModWallops)
diff --git a/src/coremods/core_who.cpp b/src/coremods/core_who.cpp
new file mode 100644
index 000000000..362e79c03
--- /dev/null
+++ b/src/coremods/core_who.cpp
@@ -0,0 +1,591 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
+ * Copyright (C) 2014 Adam <Adam@anope.org>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/account.h"
+
+enum
+{
+ // From RFC 1459.
+ RPL_ENDOFWHO = 315,
+ RPL_WHOREPLY = 352,
+
+ // From ircu.
+ RPL_WHOSPCRPL = 354
+};
+
+struct WhoData
+{
+ // The flags for matching users to include.
+ std::bitset<UCHAR_MAX> flags;
+
+ // Whether we are matching using a wildcard or a flag.
+ bool fuzzy_match;
+
+ // The text to match against.
+ std::string matchtext;
+
+ // The WHO/WHOX responses we will send to the source.
+ std::vector<Numeric::Numeric> results;
+
+ // Whether the source requested a WHOX response.
+ bool whox;
+
+ // The fields to include in the WHOX response.
+ std::bitset<UCHAR_MAX> whox_fields;
+
+ // A user specified label for the WHOX response.
+ std::string whox_querytype;
+
+ WhoData(const CommandBase::Params& parameters)
+ : whox(false)
+ {
+ // Find the matchtext and swap the 0 for a * so we can use InspIRCd::Match on it.
+ matchtext = parameters.size() > 2 ? parameters[2] : parameters[0];
+ if (matchtext == "0")
+ matchtext = "*";
+
+ // Fuzzy matches are when the source has not specified a specific user.
+ fuzzy_match = (parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos);
+
+ // If flags have been specified by the source.
+ if (parameters.size() > 1)
+ {
+ std::bitset<UCHAR_MAX>* current_bitset = &flags;
+ for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
+ {
+ unsigned char chr = static_cast<unsigned char>(*iter);
+
+ // If the source specifies a percentage the rest of the flags are WHOX fields.
+ if (chr == '%')
+ {
+ whox = true;
+ current_bitset = &whox_fields;
+ continue;
+ }
+
+ // If we are in WHOX mode and the source specifies a comma
+ // the rest of the parameter is the query type.
+ if (whox && chr == ',')
+ {
+ whox_querytype.assign(++iter, parameters[1].end());
+ break;
+ }
+
+ // The source specified a matching flag.
+ current_bitset->set(chr);
+ }
+ }
+ }
+};
+
+class CommandWho : public SplitCommand
+{
+ private:
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference hidechansmode;
+ UserModeReference invisiblemode;
+
+ /** Determines whether a user can view the users of a channel. */
+ bool CanView(Channel* chan, User* user)
+ {
+ // If we are in a channel we can view all users in it.
+ if (chan->HasUser(user))
+ return true;
+
+ // Opers with the users/auspex priv can see everything.
+ if (user->HasPrivPermission("users/auspex"))
+ return true;
+
+ // You can see inside a channel from outside unless it is secret or private.
+ return !chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode);
+ }
+
+ /** Gets the first channel which is visible between the source and the target users. */
+ Membership* GetFirstVisibleChannel(LocalUser* source, User* user)
+ {
+ for (User::ChanList::iterator iter = user->chans.begin(); iter != user->chans.end(); ++iter)
+ {
+ Membership* memb = *iter;
+
+ // TODO: move the +I check into m_hidechans.
+ bool has_modes = memb->chan->IsModeSet(secretmode) || memb->chan->IsModeSet(privatemode) || user->IsModeSet(hidechansmode);
+ if (source == user || !has_modes || memb->chan->HasUser(source))
+ return memb;
+ }
+ return NULL;
+ }
+
+ /** Determines whether WHO flags match a specific channel user. */
+ static bool MatchChannel(LocalUser* source, Membership* memb, WhoData& data);
+
+ /** Determines whether WHO flags match a specific user. */
+ static bool MatchUser(LocalUser* source, User* target, WhoData& data);
+
+ /** Performs a WHO request on a channel. */
+ void WhoChannel(LocalUser* source, const std::vector<std::string>& parameters, Channel* c, WhoData& data);
+
+ /** Template for getting a user from various types of collection. */
+ template<typename T>
+ static User* GetUser(T& t);
+
+ /** Performs a WHO request on a list of users. */
+ template<typename T>
+ void WhoUsers(LocalUser* source, const std::vector<std::string>& parameters, const T& users, WhoData& data);
+
+ public:
+ CommandWho(Module* parent)
+ : SplitCommand(parent, "WHO", 1, 3)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , hidechansmode(parent, "hidechans")
+ , invisiblemode(parent, "invisible")
+ {
+ allow_empty_last_param = false;
+ syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [[Aafhilmnoprstu] <server>|<nickname>|<channel>|<realname>|<host>|0]";
+ }
+
+ /** Sends a WHO reply to a user. */
+ void SendWhoLine(LocalUser* user, const std::vector<std::string>& parameters, Membership* memb, User* u, WhoData& data);
+
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+template<> User* CommandWho::GetUser(UserManager::OperList::const_iterator& t) { return *t; }
+template<> User* CommandWho::GetUser(user_hash::const_iterator& t) { return t->second; }
+
+bool CommandWho::MatchChannel(LocalUser* source, Membership* memb, WhoData& data)
+{
+ bool source_has_users_auspex = source->HasPrivPermission("users/auspex");
+ bool source_can_see_server = ServerInstance->Config->HideServer.empty() || source_has_users_auspex;
+
+ // The source only wants remote users. This user is eligible if:
+ // (1) The source can't see server information.
+ // (2) The source is not local to the current server.
+ LocalUser* lu = IS_LOCAL(memb->user);
+ if (data.flags['f'] && source_can_see_server && lu)
+ return false;
+
+ // The source only wants local users. This user is eligible if:
+ // (1) The source can't see server information.
+ // (2) The source is local to the current server.
+ if (data.flags['l'] && source_can_see_server && !lu)
+ return false;
+
+ // Only show operators if the oper flag has been specified.
+ if (data.flags['o'] && !memb->user->IsOper())
+ return false;
+
+ // All other flags are ignored for channels.
+ return true;
+}
+
+bool CommandWho::MatchUser(LocalUser* source, User* user, WhoData& data)
+{
+ // Users who are not fully registered can never match.
+ if (user->registered != REG_ALL)
+ return false;
+
+ bool source_has_users_auspex = source->HasPrivPermission("users/auspex");
+ bool source_can_see_target = source == user || source_has_users_auspex;
+ bool source_can_see_server = ServerInstance->Config->HideServer.empty() || source_has_users_auspex;
+
+ // The source only wants remote users. This user is eligible if:
+ // (1) The source can't see server information.
+ // (2) The source is not local to the current server.
+ LocalUser* lu = IS_LOCAL(user);
+ if (data.flags['f'] && source_can_see_server && lu)
+ return false;
+
+ // The source only wants local users. This user is eligible if:
+ // (1) The source can't see server information.
+ // (2) The source is local to the current server.
+ if (data.flags['l'] && source_can_see_server && !lu)
+ return false;
+
+ // The source wants to match against users' away messages.
+ bool match = false;
+ if (data.flags['A'])
+ match = user->IsAway() && InspIRCd::Match(user->awaymsg, data.matchtext, ascii_case_insensitive_map);
+
+ // The source wants to match against users' account names.
+ else if (data.flags['a'])
+ {
+ const AccountExtItem* accountext = GetAccountExtItem();
+ const std::string* account = accountext ? accountext->get(user) : NULL;
+ match = account && InspIRCd::Match(*account, data.matchtext);
+ }
+
+ // The source wants to match against users' hostnames.
+ else if (data.flags['h'])
+ {
+ const std::string host = user->GetHost(source_can_see_target && data.flags['x']);
+ match = InspIRCd::Match(host, data.matchtext, ascii_case_insensitive_map);
+ }
+
+ // The source wants to match against users' IP addresses.
+ else if (data.flags['i'])
+ match = source_can_see_target && InspIRCd::MatchCIDR(user->GetIPString(), data.matchtext, ascii_case_insensitive_map);
+
+ // The source wants to match against users' modes.
+ else if (data.flags['m'])
+ {
+ if (source_can_see_target)
+ {
+ bool set = true;
+ for (std::string::const_iterator iter = data.matchtext.begin(); iter != data.matchtext.end(); ++iter)
+ {
+ unsigned char chr = static_cast<unsigned char>(*iter);
+ switch (chr)
+ {
+ // The following user modes should be set.
+ case '+':
+ set = true;
+ break;
+
+ // The following user modes should be unset.
+ case '-':
+ set = false;
+ break;
+
+ default:
+ if (user->IsModeSet(chr) != set)
+ return false;
+ break;
+ }
+ }
+
+ // All of the modes matched.
+ return true;
+ }
+ }
+
+ // The source wants to match against users' nicks.
+ else if (data.flags['n'])
+ match = InspIRCd::Match(user->nick, data.matchtext);
+
+ // The source wants to match against users' connection ports.
+ else if (data.flags['p'])
+ {
+ if (source_can_see_target && lu)
+ {
+ irc::portparser portrange(data.matchtext, false);
+ long port;
+ while ((port = portrange.GetToken()))
+ {
+ if (port == lu->GetServerPort())
+ {
+ match = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // The source wants to match against users' real names.
+ else if (data.flags['r'])
+ match = InspIRCd::Match(user->GetRealName(), data.matchtext, ascii_case_insensitive_map);
+
+ else if (data.flags['s'])
+ {
+ bool show_real_server_name = ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']);
+ const std::string server = show_real_server_name ? user->server->GetName() : ServerInstance->Config->HideServer;
+ match = InspIRCd::Match(server, data.matchtext, ascii_case_insensitive_map);
+ }
+
+ // The source wants to match against users' connection times.
+ else if (data.flags['t'])
+ {
+ time_t seconds = ServerInstance->Time() - InspIRCd::Duration(data.matchtext);
+ if (user->signon >= seconds)
+ match = true;
+ }
+
+ // The source wants to match against users' idents.
+ else if (data.flags['u'])
+ match = InspIRCd::Match(user->ident, data.matchtext, ascii_case_insensitive_map);
+
+ // The <name> passed to WHO is matched against users' host, server,
+ // real name and nickname if the channel <name> cannot be found.
+ else
+ {
+ const std::string host = user->GetHost(source_can_see_target && data.flags['x']);
+ match = InspIRCd::Match(host, data.matchtext, ascii_case_insensitive_map);
+
+ if (!match)
+ {
+ bool show_real_server_name = ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']);
+ const std::string server = show_real_server_name ? user->server->GetName() : ServerInstance->Config->HideServer;
+ match = InspIRCd::Match(server, data.matchtext, ascii_case_insensitive_map);
+ }
+
+ if (!match)
+ match = InspIRCd::Match(user->GetRealName(), data.matchtext, ascii_case_insensitive_map);
+
+ if (!match)
+ match = InspIRCd::Match(user->nick, data.matchtext);
+ }
+
+ return match;
+}
+
+void CommandWho::WhoChannel(LocalUser* source, const std::vector<std::string>& parameters, Channel* chan, WhoData& data)
+{
+ if (!CanView(chan, source))
+ return;
+
+ bool inside = chan->HasUser(source);
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator iter = users.begin(); iter != users.end(); ++iter)
+ {
+ User* user = iter->first;
+ Membership* memb = iter->second;
+
+ // Only show invisible users if the source is in the channel or has the users/auspex priv.
+ if (!inside && user->IsModeSet(invisiblemode) && !source->HasPrivPermission("users/auspex"))
+ continue;
+
+ // Skip the user if it doesn't match the query.
+ if (!MatchChannel(source, memb, data))
+ continue;
+
+ SendWhoLine(source, parameters, memb, user, data);
+ }
+}
+
+template<typename T>
+void CommandWho::WhoUsers(LocalUser* source, const std::vector<std::string>& parameters, const T& users, WhoData& data)
+{
+ for (typename T::const_iterator iter = users.begin(); iter != users.end(); ++iter)
+ {
+ User* user = GetUser(iter);
+
+ // Only show users in response to a fuzzy WHO if we can see them normally.
+ bool can_see_normally = user == source || source->SharesChannelWith(user) || !user->IsModeSet(invisiblemode);
+ if (data.fuzzy_match && !can_see_normally && !source->HasPrivPermission("users/auspex"))
+ continue;
+
+ // Skip the user if it doesn't match the query.
+ if (!MatchUser(source, user, data))
+ continue;
+
+ SendWhoLine(source, parameters, NULL, user, data);
+ }
+}
+
+void CommandWho::SendWhoLine(LocalUser* source, const std::vector<std::string>& parameters, Membership* memb, User* user, WhoData& data)
+{
+ if (!memb)
+ memb = GetFirstVisibleChannel(source, user);
+
+ bool source_can_see_target = source == user || source->HasPrivPermission("users/auspex");
+ Numeric::Numeric wholine(data.whox ? RPL_WHOSPCRPL : RPL_WHOREPLY);
+ if (data.whox)
+ {
+ // The source used WHOX so we send a fancy customised response.
+
+ // Include the query type in the reply.
+ if (data.whox_fields['t'])
+ wholine.push(data.whox_querytype.empty() || data.whox_querytype.length() > 3 ? "0" : data.whox_querytype);
+
+ // Include the first channel name.
+ if (data.whox_fields['c'])
+ wholine.push(memb ? memb->chan->name : "*");
+
+ // Include the user's ident.
+ if (data.whox_fields['u'])
+ wholine.push(user->ident);
+
+ // Include the user's IP address.
+ if (data.whox_fields['i'])
+ wholine.push(source_can_see_target ? user->GetIPString() : "255.255.255.255");
+
+ // Include the user's hostname.
+ if (data.whox_fields['h'])
+ wholine.push(user->GetHost(source_can_see_target && data.flags['x']));
+
+ // Include the server name.
+ if (data.whox_fields['s'])
+ {
+ if (ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']))
+ wholine.push(user->server->GetName());
+ else
+ wholine.push(ServerInstance->Config->HideServer);
+ }
+
+ // Include the user's nickname.
+ if (data.whox_fields['n'])
+ wholine.push(user->nick);
+
+ // Include the user's flags.
+ if (data.whox_fields['f'])
+ {
+ // Away state.
+ std::string flags(user->IsAway() ? "G" : "H");
+
+ // Operator status.
+ if (user->IsOper())
+ flags.push_back('*');
+
+ // Membership prefix.
+ if (memb)
+ {
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ flags.push_back(prefix);
+ }
+
+ wholine.push(flags);
+ }
+
+ // Include the number of hops between the users.
+ if (data.whox_fields['d'])
+ wholine.push("0");
+
+ // Include the user's idle time.
+ if (data.whox_fields['l'])
+ {
+ LocalUser* lu = IS_LOCAL(user);
+ unsigned long idle = lu ? ServerInstance->Time() - lu->idle_lastmsg : 0;
+ wholine.push(ConvToStr(idle));
+ }
+
+ // Include the user's account name.
+ if (data.whox_fields['a'])
+ {
+ const AccountExtItem* accountext = GetAccountExtItem();
+ const std::string* account = accountext ? accountext->get(user) : NULL;
+ wholine.push(account ? *account : "0");
+ }
+
+ // Include the user's operator rank level.
+ if (data.whox_fields['o'])
+ wholine.push(memb ? ConvToStr(memb->getRank()) : "0");
+
+ // Include the user's real name.
+ if (data.whox_fields['r'])
+ wholine.push(user->GetRealName());
+ }
+ else
+ {
+ // We are not using WHOX so we just send a plain RFC response.
+
+ // Include the channel name.
+ wholine.push(memb ? memb->chan->name : "*");
+
+ // Include the user's ident.
+ wholine.push(user->ident);
+
+ // Include the user's hostname.
+ wholine.push(user->GetHost(source_can_see_target && data.flags['x']));
+
+ // Include the server name.
+ if (ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']))
+ wholine.push(user->server->GetName());
+ else
+ wholine.push(ServerInstance->Config->HideServer);
+
+ // Include the user's nick.
+ wholine.push(user->nick);
+
+ // Include the user's flags.
+ {
+ // Away state.
+ std::string flags(user->IsAway() ? "G" : "H");
+
+ // Operator status.
+ if (user->IsOper())
+ flags.push_back('*');
+
+ // Membership prefix.
+ if (memb)
+ {
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ flags.push_back(prefix);
+ }
+
+ wholine.push(flags);
+ }
+
+ // Include the number of hops between the users and the user's real name.
+ wholine.push("0 ");
+ wholine.GetParams().back().append(user->GetRealName());
+ }
+
+ ModResult res;
+ FIRST_MOD_RESULT(OnSendWhoLine, res, (source, parameters, user, memb, wholine));
+ if (res != MOD_RES_DENY)
+ data.results.push_back(wholine);
+}
+
+CmdResult CommandWho::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ WhoData data(parameters);
+
+ // Is the source running a WHO on a channel?
+ Channel* chan = ServerInstance->FindChan(data.matchtext);
+ if (chan)
+ WhoChannel(user, parameters, chan, data);
+
+ // If we only want to match against opers we only have to iterate the oper list.
+ else if (data.flags['o'])
+ WhoUsers(user, parameters, ServerInstance->Users->all_opers, data);
+
+ // Otherwise we have to use the global user list.
+ else
+ WhoUsers(user, parameters, ServerInstance->Users->GetUsers(), data);
+
+ // Send the results to the source.
+ for (std::vector<Numeric::Numeric>::const_iterator n = data.results.begin(); n != data.results.end(); ++n)
+ user->WriteNumeric(*n);
+ user->WriteNumeric(RPL_ENDOFWHO, (data.matchtext.empty() ? "*" : data.matchtext.c_str()), "End of /WHO list.");
+
+ // Penalize the source a bit for large queries with one unit of penalty per 200 results.
+ user->CommandFloodPenalty += data.results.size() * 5;
+ return CMD_SUCCESS;
+}
+
+class CoreModWho : public Module
+{
+ private:
+ CommandWho cmd;
+
+ public:
+ CoreModWho()
+ : cmd(this)
+ {
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["WHOX"];
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the WHO command", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModWho)
diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp
new file mode 100644
index 000000000..7fbaf2226
--- /dev/null
+++ b/src/coremods/core_whois.cpp
@@ -0,0 +1,370 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/whois.h"
+
+enum
+{
+ // From RFC 1459.
+ RPL_WHOISUSER = 311,
+ RPL_WHOISOPERATOR = 313,
+ RPL_WHOISIDLE = 317,
+ RPL_WHOISCHANNELS = 319,
+
+ // From UnrealIRCd.
+ RPL_WHOISHOST = 378,
+ RPL_WHOISMODES = 379,
+
+ // InspIRCd-specific.
+ RPL_CHANNELSMSG = 651
+};
+
+enum SplitWhoisState
+{
+ // Don't split private/secret channels into a separate RPL_WHOISCHANNELS numeric.
+ SPLITWHOIS_NONE,
+
+ // Split private/secret channels into a separate RPL_WHOISCHANNELS numeric.
+ SPLITWHOIS_SPLIT,
+
+ // Split private/secret channels into a separate RPL_WHOISCHANNELS numeric with RPL_CHANNELSMSG to explain the split.
+ SPLITWHOIS_SPLITMSG
+};
+
+class WhoisContextImpl : public Whois::Context
+{
+ Events::ModuleEventProvider& lineevprov;
+
+ public:
+ WhoisContextImpl(LocalUser* src, User* targ, Events::ModuleEventProvider& evprov)
+ : Whois::Context(src, targ)
+ , lineevprov(evprov)
+ {
+ }
+
+ using Whois::Context::SendLine;
+ void SendLine(Numeric::Numeric& numeric) CXX11_OVERRIDE;
+};
+
+void WhoisContextImpl::SendLine(Numeric::Numeric& numeric)
+{
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT_CUSTOM(lineevprov, Whois::LineEventListener, OnWhoisLine, MOD_RESULT, (*this, numeric));
+
+ if (MOD_RESULT != MOD_RES_DENY)
+ source->WriteNumeric(numeric);
+}
+
+/** Handle /WHOIS.
+ */
+class CommandWhois : public SplitCommand
+{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference snomaskmode;
+ Events::ModuleEventProvider evprov;
+ Events::ModuleEventProvider lineevprov;
+
+ void DoWhois(LocalUser* user, User* dest, time_t signon, unsigned long idle);
+ void SendChanList(WhoisContextImpl& whois);
+
+ public:
+ SplitWhoisState splitwhois;
+
+ /** Constructor for whois.
+ */
+ CommandWhois(Module* parent)
+ : SplitCommand(parent, "WHOIS", 1)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , snomaskmode(parent, "snomask")
+ , evprov(parent, "event/whois")
+ , lineevprov(parent, "event/whoisline")
+ {
+ Penalty = 2;
+ syntax = "<nick>{,<nick>}";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+ CmdResult HandleRemote(RemoteUser* target, const Params& parameters) CXX11_OVERRIDE;
+};
+
+class WhoisNumericSink
+{
+ WhoisContextImpl& whois;
+ public:
+ WhoisNumericSink(WhoisContextImpl& whoisref)
+ : whois(whoisref)
+ {
+ }
+
+ void operator()(Numeric::Numeric& numeric) const
+ {
+ whois.SendLine(numeric);
+ }
+};
+
+class WhoisChanListNumericBuilder : public Numeric::GenericBuilder<' ', false, WhoisNumericSink>
+{
+ public:
+ WhoisChanListNumericBuilder(WhoisContextImpl& whois)
+ : Numeric::GenericBuilder<' ', false, WhoisNumericSink>(WhoisNumericSink(whois), RPL_WHOISCHANNELS, false, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1)
+ {
+ GetNumeric().push(whois.GetTarget()->nick).push(std::string());
+ }
+};
+
+class WhoisChanList
+{
+ const SplitWhoisState& splitwhois;
+ WhoisChanListNumericBuilder num;
+ WhoisChanListNumericBuilder secretnum;
+ std::string prefixstr;
+
+ void AddMember(Membership* memb, WhoisChanListNumericBuilder& out)
+ {
+ prefixstr.clear();
+ const char prefix = memb->GetPrefixChar();
+ if (prefix)
+ prefixstr.push_back(prefix);
+ out.Add(prefixstr, memb->chan->name);
+ }
+
+ public:
+ WhoisChanList(WhoisContextImpl& whois, const SplitWhoisState& sws)
+ : splitwhois(sws)
+ , num(whois)
+ , secretnum(whois)
+ {
+ }
+
+ void AddVisible(Membership* memb)
+ {
+ AddMember(memb, num);
+ }
+
+ void AddHidden(Membership* memb)
+ {
+ AddMember(memb, splitwhois == SPLITWHOIS_NONE ? num : secretnum);
+ }
+
+ void Flush(WhoisContextImpl& whois)
+ {
+ num.Flush();
+ if (!secretnum.IsEmpty() && splitwhois == SPLITWHOIS_SPLITMSG)
+ whois.SendLine(RPL_CHANNELSMSG, "is on private/secret channels:");
+ secretnum.Flush();
+ }
+};
+
+void CommandWhois::SendChanList(WhoisContextImpl& whois)
+{
+ WhoisChanList chanlist(whois, splitwhois);
+
+ User* const target = whois.GetTarget();
+ bool hasoperpriv = whois.GetSource()->HasPrivPermission("users/channel-spy");
+ for (User::ChanList::iterator i = target->chans.begin(); i != target->chans.end(); ++i)
+ {
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+
+ // Anyone can view channels which are not private or secret.
+ if (!c->IsModeSet(privatemode) && !c->IsModeSet(secretmode))
+ chanlist.AddVisible(memb);
+
+ // Hidden channels are visible when the following conditions are true:
+ // (1) The source user and the target user are the same.
+ // (2) The source user is a member of the hidden channel.
+ // (3) The source user is an oper with the users/channel-spy privilege.
+ else if (whois.IsSelfWhois() || c->HasUser(whois.GetSource()) || hasoperpriv)
+ chanlist.AddHidden(memb);
+ }
+
+ chanlist.Flush(whois);
+}
+
+void CommandWhois::DoWhois(LocalUser* user, User* dest, time_t signon, unsigned long idle)
+{
+ WhoisContextImpl whois(user, dest, lineevprov);
+
+ whois.SendLine(RPL_WHOISUSER, dest->ident, dest->GetDisplayedHost(), '*', dest->GetRealName());
+ if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+ {
+ whois.SendLine(RPL_WHOISHOST, InspIRCd::Format("is connecting from %s@%s %s", dest->ident.c_str(), dest->GetRealHost().c_str(), dest->GetIPString().c_str()));
+ }
+
+ SendChanList(whois);
+
+ if (!whois.IsSelfWhois() && !ServerInstance->Config->HideServer.empty() && !user->HasPrivPermission("servers/auspex"))
+ {
+ whois.SendLine(RPL_WHOISSERVER, ServerInstance->Config->HideServer, ServerInstance->Config->Network);
+ }
+ else
+ {
+ whois.SendLine(RPL_WHOISSERVER, dest->server->GetName(), dest->server->GetDesc());
+ }
+
+ if (dest->IsAway())
+ {
+ whois.SendLine(RPL_AWAY, dest->awaymsg);
+ }
+
+ if (dest->IsOper())
+ {
+ if (ServerInstance->Config->GenericOper)
+ whois.SendLine(RPL_WHOISOPERATOR, "is an IRC operator");
+ else
+ whois.SendLine(RPL_WHOISOPERATOR, InspIRCd::Format("is %s %s on %s", (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"), dest->oper->name.c_str(), ServerInstance->Config->Network.c_str()));
+ }
+
+ if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+ {
+ if (dest->IsModeSet(snomaskmode))
+ {
+ whois.SendLine(RPL_WHOISMODES, InspIRCd::Format("is using modes %s %s", dest->GetModeLetters().c_str(), snomaskmode->GetUserParameter(dest).c_str()));
+ }
+ else
+ {
+ whois.SendLine(RPL_WHOISMODES, InspIRCd::Format("is using modes %s", dest->GetModeLetters().c_str()));
+ }
+ }
+
+ FOREACH_MOD_CUSTOM(evprov, Whois::EventListener, OnWhois, (whois));
+
+ /*
+ * We only send these if we've been provided them. That is, if hideserver is turned off, and user is local, or
+ * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
+ */
+ if ((idle) || (signon))
+ {
+ whois.SendLine(RPL_WHOISIDLE, idle, signon, "seconds idle, signon time");
+ }
+
+ whois.SendLine(RPL_ENDOFWHOIS, "End of /WHOIS list.");
+}
+
+CmdResult CommandWhois::HandleRemote(RemoteUser* target, const Params& parameters)
+{
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
+
+ User* user = ServerInstance->FindUUID(parameters[0]);
+ if (!user)
+ return CMD_FAILURE;
+
+ // User doing the whois must be on this server
+ LocalUser* localuser = IS_LOCAL(user);
+ if (!localuser)
+ return CMD_FAILURE;
+
+ unsigned long idle = ConvToNum<unsigned long>(parameters.back());
+ DoWhois(localuser, target, target->signon, idle);
+
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandWhois::HandleLocal(LocalUser* user, const Params& parameters)
+{
+ User *dest;
+ unsigned int userindex = 0;
+ unsigned long idle = 0;
+ time_t signon = 0;
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ /*
+ * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
+ * does, and use the second one, otherwise, use the only paramter. -- djGrrr
+ */
+ if (parameters.size() > 1)
+ userindex = 1;
+
+ dest = ServerInstance->FindNickOnly(parameters[userindex]);
+
+ if ((dest) && (dest->registered == REG_ALL))
+ {
+ /*
+ * Okay. Umpteenth attempt at doing this, so let's re-comment...
+ * For local users (/w localuser), we show idletime if hideserver is disabled
+ * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
+ * For remote users (/w remoteuser), we do NOT show idletime
+ * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
+ * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
+ */
+ LocalUser* localuser = IS_LOCAL(dest);
+ if (localuser && (ServerInstance->Config->HideServer.empty() || parameters.size() > 1))
+ {
+ idle = labs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
+ signon = dest->signon;
+ }
+
+ DoWhois(user,dest,signon,idle);
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteNumeric(Numerics::NoSuchNick(!parameters[userindex].empty() ? parameters[userindex] : "*"));
+ user->WriteNumeric(RPL_ENDOFWHOIS, (!parameters[userindex].empty() ? parameters[userindex] : "*"), "End of /WHOIS list.");
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+class CoreModWhois : public Module
+{
+ private:
+ CommandWhois cmd;
+
+ public:
+ CoreModWhois()
+ : cmd(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("options");
+ const std::string splitwhois = tag->getString("splitwhois", "no");
+ if (stdalgo::string::equalsci(splitwhois, "no"))
+ cmd.splitwhois = SPLITWHOIS_NONE;
+ else if (stdalgo::string::equalsci(splitwhois, "split"))
+ cmd.splitwhois = SPLITWHOIS_SPLIT;
+ else if (stdalgo::string::equalsci(splitwhois, "splitmsg"))
+ cmd.splitwhois = SPLITWHOIS_SPLITMSG;
+ else
+ throw ModuleException(splitwhois + " is an invalid <security:splitwhois> value, at " + tag->getTagLocation());
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the WHOIS command", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModWhois)
diff --git a/src/coremods/core_whowas.cpp b/src/coremods/core_whowas.cpp
new file mode 100644
index 000000000..65c83e08b
--- /dev/null
+++ b/src/coremods/core_whowas.cpp
@@ -0,0 +1,310 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "commands/cmd_whowas.h"
+#include "modules/stats.h"
+
+enum
+{
+ // From RFC 1459.
+ RPL_WHOWASUSER = 314,
+ RPL_ENDOFWHOWAS = 369,
+
+ // InspIRCd-specific.
+ RPL_WHOWASIP = 652
+};
+
+CommandWhowas::CommandWhowas( Module* parent)
+ : Command(parent, "WHOWAS", 1)
+{
+ syntax = "<nick>{,<nick>}";
+ Penalty = 2;
+}
+
+CmdResult CommandWhowas::Handle(User* user, const Params& parameters)
+{
+ /* if whowas disabled in config */
+ if (!manager.IsEnabled())
+ {
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, name, "This command has been disabled.");
+ return CMD_FAILURE;
+ }
+
+ const WhoWas::Nick* const nick = manager.FindNick(parameters[0]);
+ if (!nick)
+ {
+ user->WriteNumeric(ERR_WASNOSUCHNICK, parameters[0], "There was no such nickname");
+ }
+ else
+ {
+ const WhoWas::Nick::List& list = nick->entries;
+ for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ WhoWas::Entry* u = *i;
+
+ user->WriteNumeric(RPL_WHOWASUSER, parameters[0], u->ident, u->dhost, '*', u->real);
+
+ if (user->HasPrivPermission("users/auspex"))
+ user->WriteNumeric(RPL_WHOWASIP, parameters[0], InspIRCd::Format("was connecting from *@%s", u->host.c_str()));
+
+ std::string signon = InspIRCd::TimeString(u->signon);
+ bool hide_server = (!ServerInstance->Config->HideServer.empty() && !user->HasPrivPermission("servers/auspex"));
+ user->WriteNumeric(RPL_WHOISSERVER, parameters[0], (hide_server ? ServerInstance->Config->HideServer : u->server), signon);
+ }
+ }
+
+ user->WriteNumeric(RPL_ENDOFWHOWAS, parameters[0], "End of WHOWAS");
+ return CMD_SUCCESS;
+}
+
+WhoWas::Manager::Manager()
+ : GroupSize(0), MaxGroups(0), MaxKeep(0)
+{
+}
+
+const WhoWas::Nick* WhoWas::Manager::FindNick(const std::string& nickname) const
+{
+ whowas_users::const_iterator it = whowas.find(nickname);
+ if (it == whowas.end())
+ return NULL;
+ return it->second;
+}
+
+WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
+{
+ size_t entrycount = 0;
+ for (whowas_users::const_iterator i = whowas.begin(); i != whowas.end(); ++i)
+ {
+ WhoWas::Nick::List& list = i->second->entries;
+ entrycount += list.size();
+ }
+
+ Stats stats;
+ stats.entrycount = entrycount;
+ return stats;
+}
+
+void WhoWas::Manager::Add(User* user)
+{
+ if (!IsEnabled())
+ return;
+
+ // Insert nick if it doesn't exist
+ // 'first' will point to the newly inserted element or to the existing element with an equivalent key
+ std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(user->nick, static_cast<WhoWas::Nick*>(NULL)));
+
+ if (ret.second) // If inserted
+ {
+ // This nick is new, create a list for it and add the first record to it
+ WhoWas::Nick* nick = new WhoWas::Nick(ret.first->first);
+ nick->entries.push_back(new Entry(user));
+ ret.first->second = nick;
+
+ // Add this nick to the fifo too
+ whowas_fifo.push_back(nick);
+
+ if (whowas.size() > this->MaxGroups)
+ {
+ // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
+ PurgeNick(whowas_fifo.front());
+ }
+ }
+ else
+ {
+ // We've met this nick before, add a new record to the list
+ WhoWas::Nick::List& list = ret.first->second->entries;
+ list.push_back(new Entry(user));
+
+ // If there are too many records for this nick, remove the oldest (front)
+ if (list.size() > this->GroupSize)
+ {
+ delete list.front();
+ list.pop_front();
+ }
+ }
+}
+
+/* on rehash, refactor maps according to new conf values */
+void WhoWas::Manager::Prune()
+{
+ time_t min = ServerInstance->Time() - this->MaxKeep;
+
+ /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
+ while (!whowas_fifo.empty())
+ {
+ WhoWas::Nick* nick = whowas_fifo.front();
+ if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
+ PurgeNick(nick);
+ else
+ break;
+ }
+
+ /* Then cut the whowas sets to new size (groupsize) */
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
+ {
+ WhoWas::Nick::List& list = i->second->entries;
+ while (list.size() > this->GroupSize)
+ {
+ delete list.front();
+ list.pop_front();
+ }
+
+ if (list.empty())
+ PurgeNick(i++);
+ else
+ ++i;
+ }
+}
+
+/* call maintain once an hour to remove expired nicks */
+void WhoWas::Manager::Maintain()
+{
+ time_t min = ServerInstance->Time() - this->MaxKeep;
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
+ {
+ WhoWas::Nick::List& list = i->second->entries;
+ while (!list.empty() && list.front()->signon < min)
+ {
+ delete list.front();
+ list.pop_front();
+ }
+
+ if (list.empty())
+ PurgeNick(i++);
+ else
+ ++i;
+ }
+}
+
+WhoWas::Manager::~Manager()
+{
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+ {
+ WhoWas::Nick* nick = i->second;
+ delete nick;
+ }
+}
+
+bool WhoWas::Manager::IsEnabled() const
+{
+ return ((GroupSize != 0) && (MaxGroups != 0));
+}
+
+void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep)
+{
+ if ((NewGroupSize == GroupSize) && (NewMaxGroups == MaxGroups) && (NewMaxKeep == MaxKeep))
+ return;
+
+ GroupSize = NewGroupSize;
+ MaxGroups = NewMaxGroups;
+ MaxKeep = NewMaxKeep;
+ Prune();
+}
+
+void WhoWas::Manager::PurgeNick(whowas_users::iterator it)
+{
+ WhoWas::Nick* nick = it->second;
+ whowas_fifo.erase(nick);
+ whowas.erase(it);
+ delete nick;
+}
+
+void WhoWas::Manager::PurgeNick(WhoWas::Nick* nick)
+{
+ whowas_users::iterator it = whowas.find(nick->nick);
+ if (it == whowas.end())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in whowas database, please report");
+ return;
+ }
+ PurgeNick(it);
+}
+
+WhoWas::Entry::Entry(User* user)
+ : host(user->GetRealHost())
+ , dhost(user->GetDisplayedHost())
+ , ident(user->ident)
+ , server(user->server->GetName())
+ , real(user->GetRealName())
+ , signon(user->signon)
+{
+}
+
+WhoWas::Nick::Nick(const std::string& nickname)
+ : addtime(ServerInstance->Time())
+ , nick(nickname)
+{
+}
+
+WhoWas::Nick::~Nick()
+{
+ stdalgo::delete_all(entries);
+}
+
+class ModuleWhoWas : public Module, public Stats::EventListener
+{
+ CommandWhowas cmd;
+
+ public:
+ ModuleWhoWas()
+ : Stats::EventListener(this)
+ , cmd(this)
+ {
+ }
+
+ void OnGarbageCollect() CXX11_OVERRIDE
+ {
+ // Remove all entries older than MaxKeep
+ cmd.manager.Maintain();
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+ {
+ cmd.manager.Add(user);
+ }
+
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
+ {
+ if (stats.GetSymbol() == 'z')
+ stats.AddRow(249, "Whowas entries: "+ConvToStr(cmd.manager.GetStats().entrycount));
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
+ unsigned int NewGroupSize = tag->getUInt("groupsize", 10, 0, 10000);
+ unsigned int NewMaxGroups = tag->getUInt("maxgroups", 10240, 0, 1000000);
+ unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
+
+ cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("WHOWAS", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleWhoWas)
diff --git a/src/commands/cmd_eline.cpp b/src/coremods/core_xline/cmd_eline.cpp
index ca39f9061..0cb52298c 100644
--- a/src/commands/cmd_eline.cpp
+++ b/src/coremods/core_xline/cmd_eline.cpp
@@ -20,30 +20,18 @@
#include "inspircd.h"
#include "xline.h"
+#include "core_xline.h"
-/** Handle /ELINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandEline : public Command
+CommandEline::CommandEline(Module* parent)
+ : Command(parent, "ELINE", 1, 3)
{
- public:
- /** Constructor for eline.
- */
- CommandEline ( Module* parent) : Command(parent,"ELINE",1,3) { flags_needed = 'o'; syntax = "<ident@host> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
+ flags_needed = 'o';
+ syntax = "<ident@host> [<duration> :<reason>]";
+}
/** Handle /ELINE
*/
-CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandEline::Handle(User* user, const Params& parameters)
{
std::string target = parameters[0];
@@ -60,17 +48,17 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
else
ih = ServerInstance->XLines->IdentSplit(target);
- if (ih.first.empty())
- {
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
return CMD_FAILURE;
+ }
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ InsaneBan::IPHostMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "E", "hostmasks"))
+ return CMD_FAILURE;
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
ELine* el = new ELine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
if (ServerInstance->XLines->AddLine(el, user))
{
@@ -81,7 +69,7 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
timestr.c_str(), parameters[2].c_str());
}
@@ -89,7 +77,7 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
else
{
delete el;
- user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** E-Line for " + target + " already exists");
}
}
else
@@ -100,11 +88,9 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** E-Line " + target + " not found in list, try /stats e");
}
}
return CMD_SUCCESS;
}
-
-COMMAND_INIT(CommandEline)
diff --git a/src/commands/cmd_gline.cpp b/src/coremods/core_xline/cmd_gline.cpp
index 6505b7464..79b3ce21f 100644
--- a/src/commands/cmd_gline.cpp
+++ b/src/coremods/core_xline/cmd_gline.cpp
@@ -20,31 +20,18 @@
#include "inspircd.h"
#include "xline.h"
+#include "core_xline.h"
-/** Handle /GLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandGline : public Command
+CommandGline::CommandGline(Module* parent)
+ : Command(parent, "GLINE", 1, 3)
{
- public:
- /** Constructor for gline.
- */
- CommandGline (Module* parent) : Command(parent,"GLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ident@host> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
+ flags_needed = 'o';
+ syntax = "<ident@host> [<duration> :<reason>]";
+}
/** Handle /GLINE
*/
-CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandGline::Handle(User* user, const Params& parameters)
{
std::string target = parameters[0];
@@ -63,20 +50,21 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
if (ih.first.empty())
{
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
+ user->WriteNotice("*** Target not found");
return CMD_FAILURE;
}
- if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
+ InsaneBan::IPHostMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "G", "hostmasks"))
return CMD_FAILURE;
else if (target.find('!') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** G-Line cannot operate on nick!user@host masks",user->nick.c_str());
+ user->WriteNotice("*** G-Line cannot operate on nick!user@host masks");
return CMD_FAILURE;
}
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
GLine* gl = new GLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
if (ServerInstance->XLines->AddLine(gl, user))
{
@@ -87,7 +75,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
timestr.c_str(), parameters[2].c_str());
}
@@ -97,7 +85,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
else
{
delete gl;
- user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick.c_str(),target.c_str());
+ user->WriteNotice("** G-Line for " + target + " already exists");
}
}
@@ -109,11 +97,9 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** G-Line " + target + " not found in list, try /stats g.");
}
}
return CMD_SUCCESS;
}
-
-COMMAND_INIT(CommandGline)
diff --git a/src/commands/cmd_kline.cpp b/src/coremods/core_xline/cmd_kline.cpp
index ce3642f91..0f04224d6 100644
--- a/src/commands/cmd_kline.cpp
+++ b/src/coremods/core_xline/cmd_kline.cpp
@@ -20,33 +20,20 @@
#include "inspircd.h"
#include "xline.h"
+#include "core_xline.h"
-/** Handle /KLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKline : public Command
+CommandKline::CommandKline(Module* parent)
+ : Command(parent, "KLINE", 1, 3)
{
- public:
- /** Constructor for kline.
- */
- CommandKline ( Module* parent) : Command(parent,"KLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ident@host> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
+ flags_needed = 'o';
+ syntax = "<ident@host> [<duration> :<reason>]";
+}
/** Handle /KLINE
*/
-CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandKline::Handle(User* user, const Params& parameters)
{
- std::string target = parameters[0];
+ std::string target = parameters[0];
if (parameters.size() >= 3)
{
@@ -61,22 +48,23 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
else
ih = ServerInstance->XLines->IdentSplit(target);
- if (ih.first.empty())
- {
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
- return CMD_FAILURE;
- }
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
+ return CMD_FAILURE;
+ }
- if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
+ InsaneBan::IPHostMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "K", "hostmasks"))
return CMD_FAILURE;
if (target.find('!') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** K-Line cannot operate on nick!user@host masks",user->nick.c_str());
+ user->WriteNotice("*** K-Line cannot operate on nick!user@host masks");
return CMD_FAILURE;
}
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
KLine* kl = new KLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
if (ServerInstance->XLines->AddLine(kl,user))
{
@@ -87,7 +75,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
timestr.c_str(), parameters[2].c_str());
}
@@ -97,7 +85,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
else
{
delete kl;
- user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** K-Line for " + target + " already exists");
}
}
else
@@ -108,11 +96,9 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** K-Line " + target + " not found in list, try /stats k.");
}
}
return CMD_SUCCESS;
}
-
-COMMAND_INIT(CommandKline)
diff --git a/src/commands/cmd_qline.cpp b/src/coremods/core_xline/cmd_qline.cpp
index 3118798e6..b75973861 100644
--- a/src/commands/cmd_qline.cpp
+++ b/src/coremods/core_xline/cmd_qline.cpp
@@ -21,38 +21,30 @@
#include "inspircd.h"
#include "xline.h"
+#include "core_xline.h"
-/** Handle /QLINE. */
-class CommandQline : public Command
+CommandQline::CommandQline(Module* parent)
+ : Command(parent, "QLINE", 1, 3)
{
- public:
- /** Constructor for qline.
- */
- CommandQline ( Module* parent) : Command(parent,"QLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<nick> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to the command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
+ flags_needed = 'o';
+ syntax = "<nick> [<duration> :<reason>]";
+}
-CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandQline::Handle(User* user, const Params& parameters)
{
if (parameters.size() >= 3)
{
- if (ServerInstance->NickMatchesEveryone(parameters[0],user))
+ NickMatcher matcher;
+ if (InsaneBan::MatchesEveryone(parameters[0], matcher, user, "Q", "nickmasks"))
return CMD_FAILURE;
if (parameters[0].find('@') != std::string::npos || parameters[0].find('!') != std::string::npos || parameters[0].find('.') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick.c_str());
+ user->WriteNotice("*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.");
return CMD_FAILURE;
}
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
QLine* ql = new QLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
if (ServerInstance->XLines->AddLine(ql,user))
{
@@ -63,7 +55,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s: %s",user->nick.c_str(),parameters[0].c_str(),
timestr.c_str(), parameters[2].c_str());
}
@@ -72,7 +64,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
else
{
delete ql;
- user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** Q-Line for " + parameters[0] + " already exists");
}
}
else
@@ -83,7 +75,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** Q-Line " + parameters[0] + " not found in list, try /stats q.");
return CMD_FAILURE;
}
}
@@ -91,5 +83,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
}
-
-COMMAND_INIT(CommandQline)
+bool CommandQline::NickMatcher::Check(User* user, const std::string& nick) const
+{
+ return InspIRCd::Match(user->nick, nick);
+}
diff --git a/src/commands/cmd_zline.cpp b/src/coremods/core_xline/cmd_zline.cpp
index 91d9c6255..350f3270c 100644
--- a/src/commands/cmd_zline.cpp
+++ b/src/coremods/core_xline/cmd_zline.cpp
@@ -21,27 +21,16 @@
#include "inspircd.h"
#include "xline.h"
-/** Handle /ZLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandZline : public Command
+#include "core_xline.h"
+
+CommandZline::CommandZline(Module* parent)
+ : Command(parent, "ZLINE", 1, 3)
{
- public:
- /** Constructor for zline.
- */
- CommandZline ( Module* parent) : Command(parent,"ZLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ipmask> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
+ flags_needed = 'o';
+ syntax = "<ipmask> [<duration> :<reason>]";
+}
-CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandZline::Handle(User* user, const Params& parameters)
{
std::string target = parameters[0];
@@ -49,7 +38,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
{
if (target.find('!') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** You cannot include a nickname in a zline, a zline must ban only an IP mask",user->nick.c_str());
+ user->WriteNotice("*** You cannot include a nickname in a zline, a zline must ban only an IP mask");
return CMD_FAILURE;
}
@@ -69,11 +58,11 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
ipaddr++;
}
- if (ServerInstance->IPMatchesEveryone(ipaddr,user))
+ IPMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ipaddr, matcher, user, "Z", "ipmasks"))
return CMD_FAILURE;
- long duration = ServerInstance->Duration(parameters[1].c_str());
-
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
ZLine* zl = new ZLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ipaddr);
if (ServerInstance->XLines->AddLine(zl,user))
{
@@ -84,7 +73,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s: %s",user->nick.c_str(),ipaddr,
timestr.c_str(), parameters[2].c_str());
}
@@ -93,7 +82,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
else
{
delete zl;
- user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick.c_str(),ipaddr);
+ user->WriteNotice("*** Z-Line for " + std::string(ipaddr) + " already exists");
}
}
else
@@ -104,7 +93,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** Z-Line " + target + " not found in list, try /stats Z.");
return CMD_FAILURE;
}
}
@@ -112,4 +101,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
}
-COMMAND_INIT(CommandZline)
+bool CommandZline::IPMatcher::Check(User* user, const std::string& ip) const
+{
+ return InspIRCd::MatchCIDR(user->GetIPString(), ip, ascii_case_insensitive_map);
+}
diff --git a/src/coremods/core_xline/core_xline.cpp b/src/coremods/core_xline/core_xline.cpp
new file mode 100644
index 000000000..ab80ab4ed
--- /dev/null
+++ b/src/coremods/core_xline/core_xline.cpp
@@ -0,0 +1,93 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+bool InsaneBan::MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey)
+{
+ ConfigTag* insane = ServerInstance->Config->ConfValue("insane");
+
+ if (insane->getBool(confkey))
+ return false;
+
+ float itrigger = insane->getFloat("trigger", 95.5, 0.0, 100.0);
+
+ long matches = test.Run(mask);
+
+ if (!matches)
+ return false;
+
+ float percent = ((float)matches / (float)ServerInstance->Users->GetUsers().size()) * 100;
+ if (percent > itrigger)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a %s-line mask of %s, which covers %.2f%% of the network!", user->nick.c_str(), bantype, mask.c_str(), percent);
+ return true;
+ }
+ return false;
+}
+
+bool InsaneBan::IPHostMatcher::Check(User* user, const std::string& mask) const
+{
+ return ((InspIRCd::MatchCIDR(user->MakeHost(), mask, ascii_case_insensitive_map)) ||
+ (InspIRCd::MatchCIDR(user->MakeHostIP(), mask, ascii_case_insensitive_map)));
+}
+
+class CoreModXLine : public Module
+{
+ CommandEline cmdeline;
+ CommandGline cmdgline;
+ CommandKline cmdkline;
+ CommandQline cmdqline;
+ CommandZline cmdzline;
+
+ public:
+ CoreModXLine()
+ : cmdeline(this), cmdgline(this), cmdkline(this), cmdqline(this), cmdzline(this)
+ {
+ }
+
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
+ {
+ // Check Q-Lines (for local nick changes only, remote servers have our Q-Lines to enforce themselves)
+
+ XLine* xline = ServerInstance->XLines->MatchesLine("Q", newnick);
+ if (!xline)
+ return MOD_RES_PASSTHRU; // No match
+
+ // A Q-Line matched the new nick, tell opers if the user is registered
+ if (user->registered == REG_ALL)
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
+ newnick.c_str(), user->GetFullRealHost().c_str(), xline->reason.c_str());
+ }
+
+ // Send a numeric because if we deny then the core doesn't reply anything
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, InspIRCd::Format("Invalid nickname: %s", xline->reason.c_str()));
+ return MOD_RES_DENY;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the ELINE, GLINE, KLINE, QLINE, and ZLINE commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModXLine)
diff --git a/src/coremods/core_xline/core_xline.h b/src/coremods/core_xline/core_xline.h
new file mode 100644
index 000000000..1e0bc74cc
--- /dev/null
+++ b/src/coremods/core_xline/core_xline.h
@@ -0,0 +1,163 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+class InsaneBan
+{
+ public:
+ class MatcherBase
+ {
+ public:
+ virtual long Run(const std::string& mask) = 0;
+ };
+
+ template <typename T>
+ class Matcher : public MatcherBase
+ {
+ public:
+ long Run(const std::string& mask) CXX11_OVERRIDE
+ {
+ long matches = 0;
+ const T* c = static_cast<T*>(this);
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ if (c->Check(i->second, mask))
+ matches++;
+ }
+ return matches;
+ }
+ };
+
+ class IPHostMatcher : public Matcher<IPHostMatcher>
+ {
+ public:
+ bool Check(User* user, const std::string& mask) const;
+ };
+
+ /** Check if the given mask matches too many users according to the config, send an announcement if yes
+ * @param mask A mask to match against
+ * @param test The test that determines if a user matches the mask or not
+ * @param user A user whose nick will be included in the announcement if one is made
+ * @param bantype Type of the ban being set, will be used in the announcement if one is made
+ * @param confkey Name of the config key (inside the insane tag) which if false disables any checking
+ * @return True if the given mask matches too many users, false if not
+ */
+ static bool MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey);
+};
+
+/** Handle /ELINE.
+ */
+class CommandEline : public Command
+{
+ public:
+ /** Constructor for eline.
+ */
+ CommandEline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /GLINE.
+ */
+class CommandGline : public Command
+{
+ public:
+ /** Constructor for gline.
+ */
+ CommandGline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /KLINE.
+ */
+class CommandKline : public Command
+{
+ public:
+ /** Constructor for kline.
+ */
+ CommandKline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /QLINE.
+ */
+class CommandQline : public Command
+{
+ class NickMatcher : public InsaneBan::Matcher<NickMatcher>
+ {
+ public:
+ bool Check(User* user, const std::string& mask) const;
+ };
+
+ public:
+ /** Constructor for qline.
+ */
+ CommandQline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /ZLINE.
+ */
+class CommandZline : public Command
+{
+ class IPMatcher : public InsaneBan::Matcher<IPMatcher>
+ {
+ public:
+ bool Check(User* user, const std::string& mask) const;
+ };
+
+ public:
+ /** Constructor for zline.
+ */
+ CommandZline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
diff --git a/src/cull_list.cpp b/src/cull_list.cpp
index 956ed3494..73f2def51 100644
--- a/src/cull_list.cpp
+++ b/src/cull_list.cpp
@@ -21,7 +21,9 @@
#include "inspircd.h"
+#ifdef INSPIRCD_ENABLE_RTTI
#include <typeinfo>
+#endif
void CullList::Apply()
{
@@ -46,14 +48,18 @@ void CullList::Apply()
classbase* c = list[i];
if (gone.insert(c).second)
{
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "Deleting %s @%p", typeid(*c).name(),
+#ifdef INSPIRCD_ENABLE_RTTI
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting %s @%p", typeid(*c).name(),
(void*)c);
+#else
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting @%p", (void*)c);
+#endif
c->cull();
queue.push_back(c);
}
else
{
- ServerInstance->Logs->Log("CULLLIST",DEBUG, "WARNING: Object @%p culled twice!",
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "WARNING: Object @%p culled twice!",
(void*)c);
}
}
@@ -65,7 +71,7 @@ void CullList::Apply()
}
if (list.size())
{
- ServerInstance->Logs->Log("CULLLIST",DEBUG, "WARNING: Objects added to cull list in a destructor");
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "WARNING: Objects added to cull list in a destructor");
Apply();
}
}
diff --git a/src/dns.cpp b/src/dns.cpp
deleted file mode 100644
index 3d3bc947f..000000000
--- a/src/dns.cpp
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006, 2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $Core */
-
-/*
-dns.cpp - Nonblocking DNS functions.
-Very very loosely based on the firedns library,
-Copyright (C) 2002 Ian Gulliver. This file is no
-longer anything like firedns, there are many major
-differences between this code and the original.
-Please do not assume that firedns works like this,
-looks like this, walks like this or tastes like this.
-*/
-
-#ifndef _WIN32
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#else
-#include "inspircd_win32wrapper.h"
-#endif
-
-#include "inspircd.h"
-#include "socketengine.h"
-#include "configreader.h"
-#include "socket.h"
-
-#define DN_COMP_BITMASK 0xC000 /* highest 6 bits in a DN label header */
-
-/** Masks to mask off the responses we get from the DNSRequest methods
- */
-enum QueryInfo
-{
- ERROR_MASK = 0x10000 /* Result is an error */
-};
-
-/** Flags which can be ORed into a request or reply for different meanings
- */
-enum QueryFlags
-{
- FLAGS_MASK_RD = 0x01, /* Recursive */
- FLAGS_MASK_TC = 0x02,
- FLAGS_MASK_AA = 0x04, /* Authoritative */
- FLAGS_MASK_OPCODE = 0x78,
- FLAGS_MASK_QR = 0x80,
- FLAGS_MASK_RCODE = 0x0F, /* Request */
- FLAGS_MASK_Z = 0x70,
- FLAGS_MASK_RA = 0x80
-};
-
-
-/** Represents a dns resource record (rr)
- */
-struct ResourceRecord
-{
- QueryType type; /* Record type */
- unsigned int rr_class; /* Record class */
- unsigned long ttl; /* Time to live */
- unsigned int rdlength; /* Record length */
-};
-
-/** Represents a dns request/reply header, and its payload as opaque data.
- */
-class DNSHeader
-{
- public:
- unsigned char id[2]; /* Request id */
- unsigned int flags1; /* Flags */
- unsigned int flags2; /* Flags */
- unsigned int qdcount;
- unsigned int ancount; /* Answer count */
- unsigned int nscount; /* Nameserver count */
- unsigned int arcount;
- unsigned char payload[512]; /* Packet payload */
-};
-
-class DNSRequest
-{
- public:
- unsigned char id[2]; /* Request id */
- unsigned char* res; /* Result processing buffer */
- unsigned int rr_class; /* Request class */
- QueryType type; /* Request type */
- DNS* dnsobj; /* DNS caller (where we get our FD from) */
- unsigned long ttl; /* Time to live */
- std::string orig; /* Original requested name/ip */
-
- DNSRequest(DNS* dns, int id, const std::string &original);
- ~DNSRequest();
- DNSInfo ResultIsReady(DNSHeader &h, unsigned length);
- int SendRequests(const DNSHeader *header, const int length, QueryType qt);
-};
-
-class CacheTimer : public Timer
-{
- private:
- DNS* dns;
- public:
- CacheTimer(DNS* thisdns)
- : Timer(5*60, ServerInstance->Time(), true), dns(thisdns) { }
-
- virtual void Tick(time_t)
- {
- dns->PruneCache();
- }
-};
-
-class RequestTimeout : public Timer
-{
- DNSRequest* watch;
- int watchid;
- public:
- RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id)
- {
- }
- ~RequestTimeout()
- {
- if (ServerInstance->Res)
- Tick(0);
- }
-
- void Tick(time_t)
- {
- if (ServerInstance->Res->requests[watchid] == watch)
- {
- /* Still exists, whack it */
- if (ServerInstance->Res->Classes[watchid])
- {
- ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
- delete ServerInstance->Res->Classes[watchid];
- ServerInstance->Res->Classes[watchid] = NULL;
- }
- ServerInstance->Res->requests[watchid] = NULL;
- delete watch;
- }
- }
-};
-
-CachedQuery::CachedQuery(const std::string &res, QueryType qt, unsigned int ttl) : data(res), type(qt)
-{
- if (ttl > 5*60)
- ttl = 5*60;
- expires = ServerInstance->Time() + ttl;
-}
-
-int CachedQuery::CalcTTLRemaining()
-{
- int n = expires - ServerInstance->Time();
- return (n < 0 ? 0 : n);
-}
-
-/* Allocate the processing buffer */
-DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
-{
- /* hardening against overflow here: make our work buffer twice the theoretical
- * maximum size so that hostile input doesn't screw us over.
- */
- res = new unsigned char[sizeof(DNSHeader) * 2];
- *res = 0;
- orig = original;
- RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
- ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
-}
-
-/* Deallocate the processing buffer */
-DNSRequest::~DNSRequest()
-{
- delete[] res;
-}
-
-/** Fill a ResourceRecord class based on raw data input */
-inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
-{
- rr->type = (QueryType)((input[0] << 8) + input[1]);
- rr->rr_class = (input[2] << 8) + input[3];
- rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
- rr->rdlength = (input[8] << 8) + input[9];
-}
-
-/** Fill a DNSHeader class based on raw data input of a given length */
-inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
-{
- header->id[0] = input[0];
- header->id[1] = input[1];
- header->flags1 = input[2];
- header->flags2 = input[3];
- header->qdcount = (input[4] << 8) + input[5];
- header->ancount = (input[6] << 8) + input[7];
- header->nscount = (input[8] << 8) + input[9];
- header->arcount = (input[10] << 8) + input[11];
- memcpy(header->payload,&input[12],length);
-}
-
-/** Empty a DNSHeader class out into raw data, ready for transmission */
-inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
-{
- output[0] = header->id[0];
- output[1] = header->id[1];
- output[2] = header->flags1;
- output[3] = header->flags2;
- output[4] = header->qdcount >> 8;
- output[5] = header->qdcount & 0xFF;
- output[6] = header->ancount >> 8;
- output[7] = header->ancount & 0xFF;
- output[8] = header->nscount >> 8;
- output[9] = header->nscount & 0xFF;
- output[10] = header->arcount >> 8;
- output[11] = header->arcount & 0xFF;
- memcpy(&output[12],header->payload,length);
-}
-
-/** Send requests we have previously built down the UDP socket */
-int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
-{
- ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
-
- unsigned char payload[sizeof(DNSHeader)];
-
- this->rr_class = 1;
- this->type = qt;
-
- DNS::EmptyHeader(payload,header,length);
-
- if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12)
- return -1;
-
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
- return 0;
-}
-
-/** Add a query with a predefined header, and allocate an ID for it. */
-DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
-{
- /* Is the DNS connection down? */
- if (this->GetFd() == -1)
- return NULL;
-
- /* Create an id */
- unsigned int tries = 0;
- do {
- id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
- if (++tries == DNS::MAX_REQUEST_ID*5)
- {
- // If we couldn't find an empty slot this many times, do a sequential scan as a last
- // resort. If an empty slot is found that way, go on, otherwise throw an exception
- id = -1;
- for (int i = 0; i < DNS::MAX_REQUEST_ID; i++)
- {
- if (!requests[i])
- {
- id = i;
- break;
- }
- }
-
- if (id == -1)
- throw ModuleException("DNS: All ids are in use");
-
- break;
- }
- } while (requests[id]);
-
- DNSRequest* req = new DNSRequest(this, id, original);
-
- header->id[0] = req->id[0] = id >> 8;
- header->id[1] = req->id[1] = id & 0xFF;
- header->flags1 = FLAGS_MASK_RD;
- header->flags2 = 0;
- header->qdcount = 1;
- header->ancount = 0;
- header->nscount = 0;
- header->arcount = 0;
-
- /* At this point we already know the id doesnt exist,
- * so there needs to be no second check for the ::end()
- */
- requests[id] = req;
-
- /* According to the C++ spec, new never returns NULL. */
- return req;
-}
-
-int DNS::ClearCache()
-{
- /* This ensures the buckets are reset to sane levels */
- int rv = this->cache->size();
- delete this->cache;
- this->cache = new dnscache();
- return rv;
-}
-
-int DNS::PruneCache()
-{
- int n = 0;
- dnscache* newcache = new dnscache();
- for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
- /* Dont include expired items (theres no point) */
- if (i->second.CalcTTLRemaining())
- newcache->insert(*i);
- else
- n++;
-
- delete this->cache;
- this->cache = newcache;
- return n;
-}
-
-void DNS::Rehash()
-{
- if (this->GetFd() > -1)
- {
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
-
- /* Rehash the cache */
- this->PruneCache();
- }
- else
- {
- /* Create initial dns cache */
- this->cache = new dnscache();
- }
-
- irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver);
-
- /* Initialize mastersocket */
- int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
- this->SetFd(s);
-
- /* Have we got a socket and is it nonblocking? */
- if (this->GetFd() != -1)
- {
- ServerInstance->SE->SetReuse(s);
- ServerInstance->SE->NonBlocking(s);
- irc::sockets::sockaddrs bindto;
- memset(&bindto, 0, sizeof(bindto));
- bindto.sa.sa_family = myserver.sa.sa_family;
- if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
- {
- /* Failed to bind */
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error binding dns socket - hostnames will NOT resolve");
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
- }
- else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
- {
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
- }
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error creating DNS socket - hostnames will NOT resolve");
- }
-}
-
-/** Initialise the DNS UDP socket so that we can send requests */
-DNS::DNS()
-{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
- /* Clear the Resolver class table */
- memset(Classes,0,sizeof(Classes));
-
- /* Clear the requests class table */
- memset(requests,0,sizeof(requests));
-
- /* DNS::Rehash() sets this to a valid ptr
- */
- this->cache = NULL;
-
- /* Again, DNS::Rehash() sets this to a
- * valid value
- */
- this->SetFd(-1);
-
- /* Actually read the settings
- */
- this->Rehash();
-
- this->PruneTimer = new CacheTimer(this);
-
- ServerInstance->Timers->AddTimer(this->PruneTimer);
-}
-
-/** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
-int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
-{
- short payloadpos = 0;
- const char* tempchr, *tempchr2 = name;
- unsigned short length;
-
- /* split name up into labels, create query */
- while ((tempchr = strchr(tempchr2,'.')) != NULL)
- {
- length = tempchr - tempchr2;
- if (payloadpos + length + 1 > 507)
- return -1;
- payload[payloadpos++] = length;
- memcpy(&payload[payloadpos],tempchr2,length);
- payloadpos += length;
- tempchr2 = &tempchr[1];
- }
- length = strlen(tempchr2);
- if (length)
- {
- if (payloadpos + length + 2 > 507)
- return -1;
- payload[payloadpos++] = length;
- memcpy(&payload[payloadpos],tempchr2,length);
- payloadpos += length;
- payload[payloadpos++] = 0;
- }
- if (payloadpos > 508)
- return -1;
- length = htons(rr);
- memcpy(&payload[payloadpos],&length,2);
- length = htons(rr_class);
- memcpy(&payload[payloadpos + 2],&length,2);
- return payloadpos + 4;
-}
-
-/** Start lookup of an hostname to an IP address */
-int DNS::GetIP(const char *name)
-{
- DNSHeader h;
- int id;
- int length;
-
- if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
- return -1;
-
- DNSRequest* req = this->AddQuery(&h, id, name);
-
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
- return -1;
-
- return id;
-}
-
-/** Start lookup of an hostname to an IPv6 address */
-int DNS::GetIP6(const char *name)
-{
- DNSHeader h;
- int id;
- int length;
-
- if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
- return -1;
-
- DNSRequest* req = this->AddQuery(&h, id, name);
-
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
- return -1;
-
- return id;
-}
-
-/** Start lookup of a cname to another name */
-int DNS::GetCName(const char *alias)
-{
- DNSHeader h;
- int id;
- int length;
-
- if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
- return -1;
-
- DNSRequest* req = this->AddQuery(&h, id, alias);
-
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
- return -1;
-
- return id;
-}
-
-/** Start lookup of an IP address to a hostname */
-int DNS::GetNameForce(const char *ip, ForceProtocol fp)
-{
- char query[128];
- DNSHeader h;
- int id;
- int length;
-
- if (fp == PROTOCOL_IPV6)
- {
- in6_addr i;
- if (inet_pton(AF_INET6, ip, &i) > 0)
- {
- DNS::MakeIP6Int(query, &i);
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip);
- /* Invalid IP address */
- return -1;
- }
- }
- else
- {
- in_addr i;
- if (inet_aton(ip, &i))
- {
- unsigned char* c = (unsigned char*)&i.s_addr;
- sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip);
- /* Invalid IP address */
- return -1;
- }
- }
-
- length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload);
- if (length == -1)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query);
- return -1;
- }
-
- DNSRequest* req = this->AddQuery(&h, id, ip);
-
- if (!req)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't add query (resolver down?)");
- return -1;
- }
-
- if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't send (firewall?)");
- return -1;
- }
-
- return id;
-}
-
-/** Build an ipv6 reverse domain from an in6_addr
- */
-void DNS::MakeIP6Int(char* query, const in6_addr *ip)
-{
- const char* hex = "0123456789abcdef";
- for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
- {
- if (index % 2)
- /* low nibble */
- *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
- else
- /* high nibble */
- *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
- *query++ = '.'; /* Seperator */
- }
- strcpy(query,"ip6.arpa"); /* Suffix the string */
-}
-
-/** Return the next id which is ready, and the result attached to it */
-DNSResult DNS::GetResult()
-{
- /* Fetch dns query response and decide where it belongs */
- DNSHeader header;
- DNSRequest *req;
- unsigned char buffer[sizeof(DNSHeader)];
- irc::sockets::sockaddrs from;
- memset(&from, 0, sizeof(from));
- socklen_t x = sizeof(from);
-
- int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
-
- /* Did we get the whole header? */
- if (length < 12)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
- /* Nope - something screwed up. */
- return DNSResult(-1,"",0,"");
- }
-
- /* Check wether the reply came from a different DNS
- * server to the one we sent it to, or the source-port
- * is not 53.
- * A user could in theory still spoof dns packets anyway
- * but this is less trivial than just sending garbage
- * to the server, which is possible without this check.
- *
- * -- Thanks jilles for pointing this one out.
- */
- if (from != myserver)
- {
- std::string server1 = from.str();
- std::string server2 = myserver.str();
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
- server1.c_str(), server2.c_str());
- return DNSResult(-1,"",0,"");
- }
-
- /* Put the read header info into a header class */
- DNS::FillHeader(&header,buffer,length - 12);
-
- /* Get the id of this request.
- * Its a 16 bit value stored in two char's,
- * so we use logic shifts to create the value.
- */
- unsigned long this_id = header.id[1] + (header.id[0] << 8);
-
- /* Do we have a pending request matching this id? */
- if (!requests[this_id])
- {
- /* Somehow we got a DNS response for a request we never made... */
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
- return DNSResult(-1,"",0,"");
- }
- else
- {
- /* Remove the query from the list of pending queries */
- req = requests[this_id];
- requests[this_id] = NULL;
- }
-
- /* Inform the DNSRequest class that it has a result to be read.
- * When its finished it will return a DNSInfo which is a pair of
- * unsigned char* resource record data, and an error message.
- */
- DNSInfo data = req->ResultIsReady(header, length);
- std::string resultstr;
-
- /* Check if we got a result, if we didnt, its an error */
- if (data.first == NULL)
- {
- /* An error.
- * Mask the ID with the value of ERROR_MASK, so that
- * the dns_deal_with_classes() function knows that its
- * an error response and needs to be treated uniquely.
- * Put the error message in the second field.
- */
- std::string ro = req->orig;
- delete req;
- return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
- }
- else
- {
- unsigned long ttl = req->ttl;
- char formatted[128];
-
- /* Forward lookups come back as binary data. We must format them into ascii */
- switch (req->type)
- {
- case DNS_QUERY_A:
- snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
- resultstr = formatted;
- break;
-
- case DNS_QUERY_AAAA:
- {
- if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
- {
- std::string ro = req->orig;
- delete req;
- return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
- }
-
- resultstr = formatted;
-
- /* Special case. Sending ::1 around between servers
- * and to clients is dangerous, because the : on the
- * start makes the client or server interpret the IP
- * as the last parameter on the line with a value ":1".
- */
- if (*formatted == ':')
- resultstr.insert(0, "0");
- }
- break;
-
- case DNS_QUERY_CNAME:
- /* Identical handling to PTR */
-
- case DNS_QUERY_PTR:
- {
- /* Reverse lookups just come back as char* */
- resultstr = std::string((const char*)data.first);
- if (resultstr.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") != std::string::npos)
- {
- std::string ro = req->orig;
- delete req;
- return DNSResult(this_id | ERROR_MASK, "Invalid char(s) in reply", 0, ro);
- }
- }
- break;
-
- default:
- break;
- }
-
- /* Build the reply with the id and hostname/ip in it */
- std::string ro = req->orig;
- DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
- delete req;
- return result;
- }
-}
-
-/** A result is ready, process it */
-DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
-{
- unsigned i = 0, o;
- int q = 0;
- int curanswer;
- ResourceRecord rr;
- unsigned short ptr;
-
- /* This is just to keep _FORTIFY_SOURCE happy */
- rr.type = DNS_QUERY_NONE;
- rr.rdlength = 0;
- rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
- rr.rr_class = 0; /* Same for VC++ */
-
- if (!(header.flags1 & FLAGS_MASK_QR))
- return std::make_pair((unsigned char*)NULL,"Not a query result");
-
- if (header.flags1 & FLAGS_MASK_OPCODE)
- return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
-
- if (header.flags2 & FLAGS_MASK_RCODE)
- return std::make_pair((unsigned char*)NULL,"Domain name not found");
-
- if (header.ancount < 1)
- return std::make_pair((unsigned char*)NULL,"No resource records returned");
-
- /* Subtract the length of the header from the length of the packet */
- length -= 12;
-
- while ((unsigned int)q < header.qdcount && i < length)
- {
- if (header.payload[i] > 63)
- {
- i += 6;
- q++;
- }
- else
- {
- if (header.payload[i] == 0)
- {
- q++;
- i += 5;
- }
- else i += header.payload[i] + 1;
- }
- }
- curanswer = 0;
- while ((unsigned)curanswer < header.ancount)
- {
- q = 0;
- while (q == 0 && i < length)
- {
- if (header.payload[i] > 63)
- {
- i += 2;
- q = 1;
- }
- else
- {
- if (header.payload[i] == 0)
- {
- i++;
- q = 1;
- }
- else i += header.payload[i] + 1; /* skip length and label */
- }
- }
- if (static_cast<int>(length - i) < 10)
- return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
-
- /* XXX: We actually initialise 'rr' here including its ttl field */
- DNS::FillResourceRecord(&rr,&header.payload[i]);
-
- i += 10;
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver: rr.type is %d and this.type is %d rr.class %d this.class %d", rr.type, this->type, rr.rr_class, this->rr_class);
- if (rr.type != this->type)
- {
- curanswer++;
- i += rr.rdlength;
- continue;
- }
- if (rr.rr_class != this->rr_class)
- {
- curanswer++;
- i += rr.rdlength;
- continue;
- }
- break;
- }
- if ((unsigned int)curanswer == header.ancount)
- return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
-
- if (i + rr.rdlength > (unsigned int)length)
- return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
-
- if (rr.rdlength > 1023)
- return std::make_pair((unsigned char*)NULL,"Resource record too large");
-
- this->ttl = rr.ttl;
-
- switch (rr.type)
- {
- /*
- * CNAME and PTR are compressed. We need to decompress them.
- */
- case DNS_QUERY_CNAME:
- case DNS_QUERY_PTR:
- {
- unsigned short lowest_pos = length;
- o = 0;
- q = 0;
- while (q == 0 && i < length && o + 256 < 1023)
- {
- /* DN label found (byte over 63) */
- if (header.payload[i] > 63)
- {
- memcpy(&ptr,&header.payload[i],2);
-
- i = ntohs(ptr);
-
- /* check that highest two bits are set. if not, we've been had */
- if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK)
- return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
-
- /* mask away the two highest bits. */
- i &= ~DN_COMP_BITMASK;
-
- /* and decrease length by 12 bytes. */
- i -= 12;
-
- if (i >= lowest_pos)
- return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer");
- lowest_pos = i;
- }
- else
- {
- if (header.payload[i] == 0)
- {
- q = 1;
- }
- else
- {
- res[o] = 0;
- if (o != 0)
- res[o++] = '.';
-
- if (o + header.payload[i] > sizeof(DNSHeader))
- return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
-
- memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
- o += header.payload[i];
- i += header.payload[i] + 1;
- }
- }
- }
- res[o] = 0;
- }
- break;
- case DNS_QUERY_AAAA:
- if (rr.rdlength != sizeof(struct in6_addr))
- return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
-
- memcpy(res,&header.payload[i],rr.rdlength);
- res[rr.rdlength] = 0;
- break;
- case DNS_QUERY_A:
- if (rr.rdlength != sizeof(struct in_addr))
- return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
-
- memcpy(res,&header.payload[i],rr.rdlength);
- res[rr.rdlength] = 0;
- break;
- default:
- return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
- break;
- }
- return std::make_pair(res,"No error");
-}
-
-/** Close the master socket */
-DNS::~DNS()
-{
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- ServerInstance->Timers->DelTimer(this->PruneTimer);
- if (cache)
- delete cache;
-}
-
-CachedQuery* DNS::GetCache(const std::string &source)
-{
- dnscache::iterator x = cache->find(source.c_str());
- if (x != cache->end())
- return &(x->second);
- else
- return NULL;
-}
-
-void DNS::DelCache(const std::string &source)
-{
- cache->erase(source.c_str());
-}
-
-void Resolver::TriggerCachedResult()
-{
- if (CQ)
- OnLookupComplete(CQ->data, time_left, true);
-}
-
-/** High level abstraction of dns used by application at large */
-Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
-{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
- cached = false;
-
- CQ = ServerInstance->Res->GetCache(source);
- if (CQ)
- {
- time_left = CQ->CalcTTLRemaining();
- if (!time_left)
- {
- ServerInstance->Res->DelCache(source);
- }
- else if (CQ->type == qt)
- {
- cached = true;
- return;
- }
- CQ = NULL;
- }
-
- switch (querytype)
- {
- case DNS_QUERY_A:
- this->myid = ServerInstance->Res->GetIP(source.c_str());
- break;
-
- case DNS_QUERY_PTR4:
- querytype = DNS_QUERY_PTR;
- this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
- break;
-
- case DNS_QUERY_PTR6:
- querytype = DNS_QUERY_PTR;
- this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
- break;
-
- case DNS_QUERY_AAAA:
- this->myid = ServerInstance->Res->GetIP6(source.c_str());
- break;
-
- case DNS_QUERY_CNAME:
- this->myid = ServerInstance->Res->GetCName(source.c_str());
- break;
-
- default:
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request with unknown query type %d", querytype);
- this->myid = -1;
- break;
- }
- if (this->myid == -1)
- {
- throw ModuleException("Resolver: Couldn't get an id to make a request");
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
- }
-}
-
-/** Called when an error occurs */
-void Resolver::OnError(ResolverError, const std::string&)
-{
- /* Nothing in here */
-}
-
-/** Destroy a resolver */
-Resolver::~Resolver()
-{
- /* Nothing here (yet) either */
-}
-
-/** Get the request id associated with this class */
-int Resolver::GetId()
-{
- return this->myid;
-}
-
-Module* Resolver::GetCreator()
-{
- return this->Creator;
-}
-
-/** Process a socket read event */
-void DNS::HandleEvent(EventType, int)
-{
- /* Fetch the id and result of the next available packet */
- DNSResult res(0,"",0,"");
- res.id = 0;
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
-
- res = this->GetResult();
-
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
-
- /* Is there a usable request id? */
- if (res.id != -1)
- {
- /* Its an error reply */
- if (res.id & ERROR_MASK)
- {
- /* Mask off the error bit */
- res.id -= ERROR_MASK;
- /* Marshall the error to the correct class */
- if (Classes[res.id])
- {
- if (ServerInstance && ServerInstance->stats)
- ServerInstance->stats->statsDnsBad++;
- Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
- delete Classes[res.id];
- Classes[res.id] = NULL;
- }
- return;
- }
- else
- {
- /* It is a non-error result, marshall the result to the correct class */
- if (Classes[res.id])
- {
- if (ServerInstance && ServerInstance->stats)
- ServerInstance->stats->statsDnsGood++;
-
- if (!this->GetCache(res.original.c_str()))
- {
- if (cache->size() >= MAX_CACHE_SIZE)
- cache->clear();
- this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.type, res.ttl)));
- }
-
- Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
- delete Classes[res.id];
- Classes[res.id] = NULL;
- }
- }
-
- if (ServerInstance && ServerInstance->stats)
- ServerInstance->stats->statsDns++;
- }
-}
-
-/** Add a derived Resolver to the working set */
-bool DNS::AddResolverClass(Resolver* r)
-{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
- /* Check the pointers validity and the id's validity */
- if ((r) && (r->GetId() > -1))
- {
- /* Check the slot isnt already occupied -
- * This should NEVER happen unless we have
- * a severely broken DNS server somewhere
- */
- if (!Classes[r->GetId()])
- {
- /* Set up the pointer to the class */
- Classes[r->GetId()] = r;
- return true;
- }
- }
-
- /* Pointer or id not valid, or duplicate id.
- * Free the item and return
- */
- delete r;
- return false;
-}
-
-void DNS::CleanResolvers(Module* module)
-{
- for (int i = 0; i < MAX_REQUEST_ID; i++)
- {
- if (Classes[i])
- {
- if (Classes[i]->GetCreator() == module)
- {
- Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
- delete Classes[i];
- Classes[i] = NULL;
- }
- }
- }
-}
diff --git a/src/dynamic.cpp b/src/dynamic.cpp
index d111da548..08564a0cd 100644
--- a/src/dynamic.cpp
+++ b/src/dynamic.cpp
@@ -22,7 +22,7 @@
#include "inspircd.h"
-#include "dynamic.h"
+
#ifndef _WIN32
#include <dlfcn.h>
#else
@@ -83,7 +83,7 @@ std::string DLLManager::GetVersion()
const char* srcver = (char*)dlsym(h, "inspircd_src_version");
if (srcver)
return srcver;
- return "Unversioned module";
+ return "";
}
void DLLManager::RetrieveLastError()
diff --git a/src/filelogger.cpp b/src/filelogger.cpp
index 0575256d0..5786758da 100644
--- a/src/filelogger.cpp
+++ b/src/filelogger.cpp
@@ -19,15 +19,10 @@
*/
-/* $Core */
-
#include "inspircd.h"
#include <fstream>
-#include "socketengine.h"
-#include "filelogger.h"
-FileLogStream::FileLogStream(int loglevel, FileWriter *fw)
- : LogStream(loglevel), f(fw)
+FileLogStream::FileLogStream(LogLevel loglevel, FileWriter *fw) : LogStream(loglevel), f(fw)
{
ServerInstance->Logs->AddLoggerRef(f);
}
@@ -38,9 +33,9 @@ FileLogStream::~FileLogStream()
ServerInstance->Logs->DelLoggerRef(f);
}
-void FileLogStream::OnLog(int loglevel, const std::string &type, const std::string &text)
+void FileLogStream::OnLog(LogLevel loglevel, const std::string &type, const std::string &text)
{
- static char TIMESTR[26];
+ static std::string TIMESTR;
static time_t LAST = 0;
if (loglevel < this->loglvl)
@@ -50,14 +45,9 @@ void FileLogStream::OnLog(int loglevel, const std::string &type, const std::stri
if (ServerInstance->Time() != LAST)
{
- time_t local = ServerInstance->Time();
- struct tm *timeinfo = localtime(&local);
-
- strlcpy(TIMESTR,asctime(timeinfo),26);
- TIMESTR[24] = ':';
+ TIMESTR = InspIRCd::TimeString(ServerInstance->Time());
LAST = ServerInstance->Time();
}
- std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n";
- this->f->WriteLogLine(out);
+ this->f->WriteLogLine(TIMESTR + " " + type + ": " + text + "\n");
}
diff --git a/src/fileutils.cpp b/src/fileutils.cpp
new file mode 100644
index 000000000..731e4ea01
--- /dev/null
+++ b/src/fileutils.cpp
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include <fstream>
+
+FileReader::FileReader(const std::string& filename)
+{
+ Load(filename);
+}
+
+void FileReader::Load(const std::string& filename)
+{
+ // If the file is stored in the file cache then we used that version instead.
+ ConfigFileCache::const_iterator it = ServerInstance->Config->Files.find(filename);
+ if (it != ServerInstance->Config->Files.end())
+ {
+ this->lines = it->second;
+ }
+ else
+ {
+ const std::string realName = ServerInstance->Config->Paths.PrependConfig(filename);
+ lines.clear();
+
+ std::ifstream stream(realName.c_str());
+ if (!stream.is_open())
+ throw CoreException(filename + " does not exist or is not readable!");
+
+ std::string line;
+ while (std::getline(stream, line))
+ {
+ lines.push_back(line);
+ totalSize += line.size() + 2;
+ }
+
+ stream.close();
+ }
+}
+
+std::string FileReader::GetString() const
+{
+ std::string buffer;
+ for (file_cache::const_iterator it = this->lines.begin(); it != this->lines.end(); ++it)
+ {
+ buffer.append(*it);
+ buffer.append("\r\n");
+ }
+ return buffer;
+}
+
+std::string FileSystem::ExpandPath(const std::string& base, const std::string& fragment)
+{
+ // The fragment is an absolute path, don't modify it.
+ if (fragment[0] == '/' || FileSystem::StartsWithWindowsDriveLetter(fragment))
+ return fragment;
+
+ return base + '/' + fragment;
+}
+
+bool FileSystem::FileExists(const std::string& file)
+{
+ struct stat sb;
+ if (stat(file.c_str(), &sb) == -1)
+ return false;
+
+ if ((sb.st_mode & S_IFDIR) > 0)
+ return false;
+
+ return !access(file.c_str(), F_OK);
+}
+
+std::string FileSystem::GetFileName(const std::string& name)
+{
+#ifdef _WIN32
+ size_t pos = name.find_last_of("\\/");
+#else
+ size_t pos = name.rfind('/');
+#endif
+ return pos == std::string::npos ? name : name.substr(++pos);
+}
+
+bool FileSystem::StartsWithWindowsDriveLetter(const std::string& path)
+{
+ return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
+}
diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp
index e0347421b..8febcbb5f 100644
--- a/src/hashcomp.cpp
+++ b/src/hashcomp.cpp
@@ -20,11 +20,7 @@
*/
-/* $Core */
-
#include "inspircd.h"
-#include "hashcomp.h"
-#include "hash_map.h"
/******************************************************
*
@@ -35,7 +31,7 @@
* scene spend a lot of time debating (arguing) about
* the best way to write hash functions to hash irc
* nicknames, channels etc.
- * We are lucky as C++ developers as hash_map does
+ * We are lucky as C++ developers as unordered_map does
* a lot of this for us. It does intellegent memory
* requests, bucketing, search functions, insertion
* and deletion etc. All we have to do is write some
@@ -51,111 +47,78 @@
*
******************************************************/
-/** A mapping of uppercase to lowercase, including scandinavian
- * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
- */
-unsigned const char rfc_case_insensitive_map[256] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 0-19 */
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, /* 20-39 */
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, /* 40-59 */
- 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, /* 60-79 */
- 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 94, 95, 96, 97, 98, 99, /* 80-99 */
- 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, /* 100-119 */
- 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, /* 120-139 */
- 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, /* 140-159 */
- 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, /* 160-179 */
- 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, /* 180-199 */
- 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, /* 200-219 */
- 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, /* 220-239 */
- 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 /* 240-255 */
-};
-/** Case insensitive map, ASCII rules.
- * That is;
- * [ != {, but A == a.
+/**
+ * A case insensitive mapping of characters from upper case to lower case for
+ * the ASCII character set.
*/
unsigned const char ascii_case_insensitive_map[256] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 0-19 */
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, /* 20-39 */
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, /* 40-59 */
- 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, /* 60-79 */
- 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, /* 80-99 */
- 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, /* 100-119 */
- 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, /* 120-139 */
- 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, /* 140-159 */
- 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, /* 160-179 */
- 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, /* 180-199 */
- 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, /* 200-219 */
- 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, /* 220-239 */
- 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 /* 240-255 */
-};
-
-/** Case sensitive map.
- * Can technically also be used for ASCII case sensitive comparisons, as [ != {, etc.
- */
-unsigned const char rfc_case_sensitive_map[256] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
- 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
- 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
- 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
- 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
- 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
- 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
- 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
- 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
- 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 0-9
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 10-19
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, // 20-29
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, // 30-39
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, // 40-49
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 50-59
+ 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, // 60-69
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, // 90-99
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-249
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+ 250, 251, 252, 253, 254, 255, // 250-255
};
-/* convert a string to lowercase. Note following special circumstances
- * taken from RFC 1459. Many "official" server branches still hold to this
- * rule so i will too;
- *
- * Because of IRC's scandanavian origin, the characters {}| are
- * considered to be the lower case equivalents of the characters []\,
- * respectively. This is a critical issue when determining the
- * equivalence of two nicknames.
- */
-void nspace::strlower(char *n)
-{
- if (n)
- {
- for (char* t = n; *t; t++)
- *t = national_case_insensitive_map[(unsigned char)*t];
- }
-}
-
-#ifdef HASHMAP_DEPRECATED
- size_t CoreExport nspace::insensitive::operator()(const std::string &s) const
-#else
- size_t nspace::hash<std::string>::operator()(const std::string &s) const
-#endif
-
-{
- /* XXX: NO DATA COPIES! :)
- * The hash function here is practically
- * a copy of the one in STL's hash_fun.h,
- * only with *x replaced with national_case_insensitive_map[*x].
- * This avoids a copy to use hash<const char*>
- */
- size_t t = 0;
- for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
- t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
- return t;
-}
-size_t CoreExport irc::hash::operator()(const irc::string &s) const
-{
- size_t t = 0;
- for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
- t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
- return t;
-}
+/**
+ * A case insensitive mapping of characters from upper case to lower case for
+ * the character set of RFC 1459. This is identical to ASCII with the small
+ * exception of {}| being considered to be the lower case equivalents of the
+ * characters []\ respectively.
+ */
+unsigned const char rfc_case_insensitive_map[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 0-9
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 10-19
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, // 20-29
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, // 30-39
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, // 40-49
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 50-59
+ 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, // 60-69
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
+ 122, 123, 124, 125, 94, 95, 96, 97, 98, 99, // 90-99
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-239
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+ 250, 251, 252, 253, 254, 255, // 250-255
+};
-bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const
+bool irc::equals(const std::string& s1, const std::string& s2)
{
const unsigned char* n1 = (const unsigned char*)s1.c_str();
const unsigned char* n2 = (const unsigned char*)s2.c_str();
@@ -165,315 +128,163 @@ bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2)
return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
}
-/******************************************************
- *
- * This is the implementation of our special irc::string
- * class which is a case-insensitive equivalent to
- * std::string which is not only case-insensitive but
- * can also do scandanavian comparisons, e.g. { = [, etc.
- *
- * This class depends on the const array 'national_case_insensitive_map'.
- *
- ******************************************************/
-
-bool irc::irc_char_traits::eq(char c1st, char c2nd)
-{
- return national_case_insensitive_map[(unsigned char)c1st] == national_case_insensitive_map[(unsigned char)c2nd];
-}
-
-bool irc::irc_char_traits::ne(char c1st, char c2nd)
-{
- return national_case_insensitive_map[(unsigned char)c1st] != national_case_insensitive_map[(unsigned char)c2nd];
-}
-
-bool irc::irc_char_traits::lt(char c1st, char c2nd)
+size_t irc::find(const std::string& haystack, const std::string& needle)
{
- return national_case_insensitive_map[(unsigned char)c1st] < national_case_insensitive_map[(unsigned char)c2nd];
-}
+ // The haystack can't contain the needle if it is smaller than it.
+ if (needle.length() > haystack.length())
+ return std::string::npos;
-int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n)
-{
- for(unsigned int i = 0; i < n; i++)
+ // The inner loop checks the characters between haystack_last and the end of the haystack.
+ size_t haystack_last = haystack.length() - needle.length();
+ for (size_t hpos = 0; hpos <= haystack_last; ++hpos)
{
- if(national_case_insensitive_map[(unsigned char)*str1] > national_case_insensitive_map[(unsigned char)*str2])
- return 1;
-
- if(national_case_insensitive_map[(unsigned char)*str1] < national_case_insensitive_map[(unsigned char)*str2])
- return -1;
-
- if(*str1 == 0 || *str2 == 0)
- return 0;
+ // Check for the needle at the current haystack position.
+ bool found = true;
+ for (size_t npos = 0; npos < needle.length(); ++npos)
+ {
+ if (national_case_insensitive_map[(unsigned char)needle[npos]] != national_case_insensitive_map[(unsigned char)haystack[hpos + npos]])
+ {
+ // Uh-oh, characters at the current haystack position don't match.
+ found = false;
+ break;
+ }
+ }
- str1++;
- str2++;
+ // The entire needle was found in the haystack!
+ if (found)
+ return hpos;
}
- return 0;
-}
-const char* irc::irc_char_traits::find(const char* s1, int n, char c)
-{
- while(n-- > 0 && national_case_insensitive_map[(unsigned char)*s1] != national_case_insensitive_map[(unsigned char)c])
- s1++;
- return (n >= 0) ? s1 : NULL;
-}
-
-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();
+ // We didn't find anything.
+ return std::string::npos;
}
-irc::tokenstream::~tokenstream()
-{
-}
-bool irc::tokenstream::GetToken(std::string &token)
+bool irc::insensitive_swo::operator()(const std::string& a, const std::string& b) const
{
- std::string::iterator lsp = last_starting_position;
+ const unsigned char* charmap = national_case_insensitive_map;
+ std::string::size_type asize = a.size();
+ std::string::size_type bsize = b.size();
+ std::string::size_type maxsize = std::min(asize, bsize);
- while (n != tokens.end())
+ for (std::string::size_type i = 0; i < maxsize; i++)
{
- /** Skip multi space, converting " " into " "
- */
- while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
- n++;
-
- if ((last_pushed) && (*n == ':'))
- {
- /* If we find a token thats not the first and starts with :,
- * this is the last token on the line
- */
- std::string::iterator curr = ++n;
- n = tokens.end();
- token = std::string(curr, tokens.end());
+ unsigned char A = charmap[(unsigned char)a[i]];
+ unsigned char B = charmap[(unsigned char)b[i]];
+ if (A > B)
+ return false;
+ else if (A < B)
return true;
- }
-
- last_pushed = false;
-
- if ((*n == ' ') || (n+1 == tokens.end()))
- {
- /* If we find a space, or end of string, this is the end of a token.
- */
- last_starting_position = n+1;
- last_pushed = *n == ' ';
-
- std::string strip(lsp, n+1 == tokens.end() ? n+1 : n++);
- while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
- strip.erase(strip.end() - 1);
-
- token = strip;
- return !token.empty();
- }
-
- n++;
}
- token.clear();
- return false;
-}
-
-bool irc::tokenstream::GetToken(irc::string &token)
-{
- std::string stdstring;
- bool returnval = GetToken(stdstring);
- token = assign(stdstring);
- return returnval;
+ return (asize < bsize);
}
-bool irc::tokenstream::GetToken(int &token)
+size_t irc::insensitive::operator()(const std::string &s) const
{
- std::string tok;
- bool returnval = GetToken(tok);
- token = ConvToInt(tok);
- return returnval;
-}
-
-bool irc::tokenstream::GetToken(long &token)
-{
- std::string tok;
- bool returnval = GetToken(tok);
- token = ConvToInt(tok);
- return returnval;
+ /* XXX: NO DATA COPIES! :)
+ * The hash function here is practically
+ * a copy of the one in STL's hash_fun.h,
+ * only with *x replaced with national_case_insensitive_map[*x].
+ * This avoids a copy to use hash<const char*>
+ */
+ size_t t = 0;
+ for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
+ t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
+ return t;
}
-irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
+irc::tokenstream::tokenstream(const std::string& msg, size_t start)
+ : message(msg, start)
+ , position(0)
{
- last_starting_position = tokens.begin();
- n = tokens.begin();
}
-bool irc::sepstream::GetToken(std::string &token)
+bool irc::tokenstream::GetMiddle(std::string& token)
{
- std::string::iterator lsp = last_starting_position;
-
- while (n != tokens.end())
+ // If we are past the end of the string we can't do anything.
+ if (position >= message.length())
{
- if ((*n == sep) || (n+1 == tokens.end()))
- {
- last_starting_position = n+1;
- token = std::string(lsp, n+1 == tokens.end() ? n+1 : n++);
-
- while ((token.length()) && (token.find_last_of(sep) == token.length() - 1))
- token.erase(token.end() - 1);
-
- if (token.empty())
- n++;
-
- return n == tokens.end() ? false : true;
- }
-
- n++;
+ token.clear();
+ return false;
}
- token.clear();
- return false;
-}
-
-const std::string irc::sepstream::GetRemaining()
-{
- return std::string(n, tokens.end());
-}
-
-bool irc::sepstream::StreamEnd()
-{
- return ((n + 1) == tokens.end());
-}
-
-irc::sepstream::~sepstream()
-{
-}
-
-std::string irc::hex(const unsigned char *raw, size_t rawsz)
-{
- if (!rawsz)
- return "";
-
- /* EWW! This used to be using sprintf, which is WAY inefficient. -Special */
-
- const char *hex = "0123456789abcdef";
- static char hexbuf[MAXBUF];
-
- size_t i, j;
- for (i = 0, j = 0; j < rawsz; ++j)
+ // If we can't find another separator this is the last token in the message.
+ size_t separator = message.find(' ', position);
+ if (separator == std::string::npos)
{
- hexbuf[i++] = hex[raw[j] / 16];
- hexbuf[i++] = hex[raw[j] % 16];
+ token.assign(message, position, std::string::npos);
+ position = message.length();
+ return true;
}
- hexbuf[i] = 0;
- return hexbuf;
+ token.assign(message, position, separator - position);
+ position = message.find_first_not_of(' ', separator);
+ return true;
}
-CoreExport const char* irc::Spacify(const char* n)
+bool irc::tokenstream::GetTrailing(std::string& token)
{
- 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);
-}
+ // If we are past the end of the string we can't do anything.
+ if (position >= message.length())
+ {
+ token.clear();
+ return false;
+ }
-void irc::modestacker::Push(char modeletter)
-{
- this->Push(modeletter,"");
-}
+ // If this is true then we have a <trailing> token!
+ if (message[position] == ':')
+ {
+ token.assign(message, position + 1, std::string::npos);
+ position = message.length();
+ return true;
+ }
-void irc::modestacker::PushPlus()
-{
- this->Push('+',"");
+ // There is no <trailing> token so it must be a <middle> token.
+ return GetMiddle(token);
}
-void irc::modestacker::PushMinus()
+irc::sepstream::sepstream(const std::string& source, char separator, bool allowempty)
+ : tokens(source), sep(separator), pos(0), allow_empty(allowempty)
{
- this->Push('-',"");
}
-int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
+bool irc::sepstream::GetToken(std::string &token)
{
- if (sequence.empty())
+ if (this->StreamEnd())
{
- return 0;
+ token.clear();
+ return false;
}
- unsigned int n = 0;
- int size = 1; /* Account for initial +/- char */
- int nextsize = 0;
- int start = result.size();
- std::string modeline = adding ? "+" : "-";
- result.push_back(modeline);
-
- if (sequence.size() > 1)
- nextsize = sequence[1].length() + 2;
-
- while (!sequence[0].empty() && (sequence.size() > 1) && (n < ServerInstance->Config->Limits.MaxModes) && ((size + nextsize) < max_line_size))
+ if (!this->allow_empty)
{
- modeline += *(sequence[0].begin());
- if (!sequence[1].empty())
+ this->pos = this->tokens.find_first_not_of(this->sep, this->pos);
+ if (this->pos == std::string::npos)
{
- result.push_back(sequence[1]);
- size += nextsize; /* Account for mode character and whitespace */
+ this->pos = this->tokens.length() + 1;
+ token.clear();
+ return false;
}
- sequence[0].erase(sequence[0].begin());
- sequence.erase(sequence.begin() + 1);
-
- if (sequence.size() > 1)
- nextsize = sequence[1].length() + 2;
-
- n++;
}
- result[start] = modeline;
- return n;
-}
+ size_t p = this->tokens.find(this->sep, this->pos);
+ if (p == std::string::npos)
+ p = this->tokens.length();
-irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
-{
- if (end < begin)
- return; // nothing to do here
-
- 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)
-{
- if (end < begin)
- return; // nothing to do here
+ token.assign(tokens, this->pos, p - this->pos);
+ this->pos = p + 1;
- for (int v = begin; v < end; v++)
- joined.append(sequence[v]).append(seperator);
- joined.append(sequence[end]);
+ return true;
}
-irc::stringjoiner::stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end)
+const std::string irc::sepstream::GetRemaining()
{
- if (end < begin)
- return; // nothing to do here
-
- for (int v = begin; v < end; v++)
- joined.append(sequence[v]).append(seperator);
- joined.append(sequence[end]);
+ return !this->StreamEnd() ? this->tokens.substr(this->pos) : "";
}
-std::string& irc::stringjoiner::GetJoined()
+bool irc::sepstream::StreamEnd()
{
- return joined;
+ return this->pos > this->tokens.length();
}
irc::portparser::portparser(const std::string &source, bool allow_overlapped)
@@ -528,10 +339,9 @@ long irc::portparser::GetToken()
std::string::size_type dash = x.rfind('-');
if (dash != std::string::npos)
{
- std::string sbegin = x.substr(0, dash);
- std::string send = x.substr(dash+1, x.length());
+ std::string sbegin(x, 0, dash);
range_begin = atoi(sbegin.c_str());
- range_end = atoi(send.c_str());
+ range_end = atoi(x.c_str()+dash+1);
if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end))
{
@@ -549,25 +359,3 @@ long irc::portparser::GetToken()
return atoi(x.c_str());
}
}
-
-/*const std::basic_string& SearchAndReplace(std::string& text, const std::string& pattern, const std::string& replace)
-{
- std::string replacement;
- if ((!pattern.empty()) && (!text.empty()))
- {
- for (std::string::size_type n = 0; n != text.length(); ++n)
- {
- if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern)
- {
- replacement.append(replace);
- n = n + pattern.length() - 1;
- }
- else
- {
- replacement += text[n];
- }
- }
- }
- text = replacement;
- return text;
-}*/
diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp
index 439320c1e..6830612c7 100644
--- a/src/helperfuncs.cpp
+++ b/src/helperfuncs.cpp
@@ -22,8 +22,6 @@
*/
-/* $Core */
-
#ifdef _WIN32
#define _CRT_RAND_S
#include <stdlib.h>
@@ -34,66 +32,19 @@
#include "exitcodes.h"
#include <iostream>
-std::string InspIRCd::GetServerDescription(const std::string& servername)
-{
- std::string description;
-
- FOREACH_MOD(I_OnGetServerDescription,OnGetServerDescription(servername,description));
-
- if (!description.empty())
- {
- return description;
- }
- else
- {
- // not a remote server that can be found, it must be me.
- return Config->ServerDesc;
- }
-}
-
/* Find a user record by nickname and return a pointer to it */
User* InspIRCd::FindNick(const std::string &nick)
{
if (!nick.empty() && isdigit(*nick.begin()))
return FindUUID(nick);
-
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- /* Couldn't find it */
- return NULL;
-
- return iter->second;
-}
-
-User* InspIRCd::FindNick(const char* nick)
-{
- if (isdigit(*nick))
- return FindUUID(nick);
-
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- return NULL;
-
- return iter->second;
+ return FindNickOnly(nick);
}
User* InspIRCd::FindNickOnly(const std::string &nick)
{
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- return NULL;
-
- return iter->second;
-}
-
-User* InspIRCd::FindNickOnly(const char* nick)
-{
- user_hash::iterator iter = this->Users->clientlist->find(nick);
+ user_hash::iterator iter = this->Users->clientlist.find(nick);
- if (iter == this->Users->clientlist->end())
+ if (iter == this->Users->clientlist.end())
return NULL;
return iter->second;
@@ -101,66 +52,26 @@ User* InspIRCd::FindNickOnly(const char* nick)
User *InspIRCd::FindUUID(const std::string &uid)
{
- user_hash::iterator finduuid = this->Users->uuidlist->find(uid);
+ user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
- if (finduuid == this->Users->uuidlist->end())
+ if (finduuid == this->Users->uuidlist.end())
return NULL;
return finduuid->second;
}
-
-User *InspIRCd::FindUUID(const char *uid)
-{
- return FindUUID(std::string(uid));
-}
-
/* find a channel record by channel name and return a pointer to it */
-Channel* InspIRCd::FindChan(const char* chan)
-{
- chan_hash::iterator iter = chanlist->find(chan);
-
- if (iter == chanlist->end())
- /* Couldn't find it */
- return NULL;
-
- return iter->second;
-}
Channel* InspIRCd::FindChan(const std::string &chan)
{
- chan_hash::iterator iter = chanlist->find(chan);
+ chan_hash::iterator iter = chanlist.find(chan);
- if (iter == chanlist->end())
+ if (iter == chanlist.end())
/* Couldn't find it */
return NULL;
return iter->second;
}
-/* Send an error notice to all users, registered or not */
-void InspIRCd::SendError(const std::string &s)
-{
- for (LocalUserList::const_iterator i = this->Users->local_users.begin(); i != this->Users->local_users.end(); i++)
- {
- User* u = *i;
- if (u->registered == REG_ALL)
- {
- u->WriteServ("NOTICE %s :%s",u->nick.c_str(),s.c_str());
- }
- else
- {
- /* Unregistered connections receive ERROR, not a NOTICE */
- u->Write("ERROR :" + s);
- }
- }
-}
-
-/* return channel count */
-long InspIRCd::ChannelCount()
-{
- return chanlist->size();
-}
-
bool InspIRCd::IsValidMask(const std::string &mask)
{
const char* dest = mask.c_str();
@@ -190,7 +101,7 @@ bool InspIRCd::IsValidMask(const std::string &mask)
if (exclamation != 1 || atsign != 1)
return false;
- if (mask.length() > 250)
+ if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
return false;
return true;
@@ -216,7 +127,8 @@ void InspIRCd::StripColor(std::string &sentence)
else
seq = 0;
- if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31)))
+ // Strip all control codes too except \001 for CTCP
+ if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
i = sentence.erase(i);
else
++i;
@@ -234,19 +146,20 @@ void InspIRCd::ProcessColors(file_cache& input)
{
std::string character;
std::string replace;
- special_chars(const std::string &c, const std::string &r) : character(c), replace(r) { }
- }
-
- special[] = {
- special_chars("\\002", "\002"), // Bold
- special_chars("\\037", "\037"), // underline
- special_chars("\\003", "\003"), // Color
- special_chars("\\017", "\017"), // Stop colors
- special_chars("\\u", "\037"), // Alias for underline
- special_chars("\\b", "\002"), // Alias for Bold
- special_chars("\\x", "\017"), // Alias for stop
- special_chars("\\c", "\003"), // Alias for color
- special_chars("", "")
+ special_chars(const std::string& c, const std::string& r)
+ : character(c)
+ , replace(r)
+ {
+ }
+ } special[] = {
+ special_chars("\\b", "\x02"), // Bold
+ special_chars("\\c", "\x03"), // Color
+ special_chars("\\i", "\x1D"), // Italic
+ special_chars("\\m", "\x11"), // Monospace
+ special_chars("\\s", "\x1E"), // Strikethrough
+ special_chars("\\u", "\x1F"), // Underline
+ special_chars("\\x", "\x0F"), // Reset
+ special_chars("\\", "")
};
for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
@@ -281,47 +194,35 @@ void InspIRCd::ProcessColors(file_cache& input)
}
/* true for valid channel name, false else */
-bool IsChannelHandler::Call(const char *chname, size_t max)
+bool InspIRCd::DefaultIsChannel(const std::string& chname)
{
- const char *c = chname + 1;
+ if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
+ return false;
- /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
- if (!chname || *chname != '#')
- {
+ if (chname[0] != '#')
return false;
- }
- while (*c)
+ for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
{
- switch (*c)
+ switch (*i)
{
case ' ':
case ',':
case 7:
return false;
}
-
- c++;
- }
-
- size_t len = c - chname;
- /* too long a name - note funky pointer arithmetic here. */
- if (len > max)
- {
- return false;
}
return true;
}
/* true for valid nickname, false else */
-bool IsNickHandler::Call(const char* n, size_t max)
+bool InspIRCd::DefaultIsNick(const std::string& n)
{
- if (!n || !*n)
+ if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
return false;
- unsigned int p = 0;
- for (const char* i = n; *i; i++, p++)
+ for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
{
if ((*i >= 'A') && (*i <= '}'))
{
@@ -329,7 +230,7 @@ bool IsNickHandler::Call(const char* n, size_t max)
continue;
}
- if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
+ if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
{
/* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
continue;
@@ -339,17 +240,16 @@ bool IsNickHandler::Call(const char* n, size_t max)
return false;
}
- /* too long? or not */
- return (p <= max);
+ return true;
}
/* return true for good ident, false else */
-bool IsIdentHandler::Call(const char* n)
+bool InspIRCd::DefaultIsIdent(const std::string& n)
{
- if (!n || !*n)
+ if (n.empty())
return false;
- for (const char* i = n; *i; i++)
+ for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
{
if ((*i >= 'A') && (*i <= '}'))
{
@@ -367,36 +267,73 @@ bool IsIdentHandler::Call(const char* n)
return true;
}
-bool IsSIDHandler::Call(const std::string &str)
+bool InspIRCd::IsHost(const std::string& host)
{
- /* Returns true if the string given is exactly 3 characters long,
- * starts with a digit, and the other two characters are A-Z or digits
- */
- return ((str.length() == 3) && isdigit(str[0]) &&
- ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
- ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
-}
+ // Hostnames must be non-empty and shorter than the maximum hostname length.
+ if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
+ return false;
-/* open the proper logfile */
-bool InspIRCd::OpenLog(char**, int)
-{
- if (!Config->cmdline.writelog) return true; // Skip opening default log if -nolog
+ unsigned int numdashes = 0;
+ unsigned int numdots = 0;
+ bool seendot = false;
+ const std::string::const_iterator hostend = host.end() - 1;
+ for (std::string::const_iterator iter = host.begin(); iter != host.end(); ++iter)
+ {
+ unsigned char chr = static_cast<unsigned char>(*iter);
+
+ // If the current character is a label separator.
+ if (chr == '.')
+ {
+ numdots++;
- if (Config->cmdline.startup_log.empty())
- Config->cmdline.startup_log = LOG_PATH "/startup.log";
- FILE* startup = fopen(Config->cmdline.startup_log.c_str(), "a+");
+ // Consecutive separators are not allowed and dashes can not exist at the start or end
+ // of labels and separators must only exist between labels.
+ if (seendot || numdashes || iter == host.begin() || iter == hostend)
+ return false;
+
+ seendot = true;
+ continue;
+ }
+
+ // If this point is reached then the character is not a dot.
+ seendot = false;
+
+ // If the current character is a dash.
+ if (chr == '-')
+ {
+ // Consecutive separators are not allowed and dashes can not exist at the start or end
+ // of labels and separators must only exist between labels.
+ if (seendot || numdashes >= 2 || iter == host.begin() || iter == hostend)
+ return false;
+
+ numdashes += 1;
+ continue;
+ }
+
+ // If this point is reached then the character is not a dash.
+ numdashes = 0;
+
+ // Alphanumeric characters are allowed at any position.
+ if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
+ continue;
- if (!startup)
- {
return false;
}
- FileWriter* fw = new FileWriter(startup);
- FileLogStream *f = new FileLogStream((Config->cmdline.forcedebug ? DEBUG : DEFAULT), fw);
-
- this->Logs->AddLogType("*", f, true);
+ // Whilst simple hostnames (e.g. localhost) are valid we do not allow the server to use
+ // them to prevent issues with clients that differentiate between short client and server
+ // prefixes by checking whether the nickname contains a dot.
+ return numdots;
+}
- return true;
+bool InspIRCd::IsSID(const std::string &str)
+{
+ /* Returns true if the string given is exactly 3 characters long,
+ * starts with a digit, and the other two characters are A-Z or digits
+ */
+ return ((str.length() == 3) && isdigit(str[0]) &&
+ ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
+ ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
}
void InspIRCd::CheckRoot()
@@ -405,38 +342,16 @@ void InspIRCd::CheckRoot()
if (geteuid() == 0)
{
std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Can't start as root");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
Exit(EXIT_STATUS_ROOT);
}
#endif
}
-void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const std::string &text)
-{
- std::string copy_text = text;
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnWhoisLine, MOD_RESULT, (user, dest, numeric, copy_text));
-
- if (MOD_RESULT != MOD_RES_DENY)
- user->WriteServ("%d %s", numeric, copy_text.c_str());
-}
-
-void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
-{
- char textbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, format);
- vsnprintf(textbuffer, MAXBUF, format, argsPtr);
- va_end(argsPtr);
-
- this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
-}
-
/** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
* lookups and pointer maths.
*/
-long InspIRCd::Duration(const std::string &str)
+unsigned long InspIRCd::Duration(const std::string &str)
{
unsigned char multiplier = 0;
long total = 0;
@@ -476,31 +391,44 @@ long InspIRCd::Duration(const std::string &str)
return total + subtotal;
}
-bool InspIRCd::ULine(const std::string& sserver)
+std::string InspIRCd::Format(va_list& vaList, const char* formatString)
{
- if (sserver.empty())
- return true;
+ static std::vector<char> formatBuffer(1024);
+
+ while (true)
+ {
+ va_list dst;
+ va_copy(dst, vaList);
+
+ int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
+ va_end(dst);
+
+ if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
+ {
+ break;
+ }
- return (Config->ulines.find(sserver.c_str()) != Config->ulines.end());
+ formatBuffer.resize(formatBuffer.size() * 2);
+ }
+
+ return std::string(&formatBuffer[0]);
}
-bool InspIRCd::SilentULine(const std::string& sserver)
+std::string InspIRCd::Format(const char* formatString, ...)
{
- std::map<irc::string,bool>::iterator n = Config->ulines.find(sserver.c_str());
- if (n != Config->ulines.end())
- return n->second;
- else
- return false;
+ std::string ret;
+ VAFORMAT(ret, formatString, formatString);
+ return ret;
}
-std::string InspIRCd::TimeString(time_t curtime)
+std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
{
#ifdef _WIN32
if (curtime < 0)
curtime = 0;
#endif
- struct tm* timeinfo = localtime(&curtime);
+ struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
if (!timeinfo)
{
curtime = 0;
@@ -514,36 +442,24 @@ std::string InspIRCd::TimeString(time_t curtime)
else if (timeinfo->tm_year + 1900 < 1000)
timeinfo->tm_year = 0;
- return std::string(asctime(timeinfo),24);
-}
+ // This is the default format used by asctime without the terminating new line.
+ if (!format)
+ format = "%a %b %d %Y %H:%M:%S";
-// You should only pass a single character to this.
-void InspIRCd::AddExtBanChar(char c)
-{
- std::string &tok = Config->data005;
- std::string::size_type ebpos = tok.find(" EXTBAN=,");
+ char buffer[512];
+ if (!strftime(buffer, sizeof(buffer), format, timeinfo))
+ buffer[0] = '\0';
- if (ebpos == std::string::npos)
- {
- tok.append(" EXTBAN=,");
- tok.push_back(c);
- }
- else
- {
- ebpos += 9;
- while (isalpha(tok[ebpos]) && tok[ebpos] < c)
- ebpos++;
- tok.insert(ebpos, 1, c);
- }
+ return buffer;
}
-std::string InspIRCd::GenRandomStr(int length, bool printable)
+std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
{
char* buf = new char[length];
GenRandom(buf, length);
std::string rv;
rv.resize(length);
- for(int i=0; i < length; i++)
+ for(size_t i = 0; i < length; i++)
rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
delete[] buf;
return rv;
@@ -559,10 +475,13 @@ unsigned long InspIRCd::GenRandomInt(unsigned long max)
}
// This is overridden by a higher-quality algorithm when SSL support is loaded
-void GenRandomHandler::Call(char *output, size_t max)
+void InspIRCd::DefaultGenRandom(char* output, size_t max)
{
- for(unsigned int i=0; i < max; i++)
-#ifdef _WIN32
+#if defined HAS_ARC4RANDOM_BUF
+ arc4random_buf(output, max);
+#else
+ for (unsigned int i = 0; i < max; ++i)
+# ifdef _WIN32
{
unsigned int uTemp;
if(rand_s(&uTemp) != 0)
@@ -570,32 +489,8 @@ void GenRandomHandler::Call(char *output, size_t max)
else
output[i] = uTemp;
}
-#else
+# else
output[i] = random();
+# endif
#endif
}
-
-ModResult OnCheckExemptionHandler::Call(User* user, Channel* chan, const std::string& restriction)
-{
- unsigned int mypfx = chan->GetPrefixValue(user);
- char minmode = 0;
- std::string current;
-
- irc::spacesepstream defaultstream(ServerInstance->Config->ConfValue("options")->getString("exemptchanops"));
-
- while (defaultstream.GetToken(current))
- {
- std::string::size_type pos = current.find(':');
- if (pos == std::string::npos)
- continue;
- if (current.substr(0,pos) == restriction)
- minmode = current[pos+1];
- }
-
- ModeHandler* mh = ServerInstance->Modes->FindMode(minmode, MODETYPE_CHANNEL);
- if (mh && mypfx >= mh->GetPrefixRank())
- return MOD_RES_ALLOW;
- if (mh || minmode == '*')
- return MOD_RES_DENY;
- return MOD_RES_PASSTHRU;
-}
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index 66f9bfdc5..aa3fb9612 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -26,9 +26,7 @@
*/
-/* $Core */
#include "inspircd.h"
-#include "inspircd_version.h"
#include <signal.h>
#ifndef _WIN32
@@ -37,12 +35,6 @@
#include <sys/resource.h>
#include <dlfcn.h>
#include <getopt.h>
-
- /* Some systems don't define RUSAGE_SELF. This should fix them. */
- #ifndef RUSAGE_SELF
- #define RUSAGE_SELF 0
- #endif
-
#include <pwd.h> // setuid
#include <grp.h> // setgid
#else
@@ -54,12 +46,7 @@
#include <fstream>
#include <iostream>
#include "xline.h"
-#include "bancache.h"
-#include "socketengine.h"
-#include "socket.h"
-#include "command_parse.h"
#include "exitcodes.h"
-#include "caller.h"
#include "testsuite.h"
InspIRCd* ServerInstance = NULL;
@@ -78,28 +65,26 @@ unsigned const char *national_case_insensitive_map = rfc_case_insensitive_map;
*/
const char* ExitCodes[] =
{
- "No error", /* 0 */
- "DIE command", /* 1 */
- "execv() failed", /* 2 */
- "Internal error", /* 3 */
- "Config file error", /* 4 */
- "Logfile error", /* 5 */
- "POSIX fork failed", /* 6 */
- "Bad commandline parameters", /* 7 */
- "No ports could be bound", /* 8 */
- "Can't write PID file", /* 9 */
- "SocketEngine could not initialize", /* 10 */
- "Refusing to start up as root", /* 11 */
- "Found a <die> tag!", /* 12 */
- "Couldn't load module on startup", /* 13 */
- "Could not create windows forked process", /* 14 */
- "Received SIGTERM", /* 15 */
- "Bad command handler loaded", /* 16 */
- "RegisterServiceCtrlHandler failed", /* 17 */
- "UpdateSCMStatus failed", /* 18 */
- "CreateEvent failed" /* 19 */
+ "No error", // 0
+ "DIE command", // 1
+ "Config file error", // 2
+ "Logfile error", // 3
+ "POSIX fork failed", // 4
+ "Bad commandline parameters", // 5
+ "Can't write PID file", // 6
+ "SocketEngine could not initialize", // 7
+ "Refusing to start up as root", // 8
+ "Couldn't load module on startup", // 9
+ "Received SIGTERM" // 10
};
+#ifdef INSPIRCD_ENABLE_TESTSUITE
+/** True if we have been told to run the testsuite from the commandline,
+ * rather than entering the mainloop.
+ */
+static int do_testsuite = 0;
+#endif
+
template<typename T> static void DeleteZero(T*&n)
{
T* t = n;
@@ -109,125 +94,29 @@ template<typename T> static void DeleteZero(T*&n)
void InspIRCd::Cleanup()
{
+ // Close all listening sockets
for (unsigned int i = 0; i < ports.size(); i++)
{
- /* This calls the constructor and closes the listening socket */
ports[i]->cull();
delete ports[i];
}
ports.clear();
- /* Close all client sockets, or the new process inherits them */
- LocalUserList::reverse_iterator i = Users->local_users.rbegin();
- while (i != this->Users->local_users.rend())
- {
- User* u = *i++;
- Users->QuitUser(u, "Server shutdown");
- }
-
GlobalCulls.Apply();
Modules->UnloadAll();
/* Delete objects dynamically allocated in constructor (destructor would be more appropriate, but we're likely exiting) */
/* Must be deleted before modes as it decrements modelines */
if (FakeClient)
+ {
+ delete FakeClient->server;
FakeClient->cull();
- if (Res)
- Res->cull();
+ }
DeleteZero(this->FakeClient);
- DeleteZero(this->Users);
- DeleteZero(this->Modes);
DeleteZero(this->XLines);
- DeleteZero(this->Parser);
- DeleteZero(this->stats);
- DeleteZero(this->Modules);
- DeleteZero(this->BanCache);
- DeleteZero(this->SNO);
DeleteZero(this->Config);
- DeleteZero(this->Res);
- DeleteZero(this->chanlist);
- DeleteZero(this->PI);
- DeleteZero(this->Threads);
- DeleteZero(this->Timers);
- DeleteZero(this->SE);
- /* Close logging */
- this->Logs->CloseLogs();
- DeleteZero(this->Logs);
-}
-
-void InspIRCd::Restart(const std::string &reason)
-{
- /* SendError flushes each client's queue,
- * regardless of writeability state
- */
- this->SendError(reason);
-
- /* Figure out our filename (if theyve renamed it, we're boned) */
- std::string me;
-
- char** argv = Config->cmdline.argv;
-
-#ifdef _WIN32
- char module[MAX_PATH];
- if (GetModuleFileNameA(NULL, module, MAX_PATH))
- me = module;
-#else
- me = argv[0];
-#endif
-
- this->Cleanup();
-
- if (execv(me.c_str(), argv) == -1)
- {
- /* Will raise a SIGABRT if not trapped */
- throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
- }
-}
-
-void InspIRCd::ResetMaxBans()
-{
- for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
- i->second->ResetMaxBans();
-}
-
-/** Because hash_map doesn't free its buckets when we delete items, we occasionally
- * recreate the hash to free them up.
- * We do this by copying the entries from the old hash to a new hash, causing all
- * empty buckets to be weeded out of the hash.
- * Since this is quite expensive, it's not done very often.
- */
-void InspIRCd::RehashUsersAndChans()
-{
- user_hash* old_users = Users->clientlist;
- Users->clientlist = new user_hash;
- for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
- Users->clientlist->insert(*n);
- delete old_users;
-
- user_hash* old_uuid = Users->uuidlist;
- Users->uuidlist = new user_hash;
- for (user_hash::const_iterator n = old_uuid->begin(); n != old_uuid->end(); n++)
- Users->uuidlist->insert(*n);
- delete old_uuid;
-
- chan_hash* old_chans = chanlist;
- chanlist = new chan_hash;
- for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
- chanlist->insert(*n);
- delete old_chans;
-
- // Reset the already_sent IDs so we don't wrap it around and drop a message
- LocalUser::already_sent_id = 0;
- for (LocalUserList::const_iterator i = Users->local_users.begin(); i != Users->local_users.end(); i++)
- {
- (**i).already_sent = 0;
- (**i).RemoveExpiredInvites();
- }
-
- // HACK: ELines are not expired properly at the moment but it can't be fixed as
- // the 2.0 XLine system is a spaghetti nightmare. Instead we skip over expired
- // ELines in XLineManager::CheckELines() and expire them here instead.
- ServerInstance->XLines->GetAll("E");
+ SocketEngine::Deinit();
+ Logs->CloseLogs();
}
void InspIRCd::SetSignals()
@@ -243,15 +132,10 @@ void InspIRCd::SetSignals()
signal(SIGTERM, InspIRCd::SetSignal);
}
-void InspIRCd::QuickExit(int status)
-{
- exit(status);
-}
-
// Required for returning the proper value of EXIT_SUCCESS for the parent process
static void VoidSignalHandler(int signalreceived)
{
- exit(0);
+ exit(EXIT_STATUS_NOERROR);
}
bool InspIRCd::DaemonSeed()
@@ -260,11 +144,11 @@ bool InspIRCd::DaemonSeed()
std::cout << "InspIRCd Process ID: " << con_green << GetCurrentProcessId() << con_reset << std::endl;
return true;
#else
- // Do not use QuickExit here: It will exit with status SIGTERM which would break e.g. daemon scripts
+ // Do not use exit() here: It will exit with status SIGTERM which would break e.g. daemon scripts
signal(SIGTERM, VoidSignalHandler);
- int childpid;
- if ((childpid = fork ()) < 0)
+ int childpid = fork();
+ if (childpid < 0)
return false;
else if (childpid > 0)
{
@@ -277,7 +161,7 @@ bool InspIRCd::DaemonSeed()
*/
while (kill(childpid, 0) != -1)
sleep(1);
- exit(0);
+ exit(EXIT_STATUS_NOERROR);
}
setsid ();
std::cout << "InspIRCd Process ID: " << con_green << getpid() << con_reset << std::endl;
@@ -287,13 +171,13 @@ bool InspIRCd::DaemonSeed()
rlimit rl;
if (getrlimit(RLIMIT_CORE, &rl) == -1)
{
- this->Logs->Log("STARTUP",DEFAULT,"Failed to getrlimit()!");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to getrlimit()!");
return false;
}
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_CORE, &rl) == -1)
- this->Logs->Log("STARTUP",DEFAULT,"setrlimit() failed, cannot increase coredump size.");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setrlimit() failed, cannot increase coredump size.");
return true;
#endif
@@ -302,9 +186,15 @@ bool InspIRCd::DaemonSeed()
void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
{
#ifndef _WIN32
+ if (!ServerInstance->Config->cmdline.writepid)
+ {
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "--nopid specified on command line; PID file not written.");
+ return;
+ }
+
std::string fname(filename);
if (fname.empty())
- fname = DATA_PATH "/inspircd.pid";
+ fname = ServerInstance->Config->Paths.PrependData("inspircd.pid");
std::ofstream outfile(fname.c_str());
if (outfile.is_open())
{
@@ -312,93 +202,50 @@ void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
outfile.close();
}
else
- {
- if (exitonfail)
+ {
+ if (exitonfail)
std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s'%s",fname.c_str(), (exitonfail ? ", exiting." : ""));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : ""));
if (exitonfail)
- Exit(EXIT_STATUS_PID);
+ Exit(EXIT_STATUS_PID);
}
#endif
}
InspIRCd::InspIRCd(int argc, char** argv) :
- ConfigFileName(CONFIG_PATH "/inspircd.conf"),
+ ConfigFileName(INSPIRCD_CONFIG_PATH "/inspircd.conf"),
+ PI(&DefaultProtocolInterface),
/* Functor pointer initialisation.
*
* THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
* themselves within the class.
*/
- NICKForced("NICKForced", NULL),
- OperQuit("OperQuit", NULL),
- GenRandom(&HandleGenRandom),
- IsChannel(&HandleIsChannel),
- IsSID(&HandleIsSID),
- Rehash(&HandleRehash),
- IsNick(&HandleIsNick),
- IsIdent(&HandleIsIdent),
- FloodQuitUser(&HandleFloodQuitUser),
- OnCheckExemption(&HandleOnCheckExemption)
+ GenRandom(&DefaultGenRandom),
+ IsChannel(&DefaultIsChannel),
+ IsNick(&DefaultIsNick),
+ IsIdent(&DefaultIsIdent)
{
ServerInstance = this;
- Extensions.Register(&NICKForced);
- Extensions.Register(&OperQuit);
-
FailedPortList pl;
+ // Flag variables passed to getopt_long() later
int do_version = 0, do_nofork = 0, do_debug = 0,
- do_nolog = 0, do_root = 0, do_testsuite = 0; /* flag variables */
- int c = 0;
+ do_nolog = 0, do_nopid = 0, do_root = 0;
// Initialize so that if we exit before proper initialization they're not deleted
- this->Logs = 0;
- this->Threads = 0;
- this->PI = 0;
- this->Users = 0;
- this->chanlist = 0;
this->Config = 0;
- this->SNO = 0;
- this->BanCache = 0;
- this->Modules = 0;
- this->stats = 0;
- this->Timers = 0;
- this->Parser = 0;
this->XLines = 0;
- this->Modes = 0;
- this->Res = 0;
this->ConfigThread = NULL;
this->FakeClient = NULL;
UpdateTime();
this->startup_time = TIME.tv_sec;
- // This must be created first, so other parts of Insp can use it while starting up
- this->Logs = new LogManager;
-
- SE = CreateSocketEngine();
-
- this->Threads = new ThreadEngine;
-
- /* Default implementation does nothing */
- this->PI = new ProtocolInterface;
-
- this->s_signal = 0;
-
- // Create base manager classes early, so nothing breaks
- this->Users = new UserManager;
-
- this->Users->clientlist = new user_hash();
- this->Users->uuidlist = new user_hash();
- this->chanlist = new chan_hash();
+ SocketEngine::Init();
this->Config = new ServerConfig;
- this->SNO = new SnomaskManager;
- this->BanCache = new BanCacheManager;
- this->Modules = new ModuleManager();
- this->stats = new serverstats();
- this->Timers = new TimerManager;
- this->Parser = new CommandParser;
+ dynamic_reference_base::reset_all();
this->XLines = new XLineManager;
this->Config->cmdline.argv = argv;
@@ -424,31 +271,49 @@ InspIRCd::InspIRCd(int argc, char** argv) :
srandom(TIME.tv_nsec ^ TIME.tv_sec);
#endif
+ {
+ ServiceProvider* provs[] =
+ {
+ &rfcevents.numeric, &rfcevents.join, &rfcevents.part, &rfcevents.kick, &rfcevents.quit, &rfcevents.nick,
+ &rfcevents.mode, &rfcevents.topic, &rfcevents.privmsg, &rfcevents.invite, &rfcevents.ping, &rfcevents.pong,
+ &rfcevents.error
+ };
+ Modules.AddServices(provs, sizeof(provs)/sizeof(provs[0]));
+ }
+
struct option longopts[] =
{
{ "nofork", no_argument, &do_nofork, 1 },
- { "logfile", required_argument, NULL, 'f' },
{ "config", required_argument, NULL, 'c' },
{ "debug", no_argument, &do_debug, 1 },
{ "nolog", no_argument, &do_nolog, 1 },
+ { "nopid", no_argument, &do_nopid, 1 },
{ "runasroot", no_argument, &do_root, 1 },
{ "version", no_argument, &do_version, 1 },
+#ifdef INSPIRCD_ENABLE_TESTSUITE
{ "testsuite", no_argument, &do_testsuite, 1 },
+#endif
{ 0, 0, 0, 0 }
};
+ int c;
int index;
- while ((c = getopt_long(argc, argv, ":c:f:", longopts, &index)) != -1)
+ while ((c = getopt_long(argc, argv, ":c:", longopts, &index)) != -1)
{
switch (c)
{
- case 'f':
- /* Log filename was set */
- Config->cmdline.startup_log = optarg;
- break;
case 'c':
/* Config filename was set */
ConfigFileName = optarg;
+#ifdef _WIN32
+ TCHAR configPath[MAX_PATH + 1];
+ if (GetFullPathName(optarg, MAX_PATH, configPath, NULL) > 0)
+ ConfigFileName = configPath;
+#else
+ char configPath[PATH_MAX + 1];
+ if (realpath(optarg, configPath))
+ ConfigFileName = configPath;
+#endif
break;
case 0:
/* getopt_long_only() set an int variable, just keep going */
@@ -458,19 +323,21 @@ InspIRCd::InspIRCd(int argc, char** argv) :
default:
/* Fall through to handle other weird values too */
std::cout << "Unknown parameter '" << argv[optind-1] << "'" << std::endl;
- std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--logfile <filename>] " << std::endl <<
- std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--config <config>] [--testsuite]" << std::endl;
+ std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--nopid] [--debug] [--config <config>]" << std::endl <<
+ std::string(static_cast<size_t>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version]" << std::endl;
Exit(EXIT_STATUS_ARGV);
break;
}
}
+#ifdef INSPIRCD_ENABLE_TESTSUITE
if (do_testsuite)
do_nofork = do_debug = true;
+#endif
if (do_version)
{
- std::cout << std::endl << VERSION << " r" << REVISION << std::endl;
+ std::cout << std::endl << INSPIRCD_VERSION << std::endl;
Exit(EXIT_STATUS_NOERROR);
}
@@ -484,28 +351,23 @@ InspIRCd::InspIRCd(int argc, char** argv) :
Config->cmdline.nofork = (do_nofork != 0);
Config->cmdline.forcedebug = (do_debug != 0);
Config->cmdline.writelog = !do_nolog;
- Config->cmdline.TestSuite = (do_testsuite != 0);
+ Config->cmdline.writepid = !do_nopid;
if (do_debug)
{
- FileWriter* fw = new FileWriter(stdout);
- FileLogStream* fls = new FileLogStream(RAWIO, fw);
+ FileWriter* fw = new FileWriter(stdout, 1);
+ FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
Logs->AddLogTypes("*", fls, true);
}
- else if (!this->OpenLog(argv, argc))
- {
- std::cout << "ERROR: Could not open initial logfile " << Config->cmdline.startup_log << ": " << strerror(errno) << std::endl << std::endl;
- Exit(EXIT_STATUS_LOG);
- }
- if (!ServerConfig::FileExists(ConfigFileName.c_str()))
+ if (!FileSystem::FileExists(ConfigFileName))
{
#ifdef _WIN32
/* Windows can (and defaults to) hide file extensions, so let's play a bit nice for windows users. */
std::string txtconf = this->ConfigFileName;
txtconf.append(".txt");
- if (ServerConfig::FileExists(txtconf.c_str()))
+ if (FileSystem::FileExists(txtconf))
{
ConfigFileName = txtconf;
}
@@ -513,34 +375,27 @@ InspIRCd::InspIRCd(int argc, char** argv) :
#endif
{
std::cout << "ERROR: Cannot open config file: " << ConfigFileName << std::endl << "Exiting..." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Unable to open config file %s", ConfigFileName.c_str());
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to open config file %s", ConfigFileName.c_str());
Exit(EXIT_STATUS_CONFIG);
}
}
- std::cout << con_green << "Inspire Internet Relay Chat Server" << con_reset << ", compiled on " __DATE__ " at " __TIME__ << std::endl;
- std::cout << con_green << "(C) InspIRCd Development Team." << con_reset << std::endl << std::endl;
- std::cout << "Developers:" << std::endl;
- std::cout << con_green << "\tBrain, FrostyCoolSlug, w00t, Om, Special, peavey" << std::endl;
- std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;
- std::cout << "\tAttila" << con_reset << std::endl << std::endl;
- std::cout << "Others:\t\t\t" << con_green << "See /INFO Output" << con_reset << std::endl;
-
- this->Modes = new ModeParser;
+ std::cout << con_green << "InspIRCd - Internet Relay Chat Daemon" << con_reset << std::endl;
+ std::cout << "For contributors & authors: " << con_green << "See /INFO Output" << con_reset << std::endl;
#ifndef _WIN32
if (!do_root)
this->CheckRoot();
else
{
- std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl
- << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl
- << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl
- << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl
- << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl
- << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl
- << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl
- << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;
+ std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl
+ << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl
+ << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl
+ << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl
+ << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl
+ << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl
+ << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl
+ << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;
sleep(20);
}
#endif
@@ -552,47 +407,31 @@ InspIRCd::InspIRCd(int argc, char** argv) :
if (!this->DaemonSeed())
{
std::cout << "ERROR: could not go into daemon mode. Shutting down." << std::endl;
- Logs->Log("STARTUP", DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
Exit(EXIT_STATUS_FORK);
}
}
- SE->RecoverFromFork();
+ SocketEngine::RecoverFromFork();
- /* During startup we don't actually initialize this
- * in the thread engine.
+ /* During startup we read the configuration now, not in
+ * a seperate thread
*/
this->Config->Read();
this->Config->Apply(NULL, "");
Logs->OpenFileLogs();
- this->Res = new DNS();
-
- /*
- * Initialise SID/UID.
- * For an explanation as to exactly how this works, and why it works this way, see GetUID().
- * -- w00t
- */
+ // If we don't have a SID, generate one based on the server name and the server description
if (Config->sid.empty())
- {
- // Generate one
- unsigned int sid = 0;
- char sidstr[4];
-
- for (const char* x = Config->ServerName.c_str(); *x; ++x)
- sid = 5 * sid + *x;
- for (const char* y = Config->ServerDesc.c_str(); *y; ++y)
- sid = 5 * sid + *y;
- sprintf(sidstr, "%03d", sid % 1000);
+ Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc);
- Config->sid = sidstr;
- }
+ // Initialize the UID generator with our sid
+ this->UIDGen.init(Config->sid);
- /* set up fake client again this time with the correct uid */
- this->FakeClient = new FakeUser(Config->sid, Config->ServerName);
+ // Create the server user for this server
+ this->FakeClient = new FakeUser(Config->sid, Config->ServerName, Config->ServerDesc);
- // Get XLine to do it's thing.
- this->XLines->CheckELines();
+ // This is needed as all new XLines are marked pending until ApplyLines() is called
this->XLines->ApplyLines();
int bounditems = BindPorts(pl);
@@ -601,9 +440,9 @@ InspIRCd::InspIRCd(int argc, char** argv) :
this->Modules->LoadAll();
- /* Just in case no modules were loaded - fix for bug #101 */
- this->BuildISupport();
- Config->ApplyDisabledCommands(Config->DisabledCommands);
+ // Build ISupport as ModuleManager::LoadAll() does not do it
+ this->ISupport.Build();
+ Config->ApplyDisabledCommands();
if (!pl.empty())
{
@@ -613,13 +452,13 @@ InspIRCd::InspIRCd(int argc, char** argv) :
int j = 1;
for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
{
- std::cout << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first) << " \tReason: " << i->second << std::endl;
+ std::cout << j << ".\tAddress: " << i->first.str() << " \tReason: " << strerror(i->second) << std::endl;
}
std::cout << std::endl << "Hint: Try using a public IP instead of blank or *" << std::endl;
}
- std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SE->GetMaxFds() << " max open sockets" << std::endl;
+ std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SocketEngine::GetMaxFds() << " max open sockets" << std::endl;
#ifndef _WIN32
if (!Config->cmdline.nofork)
@@ -627,7 +466,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
if (kill(getppid(), SIGTERM) == -1)
{
std::cout << "Error killing parent process: " << strerror(errno) << std::endl;
- Logs->Log("STARTUP", DEFAULT, "Error killing parent process: %s",strerror(errno));
+ Logs->Log("STARTUP", LOG_DEFAULT, "Error killing parent process: %s",strerror(errno));
}
}
@@ -641,7 +480,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
*
* -- nenolod
*/
- if ((!do_nofork) && (!do_testsuite) && (!Config->cmdline.forcedebug))
+ if ((!do_nofork) && (!Config->cmdline.forcedebug))
{
int fd = open("/dev/null", O_RDWR);
@@ -650,16 +489,16 @@ InspIRCd::InspIRCd(int argc, char** argv) :
fclose(stdout);
if (dup2(fd, STDIN_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdin.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdin.");
if (dup2(fd, STDOUT_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdout.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdout.");
if (dup2(fd, STDERR_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stderr.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stderr.");
close(fd);
}
else
{
- Logs->Log("STARTUP", DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Keeping pseudo-tty open as we are running in the foreground.");
}
#else
/* Set win32 service as running, if we are running as a service */
@@ -671,68 +510,53 @@ InspIRCd::InspIRCd(int argc, char** argv) :
FreeConsole();
}
- QueryPerformanceFrequency(&stats->QPFrequency);
+ QueryPerformanceFrequency(&stats.QPFrequency);
#endif
- Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
+ Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %lu max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SocketEngine::GetMaxFds());
#ifndef _WIN32
- std::string SetUser = Config->ConfValue("security")->getString("runasuser");
- std::string SetGroup = Config->ConfValue("security")->getString("runasgroup");
+ ConfigTag* security = Config->ConfValue("security");
+
+ const std::string SetGroup = security->getString("runasgroup");
if (!SetGroup.empty())
{
- int ret;
-
- // setgroups
- ret = setgroups(0, NULL);
-
- if (ret == -1)
+ errno = 0;
+ if (setgroups(0, NULL) == -1)
{
- this->Logs->Log("SETGROUPS", DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
- this->QuickExit(0);
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
}
- // setgid
- struct group *g;
-
- errno = 0;
- g = getgrnam(SetGroup.c_str());
-
+ struct group* g = getgrnam(SetGroup.c_str());
if (!g)
{
- this->Logs->Log("SETGUID", DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
- this->QuickExit(0);
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
}
- ret = setgid(g->gr_gid);
-
- if (ret == -1)
+ if (setgid(g->gr_gid) == -1)
{
- this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
- this->QuickExit(0);
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid(%d) failed (wrong group?): %s", g->gr_gid, strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
}
}
+ const std::string SetUser = security->getString("runasuser");
if (!SetUser.empty())
{
- // setuid
- struct passwd *u;
-
errno = 0;
- u = getpwnam(SetUser.c_str());
-
+ struct passwd* u = getpwnam(SetUser.c_str());
if (!u)
{
- this->Logs->Log("SETGUID", DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
- this->QuickExit(0);
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
}
- int ret = setuid(u->pw_uid);
-
- if (ret == -1)
+ if (setuid(u->pw_uid) == -1)
{
- this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
- this->QuickExit(0);
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid(%d) failed (wrong user?): %s", u->pw_uid, strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
}
}
@@ -760,15 +584,17 @@ void InspIRCd::UpdateTime()
#endif
}
-int InspIRCd::Run()
+void InspIRCd::Run()
{
+#ifdef INSPIRCD_ENABLE_TESTSUITE
/* See if we're supposed to be running the test suite rather than entering the mainloop */
- if (Config->cmdline.TestSuite)
+ if (do_testsuite)
{
TestSuite* ts = new TestSuite;
delete ts;
- Exit(0);
+ return;
}
+#endif
UpdateTime();
time_t OLDTIME = TIME.tv_sec;
@@ -783,7 +609,7 @@ int InspIRCd::Run()
if (this->ConfigThread && this->ConfigThread->IsDone())
{
/* Rehash has completed */
- this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
+ this->Logs->Log("CONFIG", LOG_DEBUG, "Detected ConfigThread exiting, tidying up...");
this->ConfigThread->Finish();
@@ -803,45 +629,49 @@ int InspIRCd::Run()
{
#ifndef _WIN32
getrusage(RUSAGE_SELF, &ru);
- stats->LastSampled = TIME;
- stats->LastCPU = ru.ru_utime;
+ stats.LastSampled = TIME;
+ stats.LastCPU = ru.ru_utime;
#else
- if(QueryPerformanceCounter(&stats->LastSampled))
+ if(QueryPerformanceCounter(&stats.LastSampled))
{
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
- stats->LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
- stats->LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
+ stats.LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
+ stats.LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
}
#endif
/* Allow a buffer of two seconds drift on this so that ntpdate etc dont harass admins */
if (TIME.tv_sec < OLDTIME - 2)
{
- SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)(OLDTIME-TIME.tv_sec));
+ SNO->WriteToSnoMask('a', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)(OLDTIME-TIME.tv_sec));
}
else if (TIME.tv_sec > OLDTIME + 2)
{
- SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)(TIME.tv_sec - OLDTIME));
+ SNO->WriteToSnoMask('a', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)(TIME.tv_sec - OLDTIME));
}
-
+
OLDTIME = TIME.tv_sec;
if ((TIME.tv_sec % 3600) == 0)
{
- this->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
+ FOREACH_MOD(OnGarbageCollect, ());
+
+ // HACK: ELines are not expired properly at the moment but it can't be fixed as
+ // the 2.0 XLine system is a spaghetti nightmare. Instead we skip over expired
+ // ELines in XLineManager::CheckELines() and expire them here instead.
+ XLines->GetAll("E");
}
- Timers->TickTimers(TIME.tv_sec);
- this->DoBackgroundUserStuff();
+ Timers.TickTimers(TIME.tv_sec);
+ Users->DoBackgroundUserStuff();
if ((TIME.tv_sec % 5) == 0)
{
- FOREACH_MOD(I_OnBackgroundTimer,OnBackgroundTimer(TIME.tv_sec));
+ FOREACH_MOD(OnBackgroundTimer, (TIME.tv_sec));
SNO->FlushSnotices();
}
}
@@ -853,8 +683,8 @@ int InspIRCd::Run()
* This will cause any read or write events to be
* dispatched to their handlers.
*/
- this->SE->DispatchTrialWrites();
- this->SE->DispatchEvents();
+ SocketEngine::DispatchTrialWrites();
+ SocketEngine::DispatchEvents();
/* if any users were quit, take them out */
GlobalCulls.Apply();
@@ -866,25 +696,6 @@ int InspIRCd::Run()
s_signal = 0;
}
}
-
- return 0;
-}
-
-/**********************************************************************************/
-
-/**
- * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
- */
-
-/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
- * (until this returns true, a user will block in the waiting state, waiting to connect up to the
- * registration timeout maximum seconds)
- */
-bool InspIRCd::AllModulesReportReady(LocalUser* user)
-{
- ModResult res;
- FIRST_MOD_RESULT(OnCheckReady, res, (user));
- return (res == MOD_RES_PASSTHRU);
}
sig_atomic_t InspIRCd::s_signal = 0;
diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp
index 356904f74..69c427212 100644
--- a/src/inspsocket.cpp
+++ b/src/inspsocket.cpp
@@ -23,17 +23,15 @@
#include "inspircd.h"
-#include "socket.h"
-#include "inspstring.h"
-#include "socketengine.h"
+#include "iohook.h"
-#ifndef DISABLE_WRITEV
-#include <sys/uio.h>
-#endif
-
-#ifndef IOV_MAX
-#define IOV_MAX 1024
-#endif
+static IOHook* GetNextHook(IOHook* hook)
+{
+ IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+ if (iohm)
+ return iohm->GetNextHook();
+ return NULL;
+}
BufferedSocket::BufferedSocket()
{
@@ -47,10 +45,10 @@ BufferedSocket::BufferedSocket(int newfd)
this->fd = newfd;
this->state = I_CONNECTED;
if (fd > -1)
- ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
+ SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
}
-void BufferedSocket::DoConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip)
+void BufferedSocket::DoConnect(const std::string& ipaddr, int aport, unsigned int maxtime, const std::string& connectbindip)
{
BufferedSocketError err = BeginConnect(ipaddr, aport, maxtime, connectbindip);
if (err != I_ERR_NONE)
@@ -61,12 +59,12 @@ void BufferedSocket::DoConnect(const std::string &ipaddr, int aport, unsigned lo
}
}
-BufferedSocketError BufferedSocket::BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip)
+BufferedSocketError BufferedSocket::BeginConnect(const std::string& ipaddr, int aport, unsigned int maxtime, const std::string& connectbindip)
{
irc::sockets::sockaddrs addr, bind;
if (!irc::sockets::aptosa(ipaddr, aport, addr))
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
return I_ERR_CONNECT;
}
@@ -82,23 +80,23 @@ BufferedSocketError BufferedSocket::BeginConnect(const std::string &ipaddr, int
return BeginConnect(addr, bind, maxtime);
}
-BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned long timeout)
+BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned int timeout)
{
if (fd < 0)
- fd = socket(dest.sa.sa_family, SOCK_STREAM, 0);
+ fd = socket(dest.family(), SOCK_STREAM, 0);
if (fd < 0)
return I_ERR_SOCKET;
- if (bind.sa.sa_family != 0)
+ if (bind.family() != 0)
{
- if (ServerInstance->SE->Bind(fd, bind) < 0)
+ if (SocketEngine::Bind(fd, bind) < 0)
return I_ERR_BIND;
}
- ServerInstance->SE->NonBlocking(fd);
+ SocketEngine::NonBlocking(fd);
- if (ServerInstance->SE->Connect(this, &dest.sa, sa_size(dest)) == -1)
+ if (SocketEngine::Connect(this, dest) == -1)
{
if (errno != EINPROGRESS)
return I_ERR_CONNECT;
@@ -106,13 +104,13 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs&
this->state = I_CONNECTING;
- if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE | FD_WRITE_WILL_BLOCK))
+ if (!SocketEngine::AddFd(this, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE | FD_WRITE_WILL_BLOCK))
return I_ERR_NOMOREFDS;
- this->Timeout = new SocketTimeout(this->GetFd(), this, timeout, ServerInstance->Time());
- ServerInstance->Timers->AddTimer(this->Timeout);
+ this->Timeout = new SocketTimeout(this->GetFd(), this, timeout);
+ ServerInstance->Timers.AddTimer(this->Timeout);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"BufferedSocket::DoConnect success");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BufferedSocket::DoConnect success");
return I_ERR_NONE;
}
@@ -122,23 +120,18 @@ void StreamSocket::Close()
{
// final chance, dump as much of the sendq as we can
DoWrite();
- if (IOHook)
+
+ IOHook* hook = GetIOHook();
+ DelIOHook();
+ while (hook)
{
- try
- {
- IOHook->OnStreamSocketClose(this);
- }
- catch (CoreException& modexcept)
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s",
- modexcept.GetSource(), modexcept.GetReason());
- }
- IOHook = NULL;
+ hook->OnStreamSocketClose(this);
+ IOHook* const nexthook = GetNextHook(hook);
+ delete hook;
+ hook = nexthook;
}
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(this);
- fd = -1;
+ SocketEngine::Shutdown(this, 2);
+ SocketEngine::Close(this);
}
}
@@ -153,67 +146,79 @@ bool StreamSocket::GetNextLine(std::string& line, char delim)
std::string::size_type i = recvq.find(delim);
if (i == std::string::npos)
return false;
- line = recvq.substr(0, i);
- // TODO is this the most efficient way to split?
- recvq = recvq.substr(i + 1);
+ line.assign(recvq, 0, i);
+ recvq.erase(0, i + 1);
return true;
}
-void StreamSocket::DoRead()
+int StreamSocket::HookChainRead(IOHook* hook, std::string& rq)
{
- if (IOHook)
+ if (!hook)
+ return ReadToRecvQ(rq);
+
+ IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+ if (iohm)
{
- int rv = -1;
- try
- {
- rv = IOHook->OnStreamSocketRead(this, recvq);
- }
- catch (CoreException& modexcept)
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "%s threw an exception: %s",
- modexcept.GetSource(), modexcept.GetReason());
- return;
- }
- if (rv > 0)
- OnDataReady();
- if (rv < 0)
- SetError("Read Error"); // will not overwrite a better error message
+ // Call the next hook to put data into the recvq of the current hook
+ const int ret = HookChainRead(iohm->GetNextHook(), iohm->GetRecvQ());
+ if (ret <= 0)
+ return ret;
}
- else
+ return hook->OnStreamSocketRead(this, rq);
+}
+
+void StreamSocket::DoRead()
+{
+ const std::string::size_type prevrecvqsize = recvq.size();
+
+ const int result = HookChainRead(GetIOHook(), recvq);
+ if (result < 0)
{
+ SetError("Read Error"); // will not overwrite a better error message
+ return;
+ }
+
+ if (recvq.size() > prevrecvqsize)
+ OnDataReady();
+}
+
+int StreamSocket::ReadToRecvQ(std::string& rq)
+{
char* ReadBuffer = ServerInstance->GetReadBuffer();
- int n = ServerInstance->SE->Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
+ int n = SocketEngine::Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
if (n == ServerInstance->Config->NetBufferSize)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
- recvq.append(ReadBuffer, n);
- OnDataReady();
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+ rq.append(ReadBuffer, n);
}
else if (n > 0)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ);
- recvq.append(ReadBuffer, n);
- OnDataReady();
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ);
+ rq.append(ReadBuffer, n);
}
else if (n == 0)
{
error = "Connection closed";
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ return -1;
}
else if (SocketEngine::IgnoreError())
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
+ return 0;
}
else if (errno == EINTR)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+ return 0;
}
else
{
error = SocketEngine::LastError();
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ return -1;
}
- }
+ return n;
}
/* Don't try to prepare huge blobs of data to send to a blocked socket */
@@ -221,120 +226,56 @@ static const int MYIOV_MAX = IOV_MAX < 128 ? IOV_MAX : 128;
void StreamSocket::DoWrite()
{
- if (sendq.empty())
+ if (getSendQSize() == 0)
return;
- if (!error.empty() || fd < 0 || fd == INT_MAX)
+ if (!error.empty() || fd < 0)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DoWrite on errored or closed socket");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
return;
}
-#ifndef DISABLE_WRITEV
- if (IOHook)
-#endif
+ SendQueue* psendq = &sendq;
+ IOHook* hook = GetIOHook();
+ while (hook)
{
- int rv = -1;
- try
- {
- while (error.empty() && !sendq.empty())
- {
- if (sendq.size() > 1 && sendq[0].length() < 1024)
- {
- // Avoid multiple repeated SSL encryption invocations
- // This adds a single copy of the queue, but avoids
- // much more overhead in terms of system calls invoked
- // by the IOHook.
- //
- // The length limit of 1024 is to prevent merging strings
- // more than once when writes begin to block.
- std::string tmp;
- tmp.reserve(1280);
- while (!sendq.empty() && tmp.length() < 1024)
- {
- tmp.append(sendq.front());
- sendq.pop_front();
- }
- sendq.push_front(tmp);
- }
- std::string& front = sendq.front();
- int itemlen = front.length();
- if (IOHook)
- {
- rv = IOHook->OnStreamSocketWrite(this, front);
- if (rv > 0)
- {
- // consumed the entire string, and is ready for more
- sendq_len -= itemlen;
- sendq.pop_front();
- }
- else if (rv == 0)
- {
- // socket has blocked. Stop trying to send data.
- // IOHook has requested unblock notification from the socketengine
+ int rv = hook->OnStreamSocketWrite(this, *psendq);
+ psendq = NULL;
- // Since it is possible that a partial write took place, adjust sendq_len
- sendq_len = sendq_len - itemlen + front.length();
- return;
- }
- else
- {
- SetError("Write Error"); // will not overwrite a better error message
- return;
- }
- }
-#ifdef DISABLE_WRITEV
- else
- {
- rv = ServerInstance->SE->Send(this, front.data(), itemlen, 0);
- if (rv == 0)
- {
- SetError("Connection closed");
- return;
- }
- else if (rv < 0)
- {
- if (errno == EINTR || SocketEngine::IgnoreError())
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
- else
- SetError(SocketEngine::LastError());
- return;
- }
- else if (rv < itemlen)
- {
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
- front = front.substr(rv);
- sendq_len -= rv;
- return;
- }
- else
- {
- sendq_len -= itemlen;
- sendq.pop_front();
- if (sendq.empty())
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_EDGE_WRITE);
- }
- }
-#endif
- }
+ // rv == 0 means the socket has blocked. Stop trying to send data.
+ // IOHook has requested unblock notification from the socketengine.
+ if (rv == 0)
+ break;
+
+ if (rv < 0)
+ {
+ SetError("Write Error"); // will not overwrite a better error message
+ break;
}
- catch (CoreException& modexcept)
+
+ IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+ hook = NULL;
+ if (iohm)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s",
- modexcept.GetSource(), modexcept.GetReason());
+ psendq = &iohm->GetSendQ();
+ hook = iohm->GetNextHook();
}
}
-#ifndef DISABLE_WRITEV
- else
- {
+
+ if (psendq)
+ FlushSendQ(*psendq);
+}
+
+void StreamSocket::FlushSendQ(SendQueue& sq)
+{
// don't even try if we are known to be blocking
if (GetEventMask() & FD_WRITE_WILL_BLOCK)
return;
// start out optimistic - we won't need to write any more
int eventChange = FD_WANT_EDGE_WRITE;
- while (error.empty() && sendq_len && eventChange == FD_WANT_EDGE_WRITE)
+ while (error.empty() && !sq.empty() && eventChange == FD_WANT_EDGE_WRITE)
{
// Prepare a writev() call to write all buffers efficiently
- int bufcount = sendq.size();
+ int bufcount = sq.size();
// cap the number of buffers at MYIOV_MAX
if (bufcount > MYIOV_MAX)
@@ -343,22 +284,25 @@ void StreamSocket::DoWrite()
}
int rv_max = 0;
- iovec* iovecs = new iovec[bufcount];
- for(int i=0; i < bufcount; i++)
+ int rv;
{
- iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
- iovecs[i].iov_len = sendq[i].length();
- rv_max += sendq[i].length();
+ SocketEngine::IOVector iovecs[MYIOV_MAX];
+ size_t j = 0;
+ for (SendQueue::const_iterator i = sq.begin(), end = i+bufcount; i != end; ++i, j++)
+ {
+ const SendQueue::Element& elem = *i;
+ iovecs[j].iov_base = const_cast<char*>(elem.data());
+ iovecs[j].iov_len = elem.length();
+ rv_max += iovecs[j].iov_len;
+ }
+ rv = SocketEngine::WriteV(this, iovecs, bufcount);
}
- int rv = writev(fd, iovecs, bufcount);
- delete[] iovecs;
- if (rv == (int)sendq_len)
+ if (rv == (int)sq.bytes())
{
// it's our lucky day, everything got written out. Fast cleanup.
// This won't ever happen if the number of buffers got capped.
- sendq_len = 0;
- sendq.clear();
+ sq.clear();
}
else if (rv > 0)
{
@@ -368,20 +312,19 @@ void StreamSocket::DoWrite()
// it's going to block now
eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
}
- sendq_len -= rv;
- while (rv > 0 && !sendq.empty())
+ while (rv > 0 && !sq.empty())
{
- std::string& front = sendq.front();
+ const SendQueue::Element& front = sq.front();
if (front.length() <= (size_t)rv)
{
// this string got fully written out
rv -= front.length();
- sendq.pop_front();
+ sq.pop_front();
}
else
{
// stopped in the middle of this string
- front = front.substr(rv);
+ sq.erase_front(rv);
rv = 0;
}
}
@@ -407,38 +350,38 @@ void StreamSocket::DoWrite()
if (!error.empty())
{
// error - kill all events
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
}
else
{
- ServerInstance->SE->ChangeEventMask(this, eventChange);
+ SocketEngine::ChangeEventMask(this, eventChange);
}
- }
-#endif
}
void StreamSocket::WriteData(const std::string &data)
{
if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Attempt to write data to dead socket: %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to write data to dead socket: %s",
data.c_str());
return;
}
/* Append the data to the back of the queue ready for writing */
sendq.push_back(data);
- sendq_len += data.length();
- ServerInstance->SE->ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
}
-void SocketTimeout::Tick(time_t)
+bool SocketTimeout::Tick(time_t)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"SocketTimeout::Tick");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SocketTimeout::Tick");
- if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
- return;
+ if (SocketEngine::GetRef(this->sfd) != this->sock)
+ {
+ delete this;
+ return false;
+ }
if (this->sock->state == I_CONNECTING)
{
@@ -454,90 +397,143 @@ void SocketTimeout::Tick(time_t)
}
this->sock->Timeout = NULL;
+ delete this;
+ return false;
}
void BufferedSocket::OnConnected() { }
void BufferedSocket::OnTimeout() { return; }
-void BufferedSocket::DoWrite()
+void BufferedSocket::OnEventHandlerWrite()
{
if (state == I_CONNECTING)
{
state = I_CONNECTED;
this->OnConnected();
- if (GetIOHook())
- GetIOHook()->OnStreamSocketConnect(this);
- else
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
+ if (!GetIOHook())
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
}
- this->StreamSocket::DoWrite();
+ this->StreamSocket::OnEventHandlerWrite();
}
BufferedSocket::~BufferedSocket()
{
this->Close();
- if (Timeout)
+ // The timer is removed from the TimerManager in Timer::~Timer()
+ delete Timeout;
+}
+
+void StreamSocket::OnEventHandlerError(int errornum)
+{
+ if (!error.empty())
+ return;
+
+ if (errornum == 0)
+ SetError("Connection closed");
+ else
+ SetError(SocketEngine::GetError(errornum));
+
+ BufferedSocketError errcode = I_ERR_OTHER;
+ switch (errornum)
{
- ServerInstance->Timers->DelTimer(Timeout);
- Timeout = NULL;
+ case ETIMEDOUT:
+ errcode = I_ERR_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ case 0:
+ errcode = I_ERR_CONNECT;
+ break;
+ case EADDRINUSE:
+ errcode = I_ERR_BIND;
+ break;
+ case EPIPE:
+ case EIO:
+ errcode = I_ERR_WRITE;
+ break;
}
+
+ // Log and call OnError()
+ CheckError(errcode);
}
-void StreamSocket::HandleEvent(EventType et, int errornum)
+void StreamSocket::OnEventHandlerRead()
{
if (!error.empty())
return;
- BufferedSocketError errcode = I_ERR_OTHER;
- try {
- switch (et)
- {
- case EVENT_ERROR:
- {
- if (errornum == 0)
- SetError("Connection closed");
- else
- SetError(SocketEngine::GetError(errornum));
- switch (errornum)
- {
- case ETIMEDOUT:
- errcode = I_ERR_TIMEOUT;
- break;
- case ECONNREFUSED:
- case 0:
- errcode = I_ERR_CONNECT;
- break;
- case EADDRINUSE:
- errcode = I_ERR_BIND;
- break;
- case EPIPE:
- case EIO:
- errcode = I_ERR_WRITE;
- break;
- }
- break;
- }
- case EVENT_READ:
- {
- DoRead();
- break;
- }
- case EVENT_WRITE:
- {
- DoWrite();
- break;
- }
- }
+
+ try
+ {
+ DoRead();
}
catch (CoreException& ex)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
- fd, ex.GetReason());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Caught exception in socket processing on FD %d - '%s'", fd, ex.GetReason().c_str());
SetError(ex.GetReason());
}
+ CheckError(I_ERR_OTHER);
+}
+
+void StreamSocket::OnEventHandlerWrite()
+{
+ if (!error.empty())
+ return;
+
+ DoWrite();
+ CheckError(I_ERR_OTHER);
+}
+
+void StreamSocket::CheckError(BufferedSocketError errcode)
+{
if (!error.empty())
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
OnError(errcode);
}
}
+IOHook* StreamSocket::GetModHook(Module* mod) const
+{
+ for (IOHook* curr = GetIOHook(); curr; curr = GetNextHook(curr))
+ {
+ if (curr->prov->creator == mod)
+ return curr;
+ }
+ return NULL;
+}
+
+void StreamSocket::AddIOHook(IOHook* newhook)
+{
+ IOHook* curr = GetIOHook();
+ if (!curr)
+ {
+ iohook = newhook;
+ return;
+ }
+
+ IOHookMiddle* lasthook;
+ while (curr)
+ {
+ lasthook = IOHookMiddle::ToMiddleHook(curr);
+ if (!lasthook)
+ return;
+ curr = lasthook->GetNextHook();
+ }
+
+ lasthook->SetNextHook(newhook);
+}
+
+size_t StreamSocket::getSendQSize() const
+{
+ size_t ret = sendq.bytes();
+ IOHook* curr = GetIOHook();
+ while (curr)
+ {
+ const IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(curr);
+ if (!iohm)
+ break;
+
+ ret += iohm->GetSendQ().bytes();
+ curr = iohm->GetNextHook();
+ }
+ return ret;
+}
diff --git a/src/inspstring.cpp b/src/inspstring.cpp
index 534b7ce00..79aef52bd 100644
--- a/src/inspstring.cpp
+++ b/src/inspstring.cpp
@@ -21,140 +21,18 @@
#include "inspircd.h"
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef HAS_STRLCPY
-CoreExport size_t strlcat(char *dst, const char *src, size_t siz)
-{
- char *d = dst;
- const char *s = src;
- size_t n = siz, dlen;
-
- while (n-- != 0 && *d != '\0')
- d++;
-
- dlen = d - dst;
- n = siz - dlen;
-
- if (n == 0)
- return(dlen + strlen(s));
-
- while (*s != '\0')
- {
- if (n != 1)
- {
- *d++ = *s;
- n--;
- }
-
- s++;
- }
-
- *d = '\0';
- return(dlen + (s - src)); /* count does not include NUL */
-}
-
-CoreExport size_t strlcpy(char *dst, const char *src, size_t siz)
-{
- char *d = dst;
- const char *s = src;
- size_t n = siz;
-
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0)
- {
- do
- {
- if ((*d++ = *s++) == 0)
- break;
- } while (--n != 0);
- }
-
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0)
- {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++);
- }
-
- return(s - src - 1); /* count does not include NUL */
-}
-#endif
-
-CoreExport int charlcat(char* x,char y,int z)
-{
- char* x__n = x;
- int v = 0;
-
- while(*x__n++)
- v++;
-
- if (v < z - 1)
- {
- *--x__n = y;
- *++x__n = 0;
- }
-
- return v;
-}
-
-CoreExport bool charremove(char* mp, char remove)
-{
- char* mptr = mp;
- bool shift_down = false;
-
- while (*mptr)
- {
- if (*mptr == remove)
- shift_down = true;
-
- if (shift_down)
- *mptr = *(mptr+1);
-
- mptr++;
- }
-
- return shift_down;
-}
-
static const char hextable[] = "0123456789abcdef";
-std::string BinToHex(const std::string& data)
+std::string BinToHex(const void* raw, size_t l)
{
- int l = data.length();
+ const char* data = static_cast<const char*>(raw);
std::string rv;
rv.reserve(l * 2);
- for(int i=0; i < l; i++)
+ for (size_t i = 0; i < l; i++)
{
unsigned char c = data[i];
- rv.append(1, hextable[c >> 4]);
- rv.append(1, hextable[c & 0xF]);
+ rv.push_back(hextable[c >> 4]);
+ rv.push_back(hextable[c & 0xF]);
}
return rv;
}
@@ -230,3 +108,125 @@ std::string Base64ToBin(const std::string& data_str, const char* table)
}
return rv;
}
+
+bool InspIRCd::TimingSafeCompare(const std::string& one, const std::string& two)
+{
+ if (one.length() != two.length())
+ return false;
+
+ unsigned int diff = 0;
+ for (std::string::const_iterator i = one.begin(), j = two.begin(); i != one.end(); ++i, ++j)
+ {
+ unsigned char a = static_cast<unsigned char>(*i);
+ unsigned char b = static_cast<unsigned char>(*j);
+ diff |= a ^ b;
+ }
+
+ return (diff == 0);
+}
+
+void TokenList::AddList(const std::string& tokenlist)
+{
+ std::string token;
+ irc::spacesepstream tokenstream(tokenlist);
+ while (tokenstream.GetToken(token))
+ {
+ if (token[0] == '-')
+ Remove(token.substr(1));
+ else
+ Add(token);
+ }
+
+}
+void TokenList::Add(const std::string& token)
+{
+ // If the token is empty or contains just whitespace it is invalid.
+ if (token.empty() || token.find_first_not_of(" \t") == std::string::npos)
+ return;
+
+ // If the token is a wildcard entry then permissive mode has been enabled.
+ if (token == "*")
+ {
+ permissive = true;
+ tokens.clear();
+ return;
+ }
+
+ // If we are in permissive mode then remove the token from the token list.
+ // Otherwise, add it to the token list.
+ if (permissive)
+ tokens.erase(token);
+ else
+ tokens.insert(token);
+}
+
+void TokenList::Clear()
+{
+ permissive = false;
+ tokens.clear();
+}
+
+bool TokenList::Contains(const std::string& token) const
+{
+ // If we are in permissive mode and the token is in the list
+ // then we don't have it.
+ if (permissive && tokens.find(token) != tokens.end())
+ return false;
+
+ // If we are not in permissive mode and the token is not in
+ // the list then we don't have it.
+ if (!permissive && tokens.find(token) == tokens.end())
+ return false;
+
+ // We have the token!
+ return true;
+}
+
+void TokenList::Remove(const std::string& token)
+{
+ // If the token is empty or contains just whitespace it is invalid.
+ if (token.empty() || token.find_first_not_of(" \t") == std::string::npos)
+ return;
+
+ // If the token is a wildcard entry then permissive mode has been disabled.
+ if (token == "*")
+ {
+ permissive = false;
+ tokens.clear();
+ return;
+ }
+
+ // If we are in permissive mode then add the token to the token list.
+ // Otherwise, remove it from the token list.
+ if (permissive)
+ tokens.insert(token);
+ else
+ tokens.erase(token);
+}
+
+std::string TokenList::ToString() const
+{
+ std::string buffer(permissive ? "* " : "-* ");
+ buffer.append(stdalgo::string::join(tokens));
+ return buffer;
+}
+
+bool TokenList::operator==(const TokenList& other) const
+{
+ // Both sets must be in the same mode to be equal.
+ if (permissive != other.permissive)
+ return false;
+
+ // Both sets must be the same size to be equal.
+ if (tokens.size() != other.tokens.size())
+ return false;
+
+ for (insp::flat_set<std::string>::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
+ {
+ // Both sets must contain the same tokens to be equal.
+ if (other.tokens.find(*iter) == other.tokens.end())
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/listensocket.cpp b/src/listensocket.cpp
index ae11c3b48..60ee0b449 100644
--- a/src/listensocket.cpp
+++ b/src/listensocket.cpp
@@ -19,16 +19,17 @@
#include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
+#include "iohook.h"
+
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#endif
ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to)
: bind_tag(tag)
+ , bind_sa(bind_to)
{
- irc::sockets::satoap(bind_to, bind_addr, bind_port);
- bind_desc = irc::sockets::satouser(bind_to);
-
- fd = socket(bind_to.sa.sa_family, SOCK_STREAM, 0);
+ fd = socket(bind_to.family(), SOCK_STREAM, 0);
if (this->fd == -1)
return;
@@ -40,7 +41,7 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t
* is "::" or an IPv6 address, disable support so that an IPv4 bind will
* work on the port (by us or another application).
*/
- if (bind_to.sa.sa_family == AF_INET6)
+ if (bind_to.family() == AF_INET6)
{
std::string addr = tag->getString("address");
/* This must be >= sizeof(DWORD) on Windows */
@@ -51,23 +52,53 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t
}
#endif
- ServerInstance->SE->SetReuse(fd);
- int rv = ServerInstance->SE->Bind(this->fd, bind_to);
+ if (tag->getBool("free"))
+ {
+ socklen_t enable = 1;
+#if defined IP_FREEBIND // Linux 2.4+
+ setsockopt(fd, SOL_IP, IP_FREEBIND, &enable, sizeof(enable));
+#elif defined IP_BINDANY // FreeBSD
+ setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(enable));
+#elif defined SO_BINDANY // NetBSD/OpenBSD
+ setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, sizeof(enable));
+#else
+ (void)enable;
+#endif
+ }
+
+ SocketEngine::SetReuse(fd);
+ int rv = SocketEngine::Bind(this->fd, bind_to);
if (rv >= 0)
- rv = ServerInstance->SE->Listen(this->fd, ServerInstance->Config->MaxConn);
+ rv = SocketEngine::Listen(this->fd, ServerInstance->Config->MaxConn);
+
+ // Default defer to on for TLS listeners because in TLS the client always speaks first
+ int timeout = tag->getDuration("defer", (tag->getString("ssl").empty() ? 0 : 3));
+ if (timeout && !rv)
+ {
+#if defined TCP_DEFER_ACCEPT
+ setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(timeout));
+#elif defined SO_ACCEPTFILTER
+ struct accept_filter_arg afa;
+ memset(&afa, 0, sizeof(afa));
+ strcpy(afa.af_name, "dataready");
+ setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
+#endif
+ }
if (rv < 0)
{
int errstore = errno;
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
+ SocketEngine::Shutdown(this, 2);
+ SocketEngine::Close(this->GetFd());
this->fd = -1;
errno = errstore;
}
else
{
- ServerInstance->SE->NonBlocking(this->fd);
- ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::NonBlocking(this->fd);
+ SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+
+ this->ResetIOHookProvider();
}
}
@@ -75,58 +106,39 @@ ListenSocket::~ListenSocket()
{
if (this->GetFd() > -1)
{
- ServerInstance->SE->DelFd(this);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
- ServerInstance->SE->Shutdown(this, 2);
- if (ServerInstance->SE->Close(this) != 0)
- ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
- this->fd = -1;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Shut down listener on fd %d", this->fd);
+ SocketEngine::Shutdown(this, 2);
+
+ if (SocketEngine::Close(this) != 0)
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Failed to cancel listener: %s", strerror(errno));
+
+ if (bind_sa.family() == AF_UNIX && unlink(bind_sa.un.sun_path))
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Failed to unlink UNIX socket: %s", strerror(errno));
}
}
-/* Just seperated into another func for tidiness really.. */
-void ListenSocket::AcceptInternal()
+void ListenSocket::OnEventHandlerRead()
{
irc::sockets::sockaddrs client;
- irc::sockets::sockaddrs server;
+ irc::sockets::sockaddrs server(bind_sa);
socklen_t length = sizeof(client);
- int incomingSockfd = ServerInstance->SE->Accept(this, &client.sa, &length);
+ int incomingSockfd = SocketEngine::Accept(this, &client.sa, &length);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"HandleEvent for Listensocket %s nfd=%d", bind_desc.c_str(), incomingSockfd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Accepting connection on socket %s fd %d", bind_sa.str().c_str(), incomingSockfd);
if (incomingSockfd < 0)
{
- ServerInstance->stats->statsRefused++;
+ ServerInstance->stats.Refused++;
return;
}
socklen_t sz = sizeof(server);
if (getsockname(incomingSockfd, &server.sa, &sz))
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
- irc::sockets::aptosa(bind_addr, bind_port, server);
- }
-
- /*
- * XXX -
- * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
- * its a pretty big but for the moment valid assumption:
- * file descriptors are handed out starting at 0, and are recycled as theyre freed.
- * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
- * irc server at once (or the irc server otherwise initiating this many connections, files etc)
- * which for the time being is a physical impossibility (even the largest networks dont have more
- * than about 10,000 users on ONE server!)
- */
- if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
- {
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
- ServerInstance->SE->Shutdown(incomingSockfd, 2);
- ServerInstance->SE->Close(incomingSockfd);
- ServerInstance->stats->statsRefused++;
- return;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Can't get peername: %s", strerror(errno));
}
- if (client.sa.sa_family == AF_INET6)
+ if (client.family() == AF_INET6)
{
/*
* This case is the be all and end all patch to catch and nuke 4in6
@@ -155,15 +167,23 @@ void ListenSocket::AcceptInternal()
memcpy(&server.in4.sin_addr.s_addr, server.in6.sin6_addr.s6_addr + 12, sizeof(uint32_t));
}
}
+ else if (client.family() == AF_UNIX)
+ {
+ // Clients connecting via UNIX sockets don't have paths so give them
+ // the server path as defined in RFC 1459 section 8.1.1.
+ //
+ // strcpy is safe here because sizeof(sockaddr_un.sun_path) is equal on both.
+ strcpy(client.un.sun_path, server.un.sun_path);
+ }
- ServerInstance->SE->NonBlocking(incomingSockfd);
+ SocketEngine::NonBlocking(incomingSockfd);
ModResult res;
FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server));
if (res == MOD_RES_PASSTHRU)
{
std::string type = bind_tag->getString("type", "clients");
- if (type == "clients")
+ if (stdalgo::string::equalsci(type, "clients"))
{
ServerInstance->Users->AddUser(incomingSockfd, this, &client, &server);
res = MOD_RES_ALLOW;
@@ -171,29 +191,34 @@ void ListenSocket::AcceptInternal()
}
if (res == MOD_RES_ALLOW)
{
- ServerInstance->stats->statsAccept++;
+ ServerInstance->stats.Accept++;
}
else
{
- ServerInstance->stats->statsRefused++;
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Refusing connection on %s - %s",
- bind_desc.c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
- ServerInstance->SE->Close(incomingSockfd);
+ ServerInstance->stats.Refused++;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Refusing connection on %s - %s",
+ bind_sa.str().c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
+ SocketEngine::Close(incomingSockfd);
}
}
-void ListenSocket::HandleEvent(EventType e, int err)
+void ListenSocket::ResetIOHookProvider()
{
- switch (e)
+ iohookprovs[0].SetProvider(bind_tag->getString("hook"));
+
+ // Check that all non-last hooks support being in the middle
+ for (IOHookProvList::iterator i = iohookprovs.begin(); i != iohookprovs.end()-1; ++i)
{
- case EVENT_ERROR:
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
- break;
- case EVENT_WRITE:
- ServerInstance->Logs->Log("SOCKET",DEBUG,"*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
- break;
- case EVENT_READ:
- this->AcceptInternal();
- break;
+ IOHookProvRef& curr = *i;
+ // Ignore if cannot be in the middle
+ if ((curr) && (!curr->IsMiddle()))
+ curr.SetProvider(std::string());
}
+
+ std::string provname = bind_tag->getString("ssl");
+ if (!provname.empty())
+ provname.insert(0, "ssl/");
+
+ // SSL should be the last
+ iohookprovs.back().SetProvider(provname);
}
diff --git a/src/listmode.cpp b/src/listmode.cpp
new file mode 100644
index 000000000..74977b866
--- /dev/null
+++ b/src/listmode.cpp
@@ -0,0 +1,239 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "listmode.h"
+
+ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag)
+ : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST),
+ listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
+ configtag(ctag)
+ , extItem("listbase_mode_" + name + "_list", ExtensionItem::EXT_CHANNEL, Creator)
+{
+ list = true;
+}
+
+void ListModeBase::DisplayList(User* user, Channel* channel)
+{
+ ChanData* cd = extItem.get(channel);
+ if (cd)
+ {
+ for (ModeList::const_iterator it = cd->list.begin(); it != cd->list.end(); ++it)
+ {
+ user->WriteNumeric(listnumeric, channel->name, it->mask, it->setter, (unsigned long) it->time);
+ }
+ }
+ user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
+}
+
+void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
+{
+ user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
+}
+
+void ListModeBase::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
+{
+ ChanData* cd = extItem.get(channel);
+ if (cd)
+ {
+ for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
+ {
+ changelist.push_remove(this, it->mask);
+ }
+ }
+}
+
+void ListModeBase::DoRehash()
+{
+ ConfigTagList tags = ServerInstance->Config->ConfTags(configtag);
+
+ limitlist oldlimits = chanlimits;
+ chanlimits.clear();
+
+ for (ConfigIter i = tags.first; i != tags.second; i++)
+ {
+ // For each <banlist> tag
+ ConfigTag* c = i->second;
+ ListLimit limit(c->getString("chan"), c->getUInt("limit", 0));
+
+ if (limit.mask.size() && limit.limit > 0)
+ chanlimits.push_back(limit);
+ }
+
+ // Add the default entry. This is inserted last so if the user specifies a
+ // wildcard record in the config it will take precedence over this entry.
+ chanlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE));
+
+ // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
+ if (oldlimits == chanlimits)
+ return;
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ ChanData* cd = extItem.get(i->second);
+ if (cd)
+ cd->maxitems = -1;
+ }
+}
+
+unsigned int ListModeBase::FindLimit(const std::string& channame)
+{
+ for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); ++it)
+ {
+ if (InspIRCd::Match(channame, it->mask))
+ {
+ // We have a pattern matching the channel
+ return it->limit;
+ }
+ }
+ return DEFAULT_LIST_SIZE;
+}
+
+unsigned int ListModeBase::GetLimitInternal(const std::string& channame, ChanData* cd)
+{
+ if (cd->maxitems < 0)
+ cd->maxitems = FindLimit(channame);
+ return cd->maxitems;
+}
+
+unsigned int ListModeBase::GetLimit(Channel* channel)
+{
+ ChanData* cd = extItem.get(channel);
+ if (!cd) // just find the limit
+ return FindLimit(channel->name);
+
+ return GetLimitInternal(channel->name, cd);
+}
+
+unsigned int ListModeBase::GetLowerLimit()
+{
+ unsigned int limit = UINT_MAX;
+ for (limitlist::iterator iter = chanlimits.begin(); iter != chanlimits.end(); ++iter)
+ {
+ if (iter->limit < limit)
+ limit = iter->limit;
+ }
+ return limit == UINT_MAX ? DEFAULT_LIST_SIZE : limit;
+}
+
+ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
+{
+ // Try and grab the list
+ ChanData* cd = extItem.get(channel);
+
+ if (adding)
+ {
+ if (tidy)
+ ModeParser::CleanMask(parameter);
+
+ if (parameter.length() > 250)
+ return MODEACTION_DENY;
+
+ // If there was no list
+ if (!cd)
+ {
+ // Make one
+ cd = new ChanData;
+ extItem.set(channel, cd);
+ }
+
+ // Check if the item already exists in the list
+ for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
+ {
+ if (parameter == it->mask)
+ {
+ /* Give a subclass a chance to error about this */
+ TellAlreadyOnList(source, channel, parameter);
+
+ // it does, deny the change
+ return MODEACTION_DENY;
+ }
+ }
+
+ if ((IS_LOCAL(source)) && (cd->list.size() >= GetLimitInternal(channel->name, cd)))
+ {
+ /* List is full, give subclass a chance to send a custom message */
+ TellListTooLong(source, channel, parameter);
+ return MODEACTION_DENY;
+ }
+
+ /* Ok, it *could* be allowed, now give someone subclassing us
+ * a chance to validate the parameter.
+ * The param is passed by reference, so they can both modify it
+ * and tell us if we allow it or not.
+ *
+ * eg, the subclass could:
+ * 1) allow
+ * 2) 'fix' parameter and then allow
+ * 3) deny
+ */
+ if (ValidateParam(source, channel, parameter))
+ {
+ // And now add the mask onto the list...
+ cd->list.push_back(ListItem(parameter, source->nick, ServerInstance->Time()));
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ /* If they deny it they have the job of giving an error message */
+ return MODEACTION_DENY;
+ }
+ }
+ else
+ {
+ // We're taking the mode off
+ if (cd)
+ {
+ for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); ++it)
+ {
+ if (parameter == it->mask)
+ {
+ stdalgo::vector::swaperase(cd->list, it);
+ return MODEACTION_ALLOW;
+ }
+ }
+ }
+
+ /* Tried to remove something that wasn't set */
+ TellNotSet(source, channel, parameter);
+ return MODEACTION_DENY;
+ }
+}
+
+bool ListModeBase::ValidateParam(User*, Channel*, std::string&)
+{
+ return true;
+}
+
+void ListModeBase::OnParameterMissing(User*, User*, Channel*)
+{
+}
+
+void ListModeBase::TellListTooLong(User* source, Channel* channel, std::string& parameter)
+{
+ source->WriteNumeric(ERR_BANLISTFULL, channel->name, parameter, "Channel ban list is full");
+}
+
+void ListModeBase::TellAlreadyOnList(User*, Channel*, std::string&)
+{
+}
+
+void ListModeBase::TellNotSet(User*, Channel*, std::string&)
+{
+}
diff --git a/src/logger.cpp b/src/logger.cpp
index 89b2be019..b9f9bae0b 100644
--- a/src/logger.cpp
+++ b/src/logger.cpp
@@ -22,8 +22,6 @@
#include "inspircd.h"
-#include "filelogger.h"
-
/*
* Suggested implementation...
* class LogManager
@@ -51,9 +49,12 @@
*
*/
+const char LogStream::LogHeader[] =
+ "Log started for " INSPIRCD_VERSION " (" MODULE_INIT_STR ")";
+
LogManager::LogManager()
+ : Logging(false)
{
- Logging = false;
}
LogManager::~LogManager()
@@ -76,49 +77,49 @@ void LogManager::OpenFileLogs()
{
ConfigTag* tag = i->second;
std::string method = tag->getString("method");
- if (method != "file")
+ if (!stdalgo::string::equalsci(method, "file"))
{
continue;
}
std::string type = tag->getString("type");
std::string level = tag->getString("level");
- int loglevel = DEFAULT;
- if (level == "rawio")
+ LogLevel loglevel = LOG_DEFAULT;
+ if (stdalgo::string::equalsci(level, "rawio"))
{
- loglevel = RAWIO;
+ loglevel = LOG_RAWIO;
ServerInstance->Config->RawLog = true;
}
- else if (level == "debug")
+ else if (stdalgo::string::equalsci(level, "debug"))
{
- loglevel = DEBUG;
+ loglevel = LOG_DEBUG;
}
- else if (level == "verbose")
+ else if (stdalgo::string::equalsci(level, "verbose"))
{
- loglevel = VERBOSE;
+ loglevel = LOG_VERBOSE;
}
- else if (level == "default")
+ else if (stdalgo::string::equalsci(level, "default"))
{
- loglevel = DEFAULT;
+ loglevel = LOG_DEFAULT;
}
- else if (level == "sparse")
+ else if (stdalgo::string::equalsci(level, "sparse"))
{
- loglevel = SPARSE;
+ loglevel = LOG_SPARSE;
}
- else if (level == "none")
+ else if (stdalgo::string::equalsci(level, "none"))
{
- loglevel = NONE;
+ loglevel = LOG_NONE;
}
FileWriter* fw;
- std::string target = tag->getString("target");
+ std::string target = ServerInstance->Config->Paths.PrependLog(tag->getString("target"));
std::map<std::string, FileWriter*>::iterator fwi = logmap.find(target);
if (fwi == logmap.end())
{
- char realtarget[MAXBUF];
+ char realtarget[256];
time_t time = ServerInstance->Time();
struct tm *mytime = gmtime(&time);
- strftime(realtarget, MAXBUF, target.c_str(), mytime);
+ strftime(realtarget, sizeof(realtarget), target.c_str(), mytime);
FILE* f = fopen(realtarget, "a");
- fw = new FileWriter(f);
+ fw = new FileWriter(f, tag->getUInt("flush", 20, 1, UINT_MAX));
logmap.insert(std::make_pair(target, fw));
}
else
@@ -126,7 +127,7 @@ void LogManager::OpenFileLogs()
fw = fwi->second;
}
FileLogStream* fls = new FileLogStream(loglevel, fw);
- fls->OnLog(SPARSE, "HEADER", InspIRCd::LogHeader);
+ fls->OnLog(LOG_SPARSE, "HEADER", LogStream::LogHeader);
AddLogTypes(type, fls, true);
}
}
@@ -135,13 +136,16 @@ void LogManager::CloseLogs()
{
if (ServerInstance->Config && ServerInstance->Config->cmdline.forcedebug)
return;
- std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
- std::map<LogStream*, std::vector<std::string> >().swap(GlobalLogStreams); /* Clear it */
+
+ LogStreams.clear();
+ GlobalLogStreams.clear();
+
for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
{
delete i->first;
}
- std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
+
+ AllLogStreams.clear();
}
void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
@@ -187,36 +191,13 @@ void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autocl
bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
{
- std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
-
- if (i != LogStreams.end())
- {
- i->second.push_back(l);
- }
- else
- {
- std::vector<LogStream *> v;
- v.push_back(l);
- LogStreams[type] = v;
- }
+ LogStreams[type].push_back(l);
if (type == "*")
- {
GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
- }
if (autoclose)
- {
- std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
- if (ai == AllLogStreams.end())
- {
- AllLogStreams.insert(std::make_pair(l, 1));
- }
- else
- {
- ++ai->second;
- }
- }
+ AllLogStreams[l]++;
return true;
}
@@ -225,24 +206,20 @@ void LogManager::DelLogStream(LogStream* l)
{
for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
{
- std::vector<LogStream*>::iterator it;
- while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
+ while (stdalgo::erase(i->second, l))
{
- if (it == i->second.end())
- continue;
- i->second.erase(it);
+ // Keep erasing while it exists
}
}
- std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
- if (gi != GlobalLogStreams.end())
- {
- GlobalLogStreams.erase(gi);
- }
+
+ GlobalLogStreams.erase(l);
+
std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
if (ai == AllLogStreams.end())
{
return; /* Done. */
}
+
delete ai->first;
AllLogStreams.erase(ai);
}
@@ -252,17 +229,13 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l)
std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
if (type == "*")
{
- std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
- if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
+ GlobalLogStreams.erase(l);
}
if (i != LogStreams.end())
{
- std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
-
- if (it != i->second.end())
+ if (stdalgo::erase(i->second, l))
{
- i->second.erase(it);
if (i->second.size() == 0)
{
LogStreams.erase(i);
@@ -293,24 +266,17 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l)
return true;
}
-void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
+void LogManager::Log(const std::string &type, LogLevel loglevel, const char *fmt, ...)
{
if (Logging)
- {
return;
- }
-
- va_list a;
- static char buf[65536];
-
- va_start(a, fmt);
- vsnprintf(buf, 65536, fmt, a);
- va_end(a);
- this->Log(type, loglevel, std::string(buf));
+ std::string buf;
+ VAFORMAT(buf, fmt, fmt);
+ this->Log(type, loglevel, buf);
}
-void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
+void LogManager::Log(const std::string &type, LogLevel loglevel, const std::string &msg)
{
if (Logging)
{
@@ -321,7 +287,7 @@ void LogManager::Log(const std::string &type, int loglevel, const std::string &m
for (std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.begin(); gi != GlobalLogStreams.end(); ++gi)
{
- if (std::find(gi->second.begin(), gi->second.end(), type) != gi->second.end())
+ if (stdalgo::isin(gi->second, type))
{
continue;
}
@@ -342,8 +308,10 @@ void LogManager::Log(const std::string &type, int loglevel, const std::string &m
}
-FileWriter::FileWriter(FILE* logfile)
-: log(logfile), writeops(0)
+FileWriter::FileWriter(FILE* logfile, unsigned int flushcount)
+ : log(logfile)
+ , flush(flushcount)
+ , writeops(0)
{
}
@@ -354,8 +322,8 @@ void FileWriter::WriteLogLine(const std::string &line)
// XXX: For now, just return. Don't throw an exception. It'd be nice to find out if this is happening, but I'm terrified of breaking so close to final release. -- w00t
// throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
- fprintf(log,"%s",line.c_str());
- if (writeops++ % 20)
+ fputs(line.c_str(), log);
+ if (++writeops % flush == 0)
{
fflush(log);
}
diff --git a/src/mode.cpp b/src/mode.cpp
index 0f5d45783..0bb97cc9e 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -25,40 +25,23 @@
#include "inspircd.h"
-/* +s (secret) */
-/* +p (private) */
-/* +m (moderated) */
-/* +t (only (half) ops can change topic) */
-/* +n (no external messages) */
-/* +i (invite only) */
-/* +w (see wallops) */
-/* +i (invisible) */
-#include "modes/simplemodes.h"
-/* +b (bans) */
-#include "modes/cmode_b.h"
-/* +k (keyed channel) */
-#include "modes/cmode_k.h"
-/* +l (channel user limit) */
-#include "modes/cmode_l.h"
-/* +o (channel op) */
-#include "modes/cmode_o.h"
-/* +v (channel voice) */
-#include "modes/cmode_v.h"
-/* +o (operator) */
-#include "modes/umode_o.h"
-/* +s (server notice masks) */
-#include "modes/umode_s.h"
-
-ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type)
- : ServiceProvider(Creator, Name, SERVICE_MODE), m_paramtype(TR_TEXT),
- parameters_taken(Params), mode(modeletter), prefix(0), oper(false),
- list(false), m_type(type), levelrequired(HALFOP_VALUE)
+ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type, Class mclass)
+ : ServiceProvider(Creator, Name, SERVICE_MODE)
+ , modeid(ModeParser::MODEID_MAX)
+ , parameters_taken(Params)
+ , mode(modeletter)
+ , oper(false)
+ , list(false)
+ , m_type(type)
+ , type_id(mclass)
+ , ranktoset(HALFOP_VALUE)
+ , ranktounset(HALFOP_VALUE)
{
}
CullResult ModeHandler::cull()
{
- if (ServerInstance->Modes)
+ if (ServerInstance)
ServerInstance->Modes->DelMode(this);
return classbase::cull();
}
@@ -67,31 +50,21 @@ ModeHandler::~ModeHandler()
{
}
-bool ModeHandler::IsListMode()
-{
- return list;
-}
-
-unsigned int ModeHandler::GetPrefixRank()
-{
- return 0;
-}
-
-int ModeHandler::GetNumParams(bool adding)
+bool ModeHandler::NeedsParam(bool adding) const
{
switch (parameters_taken)
{
case PARAM_ALWAYS:
- return 1;
+ return true;
case PARAM_SETONLY:
- return adding ? 1 : 0;
+ return adding;
case PARAM_NONE:
break;
}
- return 0;
+ return false;
}
-std::string ModeHandler::GetUserParameter(User* user)
+std::string ModeHandler::GetUserParameter(const User* user) const
{
return "";
}
@@ -116,6 +89,11 @@ void ModeHandler::DisplayEmptyList(User*, Channel*)
void ModeHandler::OnParameterMissing(User* user, User* dest, Channel* channel)
{
+ const std::string message = InspIRCd::Format("You must specify a parameter for the %s mode", name.c_str());
+ if (channel)
+ user->WriteNumeric(Numerics::InvalidModeParameter(channel, this, "*", message));
+ else
+ user->WriteNumeric(Numerics::InvalidModeParameter(dest, this, "*", message));
}
bool ModeHandler::ResolveModeConflict(std::string& theirs, const std::string& ours, Channel*)
@@ -123,11 +101,17 @@ bool ModeHandler::ResolveModeConflict(std::string& theirs, const std::string& ou
return (theirs < ours);
}
+void ModeHandler::RegisterService()
+{
+ ServerInstance->Modes.AddMode(this);
+ ServerInstance->Modules.AddReferent((GetModeType() == MODETYPE_CHANNEL ? "mode/" : "umode/") + name, this);
+}
+
ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
{
/* We're either trying to add a mode we already have or
remove a mode we don't have, deny. */
- if (dest->IsModeSet(this->GetModeChar()) == adding)
+ if (dest->IsModeSet(this) == adding)
return MODEACTION_DENY;
/* adding will be either true or false, depending on if we
@@ -136,7 +120,7 @@ ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel
aren't removing a mode we don't have, we don't have to do any
other checks here to see if it's true or false, just add or
remove the mode */
- dest->SetMode(this->GetModeChar(), adding);
+ dest->SetMode(this, adding);
return MODEACTION_ALLOW;
}
@@ -146,7 +130,7 @@ ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Chan
{
/* We're either trying to add a mode we already have or
remove a mode we don't have, deny. */
- if (channel->IsModeSet(this->GetModeChar()) == adding)
+ if (channel->IsModeSet(this) == adding)
return MODEACTION_DENY;
/* adding will be either true or false, depending on if we
@@ -155,120 +139,124 @@ ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Chan
aren't removing a mode we don't have, we don't have to do any
other checks here to see if it's true or false, just add or
remove the mode */
- channel->SetMode(this->GetModeChar(), adding);
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
-ModeAction ParamChannelModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ModeWatcher::ModeWatcher(Module* Creator, const std::string& modename, ModeType type)
+ : mode(modename), m_type(type), creator(Creator)
{
- if (adding && !ParamValidate(parameter))
- return MODEACTION_DENY;
- std::string now = channel->GetModeParameter(this);
- if (parameter == now)
- return MODEACTION_DENY;
- if (adding)
- channel->SetModeParam(this, parameter);
- else
- channel->SetModeParam(this, "");
- return MODEACTION_ALLOW;
+ ServerInstance->Modes->AddModeWatcher(this);
}
-bool ParamChannelModeHandler::ParamValidate(std::string& parameter)
+ModeWatcher::~ModeWatcher()
{
- return true;
+ ServerInstance->Modes->DelModeWatcher(this);
}
-ModeWatcher::ModeWatcher(Module* Creator, char modeletter, ModeType type)
- : mode(modeletter), m_type(type), creator(Creator)
+bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
{
+ return true;
}
-ModeWatcher::~ModeWatcher()
+void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
{
}
-char ModeWatcher::GetModeChar()
+PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank, char PrefixChar)
+ : ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
+ , prefix(PrefixChar)
+ , prefixrank(Rank)
+ , selfremove(true)
{
- return mode;
+ list = true;
}
-ModeType ModeWatcher::GetModeType()
+ModResult PrefixMode::AccessCheck(User* src, Channel*, std::string& value, bool adding)
{
- return m_type;
+ if (!adding && src->nick == value && selfremove)
+ return MOD_RES_ALLOW;
+ return MOD_RES_PASSTHRU;
}
-bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool, ModeType)
+ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
{
- return true;
-}
+ User* target;
+ if (IS_LOCAL(source))
+ target = ServerInstance->FindNickOnly(parameter);
+ else
+ target = ServerInstance->FindNick(parameter);
-void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool, ModeType)
-{
+ if (!target)
+ {
+ source->WriteNumeric(Numerics::NoSuchNick(parameter));
+ return MODEACTION_DENY;
+ }
+
+ Membership* memb = chan->GetUser(target);
+ if (!memb)
+ return MODEACTION_DENY;
+
+ parameter = target->nick;
+ return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY);
}
-User* ModeParser::SanityChecks(User *user, const char *dest, Channel *chan, int)
+void PrefixMode::Update(unsigned int rank, unsigned int setrank, unsigned int unsetrank, bool selfrm)
{
- User *d;
- if ((!user) || (!dest) || (!chan) || (!*dest))
- {
- return NULL;
- }
- d = ServerInstance->FindNick(dest);
- if (!d)
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), dest);
- return NULL;
- }
- return d;
+ prefixrank = rank;
+ ranktoset = setrank;
+ ranktounset = unsetrank;
+ selfremove = selfrm;
}
-void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text)
+ModeAction ParamModeBase::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
{
- if (targetchannel)
+ if (adding)
{
- /* Display channel's current mode string */
- user->WriteNumeric(RPL_CHANNELMODEIS, "%s %s +%s",user->nick.c_str(), targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
- user->WriteNumeric(RPL_CHANNELCREATED, "%s %s %lu", user->nick.c_str(), targetchannel->name.c_str(), (unsigned long)targetchannel->age);
- return;
+ if (chan->GetModeParameter(this) == parameter)
+ return MODEACTION_DENY;
+
+ if (OnSet(source, chan, parameter) != MODEACTION_ALLOW)
+ return MODEACTION_DENY;
+
+ chan->SetMode(this, true);
+
+ // Handler might have changed the parameter internally
+ parameter.clear();
+ this->GetParameter(chan, parameter);
}
else
{
- if (targetuser == user || user->HasPrivPermission("users/auspex"))
- {
- /* Display user's current mode string */
- user->WriteNumeric(RPL_UMODEIS, "%s :+%s",targetuser->nick.c_str(),targetuser->FormatModes());
- if (IS_OPER(targetuser))
- user->WriteNumeric(RPL_SNOMASKIS, "%s +%s :Server notice mask", targetuser->nick.c_str(), targetuser->FormatNoticeMasks());
- return;
- }
- else
- {
- user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't view modes for other users", user->nick.c_str());
- return;
- }
+ if (!chan->IsModeSet(this))
+ return MODEACTION_DENY;
+ this->OnUnsetInternal(source, chan);
+ chan->SetMode(this, false);
}
+ return MODEACTION_ALLOW;
}
-ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool adding, const unsigned char modechar,
- std::string &parameter, bool SkipACL)
+ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Modes::Change& mcitem, bool SkipACL)
{
ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER;
- unsigned char mask = chan ? MASK_CHANNEL : MASK_USER;
- ModeHandler *mh = FindMode(modechar, type);
- int pcnt = mh->GetNumParams(adding);
+ ModeHandler* mh = mcitem.mh;
+ bool adding = mcitem.adding;
+ const bool needs_param = mh->NeedsParam(adding);
+ std::string& parameter = mcitem.param;
// crop mode parameter size to 250 characters
if (parameter.length() > 250 && adding)
- parameter = parameter.substr(0, 250);
+ parameter.erase(250);
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, modechar, parameter, adding, pcnt));
+ FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, parameter, adding));
if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY))
return MODEACTION_DENY;
+ const char modechar = mh->GetModeChar();
+
if (chan && !SkipACL && (MOD_RESULT != MOD_RES_ALLOW))
{
MOD_RESULT = mh->AccessCheck(user, chan, parameter, adding);
@@ -277,7 +265,7 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
return MODEACTION_DENY;
if (MOD_RESULT == MOD_RES_PASSTHRU)
{
- unsigned int neededrank = mh->GetLevelRequired();
+ unsigned int neededrank = mh->GetLevelRequired(adding);
/* Compare our rank on the channel against the rank of the required prefix,
* allow if >= ours. Because mIRC and xchat throw a tizz if the modes shown
* in NAMES(X) are not in rank order, we know the most powerful mode is listed
@@ -286,11 +274,12 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
unsigned int ourrank = chan->GetPrefixValue(user);
if (ourrank < neededrank)
{
- ModeHandler* neededmh = NULL;
- for(char c='A'; c <= 'z'; c++)
+ const PrefixMode* neededmh = NULL;
+ const PrefixModeList& prefixmodes = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = prefixmodes.begin(); i != prefixmodes.end(); ++i)
{
- ModeHandler *privmh = FindMode(c, MODETYPE_CHANNEL);
- if (privmh && privmh->GetPrefixRank() >= neededrank)
+ const PrefixMode* const privmh = *i;
+ if (privmh->GetPrefixRank() >= neededrank)
{
// this mode is sufficient to allow this action
if (!neededmh || privmh->GetPrefixRank() < neededmh->GetPrefixRank())
@@ -298,148 +287,87 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
}
}
if (neededmh)
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have channel %s access or above to %sset channel mode %c",
- user->nick.c_str(), chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You must have channel %s access or above to %sset channel mode %c",
+ neededmh->name.c_str(), adding ? "" : "un", modechar));
else
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You cannot %sset channel mode %c",
- user->nick.c_str(), chan->name.c_str(), adding ? "" : "un", modechar);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You cannot %sset channel mode %c", (adding ? "" : "un"), modechar));
return MODEACTION_DENY;
}
}
}
- unsigned char handler_id = (modechar - 'A') | mask;
-
- for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ // Ask mode watchers whether this mode change is OK
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
- if ((*watchers)->BeforeMode(user, targetuser, chan, parameter, adding, type) == false)
- return MODEACTION_DENY;
- /* A module whacked the parameter completely, and there was one. abort. */
- if (pcnt && parameter.empty())
- return MODEACTION_DENY;
+ ModeWatcher* mw = i->second;
+ if (mw->GetModeType() == type)
+ {
+ if (!mw->BeforeMode(user, targetuser, chan, parameter, adding))
+ return MODEACTION_DENY;
+
+ // A module whacked the parameter completely, and there was one. Abort.
+ if ((needs_param) && (parameter.empty()))
+ return MODEACTION_DENY;
+ }
}
- if (IS_LOCAL(user) && !IS_OPER(user))
+ if (IS_LOCAL(user) && !user->IsOper())
{
- char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
- if (disabled[modechar - 'A'])
+ const std::bitset<64>& disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
+ if (disabled.test(modechar - 'A'))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - %s mode %c has been locked by the administrator",
- user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - %s mode %c has been locked by the administrator",
+ type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
return MODEACTION_DENY;
}
}
- if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
+ if ((adding) && (IS_LOCAL(user)) && (mh->NeedsOper()) && (!user->HasModePermission(mh)))
{
/* It's an oper only mode, and they don't have access to it. */
- if (IS_OPER(user))
+ if (user->IsOper())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to set %s mode %c",
- user->nick.c_str(), user->oper->NameStr(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to set %s mode %c",
+ user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
}
else
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Only operators may set %s mode %c",
- user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Only operators may set %s mode %c",
+ type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
}
return MODEACTION_DENY;
}
- if (mh->GetTranslateType() == TR_NICK)
- {
- User* prefixtarget;
- if (IS_LOCAL(user))
- prefixtarget = ServerInstance->FindNickOnly(parameter);
- else
- prefixtarget = ServerInstance->FindNick(parameter);
-
- if (!prefixtarget)
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameter.c_str());
- return MODEACTION_DENY;
- }
- }
-
- if (mh->GetPrefixRank() && chan)
- {
- User* user_to_prefix = ServerInstance->FindNick(parameter);
- if (!user_to_prefix)
- return MODEACTION_DENY;
- if (!chan->SetPrefix(user_to_prefix, modechar, adding))
- return MODEACTION_DENY;
- }
-
/* Call the handler for the mode */
ModeAction ma = mh->OnModeChange(user, targetuser, chan, parameter, adding);
- if (pcnt && parameter.empty())
+ if ((needs_param) && (parameter.empty()))
return MODEACTION_DENY;
if (ma != MODEACTION_ALLOW)
return ma;
- for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
- (*watchers)->AfterMode(user, targetuser, chan, parameter, adding, type);
+ itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
+ {
+ ModeWatcher* mw = i->second;
+ if (mw->GetModeType() == type)
+ mw->AfterMode(user, targetuser, chan, parameter, adding);
+ }
return MODEACTION_ALLOW;
}
-void ModeParser::Process(const std::vector<std::string>& parameters, User *user, bool merge)
+void ModeParser::ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex, unsigned int endindex)
{
- const std::string& target = parameters[0];
- Channel* targetchannel = ServerInstance->FindChan(target);
- User* targetuser = NULL;
- if (!targetchannel)
- {
- if (IS_LOCAL(user))
- targetuser = ServerInstance->FindNickOnly(target);
- else
- targetuser = ServerInstance->FindNick(target);
- }
- ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
-
- LastParse.clear();
- LastParseParams.clear();
- LastParseTranslate.clear();
-
- if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),target.c_str());
- return;
- }
- if (parameters.size() == 1)
- {
- this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str());
- return;
- }
+ if (endindex > parameters.size())
+ endindex = parameters.size();
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters));
-
- bool SkipAccessChecks = false;
-
- if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW)
- SkipAccessChecks = true;
- else if (MOD_RESULT == MOD_RES_DENY)
- return;
-
- if (targetuser && !SkipAccessChecks && user != targetuser)
- {
- user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str());
- return;
- }
-
- std::string mode_sequence = parameters[1];
-
- std::string output_mode;
- std::ostringstream output_parameters;
- LastParseParams.push_back(output_mode);
- LastParseTranslate.push_back(TR_TEXT);
+ const std::string& mode_sequence = parameters[beginindex];
bool adding = true;
- char output_pm = '\0'; // current output state, '+' or '-'
- unsigned int param_at = 2;
+ unsigned int param_at = beginindex+1;
for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
@@ -454,143 +382,154 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user,
if (!mh)
{
/* No mode handler? Unknown mode character then. */
- user->WriteServ("%d %s %c :is unknown mode char to me", type == MODETYPE_CHANNEL ? 472 : 501, user->nick.c_str(), modechar);
+ user->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, modechar, "is unknown mode char to me");
continue;
}
std::string parameter;
- int pcnt = mh->GetNumParams(adding);
- if (pcnt && param_at == parameters.size())
- {
- /* No parameter, continue to the next mode */
- mh->OnParameterMissing(user, targetuser, targetchannel);
+ if ((mh->NeedsParam(adding)) && (param_at < endindex))
+ parameter = parameters[param_at++];
+
+ changelist.push(mh, adding, parameter);
+ }
+}
+
+static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
+{
+ // An empty parameter is never acceptable
+ if (item.param.empty())
+ {
+ item.mh->OnParameterMissing(user, targetuser, targetchannel);
+ return false;
+ }
+
+ // The parameter cannot begin with a ':' character or contain a space
+ if ((item.param[0] == ':') || (item.param.find(' ') != std::string::npos))
+ return false;
+
+ return true;
+}
+
+// Returns true if we should apply a merged mode, false if we should skip it
+static bool ShouldApplyMergedMode(Channel* chan, Modes::Change& item)
+{
+ ModeHandler* mh = item.mh;
+ if ((!chan) || (!chan->IsModeSet(mh)) || (mh->IsListMode()))
+ // Mode not set here or merge is not applicable, apply the incoming mode
+ return true;
+
+ // Mode handler decides
+ std::string ours = chan->GetModeParameter(mh);
+ return mh->ResolveModeConflict(item.param, ours, chan);
+}
+
+void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+{
+ // Call ProcessSingle until the entire list is processed, but at least once to ensure
+ // LastParse and LastChangeList are cleared
+ unsigned int processed = 0;
+ do
+ {
+ unsigned int n = ProcessSingle(user, targetchannel, targetuser, changelist, flags, processed);
+ processed += n;
+ }
+ while (processed < changelist.size());
+}
+
+unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
+{
+ LastChangeList.clear();
+
+ unsigned int modes_processed = 0;
+ Modes::ChangeList::List& list = changelist.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
+ {
+ modes_processed++;
+
+ Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+
+ // If a mode change has been given for a mode that does not exist then reject
+ // it. This can happen when core_reloadmodule attempts to restore a mode that
+ // no longer exists.
+ if (!mh)
continue;
- }
- else if (pcnt)
+
+ // If the mode is supposed to have a parameter then we first take a look at item.param
+ // and, if we were asked to, also handle mode merges now
+ if (mh->NeedsParam(item.adding))
{
- parameter = parameters[param_at++];
- /* Make sure the user isn't trying to slip in an invalid parameter */
- if ((parameter.empty()) || (parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
+ // Skip the mode if the parameter does not pass basic validation
+ if (!IsModeParamValid(user, targetchannel, targetuser, item))
+ continue;
+
+ // If this is a merge and we won we don't apply this mode
+ if ((flags & MODE_MERGE) && (!ShouldApplyMergedMode(targetchannel, item)))
continue;
- if (merge && targetchannel && targetchannel->IsModeSet(modechar) && !mh->IsListMode())
- {
- std::string ours = targetchannel->GetModeParameter(modechar);
- if (!mh->ResolveModeConflict(parameter, ours, targetchannel))
- /* we won the mode merge, don't apply this mode */
- continue;
- }
}
- ModeAction ma = TryMode(user, targetuser, targetchannel, adding, modechar, parameter, SkipAccessChecks);
+ ModeAction ma = TryMode(user, targetuser, targetchannel, item, (!(flags & MODE_CHECKACCESS)));
if (ma != MODEACTION_ALLOW)
continue;
- char needed_pm = adding ? '+' : '-';
- if (needed_pm != output_pm)
- {
- output_pm = needed_pm;
- output_mode.append(1, output_pm);
- }
- output_mode.append(1, modechar);
+ LastChangeList.push(mh, item.adding, item.param);
- if (pcnt)
- {
- TranslateType tt = mh->GetTranslateType();
- if (tt == TR_NICK)
- {
- User* u = ServerInstance->FindNick(parameter);
- if (u)
- parameter = u->nick;
- }
- output_parameters << " " << parameter;
- LastParseParams.push_back(parameter);
- LastParseTranslate.push_back(tt);
- }
-
- if ( (output_mode.length() + output_parameters.str().length() > 450)
- || (output_mode.length() > 100)
- || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes))
+ if (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes)
{
/* mode sequence is getting too long */
break;
}
}
- LastParseParams[0] = output_mode;
-
- if (!output_mode.empty())
+ if (!LastChangeList.empty())
{
- LastParse = targetchannel ? targetchannel->name : targetuser->nick;
- LastParse.append(" ");
- LastParse.append(output_mode);
- LastParse.append(output_parameters.str());
-
+ ClientProtocol::Events::Mode modeevent(user, targetchannel, targetuser, LastChangeList);
if (targetchannel)
{
- targetchannel->WriteChannel(user, "MODE %s", LastParse.c_str());
- FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, LastParseParams, LastParseTranslate));
+ targetchannel->Write(modeevent);
}
else
{
- targetuser->WriteFrom(user, "MODE %s", LastParse.c_str());
- FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, LastParseParams, LastParseTranslate));
+ LocalUser* localtarget = IS_LOCAL(targetuser);
+ if (localtarget)
+ localtarget->Send(modeevent);
}
+
+ FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags));
}
- else if (targetchannel && parameters.size() == 2)
- {
- /* Special case for displaying the list for listmodes,
- * e.g. MODE #chan b, or MODE #chan +b without a parameter
- */
- this->DisplayListModes(user, targetchannel, mode_sequence);
- }
+
+ return modes_processed;
}
-void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence)
+void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh)
{
- seq++;
-
- for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
- unsigned char mletter = *letter;
- if (mletter == '+')
- continue;
-
- /* Ensure the user doesnt request the same mode twice,
- * so they cant flood themselves off out of idiocy.
- */
- if (sent[mletter] == seq)
- continue;
-
- sent[mletter] = seq;
-
- ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL);
-
- if (!mh || !mh->IsListMode())
- return;
-
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mletter, "", true, 0));
+ FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, "", true));
if (MOD_RESULT == MOD_RES_DENY)
- continue;
+ return;
bool display = true;
- if (!user->HasPrivPermission("channels/auspex") && ServerInstance->Config->HideModeLists[mletter] && (chan->GetPrefixValue(user) < HALFOP_VALUE))
- {
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You do not have access to view the +%c list",
- user->nick.c_str(), chan->name.c_str(), mletter);
- display = false;
- }
-
- unsigned char handler_id = (mletter - 'A') | MASK_CHANNEL;
- for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ // Ask mode watchers whether it's OK to show the list
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
- std::string dummyparam;
+ ModeWatcher* mw = i->second;
+ if (mw->GetModeType() == MODETYPE_CHANNEL)
+ {
+ std::string dummyparam;
- if (!((*watchers)->BeforeMode(user, NULL, chan, dummyparam, true, MODETYPE_CHANNEL)))
- display = false;
+ if (!mw->BeforeMode(user, NULL, chan, dummyparam, true))
+ {
+ // A mode watcher doesn't want us to show the list
+ display = false;
+ break;
+ }
+ }
}
+
if (display)
mh->DisplayList(user, chan);
else
@@ -598,11 +537,6 @@ void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_s
}
}
-const std::string& ModeParser::GetLastParse()
-{
- return LastParse;
-}
-
void ModeParser::CleanMask(std::string &mask)
{
std::string::size_type pos_of_pling = mask.find_first_of('!');
@@ -639,50 +573,77 @@ void ModeParser::CleanMask(std::string &mask)
}
}
-bool ModeParser::AddMode(ModeHandler* mh)
+ModeHandler::Id ModeParser::AllocateModeId(ModeType mt)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
+ for (ModeHandler::Id i = 0; i != MODEID_MAX; ++i)
+ {
+ if (!modehandlersbyid[mt][i])
+ return i;
+ }
- /* Yes, i know, this might let people declare modes like '_' or '^'.
- * If they do that, thats their problem, and if i ever EVER see an
- * official InspIRCd developer do that, i'll beat them with a paddle!
- */
- if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z') || (mh->GetPrefix() > 126))
- return false;
+ throw ModuleException("Out of ModeIds");
+}
+
+void ModeParser::AddMode(ModeHandler* mh)
+{
+ if (!ModeParser::IsModeChar(mh->GetModeChar()))
+ throw ModuleException("Invalid letter for mode " + mh->name);
/* A mode prefix of ',' is not acceptable, it would fuck up server to server.
* A mode prefix of ':' will fuck up both server to server, and client to server.
* A mode prefix of '#' will mess up /whois and /privmsg
*/
- if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#'))
- return false;
+ PrefixMode* pm = mh->IsPrefixMode();
+ if (pm)
+ {
+ if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#'))
+ throw ModuleException("Invalid prefix for mode " + mh->name);
- if (mh->GetPrefix() && FindPrefix(mh->GetPrefix()))
- return false;
+ if (FindPrefix(pm->GetPrefix()))
+ throw ModuleException("Prefix already exists for mode " + mh->name);
+ }
- mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mh->GetModeChar()-65) | mask;
+ ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+ if (slot)
+ throw ModuleException("Letter is already in use for mode " + mh->name);
- if (modehandlers[pos])
- return false;
+ // The mode needs an id if it is either a user mode, a simple mode (flag) or a parameter mode.
+ // Otherwise (for listmodes and prefix modes) the id remains MODEID_MAX, which is invalid.
+ ModeHandler::Id modeid = MODEID_MAX;
+ if ((mh->GetModeType() == MODETYPE_USER) || (mh->IsParameterMode()) || (!mh->IsListMode()))
+ modeid = AllocateModeId(mh->GetModeType());
- modehandlers[pos] = mh;
- return true;
+ if (!modehandlersbyname[mh->GetModeType()].insert(std::make_pair(mh->name, mh)).second)
+ throw ModuleException("Mode name already in use: " + mh->name);
+
+ // Everything is fine, add the mode
+
+ // If we allocated an id for this mode then save it and put the mode handler into the slot
+ if (modeid != MODEID_MAX)
+ {
+ mh->modeid = modeid;
+ modehandlersbyid[mh->GetModeType()][modeid] = mh;
+ }
+
+ slot = mh;
+ if (pm)
+ mhlist.prefix.push_back(pm);
+ else if (mh->IsListModeBase())
+ mhlist.list.push_back(mh->IsListModeBase());
}
bool ModeParser::DelMode(ModeHandler* mh)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
- if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
+ if (!ModeParser::IsModeChar(mh->GetModeChar()))
return false;
- mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mh->GetModeChar()-65) | mask;
+ ModeHandlerMap& mhmap = modehandlersbyname[mh->GetModeType()];
+ ModeHandlerMap::iterator mhmapit = mhmap.find(mh->name);
+ if ((mhmapit == mhmap.end()) || (mhmapit->second != mh))
+ return false;
- if (modehandlers[pos] != mh)
+ ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+ if (slot != mh)
return false;
/* Note: We can't stack here, as we have modes potentially being removed across many different channels.
@@ -691,106 +652,83 @@ bool ModeParser::DelMode(ModeHandler* mh)
switch (mh->GetModeType())
{
case MODETYPE_USER:
- for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); )
+ {
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); )
{
User* user = i->second;
++i;
mh->RemoveMode(user);
}
+ }
break;
case MODETYPE_CHANNEL:
- for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); )
+ {
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
{
// The channel may not be in the hash after RemoveMode(), see m_permchannels
Channel* chan = i->second;
++i;
- mh->RemoveMode(chan);
+
+ Modes::ChangeList changelist;
+ mh->RemoveMode(chan, changelist);
+ this->Process(ServerInstance->FakeClient, chan, NULL, changelist, MODE_LOCALONLY);
}
+ }
break;
}
- modehandlers[pos] = NULL;
-
+ mhmap.erase(mhmapit);
+ if (mh->GetId() != MODEID_MAX)
+ modehandlersbyid[mh->GetModeType()][mh->GetId()] = NULL;
+ slot = NULL;
+ if (mh->IsPrefixMode())
+ mhlist.prefix.erase(std::find(mhlist.prefix.begin(), mhlist.prefix.end(), mh->IsPrefixMode()));
+ else if (mh->IsListModeBase())
+ mhlist.list.erase(std::find(mhlist.list.begin(), mhlist.list.end(), mh->IsListModeBase()));
return true;
}
-ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
-{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
- if ((modeletter < 'A') || (modeletter > 'z'))
- return NULL;
-
- mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (modeletter-65) | mask;
-
- return modehandlers[pos];
-}
-
-std::string ModeParser::UserModeList()
+ModeHandler* ModeParser::FindMode(const std::string& modename, ModeType mt)
{
- char modestr[256];
- int pointer = 0;
+ ModeHandlerMap& mhmap = modehandlersbyname[mt];
+ ModeHandlerMap::const_iterator it = mhmap.find(modename);
+ if (it != mhmap.end())
+ return it->second;
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
- {
- unsigned char pos = (mode-65) | MASK_USER;
-
- if (modehandlers[pos])
- modestr[pointer++] = mode;
- }
- modestr[pointer++] = 0;
- return modestr;
+ return NULL;
}
-std::string ModeParser::ChannelModeList()
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
{
- char modestr[256];
- int pointer = 0;
-
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
- {
- unsigned char pos = (mode-65) | MASK_CHANNEL;
+ if (!ModeParser::IsModeChar(modeletter))
+ return NULL;
- if (modehandlers[pos])
- modestr[pointer++] = mode;
- }
- modestr[pointer++] = 0;
- return modestr;
+ return modehandlers[mt][modeletter-65];
}
-std::string ModeParser::ParaModeList()
+PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
{
- char modestr[256];
- int pointer = 0;
-
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
- {
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))
- modestr[pointer++] = mode;
- }
- modestr[pointer++] = 0;
- return modestr;
+ ModeHandler* mh = FindMode(modeletter, MODETYPE_CHANNEL);
+ if (!mh)
+ return NULL;
+ return mh->IsPrefixMode();
}
-ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)
+PrefixMode* ModeParser::FindPrefix(unsigned const char pfxletter)
{
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))
- {
- return modehandlers[pos];
- }
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix() == pfxletter)
+ return pm;
}
return NULL;
}
-std::string ModeParser::GiveModeList(ModeMasks m)
+std::string ModeParser::GiveModeList(ModeType mt)
{
std::string type1; /* Listmodes EXCEPT those with a prefix */
std::string type2; /* Modes that take a param when adding or removing */
@@ -799,37 +737,38 @@ std::string ModeParser::GiveModeList(ModeMasks m)
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
- unsigned char pos = (mode-65) | m;
+ ModeHandler* mh = modehandlers[mt][mode-65];
/* One parameter when adding */
- if (modehandlers[pos])
+ if (mh)
{
- if (modehandlers[pos]->GetNumParams(true))
+ if (mh->NeedsParam(true))
{
- if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))
+ PrefixMode* pm = mh->IsPrefixMode();
+ if ((mh->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0)))
{
- type1 += modehandlers[pos]->GetModeChar();
+ type1 += mh->GetModeChar();
}
else
{
/* ... and one parameter when removing */
- if (modehandlers[pos]->GetNumParams(false))
+ if (mh->NeedsParam(false))
{
/* But not a list mode */
- if (!modehandlers[pos]->GetPrefix())
+ if (!pm)
{
- type2 += modehandlers[pos]->GetModeChar();
+ type2 += mh->GetModeChar();
}
}
else
{
/* No parameters when removing */
- type3 += modehandlers[pos]->GetModeChar();
+ type3 += mh->GetModeChar();
}
}
}
else
{
- type4 += modehandlers[pos]->GetModeChar();
+ type4 += mh->GetModeChar();
}
}
}
@@ -839,7 +778,7 @@ std::string ModeParser::GiveModeList(ModeMasks m)
struct PrefixModeSorter
{
- bool operator()(ModeHandler* lhs, ModeHandler* rhs)
+ bool operator()(PrefixMode* lhs, PrefixMode* rhs)
{
return lhs->GetPrefixRank() < rhs->GetPrefixRank();
}
@@ -849,20 +788,18 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes)
{
std::string mletters;
std::string mprefixes;
- std::vector<ModeHandler*> prefixes;
+ std::vector<PrefixMode*> prefixes;
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
- {
- prefixes.push_back(modehandlers[pos]);
- }
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix())
+ prefixes.push_back(pm);
}
std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter());
- for (std::vector<ModeHandler*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
+ for (std::vector<PrefixMode*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
{
mletters += (*n)->GetPrefix();
mprefixes += (*n)->GetModeChar();
@@ -871,149 +808,69 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes)
return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
}
-bool ModeParser::AddModeWatcher(ModeWatcher* mw)
+void ModeParser::AddModeWatcher(ModeWatcher* mw)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
- if (!mw)
- return false;
-
- if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
- return false;
-
- mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mw->GetModeChar()-65) | mask;
-
- modewatchers[pos].push_back(mw);
-
- return true;
+ modewatchermap.insert(std::make_pair(mw->GetModeName(), mw));
}
bool ModeParser::DelModeWatcher(ModeWatcher* mw)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
- if (!mw)
- return false;
-
- if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
- return false;
-
- mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mw->GetModeChar()-65) | mask;
-
- ModeWatchIter a = std::find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);
-
- if (a == modewatchers[pos].end())
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mw->GetModeName());
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
- return false;
+ if (i->second == mw)
+ {
+ modewatchermap.erase(i);
+ return true;
+ }
}
- modewatchers[pos].erase(a);
-
- return true;
+ return false;
}
-/** This default implementation can remove simple user modes
- */
-void ModeHandler::RemoveMode(User* user, irc::modestacker* stack)
+void ModeHandler::RemoveMode(User* user)
{
+ // Remove the mode if it's set on the user
if (user->IsModeSet(this->GetModeChar()))
{
- if (stack)
- {
- stack->Push(this->GetModeChar());
- }
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(user->nick);
- parameters.push_back("-");
- parameters[1].push_back(this->GetModeChar());
- ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient);
- }
+ Modes::ChangeList changelist;
+ changelist.push_remove(this);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, NULL, user, changelist, ModeParser::MODE_LOCALONLY);
}
}
-/** This default implementation can remove simple channel modes
- * (no parameters)
- */
-void ModeHandler::RemoveMode(Channel* channel, irc::modestacker* stack)
+void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
{
- if (channel->IsModeSet(this->GetModeChar()))
+ if (channel->IsModeSet(this))
{
- if (stack)
- {
- stack->Push(this->GetModeChar());
- }
+ if (this->NeedsParam(false))
+ // Removing this mode requires a parameter
+ changelist.push_remove(this, channel->GetModeParameter(this));
else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-");
- parameters[1].push_back(this->GetModeChar());
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
+ changelist.push_remove(this);
}
}
-struct builtin_modes
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
{
- ModeChannelSecret s;
- ModeChannelPrivate p;
- ModeChannelModerated m;
- ModeChannelTopicOps t;
-
- ModeChannelNoExternal n;
- ModeChannelInviteOnly i;
- ModeChannelKey k;
- ModeChannelLimit l;
-
- ModeChannelBan b;
- ModeChannelOp o;
- ModeChannelVoice v;
-
- ModeUserWallops uw;
- ModeUserInvisible ui;
- ModeUserOperator uo;
- ModeUserServerNoticeMask us;
-
- void init(ModeParser* modes)
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
{
- modes->AddMode(&s);
- modes->AddMode(&p);
- modes->AddMode(&m);
- modes->AddMode(&t);
- modes->AddMode(&n);
- modes->AddMode(&i);
- modes->AddMode(&k);
- modes->AddMode(&l);
- modes->AddMode(&b);
- modes->AddMode(&o);
- modes->AddMode(&v);
- modes->AddMode(&uw);
- modes->AddMode(&ui);
- modes->AddMode(&uo);
- modes->AddMode(&us);
+ if (i->second->HasMode(this))
+ changelist.push_remove(this, i->first->nick);
}
-};
+}
-static builtin_modes static_modes;
+bool ModeParser::IsModeChar(char chr)
+{
+ return ((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'));
+}
ModeParser::ModeParser()
{
/* Clear mode handler list */
memset(modehandlers, 0, sizeof(modehandlers));
-
- /* Last parse string */
- LastParse.clear();
-
- seq = 0;
- memset(&sent, 0, sizeof(sent));
-
- static_modes.init(this);
+ memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
}
ModeParser::~ModeParser()
diff --git a/src/modes/cmode_b.cpp b/src/modes/cmode_b.cpp
deleted file mode 100644
index e45f191f7..000000000
--- a/src/modes/cmode_b.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include <string>
-#include <vector>
-#include "inspircd_config.h"
-#include "configreader.h"
-#include "hash_map.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "inspstring.h"
-#include "hashcomp.h"
-#include "modes/cmode_b.h"
-
-ModeChannelBan::ModeChannelBan() : ModeHandler(NULL, "ban", 'b', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
-}
-
-ModeAction ModeChannelBan::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
- int status = channel->GetPrefixValue(source);
- /* Call the correct method depending on wether we're adding or removing the mode */
- if (adding)
- {
- this->AddBan(source, parameter, channel, status);
- }
- else
- {
- this->DelBan(source, parameter, channel, status);
- }
- /* If the method above 'ate' the parameter by reducing it to an empty string, then
- * it won't matter wether we return ALLOW or DENY here, as an empty string overrides
- * the return value and is always MODEACTION_DENY if the mode is supposed to have
- * a parameter.
- */
- return MODEACTION_ALLOW;
-}
-
-void ModeChannelBan::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- BanList copy;
-
- for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
- {
- copy.push_back(*i);
- }
-
- for (BanList::iterator i = copy.begin(); i != copy.end(); i++)
- {
- if (stack)
- {
- stack->Push(this->GetModeChar(), i->data);
- }
- else
- {
- std::vector<std::string> parameters; parameters.push_back(channel->name); parameters.push_back("-b"); parameters.push_back(i->data);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelBan::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-void ModeChannelBan::DisplayList(User* user, Channel* channel)
-{
- /* Display the channel banlist */
- for (BanList::reverse_iterator i = channel->bans.rbegin(); i != channel->bans.rend(); ++i)
- {
- user->WriteServ("367 %s %s %s %s %lu",user->nick.c_str(), channel->name.c_str(), i->data.c_str(), i->set_by.c_str(), (unsigned long)i->set_time);
- }
- user->WriteServ("368 %s %s :End of channel ban list",user->nick.c_str(), channel->name.c_str());
- return;
-}
-
-void ModeChannelBan::DisplayEmptyList(User* user, Channel* channel)
-{
- user->WriteServ("368 %s %s :End of channel ban list",user->nick.c_str(), channel->name.c_str());
-}
-
-std::string& ModeChannelBan::AddBan(User *user, std::string &dest, Channel *chan, int)
-{
- if ((!user) || (!chan))
- {
- ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** AddBan was given an invalid parameter");
- dest.clear();
- return dest;
- }
-
- /* Attempt to tidy the mask */
- ModeParser::CleanMask(dest);
- /* If the mask was invalid, we exit */
- if (dest.empty() || dest.length() > 250)
- return dest;
-
- long maxbans = chan->GetMaxBans();
- if (IS_LOCAL(user) && ((unsigned)chan->bans.size() >= (unsigned)maxbans))
- {
- user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %ld)",user->nick.c_str(), chan->name.c_str(), chan->name.c_str(), maxbans);
- dest.clear();
- return dest;
- }
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnAddBan, MOD_RESULT, (user,chan,dest));
- if (MOD_RESULT == MOD_RES_DENY)
- {
- dest.clear();
- return dest;
- }
-
- for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
- {
- if (i->data == dest)
- {
- /* dont allow a user to set the same ban twice */
- dest.clear();
- return dest;
- }
- }
-
- b.set_time = ServerInstance->Time();
- b.data.assign(dest, 0, MAXBUF);
- b.set_by.assign(user->nick, 0, 64);
- chan->bans.push_back(b);
- return dest;
-}
-
-std::string& ModeChannelBan::DelBan(User *user, std::string& dest, Channel *chan, int)
-{
- if ((!user) || (!chan))
- {
- ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** TakeBan was given an invalid parameter");
- dest.clear();
- return dest;
- }
-
- for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
- {
- if (!strcasecmp(i->data.c_str(), dest.c_str()))
- {
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnDelBan, MOD_RESULT, (user, chan, dest));
- if (MOD_RESULT == MOD_RES_DENY)
- {
- dest.clear();
- return dest;
- }
- dest = i->data;
- chan->bans.erase(i);
- return dest;
- }
- }
- dest.clear();
- return dest;
-}
-
diff --git a/src/modes/cmode_o.cpp b/src/modes/cmode_o.cpp
deleted file mode 100644
index 0a13b39ce..000000000
--- a/src/modes/cmode_o.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "modes/cmode_o.h"
-
-ModeChannelOp::ModeChannelOp() : ModeHandler(NULL, "op", 'o', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
- prefix = '@';
- levelrequired = OP_VALUE;
- m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelOp::GetPrefixRank()
-{
- return OP_VALUE;
-}
-
-void ModeChannelOp::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- const UserMembList* clist = channel->GetUsers();
-
- for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-o");
- parameters.push_back(i->first->nick);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelOp::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelOp::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
- return MODEACTION_ALLOW;
-}
diff --git a/src/modes/cmode_v.cpp b/src/modes/cmode_v.cpp
deleted file mode 100644
index 4a00f60f1..000000000
--- a/src/modes/cmode_v.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "modes/cmode_v.h"
-
-ModeChannelVoice::ModeChannelVoice() : ModeHandler(NULL, "voice", 'v', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
- prefix = '+';
- levelrequired = HALFOP_VALUE;
- m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelVoice::GetPrefixRank()
-{
- return VOICE_VALUE;
-}
-
-void ModeChannelVoice::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- const UserMembList* clist = channel->GetUsers();
-
- for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-v");
- parameters.push_back(i->first->nick);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelVoice::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelVoice::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
- return MODEACTION_ALLOW;
-}
diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp
deleted file mode 100644
index 1b782ae85..000000000
--- a/src/modes/umode_s.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_s.h"
-
-ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
-{
- oper = true;
-}
-
-ModeAction ModeUserServerNoticeMask::OnModeChange(User* source, User* dest, Channel*, std::string &parameter, bool adding)
-{
- /* Set the array fields */
- if (adding)
- {
- /* Fix for bug #310 reported by Smartys */
- if (!dest->modes[UM_SNOMASK])
- dest->snomasks.reset();
-
- dest->modes[UM_SNOMASK] = true;
- parameter = dest->ProcessNoticeMasks(parameter.c_str());
- return MODEACTION_ALLOW;
- }
- else
- {
- if (dest->modes[UM_SNOMASK] != false)
- {
- dest->modes[UM_SNOMASK] = false;
- return MODEACTION_ALLOW;
- }
- }
-
- /* Allow the change */
- return MODEACTION_DENY;
-}
-
-std::string ModeUserServerNoticeMask::GetUserParameter(User* user)
-{
- std::string masks = user->FormatNoticeMasks();
- if (masks.length())
- masks = "+" + masks;
- return masks;
-}
-
-void ModeUserServerNoticeMask::OnParameterMissing(User* user, User* dest, Channel* channel)
-{
- user->WriteServ("NOTICE %s :*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.",
- user->nick.c_str());
-}
-
diff --git a/src/modmanager_dynamic.cpp b/src/modmanager_dynamic.cpp
deleted file mode 100644
index 050f41c75..000000000
--- a/src/modmanager_dynamic.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
-#include "exitcodes.h"
-#include <iostream>
-
-#ifndef _WIN32
-#include <dirent.h>
-#endif
-
-#ifndef PURE_STATIC
-
-bool ModuleManager::Load(const std::string& filename, bool defer)
-{
- /* Don't allow people to specify paths for modules, it doesn't work as expected */
- if (filename.find('/') != std::string::npos)
- {
- LastModuleError = "You can't load modules with a path: " + filename;
- return false;
- }
-
- char modfile[MAXBUF];
- snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename.c_str());
-
- if (!ServerConfig::FileExists(modfile))
- {
- LastModuleError = "Module file could not be found: " + filename;
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
- }
-
- if (Modules.find(filename) != Modules.end())
- {
- LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
- }
-
- Module* newmod = NULL;
- DLLManager* newhandle = new DLLManager(modfile);
-
- try
- {
- newmod = newhandle->CallInit();
-
- if (newmod)
- {
- newmod->ModuleSourceFile = filename;
- newmod->ModuleDLLManager = newhandle;
- newmod->dying = false;
- Modules[filename] = newmod;
- std::string version = newhandle->GetVersion();
- if (defer)
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)",
- filename.c_str(), version.c_str());
- }
- else
- {
- newmod->init();
-
- Version v = newmod->GetVersion();
- ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
- filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
- }
- }
- else
- {
- LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- delete newhandle;
- return false;
- }
- }
- catch (CoreException& modexcept)
- {
- // failure in module constructor
- if (newmod)
- {
- DoSafeUnload(newmod);
- ServerInstance->GlobalCulls.AddItem(newhandle);
- }
- else
- delete newhandle;
- LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
- }
-
- this->ModCount++;
- if (defer)
- return true;
-
- FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename);
- }
-
- ServerInstance->BuildISupport();
- return true;
-}
-
-namespace {
- struct UnloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- UnloadAction(Module* m) : mod(m) {}
- void Call()
- {
- DLLManager* dll = mod->ModuleDLLManager;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- delete dll;
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-
- struct ReloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- HandlerBase1<void, bool>* const callback;
- ReloadAction(Module* m, HandlerBase1<void, bool>* c)
- : mod(m), callback(c) {}
- void Call()
- {
- DLLManager* dll = mod->ModuleDLLManager;
- std::string name = mod->ModuleSourceFile;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- delete dll;
- bool rv = ServerInstance->Modules->Load(name.c_str());
- if (callback)
- callback->Call(rv);
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-}
-
-bool ModuleManager::Unload(Module* mod)
-{
- if (!CanUnload(mod))
- return false;
- ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
- return true;
-}
-
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
- if (CanUnload(mod))
- ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
- else if (callback)
- callback->Call(false);
-}
-
-/* We must load the modules AFTER initializing the socket engine, now */
-void ModuleManager::LoadAll()
-{
- ModCount = 0;
-
- std::cout << std::endl << "Loading core commands";
- fflush(stdout);
-
- DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
- if (library)
- {
- dirent* entry = NULL;
- while (0 != (entry = readdir(library)))
- {
- if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
- {
- std::cout << ".";
- fflush(stdout);
-
- if (!Load(entry->d_name, true))
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
- }
- closedir(library);
- std::cout << std::endl;
- }
-
- ConfigTagList tags = ServerInstance->Config->ConfTags("module");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string name = tag->getString("name");
- std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
-
- if (!this->Load(name, true))
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
- {
- Module* mod = i->second;
- try
- {
- ServerInstance->Logs->Log("MODULE", DEBUG, "Initializing %s", i->first.c_str());
- mod->init();
- }
- catch (CoreException& modexcept)
- {
- LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-}
-
-#endif
diff --git a/src/modmanager_static.cpp b/src/modmanager_static.cpp
deleted file mode 100644
index cea40c7a3..000000000
--- a/src/modmanager_static.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#define MODNAME cmd_all
-
-#include "inspircd.h"
-#include "exitcodes.h"
-#include <iostream>
-
-#ifdef PURE_STATIC
-
-typedef std::map<std::string, AllModuleList*> modmap;
-static std::vector<AllCommandList::fn>* cmdlist = NULL;
-static modmap* modlist = NULL;
-
-AllCommandList::AllCommandList(fn cmd)
-{
- if (!cmdlist)
- cmdlist = new std::vector<AllCommandList::fn>();
- cmdlist->push_back(cmd);
-}
-
-AllModuleList::AllModuleList(AllModuleList::fn mod, const std::string& Name) : init(mod), name(Name)
-{
- if (!modlist)
- modlist = new modmap();
- modlist->insert(std::make_pair(Name, this));
-}
-
-class AllModule : public Module
-{
- std::vector<Command*> cmds;
- public:
- AllModule()
- {
- if (!cmdlist)
- return;
- try
- {
- cmds.reserve(cmdlist->size());
- for(std::vector<AllCommandList::fn>::iterator i = cmdlist->begin(); i != cmdlist->end(); ++i)
- {
- Command* c = (*i)(this);
- cmds.push_back(c);
- ServerInstance->AddCommand(c);
- }
- }
- catch (...)
- {
- this->AllModule::~AllModule();
- throw;
- }
- }
-
- ~AllModule()
- {
- for(std::vector<Command*>::iterator i = cmds.begin(); i != cmds.end(); ++i)
- delete *i;
- }
-
- Version GetVersion()
- {
- return Version("All commands", VF_VENDOR|VF_CORE);
- }
-};
-
-MODULE_INIT(AllModule)
-
-bool ModuleManager::Load(const std::string& name, bool defer)
-{
- modmap::iterator it = modlist->find(name);
- if (it == modlist->end())
- return false;
- Module* mod = NULL;
- try
- {
- mod = (*it->second->init)();
- mod->ModuleSourceFile = name;
- mod->ModuleDLLManager = NULL;
- mod->dying = false;
- Modules[name] = mod;
- if (defer)
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s", name.c_str());
- return true;
- }
- else
- {
- mod->init();
- }
- }
- catch (CoreException& modexcept)
- {
- if (mod)
- DoSafeUnload(mod);
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Unable to load " + name + ": " + modexcept.GetReason());
- return false;
- }
- FOREACH_MOD(I_OnLoadModule,OnLoadModule(mod));
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + name);
- }
-
- ServerInstance->BuildISupport();
- return true;
-}
-
-namespace {
- struct UnloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- UnloadAction(Module* m) : mod(m) {}
- void Call()
- {
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-
- struct ReloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- HandlerBase1<void, bool>* const callback;
- ReloadAction(Module* m, HandlerBase1<void, bool>* c)
- : mod(m), callback(c) {}
- void Call()
- {
- std::string name = mod->ModuleSourceFile;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- bool rv = ServerInstance->Modules->Load(name.c_str());
- if (callback)
- callback->Call(rv);
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-}
-
-bool ModuleManager::Unload(Module* mod)
-{
- if (!CanUnload(mod))
- return false;
- ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
- return true;
-}
-
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
- if (CanUnload(mod))
- ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
- else if (callback)
- callback->Call(false);
-}
-
-void ModuleManager::LoadAll()
-{
- Load("cmd_all", true);
- Load("cmd_whowas.so", true);
- Load("cmd_lusers.so", true);
-
- ConfigTagList tags = ServerInstance->Config->ConfTags("module");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string name = tag->getString("name");
- std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
-
- if (!this->Load(name, true))
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
- {
- Module* mod = i->second;
- try
- {
- mod->init();
- }
- catch (CoreException& modexcept)
- {
- LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-}
-
-#endif
diff --git a/src/modulemanager.cpp b/src/modulemanager.cpp
new file mode 100644
index 000000000..eb40c5971
--- /dev/null
+++ b/src/modulemanager.cpp
@@ -0,0 +1,157 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "exitcodes.h"
+#include <iostream>
+
+#ifndef _WIN32
+#include <dirent.h>
+#endif
+
+bool ModuleManager::Load(const std::string& modname, bool defer)
+{
+ /* Don't allow people to specify paths for modules, it doesn't work as expected */
+ if (modname.find('/') != std::string::npos)
+ {
+ LastModuleError = "You can't load modules with a path: " + modname;
+ return false;
+ }
+
+ const std::string filename = ExpandModName(modname);
+ const std::string moduleFile = ServerInstance->Config->Paths.PrependModule(filename);
+
+ if (!FileSystem::FileExists(moduleFile))
+ {
+ LastModuleError = "Module file could not be found: " + filename;
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ return false;
+ }
+
+ if (Modules.find(filename) != Modules.end())
+ {
+ LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ return false;
+ }
+
+ Module* newmod = NULL;
+ DLLManager* newhandle = new DLLManager(moduleFile.c_str());
+ ServiceList newservices;
+ if (!defer)
+ this->NewServices = &newservices;
+
+ try
+ {
+ newmod = newhandle->CallInit();
+ this->NewServices = NULL;
+
+ if (newmod)
+ {
+ newmod->ModuleSourceFile = filename;
+ newmod->ModuleDLLManager = newhandle;
+ newmod->dying = false;
+ Modules[filename] = newmod;
+ std::string version = newhandle->GetVersion();
+ if (version.empty())
+ version.assign("unknown");
+ if (defer)
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)",
+ filename.c_str(), version.c_str());
+ }
+ else
+ {
+ ConfigStatus confstatus;
+
+ AttachAll(newmod);
+ AddServices(newservices);
+ newmod->init();
+ newmod->ReadConfig(confstatus);
+
+ Version v = newmod->GetVersion();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)%s",
+ filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
+ }
+ }
+ else
+ {
+ LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ delete newhandle;
+ return false;
+ }
+ }
+ catch (CoreException& modexcept)
+ {
+ this->NewServices = NULL;
+
+ // failure in module constructor
+ if (newmod)
+ {
+ DoSafeUnload(newmod);
+ ServerInstance->GlobalCulls.AddItem(newhandle);
+ }
+ else
+ delete newhandle;
+ LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ return false;
+ }
+
+ if (defer)
+ return true;
+
+ FOREACH_MOD(OnLoadModule, (newmod));
+ PrioritizeHooks();
+ ServerInstance->ISupport.Build();
+ return true;
+}
+
+/* We must load the modules AFTER initializing the socket engine, now */
+void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
+{
+ std::cout << std::endl << "Loading core commands";
+ fflush(stdout);
+
+ DIR* library = opendir(ServerInstance->Config->Paths.Module.c_str());
+ if (library)
+ {
+ dirent* entry = NULL;
+ while (0 != (entry = readdir(library)))
+ {
+ if (InspIRCd::Match(entry->d_name, "core_*.so", ascii_case_insensitive_map))
+ {
+ std::cout << ".";
+ fflush(stdout);
+
+ this->NewServices = &servicemap[entry->d_name];
+
+ if (!Load(entry->d_name, true))
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
+ std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+ }
+ }
+ }
+ closedir(library);
+ std::cout << std::endl;
+ }
+}
diff --git a/src/modules.cpp b/src/modules.cpp
index 7df7579bf..3062aad43 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -24,26 +24,22 @@
*/
+#include <iostream>
#include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
#include "exitcodes.h"
#ifndef _WIN32
#include <dirent.h>
#endif
-static std::vector<dynamic_reference_base*>* dynrefs = NULL;
+static insp::intrusive_list<dynamic_reference_base>* dynrefs = NULL;
void dynamic_reference_base::reset_all()
{
if (!dynrefs)
return;
- for(unsigned int i = 0; i < dynrefs->size(); i++)
- (*dynrefs)[i]->ClearCache();
+ for (insp::intrusive_list<dynamic_reference_base>::iterator i = dynrefs->begin(); i != dynrefs->end(); ++i)
+ (*i)->resolve();
}
// Version is a simple class for holding a modules version number
@@ -56,24 +52,6 @@ Version::Version(const std::string &desc, int flags, const std::string& linkdata
{
}
-Request::Request(Module* src, Module* dst, const char* idstr)
-: id(idstr), source(src), dest(dst)
-{
-}
-
-void Request::Send()
-{
- if (dest)
- dest->OnRequest(*this);
-}
-
-Event::Event(Module* src, const std::string &eventid) : source(src), id(eventid) { }
-
-void Event::Send()
-{
- FOREACH_MOD(I_OnEvent,OnEvent(*this));
-}
-
// These declarations define the behavours of the base class Module (which does nothing at all)
Module::Module() { }
@@ -85,101 +63,100 @@ Module::~Module()
{
}
-ModResult Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { return MOD_RES_PASSTHRU; }
-void Module::OnUserConnect(LocalUser*) { }
-void Module::OnUserQuit(User*, const std::string&, const std::string&) { }
-void Module::OnUserDisconnect(LocalUser*) { }
-void Module::OnUserJoin(Membership*, bool, bool, CUList&) { }
-void Module::OnPostJoin(Membership*) { }
-void Module::OnUserPart(Membership*, std::string&, CUList&) { }
-void Module::OnPreRehash(User*, const std::string&) { }
-void Module::OnModuleRehash(User*, const std::string&) { }
-void Module::OnRehash(User*) { }
-ModResult Module::OnUserPreJoin(User*, Channel*, const char*, std::string&, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnMode(User*, void*, int, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
-void Module::OnOper(User*, const std::string&) { }
-void Module::OnPostOper(User*, const std::string&, const std::string &) { }
-void Module::OnPostDeoper(User*) { }
-void Module::OnInfo(User*) { }
-void Module::OnWhois(User*, User*) { }
-ModResult Module::OnUserPreInvite(User*, User*, Channel*, time_t) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreNotice(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreNick(User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnUserPostNick(User*, const std::string&) { }
-ModResult Module::OnPreMode(User*, User*, Channel*, const std::vector<std::string>&) { return MOD_RES_PASSTHRU; }
-void Module::On005Numeric(std::string&) { }
-ModResult Module::OnKill(User*, User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnLoadModule(Module*) { }
-void Module::OnUnloadModule(Module*) { }
-void Module::OnBackgroundTimer(time_t) { }
-ModResult Module::OnPreCommand(std::string&, std::vector<std::string>&, LocalUser*, bool, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnPostCommand(const std::string&, const std::vector<std::string>&, LocalUser*, CmdResult, const std::string&) { }
-void Module::OnUserInit(LocalUser*) { }
-ModResult Module::OnCheckReady(LocalUser*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserRegister(LocalUser*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreKick(User*, Membership*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { }
-ModResult Module::OnRawMode(User*, Channel*, const char, const std::string &, bool, int) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckInvite(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckKey(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckLimit(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckChannelBan(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckBan(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnExtBanCheck(User*, Channel*, char) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnStats(char, User*, string_list&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnPreTopicChange(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnEvent(Event&) { }
-void Module::OnRequest(Request&) { }
-ModResult Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { return MOD_RES_PASSTHRU; }
-void Module::OnGlobalOper(User*) { }
-void Module::OnPostConnect(User*) { }
-ModResult Module::OnAddBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnDelBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
-void Module::OnStreamSocketAccept(StreamSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { }
-int Module::OnStreamSocketWrite(StreamSocket*, std::string&) { return -1; }
-void Module::OnStreamSocketClose(StreamSocket*) { }
-void Module::OnStreamSocketConnect(StreamSocket*) { }
-int Module::OnStreamSocketRead(StreamSocket*, std::string&) { return -1; }
-void Module::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&) { }
-void Module::OnUserNotice(User*, void*, int, const std::string&, char, const CUList&) { }
-void Module::OnRemoteKill(User*, User*, const std::string&, const std::string&) { }
-void Module::OnUserInvite(User*, User*, Channel*, time_t) { }
-void Module::OnPostTopicChange(User*, Channel*, const std::string&) { }
-void Module::OnGetServerDescription(const std::string&, std::string&) { }
-void Module::OnSyncUser(User*, Module*, void*) { }
-void Module::OnSyncChannel(Channel*, Module*, void*) { }
-void Module::OnSyncNetwork(Module*, void*) { }
-void Module::ProtoSendMode(void*, TargetTypeFlags, void*, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
-void Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { }
-void Module::ProtoSendMetaData(void*, Extensible*, const std::string&, const std::string&) { }
-void Module::OnWallops(User*, const std::string&) { }
-void Module::OnChangeHost(User*, const std::string&) { }
-void Module::OnChangeName(User*, const std::string&) { }
-void Module::OnChangeIdent(User*, const std::string&) { }
-void Module::OnAddLine(User*, XLine*) { }
-void Module::OnDelLine(User*, XLine*) { }
-void Module::OnExpireLine(XLine*) { }
-void Module::OnCleanup(int, void*) { }
-ModResult Module::OnChannelPreDelete(Channel*) { return MOD_RES_PASSTHRU; }
-void Module::OnChannelDelete(Channel*) { }
-ModResult Module::OnSetAway(User*, const std::string &) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnWhoisLine(User*, User*, int&, std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnBuildNeighborList(User*, UserChanList&, std::map<User*,bool>&) { }
-void Module::OnGarbageCollect() { }
-ModResult Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { return MOD_RES_PASSTHRU; }
-void Module::OnText(User*, void*, int, const std::string&, char, CUList&) { }
+void Module::DetachEvent(Implementation i)
+{
+ ServerInstance->Modules->Detach(i, this);
+}
+
+void Module::ReadConfig(ConfigStatus& status) { }
+ModResult Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { DetachEvent(I_OnSendSnotice); return MOD_RES_PASSTHRU; }
+void Module::OnUserConnect(LocalUser*) { DetachEvent(I_OnUserConnect); }
+void Module::OnUserQuit(User*, const std::string&, const std::string&) { DetachEvent(I_OnUserQuit); }
+void Module::OnUserDisconnect(LocalUser*) { DetachEvent(I_OnUserDisconnect); }
+void Module::OnUserJoin(Membership*, bool, bool, CUList&) { DetachEvent(I_OnUserJoin); }
+void Module::OnPostJoin(Membership*) { DetachEvent(I_OnPostJoin); }
+void Module::OnUserPart(Membership*, std::string&, CUList&) { DetachEvent(I_OnUserPart); }
+void Module::OnPreRehash(User*, const std::string&) { DetachEvent(I_OnPreRehash); }
+void Module::OnModuleRehash(User*, const std::string&) { DetachEvent(I_OnModuleRehash); }
+ModResult Module::OnUserPreJoin(LocalUser*, Channel*, const std::string&, std::string&, const std::string&) { DetachEvent(I_OnUserPreJoin); return MOD_RES_PASSTHRU; }
+void Module::OnMode(User*, User*, Channel*, const Modes::ChangeList&, ModeParser::ModeProcessFlag) { DetachEvent(I_OnMode); }
+void Module::OnOper(User*, const std::string&) { DetachEvent(I_OnOper); }
+void Module::OnPostOper(User*, const std::string&, const std::string &) { DetachEvent(I_OnPostOper); }
+void Module::OnPostDeoper(User*) { DetachEvent(I_OnPostDeoper); }
+ModResult Module::OnUserPreInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserPreInvite); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreMessage(User*, const MessageTarget&, MessageDetails&) { DetachEvent(I_OnUserPreMessage); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreNick(LocalUser*, const std::string&) { DetachEvent(I_OnUserPreNick); return MOD_RES_PASSTHRU; }
+void Module::OnUserPostNick(User*, const std::string&) { DetachEvent(I_OnUserPostNick); }
+ModResult Module::OnPreMode(User*, User*, Channel*, Modes::ChangeList&) { DetachEvent(I_OnPreMode); return MOD_RES_PASSTHRU; }
+void Module::On005Numeric(std::map<std::string, std::string>&) { DetachEvent(I_On005Numeric); }
+ModResult Module::OnKill(User*, User*, const std::string&) { DetachEvent(I_OnKill); return MOD_RES_PASSTHRU; }
+void Module::OnLoadModule(Module*) { DetachEvent(I_OnLoadModule); }
+void Module::OnUnloadModule(Module*) { DetachEvent(I_OnUnloadModule); }
+void Module::OnBackgroundTimer(time_t) { DetachEvent(I_OnBackgroundTimer); }
+ModResult Module::OnPreCommand(std::string&, CommandBase::Params&, LocalUser*, bool) { DetachEvent(I_OnPreCommand); return MOD_RES_PASSTHRU; }
+void Module::OnPostCommand(Command*, const CommandBase::Params&, LocalUser*, CmdResult, bool) { DetachEvent(I_OnPostCommand); }
+void Module::OnUserInit(LocalUser*) { DetachEvent(I_OnUserInit); }
+ModResult Module::OnCheckReady(LocalUser*) { DetachEvent(I_OnCheckReady); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserRegister(LocalUser*) { DetachEvent(I_OnUserRegister); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreKick(User*, Membership*, const std::string&) { DetachEvent(I_OnUserPreKick); return MOD_RES_PASSTHRU; }
+void Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { DetachEvent(I_OnUserKick); }
+ModResult Module::OnRawMode(User*, Channel*, ModeHandler*, const std::string&, bool) { DetachEvent(I_OnRawMode); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckInvite(User*, Channel*) { DetachEvent(I_OnCheckInvite); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckKey(User*, Channel*, const std::string&) { DetachEvent(I_OnCheckKey); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckLimit(User*, Channel*) { DetachEvent(I_OnCheckLimit); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckChannelBan(User*, Channel*) { DetachEvent(I_OnCheckChannelBan); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckBan(User*, Channel*, const std::string&) { DetachEvent(I_OnCheckBan); return MOD_RES_PASSTHRU; }
+ModResult Module::OnExtBanCheck(User*, Channel*, char) { DetachEvent(I_OnExtBanCheck); return MOD_RES_PASSTHRU; }
+ModResult Module::OnPreChangeHost(LocalUser*, const std::string&) { DetachEvent(I_OnPreChangeHost); return MOD_RES_PASSTHRU; }
+ModResult Module::OnPreChangeRealName(LocalUser*, const std::string&) { DetachEvent(I_OnPreChangeRealName); return MOD_RES_PASSTHRU; }
+ModResult Module::OnPreTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPreTopicChange); return MOD_RES_PASSTHRU; }
+ModResult Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { DetachEvent(I_OnPassCompare); return MOD_RES_PASSTHRU; }
+void Module::OnPostConnect(User*) { DetachEvent(I_OnPostConnect); }
+void Module::OnUserPostMessage(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserPostMessage); }
+void Module::OnUserMessageBlocked(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserMessageBlocked); }
+void Module::OnUserInvite(User*, User*, Channel*, time_t, unsigned int, CUList&) { DetachEvent(I_OnUserInvite); }
+void Module::OnPostTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPostTopicChange); }
+void Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { DetachEvent(I_OnDecodeMetaData); }
+void Module::OnChangeHost(User*, const std::string&) { DetachEvent(I_OnChangeHost); }
+void Module::OnChangeRealName(User*, const std::string&) { DetachEvent(I_OnChangeRealName); }
+void Module::OnChangeIdent(User*, const std::string&) { DetachEvent(I_OnChangeIdent); }
+void Module::OnAddLine(User*, XLine*) { DetachEvent(I_OnAddLine); }
+void Module::OnDelLine(User*, XLine*) { DetachEvent(I_OnDelLine); }
+void Module::OnExpireLine(XLine*) { DetachEvent(I_OnExpireLine); }
+void Module::OnCleanup(ExtensionItem::ExtensibleType, Extensible*) { }
+ModResult Module::OnChannelPreDelete(Channel*) { DetachEvent(I_OnChannelPreDelete); return MOD_RES_PASSTHRU; }
+void Module::OnChannelDelete(Channel*) { DetachEvent(I_OnChannelDelete); }
+void Module::OnBuildNeighborList(User*, IncludeChanList&, std::map<User*,bool>&) { DetachEvent(I_OnBuildNeighborList); }
+void Module::OnGarbageCollect() { DetachEvent(I_OnGarbageCollect); }
+ModResult Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { DetachEvent(I_OnSetConnectClass); return MOD_RES_PASSTHRU; }
+void Module::OnUserMessage(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserMessage); }
+ModResult Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { DetachEvent(I_OnNamesListItem); return MOD_RES_PASSTHRU; }
+ModResult Module::OnNumeric(User*, const Numeric::Numeric&) { DetachEvent(I_OnNumeric); return MOD_RES_PASSTHRU; }
+ModResult Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { DetachEvent(I_OnAcceptConnection); return MOD_RES_PASSTHRU; }
+ModResult Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, Numeric::Numeric&) { DetachEvent(I_OnSendWhoLine); return MOD_RES_PASSTHRU; }
+void Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
+void Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); }
+void Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); }
+ModResult Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; }
+
+#ifdef INSPIRCD_ENABLE_TESTSUITE
void Module::OnRunTestSuite() { }
-void Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { }
-ModResult Module::OnNumeric(User*, unsigned int, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnHookIO(StreamSocket*, ListenSocket*) { }
-ModResult Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { return MOD_RES_PASSTHRU; }
-void Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, std::string&) { }
-void Module::OnSetUserIP(LocalUser*) { }
+#endif
+
+ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type)
+ : creator(Creator), name(Name), service(Type)
+{
+ if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+ ServerInstance->Modules->NewServices->push_back(this);
+}
-ModuleManager::ModuleManager() : ModCount(0)
+void ServiceProvider::DisableAutoRegister()
+{
+ if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+ stdalgo::erase(*ServerInstance->Modules->NewServices, this);
+}
+
+ModuleManager::ModuleManager()
{
}
@@ -189,7 +166,7 @@ ModuleManager::~ModuleManager()
bool ModuleManager::Attach(Implementation i, Module* mod)
{
- if (std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod) != EventHandlers[i].end())
+ if (stdalgo::isin(EventHandlers[i], mod))
return false;
EventHandlers[i].push_back(mod);
@@ -198,13 +175,7 @@ bool ModuleManager::Attach(Implementation i, Module* mod)
bool ModuleManager::Detach(Implementation i, Module* mod)
{
- EventHandlerIter x = std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod);
-
- if (x == EventHandlers[i].end())
- return false;
-
- EventHandlers[i].erase(x);
- return true;
+ return stdalgo::erase(EventHandlers[i], mod);
}
void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
@@ -213,18 +184,22 @@ void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
Attach(i[n], mod);
}
+void ModuleManager::AttachAll(Module* mod)
+{
+ for (size_t i = 0; i != I_END; ++i)
+ Attach((Implementation)i, mod);
+}
+
void ModuleManager::DetachAll(Module* mod)
{
- for (size_t n = I_BEGIN + 1; n != I_END; ++n)
+ for (size_t n = 0; n != I_END; ++n)
Detach((Implementation)n, mod);
}
-bool ModuleManager::SetPriority(Module* mod, Priority s)
+void ModuleManager::SetPriority(Module* mod, Priority s)
{
- for (size_t n = I_BEGIN + 1; n != I_END; ++n)
+ for (size_t n = 0; n != I_END; ++n)
SetPriority(mod, (Implementation)n, s);
-
- return true;
}
bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Module* which)
@@ -255,22 +230,25 @@ bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Modul
return false;
found_src:
+ // The modules registered for a hook are called in reverse order (to allow for easier removal
+ // of list entries while looping), meaning that the Priority given to us has the exact opposite effect
+ // on the list, e.g.: PRIORITY_BEFORE will actually put 'mod' after 'which', etc.
size_t swap_pos = my_pos;
switch (s)
{
- case PRIORITY_FIRST:
+ case PRIORITY_LAST:
if (prioritizationState != PRIO_STATE_FIRST)
return true;
else
swap_pos = 0;
break;
- case PRIORITY_LAST:
+ case PRIORITY_FIRST:
if (prioritizationState != PRIO_STATE_FIRST)
return true;
else
swap_pos = EventHandlers[i].size() - 1;
break;
- case PRIORITY_AFTER:
+ case PRIORITY_BEFORE:
{
/* Find the latest possible position, only searching AFTER our position */
for (size_t x = EventHandlers[i].size() - 1; x > my_pos; --x)
@@ -285,7 +263,7 @@ found_src:
return true;
}
/* Place this module before a set of other modules */
- case PRIORITY_BEFORE:
+ case PRIORITY_AFTER:
{
for (size_t x = 0; x < my_pos; ++x)
{
@@ -325,6 +303,29 @@ swap_now:
return true;
}
+bool ModuleManager::PrioritizeHooks()
+{
+ /* We give every module a chance to re-prioritize when we introduce a new one,
+ * not just the one thats loading, as the new module could affect the preference
+ * of others
+ */
+ for (int tries = 0; tries < 20; tries++)
+ {
+ prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
+ for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
+ n->second->Prioritize();
+
+ if (prioritizationState == PRIO_STATE_LAST)
+ break;
+ if (tries == 19)
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Hook priority dependency loop detected");
+ return false;
+ }
+ }
+ return true;
+}
+
bool ModuleManager::CanUnload(Module* mod)
{
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
@@ -332,13 +333,7 @@ bool ModuleManager::CanUnload(Module* mod)
if ((modfind == Modules.end()) || (modfind->second != mod) || (mod->dying))
{
LastModuleError = "Module " + mod->ModuleSourceFile + " is not loaded, cannot unload it!";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
- }
- if (mod->GetVersion().Flags & VF_STATIC)
- {
- LastModuleError = "Module " + mod->ModuleSourceFile + " not unloadable (marked static)";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
@@ -346,41 +341,59 @@ bool ModuleManager::CanUnload(Module* mod)
return true;
}
+void ModuleManager::UnregisterModes(Module* mod, ModeType modetype)
+{
+ const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes.GetModes(modetype);
+ for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); )
+ {
+ ModeHandler* const mh = i->second;
+ ++i;
+ if (mh->creator == mod)
+ this->DelService(*mh);
+ }
+}
+
void ModuleManager::DoSafeUnload(Module* mod)
{
+ // First, notify all modules that a module is about to be unloaded, so in case
+ // they pass execution to the soon to be unloaded module, it will happen now,
+ // i.e. before we unregister the services of the module being unloaded
+ FOREACH_MOD(OnUnloadModule, (mod));
+
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
+ // Unregister modes before extensions because modes may require their extension to show the mode being unset
+ UnregisterModes(mod, MODETYPE_USER);
+ UnregisterModes(mod, MODETYPE_CHANNEL);
+
std::vector<reference<ExtensionItem> > items;
ServerInstance->Extensions.BeginUnregister(modfind->second, items);
/* Give the module a chance to tidy out all its metadata */
- for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); )
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator c = chans.begin(); c != chans.end(); )
{
Channel* chan = c->second;
++c;
- mod->OnCleanup(TYPE_CHANNEL, chan);
+ mod->OnCleanup(ExtensionItem::EXT_CHANNEL, chan);
chan->doUnhookExtensions(items);
- const UserMembList* users = chan->GetUsers();
- for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator mi = users.begin(); mi != users.end(); ++mi)
+ {
+ mod->OnCleanup(ExtensionItem::EXT_MEMBERSHIP, mi->second);
mi->second->doUnhookExtensions(items);
+ }
}
- for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); )
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); )
{
User* user = u->second;
// The module may quit the user (e.g. SSL mod unloading) and that will remove it from the container
++u;
- mod->OnCleanup(TYPE_USER, user);
+ mod->OnCleanup(ExtensionItem::EXT_USER, user);
user->doUnhookExtensions(items);
}
- for(char m='A'; m <= 'z'; m++)
- {
- ModeHandler* mh;
- mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
- if (mh && mh->creator == mod)
- ServerInstance->Modes->DelMode(mh);
- mh = ServerInstance->Modes->FindMode(m, MODETYPE_CHANNEL);
- if (mh && mh->creator == mod)
- ServerInstance->Modes->DelMode(mh);
- }
+
for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
{
std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
@@ -390,19 +403,13 @@ void ModuleManager::DoSafeUnload(Module* mod)
dynamic_reference_base::reset_all();
- /* Tidy up any dangling resolvers */
- ServerInstance->Res->CleanResolvers(mod);
-
- FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
-
DetachAll(mod);
Modules.erase(modfind);
ServerInstance->GlobalCulls.AddItem(mod);
- ServerInstance->Logs->Log("MODULE", DEFAULT,"Module %s unloaded",mod->ModuleSourceFile.c_str());
- this->ModCount--;
- ServerInstance->BuildISupport();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Module %s unloaded",mod->ModuleSourceFile.c_str());
+ ServerInstance->ISupport.Build();
}
void ModuleManager::UnloadAll()
@@ -428,40 +435,109 @@ void ModuleManager::UnloadAll()
}
}
-std::string& ModuleManager::LastError()
+namespace
{
- return LastModuleError;
+ struct UnloadAction : public ActionBase
+ {
+ Module* const mod;
+ UnloadAction(Module* m) : mod(m) {}
+ void Call() CXX11_OVERRIDE
+ {
+ DLLManager* dll = mod->ModuleDLLManager;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ // In pure static mode this is always NULL
+ delete dll;
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+ };
}
-CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user)
+bool ModuleManager::Unload(Module* mod)
{
- return this->Parser->CallHandler(commandname, parameters, user);
+ if (!CanUnload(mod))
+ return false;
+ ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
+ return true;
}
-bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, User* user)
+void ModuleManager::LoadAll()
{
- return this->Parser->IsValidCommand(commandname, pcnt, user);
+ std::map<std::string, ServiceList> servicemap;
+ LoadCoreModules(servicemap);
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("module");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string name = ExpandModName(tag->getString("name"));
+ this->NewServices = &servicemap[name];
+
+ // Skip modules which are already loaded.
+ if (Modules.find(name) != Modules.end())
+ continue;
+
+ std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
+ if (!this->Load(name, true))
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
+ std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+ }
+ }
+
+ ConfigStatus confstatus;
+
+ for (ModuleMap::const_iterator i = Modules.begin(); i != Modules.end(); ++i)
+ {
+ Module* mod = i->second;
+ try
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Initializing %s", i->first.c_str());
+ AttachAll(mod);
+ AddServices(servicemap[i->first]);
+ mod->init();
+ mod->ReadConfig(confstatus);
+ }
+ catch (CoreException& modexcept)
+ {
+ LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+ }
+ }
+
+ this->NewServices = NULL;
+
+ if (!PrioritizeHooks())
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+}
+
+std::string& ModuleManager::LastError()
+{
+ return LastModuleError;
+}
+
+void ModuleManager::AddServices(const ServiceList& list)
+{
+ for (ServiceList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ ServiceProvider& s = **i;
+ AddService(s);
+ }
}
void ModuleManager::AddService(ServiceProvider& item)
{
switch (item.service)
{
- case SERVICE_COMMAND:
- if (!ServerInstance->Parser->AddCommand(static_cast<Command*>(&item)))
- throw ModuleException("Command "+std::string(item.name)+" already exists.");
- return;
- case SERVICE_MODE:
- if (!ServerInstance->Modes->AddMode(static_cast<ModeHandler*>(&item)))
- throw ModuleException("Mode "+std::string(item.name)+" already exists.");
- return;
- case SERVICE_METADATA:
- if (!ServerInstance->Extensions.Register(static_cast<ExtensionItem*>(&item)))
- throw ModuleException("Extension " + std::string(item.name) + " already exists.");
- return;
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
+ if ((!item.name.compare(0, 5, "mode/", 5)) || (!item.name.compare(0, 6, "umode/", 6)))
+ throw ModuleException("The \"mode/\" and the \"umode\" service name prefixes are reserved.");
+
DataProviders.insert(std::make_pair(item.name, &item));
std::string::size_type slash = item.name.find('/');
if (slash != std::string::npos)
@@ -469,11 +545,14 @@ void ModuleManager::AddService(ServiceProvider& item)
DataProviders.insert(std::make_pair(item.name.substr(0, slash), &item));
DataProviders.insert(std::make_pair(item.name.substr(slash + 1), &item));
}
- return;
+ dynamic_reference_base::reset_all();
+ break;
}
default:
- throw ModuleException("Cannot add unknown service type");
+ item.RegisterService();
}
+
+ FOREACH_MOD(OnServiceAdd, (item));
}
void ModuleManager::DelService(ServiceProvider& item)
@@ -483,22 +562,18 @@ void ModuleManager::DelService(ServiceProvider& item)
case SERVICE_MODE:
if (!ServerInstance->Modes->DelMode(static_cast<ModeHandler*>(&item)))
throw ModuleException("Mode "+std::string(item.name)+" does not exist.");
- return;
+ // Fall through
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
- for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
- {
- std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
- if (curr->second == &item)
- DataProviders.erase(curr);
- }
- dynamic_reference_base::reset_all();
+ DelReferent(&item);
return;
}
default:
throw ModuleException("Cannot delete unknown service type");
}
+
+ FOREACH_MOD(OnServiceDel, (item));
}
ServiceProvider* ModuleManager::FindService(ServiceType type, const std::string& name)
@@ -519,85 +594,66 @@ ServiceProvider* ModuleManager::FindService(ServiceType type, const std::string&
}
}
+std::string ModuleManager::ExpandModName(const std::string& modname)
+{
+ // Transform "callerid" -> "m_callerid.so" unless it already has a ".so" extension,
+ // so coremods in the "core_*.so" form aren't changed
+ std::string ret = modname;
+ if ((modname.length() < 3) || (modname.compare(modname.size() - 3, 3, ".so")))
+ ret.insert(0, "m_").append(".so");
+ return ret;
+}
+
dynamic_reference_base::dynamic_reference_base(Module* Creator, const std::string& Name)
- : name(Name), value(NULL), creator(Creator)
+ : name(Name), hook(NULL), value(NULL), creator(Creator)
{
if (!dynrefs)
- dynrefs = new std::vector<dynamic_reference_base*>;
- dynrefs->push_back(this);
+ dynrefs = new insp::intrusive_list<dynamic_reference_base>;
+ dynrefs->push_front(this);
+
+ // Resolve unless there is no ModuleManager (part of class InspIRCd)
+ if (ServerInstance)
+ resolve();
}
dynamic_reference_base::~dynamic_reference_base()
{
- for(unsigned int i = 0; i < dynrefs->size(); i++)
+ dynrefs->erase(this);
+ if (dynrefs->empty())
{
- if (dynrefs->at(i) == this)
- {
- unsigned int last = dynrefs->size() - 1;
- if (i != last)
- dynrefs->at(i) = dynrefs->at(last);
- dynrefs->erase(dynrefs->begin() + last);
- if (dynrefs->empty())
- {
- delete dynrefs;
- dynrefs = NULL;
- }
- return;
- }
+ delete dynrefs;
+ dynrefs = NULL;
}
}
void dynamic_reference_base::SetProvider(const std::string& newname)
{
name = newname;
- ClearCache();
-}
-
-void dynamic_reference_base::lookup()
-{
- if (!*this)
- throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
+ resolve();
}
-dynamic_reference_base::operator bool()
+void dynamic_reference_base::resolve()
{
- if (!value)
+ // Because find() may return any element with a matching key in case count(key) > 1 use lower_bound()
+ // to ensure a dynref with the same name as another one resolves to the same object
+ std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules.DataProviders.lower_bound(name);
+ if ((i != ServerInstance->Modules.DataProviders.end()) && (i->first == this->name))
{
- std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.find(name);
- if (i != ServerInstance->Modules->DataProviders.end())
- value = static_cast<DataProvider*>(i->second);
+ ServiceProvider* newvalue = i->second;
+ if (value != newvalue)
+ {
+ value = newvalue;
+ if (hook)
+ hook->OnCapture();
+ }
}
- return (value != NULL);
-}
-
-void InspIRCd::SendMode(const std::vector<std::string>& parameters, User *user)
-{
- this->Modes->Process(parameters, user);
-}
-
-
-void InspIRCd::SendGlobalMode(const std::vector<std::string>& parameters, User *user)
-{
- Modes->Process(parameters, user);
- if (!Modes->GetLastParse().empty())
- this->PI->SendMode(parameters[0], Modes->GetLastParseParams(), Modes->GetLastParseTranslate());
-}
-
-bool InspIRCd::AddResolver(Resolver* r, bool cached)
-{
- if (!cached)
- return this->Res->AddResolverClass(r);
else
- {
- r->TriggerCachedResult();
- delete r;
- return true;
- }
+ value = NULL;
}
Module* ModuleManager::Find(const std::string &name)
{
- std::map<std::string, Module*>::iterator modfind = Modules.find(name);
+ std::map<std::string, Module*>::const_iterator modfind = Modules.find(ExpandModName(name));
if (modfind == Modules.end())
return NULL;
@@ -605,181 +661,21 @@ Module* ModuleManager::Find(const std::string &name)
return modfind->second;
}
-const std::vector<std::string> ModuleManager::GetAllModuleNames(int filter)
+void ModuleManager::AddReferent(const std::string& name, ServiceProvider* service)
{
- std::vector<std::string> retval;
- for (std::map<std::string, Module*>::iterator x = Modules.begin(); x != Modules.end(); ++x)
- if (!filter || (x->second->GetVersion().Flags & filter))
- retval.push_back(x->first);
- return retval;
-}
-
-ConfigReader::ConfigReader()
-{
- this->error = 0;
- ServerInstance->Logs->Log("MODULE", DEBUG, "ConfigReader is deprecated in 2.0; "
- "use ServerInstance->Config->ConfValue(\"key\") or ->ConfTags(\"key\") instead");
-}
-
-
-ConfigReader::~ConfigReader()
-{
-}
-
-static ConfigTag* SlowGetTag(const std::string &tag, int index)
-{
- ConfigTagList tags = ServerInstance->Config->ConfTags(tag);
- while (tags.first != tags.second)
- {
- if (!index)
- return tags.first->second;
- tags.first++;
- index--;
- }
- return NULL;
-}
-
-std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
-{
- std::string result = default_value;
- ConfigTag* conftag = SlowGetTag(tag, index);
- if (!conftag || !conftag->readString(name, result, allow_linefeeds))
- {
- this->error = CONF_VALUE_NOT_FOUND;
- }
- return result;
-}
-
-std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds)
-{
- return ReadValue(tag, name, "", index, allow_linefeeds);
-}
-
-bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index)
-{
- bool def = (default_value == "yes");
- ConfigTag* conftag = SlowGetTag(tag, index);
- return conftag ? conftag->getBool(name, def) : def;
-}
-
-bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
-{
- return ReadFlag(tag, name, "", index);
-}
-
-
-int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive)
-{
- int v = atoi(default_value.c_str());
- ConfigTag* conftag = SlowGetTag(tag, index);
- int result = conftag ? conftag->getInt(name, v) : v;
-
- if ((need_positive) && (result < 0))
- {
- this->error = CONF_INT_NEGATIVE;
- return 0;
- }
-
- return result;
-}
-
-int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive)
-{
- return ReadInteger(tag, name, "", index, need_positive);
-}
-
-long ConfigReader::GetError()
-{
- long olderr = this->error;
- this->error = 0;
- return olderr;
-}
-
-int ConfigReader::Enumerate(const std::string &tag)
-{
- ServerInstance->Logs->Log("MODULE", DEBUG, "Module is using ConfigReader::Enumerate on %s; this is slow!",
- tag.c_str());
- int i=0;
- while (SlowGetTag(tag, i)) i++;
- return i;
-}
-
-FileReader::FileReader(const std::string &filename)
-{
- LoadFile(filename);
-}
-
-FileReader::FileReader()
-{
-}
-
-std::string FileReader::Contents()
-{
- std::string x;
- for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
- {
- x.append(*a);
- x.append("\r\n");
- }
- return x;
-}
-
-unsigned long FileReader::ContentSize()
-{
- return this->contentsize;
-}
-
-void FileReader::CalcSize()
-{
- unsigned long n = 0;
- for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
- n += (a->length() + 2);
- this->contentsize = n;
+ DataProviders.insert(std::make_pair(name, service));
+ dynamic_reference_base::reset_all();
}
-void FileReader::LoadFile(const std::string &filename)
+void ModuleManager::DelReferent(ServiceProvider* service)
{
- std::map<std::string, file_cache>::iterator file = ServerInstance->Config->Files.find(filename);
- if (file != ServerInstance->Config->Files.end())
+ for (std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
{
- this->fc = file->second;
+ ServiceProvider* curr = i->second;
+ if (curr == service)
+ DataProviders.erase(i++);
+ else
+ ++i;
}
- else
- {
- fc.clear();
- FILE* f = fopen(filename.c_str(), "r");
- if (!f)
- return;
- char linebuf[MAXBUF*10];
- while (fgets(linebuf, sizeof(linebuf), f))
- {
- int len = strlen(linebuf);
- if (len)
- fc.push_back(std::string(linebuf, len - 1));
- }
- fclose(f);
- }
- CalcSize();
-}
-
-
-FileReader::~FileReader()
-{
-}
-
-bool FileReader::Exists()
-{
- return (!(fc.size() == 0));
-}
-
-std::string FileReader::GetLine(int x)
-{
- if ((x<0) || ((unsigned)x>=fc.size()))
- return "";
- return fc[x];
-}
-
-int FileReader::FileSize()
-{
- return fc.size();
+ dynamic_reference_base::reset_all();
}
diff --git a/src/modules/extra/m_geoip.cpp b/src/modules/extra/m_geoip.cpp
index 03b7a55f7..0d7c2eb70 100644
--- a/src/modules/extra/m_geoip.cpp
+++ b/src/modules/extra/m_geoip.cpp
@@ -17,9 +17,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("geoip" "")
+/// $LinkerFlags: find_linker_flags("geoip" "-lGeoIP")
+
+/// $PackageInfo: require_system("centos" "7.0") GeoIP-devel pkgconfig
+/// $PackageInfo: require_system("darwin") geoip pkg-config
+/// $PackageInfo: require_system("debian") libgeoip-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libgeoip-dev pkg-config
#include "inspircd.h"
#include "xline.h"
+#include "modules/stats.h"
+#include "modules/whois.h"
+
+// Fix warnings about the use of commas at end of enumerator lists on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-pedantic"
+#endif
#include <GeoIP.h>
@@ -27,41 +43,46 @@
# pragma comment(lib, "GeoIP.lib")
#endif
-/* $ModDesc: Provides a way to restrict users by country using GeoIP lookup */
-/* $LinkerFlags: -lGeoIP */
+enum
+{
+ // InspIRCd-specific.
+ RPL_WHOISCOUNTRY = 344
+};
-class ModuleGeoIP : public Module
+class ModuleGeoIP : public Module, public Stats::EventListener, public Whois::EventListener
{
- LocalStringExt ext;
+ StringExtItem ext;
+ bool extban;
GeoIP* gi;
- std::string* SetExt(LocalUser* user)
+ std::string* SetExt(User* user)
{
- const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString());
+ const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString().c_str());
if (!c)
c = "UNK";
- std::string* cc = new std::string(c);
- ext.set(user, cc);
- return cc;
+ ext.set(user, c);
+ return ext.get(user);
}
public:
- ModuleGeoIP() : ext("geoip_cc", this), gi(NULL)
+ ModuleGeoIP()
+ : Stats::EventListener(this)
+ , Whois::EventListener(this)
+ , ext("geoip_cc", ExtensionItem::EXT_USER, this)
+ , extban(true)
+ , gi(NULL)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
gi = GeoIP_new(GEOIP_STANDARD);
if (gi == NULL)
throw ModuleException("Unable to initialize geoip, are you missing GeoIP.dat?");
- ServerInstance->Modules->AddService(ext);
- Implementation eventlist[] = { I_OnSetConnectClass, I_OnSetUserIP, I_OnStats };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
LocalUser* user = *i;
if ((user->registered == REG_ALL) && (!ext.get(user)))
@@ -77,12 +98,38 @@ class ModuleGeoIP : public Module
GeoIP_delete(gi);
}
- Version GetVersion()
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
{
- return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_VENDOR);
+ ConfigTag* tag = ServerInstance->Config->ConfValue("geoip");
+ extban = tag->getBool("extban");
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_OPTCOMMON|VF_VENDOR);
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ if (extban)
+ tokens["EXTBAN"].push_back('G');
+ }
+
+ ModResult OnCheckBan(User* user, Channel*, const std::string& mask) CXX11_OVERRIDE
+ {
+ if (extban && (mask.length() > 2) && (mask[0] == 'G') && (mask[1] == ':'))
+ {
+ std::string* cc = ext.get(user);
+ if (!cc)
+ cc = SetExt(user);
+
+ if (InspIRCd::Match(*cc, mask.substr(2)))
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+ }
+
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
std::string* cc = ext.get(user);
if (!cc)
@@ -99,21 +146,36 @@ class ModuleGeoIP : public Module
return MOD_RES_DENY;
}
- void OnSetUserIP(LocalUser* user)
+ void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
{
// If user has sent NICK/USER, re-set the ExtItem as this is likely CGI:IRC changing the IP
if (user->registered == REG_NICKUSER)
SetExt(user);
}
- ModResult OnStats(char symbol, User* user, string_list &out)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
+ {
+ // If the extban is disabled we don't expose users location.
+ if (!extban)
+ return;
+
+ std::string* cc = ext.get(whois.GetTarget());
+ if (!cc)
+ cc = SetExt(whois.GetTarget());
+
+ whois.SendLine(RPL_WHOISCOUNTRY, *cc, "is located in this country");
+ }
+
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'G')
+ if (stats.GetSymbol() != 'G')
return MOD_RES_PASSTHRU;
unsigned int unknown = 0;
std::map<std::string, unsigned int> results;
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
std::string* cc = ext.get(*i);
if (cc)
@@ -122,18 +184,16 @@ class ModuleGeoIP : public Module
unknown++;
}
- std::string p = ServerInstance->Config->ServerName + " 801 " + user->nick + " :GeoIPSTATS ";
for (std::map<std::string, unsigned int>::const_iterator i = results.begin(); i != results.end(); ++i)
{
- out.push_back(p + i->first + " " + ConvToStr(i->second));
+ stats.AddRow(801, "GeoIPSTATS " + i->first + " " + ConvToStr(i->second));
}
if (unknown)
- out.push_back(p + "Unknown " + ConvToStr(unknown));
+ stats.AddRow(801, "GeoIPSTATS Unknown " + ConvToStr(unknown));
return MOD_RES_DENY;
}
};
MODULE_INIT(ModuleGeoIP)
-
diff --git a/src/modules/extra/m_ldap.cpp b/src/modules/extra/m_ldap.cpp
new file mode 100644
index 000000000..8c2752dbf
--- /dev/null
+++ b/src/modules/extra/m_ldap.cpp
@@ -0,0 +1,677 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013-2015 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2015 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// $LinkerFlags: -llber -lldap_r
+
+/// $PackageInfo: require_system("centos") openldap-devel
+/// $PackageInfo: require_system("debian") libldap2-dev
+/// $PackageInfo: require_system("ubuntu") libldap2-dev
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+// Ignore OpenLDAP deprecation warnings on OS X Yosemite and newer.
+#if defined __APPLE__
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+#include <ldap.h>
+
+#ifdef _WIN32
+# pragma comment(lib, "libldap_r.lib")
+# pragma comment(lib, "liblber.lib")
+#endif
+
+class LDAPService;
+
+class LDAPRequest
+{
+ public:
+ LDAPService* service;
+ LDAPInterface* inter;
+ LDAPMessage* message; /* message returned by ldap_ */
+ LDAPResult* result; /* final result */
+ struct timeval tv;
+ QueryType type;
+
+ LDAPRequest(LDAPService* s, LDAPInterface* i)
+ : service(s)
+ , inter(i)
+ , message(NULL)
+ , result(NULL)
+ {
+ type = QUERY_UNKNOWN;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ }
+
+ virtual ~LDAPRequest()
+ {
+ delete result;
+ if (message != NULL)
+ ldap_msgfree(message);
+ }
+
+ virtual int run() = 0;
+};
+
+class LDAPBind : public LDAPRequest
+{
+ std::string who, pass;
+
+ public:
+ LDAPBind(LDAPService* s, LDAPInterface* i, const std::string& w, const std::string& p)
+ : LDAPRequest(s, i)
+ , who(w)
+ , pass(p)
+ {
+ type = QUERY_BIND;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPSearch : public LDAPRequest
+{
+ std::string base;
+ int searchscope;
+ std::string filter;
+
+ public:
+ LDAPSearch(LDAPService* s, LDAPInterface* i, const std::string& b, int se, const std::string& f)
+ : LDAPRequest(s, i)
+ , base(b)
+ , searchscope(se)
+ , filter(f)
+ {
+ type = QUERY_SEARCH;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPAdd : public LDAPRequest
+{
+ std::string dn;
+ LDAPMods attributes;
+
+ public:
+ LDAPAdd(LDAPService* s, LDAPInterface* i, const std::string& d, const LDAPMods& attr)
+ : LDAPRequest(s, i)
+ , dn(d)
+ , attributes(attr)
+ {
+ type = QUERY_ADD;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPDel : public LDAPRequest
+{
+ std::string dn;
+
+ public:
+ LDAPDel(LDAPService* s, LDAPInterface* i, const std::string& d)
+ : LDAPRequest(s, i)
+ , dn(d)
+ {
+ type = QUERY_DELETE;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPModify : public LDAPRequest
+{
+ std::string base;
+ LDAPMods attributes;
+
+ public:
+ LDAPModify(LDAPService* s, LDAPInterface* i, const std::string& b, const LDAPMods& attr)
+ : LDAPRequest(s, i)
+ , base(b)
+ , attributes(attr)
+ {
+ type = QUERY_MODIFY;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPCompare : public LDAPRequest
+{
+ std::string dn, attr, val;
+
+ public:
+ LDAPCompare(LDAPService* s, LDAPInterface* i, const std::string& d, const std::string& a, const std::string& v)
+ : LDAPRequest(s, i)
+ , dn(d)
+ , attr(a)
+ , val(v)
+ {
+ type = QUERY_COMPARE;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPService : public LDAPProvider, public SocketThread
+{
+ LDAP* con;
+ reference<ConfigTag> config;
+ time_t last_connect;
+ int searchscope;
+ time_t timeout;
+
+ public:
+ static LDAPMod** BuildMods(const LDAPMods& attributes)
+ {
+ LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
+ memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
+ for (unsigned int x = 0; x < attributes.size(); ++x)
+ {
+ const LDAPModification& l = attributes[x];
+ LDAPMod* mod = new LDAPMod;
+ mods[x] = mod;
+
+ if (l.op == LDAPModification::LDAP_ADD)
+ mod->mod_op = LDAP_MOD_ADD;
+ else if (l.op == LDAPModification::LDAP_DEL)
+ mod->mod_op = LDAP_MOD_DELETE;
+ else if (l.op == LDAPModification::LDAP_REPLACE)
+ mod->mod_op = LDAP_MOD_REPLACE;
+ else if (l.op != 0)
+ {
+ FreeMods(mods);
+ throw LDAPException("Unknown LDAP operation");
+ }
+ mod->mod_type = strdup(l.name.c_str());
+ mod->mod_values = new char*[l.values.size() + 1];
+ memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
+ for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
+ if (!l.values[j].empty())
+ mod->mod_values[c++] = strdup(l.values[j].c_str());
+ }
+ return mods;
+ }
+
+ static void FreeMods(LDAPMod** mods)
+ {
+ for (unsigned int i = 0; mods[i] != NULL; ++i)
+ {
+ LDAPMod* mod = mods[i];
+ if (mod->mod_type != NULL)
+ free(mod->mod_type);
+ if (mod->mod_values != NULL)
+ {
+ for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
+ free(mod->mod_values[j]);
+ delete[] mod->mod_values;
+ }
+ }
+ delete[] mods;
+ }
+
+ private:
+ void Reconnect()
+ {
+ // Only try one connect a minute. It is an expensive blocking operation
+ if (last_connect > ServerInstance->Time() - 60)
+ throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
+ last_connect = ServerInstance->Time();
+
+ ldap_unbind_ext(this->con, NULL, NULL);
+ Connect();
+ }
+
+ void QueueRequest(LDAPRequest* r)
+ {
+ this->LockQueue();
+ this->queries.push_back(r);
+ this->UnlockQueueWakeup();
+ }
+
+ public:
+ typedef std::vector<LDAPRequest*> query_queue;
+ query_queue queries, results;
+ Mutex process_mutex; /* held when processing requests not in either queue */
+
+ LDAPService(Module* c, ConfigTag* tag)
+ : LDAPProvider(c, "LDAP/" + tag->getString("id"))
+ , con(NULL), config(tag), last_connect(0)
+ {
+ std::string scope = config->getString("searchscope");
+ if (stdalgo::string::equalsci(scope, "base"))
+ searchscope = LDAP_SCOPE_BASE;
+ else if (stdalgo::string::equalsci(scope, "onelevel"))
+ searchscope = LDAP_SCOPE_ONELEVEL;
+ else
+ searchscope = LDAP_SCOPE_SUBTREE;
+ timeout = config->getDuration("timeout", 5);
+
+ Connect();
+ }
+
+ ~LDAPService()
+ {
+ this->LockQueue();
+
+ for (unsigned int i = 0; i < this->queries.size(); ++i)
+ {
+ LDAPRequest* req = this->queries[i];
+
+ /* queries have no results yet */
+ req->result = new LDAPResult();
+ req->result->type = req->type;
+ req->result->error = "LDAP Interface is going away";
+ req->inter->OnError(*req->result);
+
+ delete req;
+ }
+ this->queries.clear();
+
+ for (unsigned int i = 0; i < this->results.size(); ++i)
+ {
+ LDAPRequest* req = this->results[i];
+
+ /* even though this may have already finished successfully we return that it didn't */
+ req->result->error = "LDAP Interface is going away";
+ req->inter->OnError(*req->result);
+
+ delete req;
+ }
+ this->results.clear();
+
+ this->UnlockQueue();
+
+ ldap_unbind_ext(this->con, NULL, NULL);
+ }
+
+ void Connect()
+ {
+ std::string server = config->getString("server");
+ int i = ldap_initialize(&this->con, server.c_str());
+ if (i != LDAP_SUCCESS)
+ throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
+
+ const int version = LDAP_VERSION3;
+ i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (i != LDAP_OPT_SUCCESS)
+ {
+ ldap_unbind_ext(this->con, NULL, NULL);
+ this->con = NULL;
+ throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
+ }
+
+ const struct timeval tv = { 0, 0 };
+ i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+ if (i != LDAP_OPT_SUCCESS)
+ {
+ ldap_unbind_ext(this->con, NULL, NULL);
+ this->con = NULL;
+ throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i));
+ }
+ }
+
+ void BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
+ {
+ std::string binddn = config->getString("binddn");
+ std::string bindauth = config->getString("bindauth");
+ this->Bind(i, binddn, bindauth);
+ }
+
+ void Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
+ {
+ LDAPBind* b = new LDAPBind(this, i, who, pass);
+ QueueRequest(b);
+ }
+
+ void Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
+ {
+ if (i == NULL)
+ throw LDAPException("No interface");
+
+ LDAPSearch* s = new LDAPSearch(this, i, base, searchscope, filter);
+ QueueRequest(s);
+ }
+
+ void Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
+ {
+ LDAPAdd* add = new LDAPAdd(this, i, dn, attributes);
+ QueueRequest(add);
+ }
+
+ void Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
+ {
+ LDAPDel* del = new LDAPDel(this, i, dn);
+ QueueRequest(del);
+ }
+
+ void Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
+ {
+ LDAPModify* mod = new LDAPModify(this, i, base, attributes);
+ QueueRequest(mod);
+ }
+
+ void Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
+ {
+ LDAPCompare* comp = new LDAPCompare(this, i, dn, attr, val);
+ QueueRequest(comp);
+ }
+
+ private:
+ void BuildReply(int res, LDAPRequest* req)
+ {
+ LDAPResult* ldap_result = req->result = new LDAPResult();
+ req->result->type = req->type;
+
+ if (res != LDAP_SUCCESS)
+ {
+ ldap_result->error = ldap_err2string(res);
+ return;
+ }
+
+ if (req->message == NULL)
+ {
+ return;
+ }
+
+ /* a search result */
+
+ for (LDAPMessage* cur = ldap_first_message(this->con, req->message); cur; cur = ldap_next_message(this->con, cur))
+ {
+ LDAPAttributes attributes;
+
+ char* dn = ldap_get_dn(this->con, cur);
+ if (dn != NULL)
+ {
+ attributes["dn"].push_back(dn);
+ ldap_memfree(dn);
+ dn = NULL;
+ }
+
+ BerElement* ber = NULL;
+
+ for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
+ {
+ berval** vals = ldap_get_values_len(this->con, cur, attr);
+ int count = ldap_count_values_len(vals);
+
+ std::vector<std::string> attrs;
+ for (int j = 0; j < count; ++j)
+ attrs.push_back(vals[j]->bv_val);
+ attributes[attr] = attrs;
+
+ ldap_value_free_len(vals);
+ ldap_memfree(attr);
+ }
+ if (ber != NULL)
+ ber_free(ber, 0);
+
+ ldap_result->messages.push_back(attributes);
+ }
+ }
+
+ void SendRequests()
+ {
+ process_mutex.Lock();
+
+ query_queue q;
+ this->LockQueue();
+ queries.swap(q);
+ this->UnlockQueue();
+
+ if (q.empty())
+ {
+ process_mutex.Unlock();
+ return;
+ }
+
+ for (unsigned int i = 0; i < q.size(); ++i)
+ {
+ LDAPRequest* req = q[i];
+ int ret = req->run();
+
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ /* try again */
+ try
+ {
+ Reconnect();
+ }
+ catch (const LDAPException &)
+ {
+ }
+
+ ret = req->run();
+ }
+
+ BuildReply(ret, req);
+
+ this->LockQueue();
+ this->results.push_back(req);
+ this->UnlockQueue();
+ }
+
+ this->NotifyParent();
+
+ process_mutex.Unlock();
+ }
+
+ public:
+ void Run() CXX11_OVERRIDE
+ {
+ while (!this->GetExitFlag())
+ {
+ this->LockQueue();
+ if (this->queries.empty())
+ this->WaitForQueue();
+ this->UnlockQueue();
+
+ SendRequests();
+ }
+ }
+
+ void OnNotify() CXX11_OVERRIDE
+ {
+ query_queue r;
+
+ this->LockQueue();
+ this->results.swap(r);
+ this->UnlockQueue();
+
+ for (unsigned int i = 0; i < r.size(); ++i)
+ {
+ LDAPRequest* req = r[i];
+ LDAPInterface* li = req->inter;
+ LDAPResult* res = req->result;
+
+ if (!res->error.empty())
+ li->OnError(*res);
+ else
+ li->OnResult(*res);
+
+ delete req;
+ }
+ }
+
+ LDAP* GetConnection()
+ {
+ return con;
+ }
+};
+
+class ModuleLDAP : public Module
+{
+ typedef insp::flat_map<std::string, LDAPService*> ServiceMap;
+ ServiceMap LDAPServices;
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ServiceMap conns;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("database");
+ for (ConfigIter i = tags.first; i != tags.second; i++)
+ {
+ const reference<ConfigTag>& tag = i->second;
+
+ if (!stdalgo::string::equalsci(tag->getString("module"), "ldap"))
+ continue;
+
+ std::string id = tag->getString("id");
+
+ ServiceMap::iterator curr = LDAPServices.find(id);
+ if (curr == LDAPServices.end())
+ {
+ LDAPService* conn = new LDAPService(this, tag);
+ conns[id] = conn;
+
+ ServerInstance->Modules->AddService(*conn);
+ ServerInstance->Threads.Start(conn);
+ }
+ else
+ {
+ conns.insert(*curr);
+ LDAPServices.erase(curr);
+ }
+ }
+
+ for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ {
+ LDAPService* conn = i->second;
+ ServerInstance->Modules->DelService(*conn);
+ conn->join();
+ conn->OnNotify();
+ delete conn;
+ }
+
+ LDAPServices.swap(conns);
+ }
+
+ void OnUnloadModule(Module* m) CXX11_OVERRIDE
+ {
+ for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
+ {
+ LDAPService* s = it->second;
+
+ s->process_mutex.Lock();
+ s->LockQueue();
+
+ for (unsigned int i = s->queries.size(); i > 0; --i)
+ {
+ LDAPRequest* req = s->queries[i - 1];
+ LDAPInterface* li = req->inter;
+
+ if (li->creator == m)
+ {
+ s->queries.erase(s->queries.begin() + i - 1);
+ delete req;
+ }
+ }
+
+ for (unsigned int i = s->results.size(); i > 0; --i)
+ {
+ LDAPRequest* req = s->results[i - 1];
+ LDAPInterface* li = req->inter;
+
+ if (li->creator == m)
+ {
+ s->results.erase(s->results.begin() + i - 1);
+ delete req;
+ }
+ }
+
+ s->UnlockQueue();
+ s->process_mutex.Unlock();
+ }
+ }
+
+ ~ModuleLDAP()
+ {
+ for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ {
+ LDAPService* conn = i->second;
+ conn->join();
+ conn->OnNotify();
+ delete conn;
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("LDAP support", VF_VENDOR);
+ }
+};
+
+int LDAPBind::run()
+{
+ berval cred;
+ cred.bv_val = strdup(pass.c_str());
+ cred.bv_len = pass.length();
+
+ int i = ldap_sasl_bind_s(service->GetConnection(), who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
+
+ free(cred.bv_val);
+
+ return i;
+}
+
+int LDAPSearch::run()
+{
+ return ldap_search_ext_s(service->GetConnection(), base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, &tv, 0, &message);
+}
+
+int LDAPAdd::run()
+{
+ LDAPMod** mods = LDAPService::BuildMods(attributes);
+ int i = ldap_add_ext_s(service->GetConnection(), dn.c_str(), mods, NULL, NULL);
+ LDAPService::FreeMods(mods);
+ return i;
+}
+
+int LDAPDel::run()
+{
+ return ldap_delete_ext_s(service->GetConnection(), dn.c_str(), NULL, NULL);
+}
+
+int LDAPModify::run()
+{
+ LDAPMod** mods = LDAPService::BuildMods(attributes);
+ int i = ldap_modify_ext_s(service->GetConnection(), base.c_str(), mods, NULL, NULL);
+ LDAPService::FreeMods(mods);
+ return i;
+}
+
+int LDAPCompare::run()
+{
+ berval cred;
+ cred.bv_val = strdup(val.c_str());
+ cred.bv_len = val.length();
+
+ int ret = ldap_compare_ext_s(service->GetConnection(), dn.c_str(), attr.c_str(), &cred, NULL, NULL);
+
+ free(cred.bv_val);
+
+ return ret;
+
+}
+
+MODULE_INIT(ModuleLDAP)
diff --git a/src/modules/extra/m_ldapauth.cpp b/src/modules/extra/m_ldapauth.cpp
deleted file mode 100644
index 405bab082..000000000
--- a/src/modules/extra/m_ldapauth.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
- * Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "libldap.lib")
-# pragma comment(lib, "liblber.lib")
-#endif
-
-/* $ModDesc: Allow/Deny connections based upon answer from LDAP server */
-/* $LinkerFlags: -lldap */
-
-struct RAIILDAPString
-{
- char *str;
-
- RAIILDAPString(char *Str)
- : str(Str)
- {
- }
-
- ~RAIILDAPString()
- {
- ldap_memfree(str);
- }
-
- operator char*()
- {
- return str;
- }
-
- operator std::string()
- {
- return str;
- }
-};
-
-struct RAIILDAPMessage
-{
- RAIILDAPMessage()
- {
- }
-
- ~RAIILDAPMessage()
- {
- dealloc();
- }
-
- void dealloc()
- {
- ldap_msgfree(msg);
- }
-
- operator LDAPMessage*()
- {
- return msg;
- }
-
- LDAPMessage **operator &()
- {
- return &msg;
- }
-
- LDAPMessage *msg;
-};
-
-class ModuleLDAPAuth : public Module
-{
- LocalIntExt ldapAuthed;
- LocalStringExt ldapVhost;
- std::string base;
- std::string attribute;
- std::string ldapserver;
- std::string allowpattern;
- std::string killreason;
- std::string username;
- std::string password;
- std::string vhost;
- std::vector<std::string> whitelistedcidrs;
- std::vector<std::pair<std::string, std::string> > requiredattributes;
- int searchscope;
- bool verbose;
- bool useusername;
- LDAP *conn;
-
-public:
- ModuleLDAPAuth()
- : ldapAuthed("ldapauth", this)
- , ldapVhost("ldapauth_vhost", this)
- {
- conn = NULL;
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(ldapAuthed);
- ServerInstance->Modules->AddService(ldapVhost);
- Implementation eventlist[] = { I_OnCheckReady, I_OnRehash,I_OnUserRegister, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- ~ModuleLDAPAuth()
- {
- if (conn)
- ldap_unbind_ext(conn, NULL, NULL);
- }
-
- void OnRehash(User* user)
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
- whitelistedcidrs.clear();
- requiredattributes.clear();
-
- base = tag->getString("baserdn");
- attribute = tag->getString("attribute");
- ldapserver = tag->getString("server");
- allowpattern = tag->getString("allowpattern");
- killreason = tag->getString("killreason");
- std::string scope = tag->getString("searchscope");
- username = tag->getString("binddn");
- password = tag->getString("bindauth");
- vhost = tag->getString("host");
- verbose = tag->getBool("verbose"); /* Set to true if failed connects should be reported to operators */
- useusername = tag->getBool("userfield");
-
- ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
-
- for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
- {
- std::string cidr = i->second->getString("cidr");
- if (!cidr.empty()) {
- whitelistedcidrs.push_back(cidr);
- }
- }
-
- ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
-
- for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
- {
- const std::string attr = i->second->getString("attribute");
- const std::string val = i->second->getString("value");
-
- if (!attr.empty() && !val.empty())
- requiredattributes.push_back(make_pair(attr, val));
- }
-
- if (scope == "base")
- searchscope = LDAP_SCOPE_BASE;
- else if (scope == "onelevel")
- searchscope = LDAP_SCOPE_ONELEVEL;
- else searchscope = LDAP_SCOPE_SUBTREE;
-
- Connect();
- }
-
- bool Connect()
- {
- if (conn != NULL)
- ldap_unbind_ext(conn, NULL, NULL);
- int res, v = LDAP_VERSION3;
- res = ldap_initialize(&conn, ldapserver.c_str());
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "LDAP connection failed: %s", ldap_err2string(res));
- conn = NULL;
- return false;
- }
-
- res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "LDAP set protocol to v3 failed: %s", ldap_err2string(res));
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- return true;
- }
-
- std::string SafeReplace(const std::string &text, std::map<std::string,
- std::string> &replacements)
- {
- std::string result;
- result.reserve(MAXBUF);
-
- for (unsigned int i = 0; i < text.length(); ++i) {
- char c = text[i];
- if (c == '$') {
- // find the first nonalpha
- i++;
- unsigned int start = i;
-
- while (i < text.length() - 1 && isalpha(text[i + 1]))
- ++i;
-
- std::string key = text.substr(start, (i - start) + 1);
- result.append(replacements[key]);
- } else {
- result.push_back(c);
- }
- }
-
- return result;
- }
-
- virtual void OnUserConnect(LocalUser *user)
- {
- std::string* cc = ldapVhost.get(user);
- if (cc)
- {
- user->ChangeDisplayedHost(cc->c_str());
- ldapVhost.unset(user);
- }
- }
-
- ModResult OnUserRegister(LocalUser* user)
- {
- if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
- {
- ldapAuthed.set(user,1);
- return MOD_RES_PASSTHRU;
- }
-
- for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
- {
- if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
- {
- ldapAuthed.set(user,1);
- return MOD_RES_PASSTHRU;
- }
- }
-
- if (!CheckCredentials(user))
- {
- ServerInstance->Users->QuitUser(user, killreason);
- return MOD_RES_DENY;
- }
- return MOD_RES_PASSTHRU;
- }
-
- bool CheckCredentials(LocalUser* user)
- {
- if (conn == NULL)
- if (!Connect())
- return false;
-
- if (user->password.empty())
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
- return false;
- }
-
- int res;
- // bind anonymously if no bind DN and authentication are given in the config
- struct berval cred;
- cred.bv_val = const_cast<char*>(password.c_str());
- cred.bv_len = password.length();
-
- if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (res == LDAP_SERVER_DOWN)
- {
- // Attempt to reconnect if the connection dropped
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
- Connect();
- res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
- }
-
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP bind failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- }
-
- RAIILDAPMessage msg;
- std::string what;
- std::string::size_type pos = user->password.find(':');
- // If a username is provided in PASS, use it, othewrise user their nick or ident
- if (pos != std::string::npos)
- {
- what = (attribute + "=" + user->password.substr(0, pos));
-
- // Trim the user: prefix, leaving just 'pass' for later password check
- user->password = user->password.substr(pos + 1);
- }
- else
- {
- what = (attribute + "=" + (useusername ? user->ident : user->nick));
- }
- if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
- if (ldap_count_entries(conn, msg) > 1)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned more than one result: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
-
- LDAPMessage *entry;
- if ((entry = ldap_first_entry(conn, msg)) == NULL)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned no results: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
- cred.bv_val = (char*)user->password.data();
- cred.bv_len = user->password.length();
- RAIILDAPString DN(ldap_get_dn(conn, entry));
- if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
-
- if (!requiredattributes.empty())
- {
- bool authed = false;
-
- for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
- {
- const std::string &attr = it->first;
- const std::string &val = it->second;
-
- struct berval attr_value;
- attr_value.bv_val = const_cast<char*>(val.c_str());
- attr_value.bv_len = val.length();
-
- ServerInstance->Logs->Log("m_ldapauth", DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
-
- authed = (ldap_compare_ext_s(conn, DN, attr.c_str(), &attr_value, NULL, NULL) == LDAP_COMPARE_TRUE);
-
- if (authed)
- break;
- }
-
- if (!authed)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Lacks required LDAP attributes)", user->GetFullRealHost().c_str());
- return false;
- }
- }
-
- if (!vhost.empty())
- {
- irc::commasepstream stream(DN);
-
- // mashed map of key:value parts of the DN
- std::map<std::string, std::string> dnParts;
-
- std::string dnPart;
- while (stream.GetToken(dnPart))
- {
- pos = dnPart.find('=');
- if (pos == std::string::npos) // malformed
- continue;
-
- std::string key = dnPart.substr(0, pos);
- std::string value = dnPart.substr(pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
- dnParts[key] = value;
- }
-
- // change host according to config key
- ldapVhost.set(user, SafeReplace(vhost, dnParts));
- }
-
- ldapAuthed.set(user,1);
- return true;
- }
-
- ModResult OnCheckReady(LocalUser* user)
- {
- return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
- }
-
- Version GetVersion()
- {
- return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
- }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/extra/m_ldapoper.cpp b/src/modules/extra/m_ldapoper.cpp
deleted file mode 100644
index 1f46361d4..000000000
--- a/src/modules/extra/m_ldapoper.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "libldap.lib")
-# pragma comment(lib, "liblber.lib")
-#endif
-
-/* $ModDesc: Adds the ability to authenticate opers via LDAP */
-/* $LinkerFlags: -lldap */
-
-// Duplicated code, also found in cmd_oper and m_sqloper
-static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
-
-struct RAIILDAPString
-{
- char *str;
-
- RAIILDAPString(char *Str)
- : str(Str)
- {
- }
-
- ~RAIILDAPString()
- {
- ldap_memfree(str);
- }
-
- operator char*()
- {
- return str;
- }
-
- operator std::string()
- {
- return str;
- }
-};
-
-class ModuleLDAPAuth : public Module
-{
- std::string base;
- std::string ldapserver;
- std::string username;
- std::string password;
- std::string attribute;
- int searchscope;
- LDAP *conn;
-
- bool HandleOper(LocalUser* user, const std::string& opername, const std::string& inputpass)
- {
- OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
- if (it == ServerInstance->Config->oper_blocks.end())
- return false;
-
- ConfigTag* tag = it->second->oper_block;
- if (!tag)
- return false;
-
- std::string acceptedhosts = tag->getString("host");
- std::string hostname = user->ident + "@" + user->host;
- if (!OneOfMatches(hostname.c_str(), user->GetIPString(), acceptedhosts))
- return false;
-
- if (!LookupOper(opername, inputpass))
- return false;
-
- user->Oper(it->second);
- return true;
- }
-
-public:
- ModuleLDAPAuth()
- : conn(NULL)
- {
- }
-
- void init()
- {
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- virtual ~ModuleLDAPAuth()
- {
- if (conn)
- ldap_unbind_ext(conn, NULL, NULL);
- }
-
- virtual void OnRehash(User* user)
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
-
- base = tag->getString("baserdn");
- ldapserver = tag->getString("server");
- std::string scope = tag->getString("searchscope");
- username = tag->getString("binddn");
- password = tag->getString("bindauth");
- attribute = tag->getString("attribute");
-
- if (scope == "base")
- searchscope = LDAP_SCOPE_BASE;
- else if (scope == "onelevel")
- searchscope = LDAP_SCOPE_ONELEVEL;
- else searchscope = LDAP_SCOPE_SUBTREE;
-
- Connect();
- }
-
- bool Connect()
- {
- if (conn != NULL)
- ldap_unbind_ext(conn, NULL, NULL);
- int res, v = LDAP_VERSION3;
- res = ldap_initialize(&conn, ldapserver.c_str());
- if (res != LDAP_SUCCESS)
- {
- conn = NULL;
- return false;
- }
-
- res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
- if (res != LDAP_SUCCESS)
- {
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- return true;
- }
-
- ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line)
- {
- if (validated && command == "OPER" && parameters.size() >= 2)
- {
- if (HandleOper(user, parameters[0], parameters[1]))
- return MOD_RES_DENY;
- }
- return MOD_RES_PASSTHRU;
- }
-
- bool LookupOper(const std::string& opername, const std::string& opassword)
- {
- if (conn == NULL)
- if (!Connect())
- return false;
-
- int res;
- char* authpass = strdup(password.c_str());
- // bind anonymously if no bind DN and authentication are given in the config
- struct berval cred;
- cred.bv_val = authpass;
- cred.bv_len = password.length();
-
- if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (res == LDAP_SERVER_DOWN)
- {
- // Attempt to reconnect if the connection dropped
- ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
- Connect();
- res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
- }
-
- if (res != LDAP_SUCCESS)
- {
- free(authpass);
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- }
- free(authpass);
-
- LDAPMessage *msg, *entry;
- std::string what = attribute + "=" + opername;
- if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
- {
- return false;
- }
- if (ldap_count_entries(conn, msg) > 1)
- {
- ldap_msgfree(msg);
- return false;
- }
- if ((entry = ldap_first_entry(conn, msg)) == NULL)
- {
- ldap_msgfree(msg);
- return false;
- }
- authpass = strdup(opassword.c_str());
- cred.bv_val = authpass;
- cred.bv_len = opassword.length();
- RAIILDAPString DN(ldap_get_dn(conn, entry));
- if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
- {
- free(authpass);
- ldap_msgfree(msg);
- return true;
- }
- else
- {
- free(authpass);
- ldap_msgfree(msg);
- return false;
- }
- }
-
- virtual Version GetVersion()
- {
- return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
- }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/extra/m_mssql.cpp b/src/modules/extra/m_mssql.cpp
deleted file mode 100644
index 598f9aac9..000000000
--- a/src/modules/extra/m_mssql.cpp
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008-2009 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008-2009 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include <tds.h>
-#include <tdsconvert.h>
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include "m_sqlv2.h"
-
-/* $ModDesc: MsSQL provider */
-/* $CompileFlags: exec("grep VERSION_NO /usr/include/tdsver.h 2>/dev/null | perl -e 'print "-D_TDSVER=".((<> =~ /freetds v(\d+\.\d+)/i) ? $1*100 : 0);'") */
-/* $LinkerFlags: -ltds */
-/* $ModDep: m_sqlv2.h */
-
-class SQLConn;
-class MsSQLResult;
-class ModuleMsSQL;
-
-typedef std::map<std::string, SQLConn*> ConnMap;
-typedef std::deque<MsSQLResult*> ResultQueue;
-
-unsigned long count(const char * const str, char a)
-{
- unsigned long n = 0;
- for (const char *p = str; *p; ++p)
- {
- if (*p == '?')
- ++n;
- }
- return n;
-}
-
-ConnMap connections;
-Mutex* ResultsMutex;
-Mutex* LoggingMutex;
-
-class QueryThread : public SocketThread
-{
- private:
- ModuleMsSQL* const Parent;
- public:
- QueryThread(ModuleMsSQL* mod) : Parent(mod) { }
- ~QueryThread() { }
- virtual void Run();
- virtual void OnNotify();
-};
-
-class MsSQLResult : public SQLresult
-{
- private:
- int currentrow;
- int rows;
- int cols;
-
- std::vector<std::string> colnames;
- std::vector<SQLfieldList> fieldlists;
- SQLfieldList emptyfieldlist;
-
- SQLfieldList* fieldlist;
- SQLfieldMap* fieldmap;
-
- public:
- MsSQLResult(Module* self, Module* to, unsigned int rid)
- : SQLresult(self, to, rid), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL)
- {
- }
-
- ~MsSQLResult()
- {
- }
-
- void AddRow(int colsnum, char **dat, char **colname)
- {
- colnames.clear();
- cols = colsnum;
- for (int i = 0; i < colsnum; i++)
- {
- fieldlists.resize(fieldlists.size()+1);
- colnames.push_back(colname[i]);
- SQLfield sf(dat[i] ? dat[i] : "", dat[i] ? false : true);
- fieldlists[rows].push_back(sf);
- }
- rows++;
- }
-
- void UpdateAffectedCount()
- {
- rows++;
- }
-
- virtual int Rows()
- {
- return rows;
- }
-
- virtual int Cols()
- {
- return cols;
- }
-
- virtual std::string ColName(int column)
- {
- if (column < (int)colnames.size())
- {
- return colnames[column];
- }
- else
- {
- throw SQLbadColName();
- }
- return "";
- }
-
- virtual int ColNum(const std::string &column)
- {
- for (unsigned int i = 0; i < colnames.size(); i++)
- {
- if (column == colnames[i])
- return i;
- }
- throw SQLbadColName();
- return 0;
- }
-
- virtual SQLfield GetValue(int row, int column)
- {
- if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
- {
- return fieldlists[row][column];
- }
-
- throw SQLbadColName();
-
- /* XXX: We never actually get here because of the throw */
- return SQLfield("",true);
- }
-
- virtual SQLfieldList& GetRow()
- {
- if (currentrow < rows)
- return fieldlists[currentrow];
- else
- return emptyfieldlist;
- }
-
- virtual SQLfieldMap& GetRowMap()
- {
- /* In an effort to reduce overhead we don't actually allocate the map
- * until the first time it's needed...so...
- */
- if(fieldmap)
- {
- fieldmap->clear();
- }
- else
- {
- fieldmap = new SQLfieldMap;
- }
-
- if (currentrow < rows)
- {
- for (int i = 0; i < Cols(); i++)
- {
- fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
- }
- currentrow++;
- }
-
- return *fieldmap;
- }
-
- virtual SQLfieldList* GetRowPtr()
- {
- fieldlist = new SQLfieldList();
-
- if (currentrow < rows)
- {
- for (int i = 0; i < Rows(); i++)
- {
- fieldlist->push_back(fieldlists[currentrow][i]);
- }
- currentrow++;
- }
- return fieldlist;
- }
-
- virtual SQLfieldMap* GetRowMapPtr()
- {
- fieldmap = new SQLfieldMap();
-
- if (currentrow < rows)
- {
- for (int i = 0; i < Cols(); i++)
- {
- fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
- }
- currentrow++;
- }
-
- return fieldmap;
- }
-
- virtual void Free(SQLfieldMap* fm)
- {
- delete fm;
- }
-
- virtual void Free(SQLfieldList* fl)
- {
- delete fl;
- }
-};
-
-class SQLConn : public classbase
-{
- private:
- ResultQueue results;
- Module* mod;
- SQLhost host;
- TDSLOGIN* login;
- TDSSOCKET* sock;
- TDSCONTEXT* context;
-
- public:
- QueryQueue queue;
-
- SQLConn(Module* m, const SQLhost& hi)
- : mod(m), host(hi), login(NULL), sock(NULL), context(NULL)
- {
- if (OpenDB())
- {
- std::string query("USE " + host.name);
- if (tds_submit_query(sock, query.c_str()) == TDS_SUCCEED)
- {
- if (tds_process_simple_query(sock) != TDS_SUCCEED)
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
- LoggingMutex->Unlock();
- CloseDB();
- }
- }
- else
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
- LoggingMutex->Unlock();
- CloseDB();
- }
- }
- else
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
- LoggingMutex->Unlock();
- CloseDB();
- }
- }
-
- ~SQLConn()
- {
- CloseDB();
- }
-
- SQLerror Query(SQLrequest* req)
- {
- if (!sock)
- return SQLerror(SQL_BAD_CONN, "Socket was NULL, check if SQL server is running.");
-
- /* Pointer to the buffer we screw around with substitution in */
- char* query;
-
- /* Pointer to the current end of query, where we append new stuff */
- char* queryend;
-
- /* Total length of the unescaped parameters */
- unsigned long maxparamlen, paramcount;
-
- /* The length of the longest parameter */
- maxparamlen = 0;
-
- for(ParamL::iterator i = req->query.p.begin(); i != req->query.p.end(); i++)
- {
- if (i->size() > maxparamlen)
- maxparamlen = i->size();
- }
-
- /* How many params are there in the query? */
- paramcount = count(req->query.q.c_str(), '?');
-
- /* This stores copy of params to be inserted with using numbered params 1;3B*/
- ParamL paramscopy(req->query.p);
-
- /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
- * sizeofquery + (maxtotalparamlength*2) + 1
- *
- * The +1 is for null-terminating the string
- */
-
- query = new char[req->query.q.length() + (maxparamlen*paramcount*2) + 1];
- queryend = query;
-
- for(unsigned long i = 0; i < req->query.q.length(); i++)
- {
- if(req->query.q[i] == '?')
- {
- /* We found a place to substitute..what fun.
- * use mssql calls to escape and write the
- * escaped string onto the end of our query buffer,
- * then we "just" need to make sure queryend is
- * pointing at the right place.
- */
-
- /* Is it numbered parameter?
- */
-
- bool numbered;
- numbered = false;
-
- /* Numbered parameter number :|
- */
- unsigned int paramnum;
- paramnum = 0;
-
- /* Let's check if it's a numbered param. And also calculate it's number.
- */
-
- while ((i < req->query.q.length() - 1) && (req->query.q[i+1] >= '0') && (req->query.q[i+1] <= '9'))
- {
- numbered = true;
- ++i;
- paramnum = paramnum * 10 + req->query.q[i] - '0';
- }
-
- if (paramnum > paramscopy.size() - 1)
- {
- /* index is out of range!
- */
- numbered = false;
- }
-
- if (numbered)
- {
- /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
- */
- char* escaped = new char[(paramscopy[paramnum].length() * 2) + 1];
- char* escend = escaped;
- for (std::string::iterator p = paramscopy[paramnum].begin(); p < paramscopy[paramnum].end(); p++)
- {
- if (*p == '\'')
- {
- *escend = *p;
- escend++;
- *escend = *p;
- }
- *escend = *p;
- escend++;
- }
- *escend = 0;
-
- for (char* n = escaped; *n; n++)
- {
- *queryend = *n;
- queryend++;
- }
- delete[] escaped;
- }
- else if (req->query.p.size())
- {
- /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
- */
- char* escaped = new char[(req->query.p.front().length() * 2) + 1];
- char* escend = escaped;
- for (std::string::iterator p = req->query.p.front().begin(); p < req->query.p.front().end(); p++)
- {
- if (*p == '\'')
- {
- *escend = *p;
- escend++;
- *escend = *p;
- }
- *escend = *p;
- escend++;
- }
- *escend = 0;
-
- for (char* n = escaped; *n; n++)
- {
- *queryend = *n;
- queryend++;
- }
- delete[] escaped;
- req->query.p.pop_front();
- }
- else
- break;
- }
- else
- {
- *queryend = req->query.q[i];
- queryend++;
- }
- }
- *queryend = 0;
- req->query.q = query;
-
- MsSQLResult* res = new MsSQLResult((Module*)mod, req->source, req->id);
- res->dbid = host.id;
- res->query = req->query.q;
-
- char* msquery = strdup(req->query.q.data());
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEBUG,"doing Query: %s",msquery);
- LoggingMutex->Unlock();
- if (tds_submit_query(sock, msquery) != TDS_SUCCEED)
- {
- std::string error("failed to execute: "+std::string(req->query.q.data()));
- delete[] query;
- delete res;
- free(msquery);
- return SQLerror(SQL_QSEND_FAIL, error);
- }
- delete[] query;
- free(msquery);
-
- int tds_res;
- while (tds_process_tokens(sock, &tds_res, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCEED)
- {
- //ServerInstance->Logs->Log("m_mssql",DEBUG,"<******> result type: %d", tds_res);
- //ServerInstance->Logs->Log("m_mssql",DEBUG,"AFFECTED ROWS: %d", sock->rows_affected);
- switch (tds_res)
- {
- case TDS_ROWFMT_RESULT:
- break;
-
- case TDS_DONE_RESULT:
- if (sock->rows_affected > -1)
- {
- for (int c = 0; c < sock->rows_affected; c++) res->UpdateAffectedCount();
- continue;
- }
- break;
-
- case TDS_ROW_RESULT:
- while (tds_process_tokens(sock, &tds_res, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCEED)
- {
- if (tds_res != TDS_ROW_RESULT)
- break;
-
- if (!sock->current_results)
- continue;
-
- if (sock->res_info->row_count > 0)
- {
- int cols = sock->res_info->num_cols;
- char** name = new char*[MAXBUF];
- char** data = new char*[MAXBUF];
- for (int j=0; j<cols; j++)
- {
- TDSCOLUMN* col = sock->current_results->columns[j];
- name[j] = col->column_name;
-
- int ctype;
- int srclen;
- unsigned char* src;
- CONV_RESULT dres;
- ctype = tds_get_conversion_type(col->column_type, col->column_size);
-#if _TDSVER >= 82
- src = col->column_data;
-#else
- src = &(sock->current_results->current_row[col->column_offset]);
-#endif
- srclen = col->column_cur_size;
- tds_convert(sock->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBCHAR, &dres);
- data[j] = (char*)dres.ib;
- }
- ResultReady(res, cols, data, name);
- }
- }
- break;
-
- default:
- break;
- }
- }
- ResultsMutex->Lock();
- results.push_back(res);
- ResultsMutex->Unlock();
- return SQLerror();
- }
-
- static int HandleMessage(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
- {
- SQLConn* sc = (SQLConn*)pContext->parent;
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql", DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
- LoggingMutex->Unlock();
- return 0;
- }
-
- static int HandleError(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
- {
- SQLConn* sc = (SQLConn*)pContext->parent;
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql", DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
- LoggingMutex->Unlock();
- return 0;
- }
-
- void ResultReady(MsSQLResult *res, int cols, char **data, char **colnames)
- {
- res->AddRow(cols, data, colnames);
- }
-
- void AffectedReady(MsSQLResult *res)
- {
- res->UpdateAffectedCount();
- }
-
- bool OpenDB()
- {
- CloseDB();
-
- TDSCONNECTION* conn = NULL;
-
- login = tds_alloc_login();
- tds_set_app(login, "TSQL");
- tds_set_library(login,"TDS-Library");
- tds_set_host(login, "");
- tds_set_server(login, host.host.c_str());
- tds_set_server_addr(login, host.host.c_str());
- tds_set_user(login, host.user.c_str());
- tds_set_passwd(login, host.pass.c_str());
- tds_set_port(login, host.port);
- tds_set_packet(login, 512);
-
- context = tds_alloc_context(this);
- context->msg_handler = HandleMessage;
- context->err_handler = HandleError;
-
- sock = tds_alloc_socket(context, 512);
- tds_set_parent(sock, NULL);
-
- conn = tds_read_config_info(NULL, login, context->locale);
-
- if (tds_connect(sock, conn) == TDS_SUCCEED)
- {
- tds_free_connection(conn);
- return 1;
- }
- tds_free_connection(conn);
- return 0;
- }
-
- void CloseDB()
- {
- if (sock)
- {
- tds_free_socket(sock);
- sock = NULL;
- }
- if (context)
- {
- tds_free_context(context);
- context = NULL;
- }
- if (login)
- {
- tds_free_login(login);
- login = NULL;
- }
- }
-
- SQLhost GetConfHost()
- {
- return host;
- }
-
- void SendResults()
- {
- while (results.size())
- {
- MsSQLResult* res = results[0];
- ResultsMutex->Lock();
- if (res->dest)
- {
- res->Send();
- }
- else
- {
- /* If the client module is unloaded partway through a query then the provider will set
- * the pointer to NULL. We cannot just cancel the query as the result will still come
- * through at some point...and it could get messy if we play with invalid pointers...
- */
- delete res;
- }
- results.pop_front();
- ResultsMutex->Unlock();
- }
- }
-
- void ClearResults()
- {
- while (results.size())
- {
- MsSQLResult* res = results[0];
- delete res;
- results.pop_front();
- }
- }
-
- void DoLeadingQuery()
- {
- SQLrequest* req = queue.front();
- req->error = Query(req);
- }
-
-};
-
-
-class ModuleMsSQL : public Module
-{
- private:
- unsigned long currid;
- QueryThread* queryDispatcher;
- ServiceProvider sqlserv;
-
- public:
- ModuleMsSQL()
- : currid(0), sqlserv(this, "SQL/mssql", SERVICE_DATA)
- {
- LoggingMutex = new Mutex();
- ResultsMutex = new Mutex();
- queryDispatcher = new QueryThread(this);
- }
-
- void init()
- {
- ReadConf();
-
- ServerInstance->Threads->Start(queryDispatcher);
-
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- ServerInstance->Modules->AddService(sqlserv);
- }
-
- virtual ~ModuleMsSQL()
- {
- queryDispatcher->join();
- delete queryDispatcher;
- ClearQueue();
- ClearAllConnections();
-
- delete LoggingMutex;
- delete ResultsMutex;
- }
-
- void SendQueue()
- {
- for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
- {
- iter->second->SendResults();
- }
- }
-
- void ClearQueue()
- {
- for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
- {
- iter->second->ClearResults();
- }
- }
-
- bool HasHost(const SQLhost &host)
- {
- for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
- {
- if (host == iter->second->GetConfHost())
- return true;
- }
- return false;
- }
-
- bool HostInConf(const SQLhost &h)
- {
- ConfigTagList tags = ServerInstance->Config->ConfTags("database");
- for (ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- SQLhost host;
- host.id = tag->getString("id");
- host.host = tag->getString("hostname");
- host.port = tag->getInt("port", 1433);
- host.name = tag->getString("name");
- host.user = tag->getString("username");
- host.pass = tag->getString("password");
- if (h == host)
- return true;
- }
- return false;
- }
-
- void ReadConf()
- {
- ClearOldConnections();
-
- ConfigTagList tags = ServerInstance->Config->ConfTags("database");
- for (ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- SQLhost host;
-
- host.id = tag->getString("id");
- host.host = tag->getString("hostname");
- host.port = tag->getInt("port", 1433);
- host.name = tag->getString("name");
- host.user = tag->getString("username");
- host.pass = tag->getString("password");
-
- if (HasHost(host))
- continue;
-
- this->AddConn(host);
- }
- }
-
- void AddConn(const SQLhost& hi)
- {
- if (HasHost(hi))
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
- LoggingMutex->Unlock();
- return;
- }
-
- SQLConn* newconn;
-
- newconn = new SQLConn(this, hi);
-
- connections.insert(std::make_pair(hi.id, newconn));
- }
-
- void ClearOldConnections()
- {
- ConnMap::iterator iter,safei;
- for (iter = connections.begin(); iter != connections.end(); iter++)
- {
- if (!HostInConf(iter->second->GetConfHost()))
- {
- delete iter->second;
- safei = iter;
- --iter;
- connections.erase(safei);
- }
- }
- }
-
- void ClearAllConnections()
- {
- for(ConnMap::iterator i = connections.begin(); i != connections.end(); ++i)
- delete i->second;
- connections.clear();
- }
-
- virtual void OnRehash(User* user)
- {
- queryDispatcher->LockQueue();
- ReadConf();
- queryDispatcher->UnlockQueueWakeup();
- }
-
- void OnRequest(Request& request)
- {
- if(strcmp(SQLREQID, request.id) == 0)
- {
- SQLrequest* req = (SQLrequest*)&request;
-
- queryDispatcher->LockQueue();
-
- ConnMap::iterator iter;
-
- if((iter = connections.find(req->dbid)) != connections.end())
- {
- req->id = NewID();
- iter->second->queue.push(new SQLrequest(*req));
- }
- else
- {
- req->error.Id(SQL_BAD_DBID);
- }
- queryDispatcher->UnlockQueueWakeup();
- }
- }
-
- unsigned long NewID()
- {
- if (currid+1 == 0)
- currid++;
-
- return ++currid;
- }
-
- virtual Version GetVersion()
- {
- return Version("MsSQL provider", VF_VENDOR);
- }
-
-};
-
-void QueryThread::OnNotify()
-{
- Parent->SendQueue();
-}
-
-void QueryThread::Run()
-{
- this->LockQueue();
- while (this->GetExitFlag() == false)
- {
- SQLConn* conn = NULL;
- for (ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
- {
- if (i->second->queue.totalsize())
- {
- conn = i->second;
- break;
- }
- }
- if (conn)
- {
- this->UnlockQueue();
- conn->DoLeadingQuery();
- this->NotifyParent();
- this->LockQueue();
- conn->queue.pop();
- }
- else
- {
- this->WaitForQueue();
- }
- }
- this->UnlockQueue();
-}
-
-MODULE_INIT(ModuleMsSQL)
diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp
index 159a0b8b2..41c3a2a65 100644
--- a/src/modules/extra/m_mysql.cpp
+++ b/src/modules/extra/m_mysql.cpp
@@ -19,13 +19,26 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: execute("mysql_config --include" "MYSQL_CXXFLAGS")
+/// $LinkerFlags: execute("mysql_config --libs_r" "MYSQL_LDFLAGS" "-lmysqlclient")
-/* Stop mysql wanting to use long long */
-#define NO_CLIENT_LONG_LONG
+/// $PackageInfo: require_system("centos" "6.0" "6.99") mysql-devel
+/// $PackageInfo: require_system("centos" "7.0") mariadb-devel
+/// $PackageInfo: require_system("darwin") mysql-connector-c
+/// $PackageInfo: require_system("debian") libmysqlclient-dev
+/// $PackageInfo: require_system("ubuntu") libmysqlclient-dev
+
+
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
#include "inspircd.h"
#include <mysql.h>
-#include "sql.h"
+#include "modules/sql.h"
#ifdef _WIN32
# pragma comment(lib, "libmysql.lib")
@@ -33,10 +46,6 @@
/* VERSION 3 API: With nonblocking (threaded) requests */
-/* $ModDesc: SQL Service Provider module for all other m_sql* modules */
-/* $CompileFlags: exec("mysql_config --include") */
-/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */
-
/* THE NONBLOCKING MYSQL API!
*
* MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend
@@ -75,20 +84,20 @@ class DispatcherThread;
struct QQueueItem
{
- SQLQuery* q;
+ SQL::Query* q;
std::string query;
SQLConnection* c;
- QQueueItem(SQLQuery* Q, const std::string& S, SQLConnection* C) : q(Q), query(S), c(C) {}
+ QQueueItem(SQL::Query* Q, const std::string& S, SQLConnection* C) : q(Q), query(S), c(C) {}
};
struct RQueueItem
{
- SQLQuery* q;
+ SQL::Query* q;
MySQLresult* r;
- RQueueItem(SQLQuery* Q, MySQLresult* R) : q(Q), r(R) {}
+ RQueueItem(SQL::Query* Q, MySQLresult* R) : q(Q), r(R) {}
};
-typedef std::map<std::string, SQLConnection*> ConnMap;
+typedef insp::flat_map<std::string, SQLConnection*> ConnMap;
typedef std::deque<QQueueItem> QueryQueue;
typedef std::deque<RQueueItem> ResultQueue;
@@ -103,11 +112,11 @@ class ModuleSQL : public Module
ConnMap connections; // main thread only
ModuleSQL();
- void init();
+ void init() CXX11_OVERRIDE;
~ModuleSQL();
- void OnRehash(User* user);
- void OnUnloadModule(Module* mod);
- Version GetVersion();
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
+ Version GetVersion() CXX11_OVERRIDE;
};
class DispatcherThread : public SocketThread
@@ -117,8 +126,8 @@ class DispatcherThread : public SocketThread
public:
DispatcherThread(ModuleSQL* CreatorModule) : Parent(CreatorModule) { }
~DispatcherThread() { }
- virtual void Run();
- virtual void OnNotify();
+ void Run() CXX11_OVERRIDE;
+ void OnNotify() CXX11_OVERRIDE;
};
#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
@@ -127,16 +136,16 @@ class DispatcherThread : public SocketThread
/** Represents a mysql result set
*/
-class MySQLresult : public SQLResult
+class MySQLresult : public SQL::Result
{
public:
- SQLerror err;
+ SQL::Error err;
int currentrow;
int rows;
std::vector<std::string> colnames;
- std::vector<SQLEntries> fieldlists;
+ std::vector<SQL::Row> fieldlists;
- MySQLresult(MYSQL_RES* res, int affected_rows) : err(SQL_NO_ERROR), currentrow(0), rows(0)
+ MySQLresult(MYSQL_RES* res, int affected_rows) : err(SQL::SUCCESS), currentrow(0), rows(0)
{
if (affected_rows >= 1)
{
@@ -165,9 +174,9 @@ class MySQLresult : public SQLResult
{
std::string a = (fields[field_count].name ? fields[field_count].name : "");
if (row[field_count])
- fieldlists[n].push_back(SQLEntry(row[field_count]));
+ fieldlists[n].push_back(SQL::Field(row[field_count]));
else
- fieldlists[n].push_back(SQLEntry());
+ fieldlists[n].push_back(SQL::Field());
colnames.push_back(a);
field_count++;
}
@@ -179,35 +188,44 @@ class MySQLresult : public SQLResult
}
}
- MySQLresult(SQLerror& e) : err(e)
+ MySQLresult(SQL::Error& e) : err(e)
{
}
- ~MySQLresult()
+ int Rows() CXX11_OVERRIDE
{
+ return rows;
}
- virtual int Rows()
+ void GetCols(std::vector<std::string>& result) CXX11_OVERRIDE
{
- return rows;
+ result.assign(colnames.begin(), colnames.end());
}
- virtual void GetCols(std::vector<std::string>& result)
+ bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
{
- result.assign(colnames.begin(), colnames.end());
+ for (size_t i = 0; i < colnames.size(); ++i)
+ {
+ if (colnames[i] == column)
+ {
+ index = i;
+ return true;
+ }
+ }
+ return false;
}
- virtual SQLEntry GetValue(int row, int column)
+ SQL::Field GetValue(int row, int column)
{
if ((row >= 0) && (row < rows) && (column >= 0) && (column < (int)fieldlists[row].size()))
{
return fieldlists[row][column];
}
- return SQLEntry();
+ return SQL::Field();
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQL::Row& result) CXX11_OVERRIDE
{
if (currentrow < rows)
{
@@ -225,7 +243,7 @@ class MySQLresult : public SQLResult
/** Represents a connection to a mysql database
*/
-class SQLConnection : public SQLProvider
+class SQLConnection : public SQL::Provider
{
public:
reference<ConfigTag> config;
@@ -233,7 +251,7 @@ class SQLConnection : public SQLProvider
Mutex lock;
// This constructor creates an SQLConnection object with the given credentials, but does not connect yet.
- SQLConnection(Module* p, ConfigTag* tag) : SQLProvider(p, "SQL/" + tag->getString("id")),
+ SQLConnection(Module* p, ConfigTag* tag) : SQL::Provider(p, "SQL/" + tag->getString("id")),
config(tag), connection(NULL)
{
}
@@ -254,10 +272,16 @@ class SQLConnection : public SQLProvider
std::string user = config->getString("user");
std::string pass = config->getString("pass");
std::string dbname = config->getString("name");
- int port = config->getInt("port");
+ unsigned int port = config->getUInt("port", 3306);
bool rv = mysql_real_connect(connection, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), port, NULL, 0);
if (!rv)
return rv;
+
+ // Enable character set settings
+ std::string charset = config->getString("charset");
+ if ((!charset.empty()) && (mysql_set_character_set(connection, charset.c_str())))
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not set character set to \"%s\"", charset.c_str());
+
std::string initquery;
if (config->readString("initialquery", initquery))
{
@@ -286,7 +310,7 @@ class SQLConnection : public SQLProvider
{
/* XXX: See /usr/include/mysql/mysqld_error.h for a list of
* possible error numbers and error messages */
- SQLerror e(SQL_QREPLY_FAIL, ConvToStr(mysql_errno(connection)) + ": " + mysql_error(connection));
+ SQL::Error e(SQL::QREPLY_FAIL, InspIRCd::Format("%u: %s", mysql_errno(connection), mysql_error(connection)));
return new MySQLresult(e);
}
}
@@ -308,14 +332,14 @@ class SQLConnection : public SQLProvider
mysql_close(connection);
}
- void submit(SQLQuery* q, const std::string& qs)
+ void Submit(SQL::Query* q, const std::string& qs) CXX11_OVERRIDE
{
Parent()->Dispatcher->LockQueue();
Parent()->qq.push_back(QQueueItem(q, qs, this));
Parent()->Dispatcher->UnlockQueueWakeup();
}
- void submit(SQLQuery* call, const std::string& q, const ParamL& p)
+ void Submit(SQL::Query* call, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
{
std::string res;
unsigned int param = 0;
@@ -332,18 +356,17 @@ class SQLConnection : public SQLProvider
// and one byte is the terminating null
std::vector<char> buffer(parm.length() * 2 + 1);
- // The return value of mysql_escape_string() is the length of the encoded string,
+ // The return value of mysql_real_escape_string() is the length of the encoded string,
// not including the terminating null
- unsigned long escapedsize = mysql_escape_string(&buffer[0], parm.c_str(), parm.length());
-// mysql_real_escape_string(connection, queryend, paramscopy[paramnum].c_str(), paramscopy[paramnum].length());
+ unsigned long escapedsize = mysql_real_escape_string(connection, &buffer[0], parm.c_str(), parm.length());
res.append(&buffer[0], escapedsize);
}
}
}
- submit(call, res);
+ Submit(call, res);
}
- void submit(SQLQuery* call, const std::string& q, const ParamM& p)
+ void Submit(SQL::Query* call, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE
{
std::string res;
for(std::string::size_type i = 0; i < q.length(); i++)
@@ -358,7 +381,7 @@ class SQLConnection : public SQLProvider
field.push_back(q[i++]);
i--;
- ParamM::const_iterator it = p.find(field);
+ SQL::ParamMap::const_iterator it = p.find(field);
if (it != p.end())
{
std::string parm = it->second;
@@ -369,7 +392,7 @@ class SQLConnection : public SQLProvider
}
}
}
- submit(call, res);
+ Submit(call, res);
}
};
@@ -381,12 +404,7 @@ ModuleSQL::ModuleSQL()
void ModuleSQL::init()
{
Dispatcher = new DispatcherThread(this);
- ServerInstance->Threads->Start(Dispatcher);
-
- Implementation eventlist[] = { I_OnRehash, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
+ ServerInstance->Threads.Start(Dispatcher);
}
ModuleSQL::~ModuleSQL()
@@ -403,13 +421,13 @@ ModuleSQL::~ModuleSQL()
}
}
-void ModuleSQL::OnRehash(User* user)
+void ModuleSQL::ReadConfig(ConfigStatus& status)
{
ConnMap conns;
ConfigTagList tags = ServerInstance->Config->ConfTags("database");
for(ConfigIter i = tags.first; i != tags.second; i++)
{
- if (i->second->getString("module", "mysql") != "mysql")
+ if (!stdalgo::string::equalsci(i->second->getString("provider"), "mysql"))
continue;
std::string id = i->second->getString("id");
ConnMap::iterator curr = connections.find(id);
@@ -428,7 +446,7 @@ void ModuleSQL::OnRehash(User* user)
// now clean up the deleted databases
Dispatcher->LockQueue();
- SQLerror err(SQL_BAD_DBID);
+ SQL::Error err(SQL::BAD_DBID);
for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
{
ServerInstance->Modules->DelService(*i->second);
@@ -455,7 +473,7 @@ void ModuleSQL::OnRehash(User* user)
void ModuleSQL::OnUnloadModule(Module* mod)
{
- SQLerror err(SQL_BAD_DBID);
+ SQL::Error err(SQL::BAD_DBID);
Dispatcher->LockQueue();
unsigned int i = qq.size();
while (i > 0)
@@ -535,7 +553,7 @@ void DispatcherThread::OnNotify()
for(ResultQueue::iterator i = Parent->rq.begin(); i != Parent->rq.end(); i++)
{
MySQLresult* res = i->r;
- if (res->err.id == SQL_NO_ERROR)
+ if (res->err.code == SQL::SUCCESS)
i->q->OnResult(*res);
else
i->q->OnError(res->err);
diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp
index ac247548a..ec89208dd 100644
--- a/src/modules/extra/m_pgsql.cpp
+++ b/src/modules/extra/m_pgsql.cpp
@@ -21,16 +21,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: -Iexecute("pg_config --includedir" "POSTGRESQL_INCLUDE_DIR")
+/// $LinkerFlags: -Lexecute("pg_config --libdir" "POSTGRESQL_LIBRARY_DIR") -lpq
+
+/// $PackageInfo: require_system("centos") postgresql-devel
+/// $PackageInfo: require_system("darwin") postgresql
+/// $PackageInfo: require_system("debian") libpq-dev
+/// $PackageInfo: require_system("ubuntu") libpq-dev
+
#include "inspircd.h"
#include <cstdlib>
-#include <sstream>
#include <libpq-fe.h>
-#include "sql.h"
-
-/* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */
-/* $CompileFlags: -Iexec("pg_config --includedir") eval("my $s = `pg_config --version`;$s =~ /^.*?(\d+)\.(\d+)\.(\d+).*?$/;my $v = hex(sprintf("0x%02x%02x%02x", $1, $2, $3));print "-DPGSQL_HAS_ESCAPECONN" if(($v >= 0x080104) || ($v >= 0x07030F && $v < 0x070400) || ($v >= 0x07040D && $v < 0x080000) || ($v >= 0x080008 && $v < 0x080100));") */
-/* $LinkerFlags: -Lexec("pg_config --libdir") -lpq */
+#include "modules/sql.h"
/* SQLConn rewritten by peavey to
* use EventHandler instead of
@@ -43,7 +46,7 @@
class SQLConn;
class ModulePgSQL;
-typedef std::map<std::string, SQLConn*> ConnMap;
+typedef insp::flat_map<std::string, SQLConn*> ConnMap;
/* CREAD, Connecting and wants read event
* CWRITE, Connecting and wants write event
@@ -59,17 +62,17 @@ class ReconnectTimer : public Timer
private:
ModulePgSQL* mod;
public:
- ReconnectTimer(ModulePgSQL* m) : Timer(5, ServerInstance->Time(), false), mod(m)
+ ReconnectTimer(ModulePgSQL* m) : Timer(5, false), mod(m)
{
}
- virtual void Tick(time_t TIME);
+ bool Tick(time_t TIME) CXX11_OVERRIDE;
};
struct QueueItem
{
- SQLQuery* c;
+ SQL::Query* c;
std::string q;
- QueueItem(SQLQuery* C, const std::string& Q) : c(C), q(Q) {}
+ QueueItem(SQL::Query* C, const std::string& Q) : c(C), q(Q) {}
};
/** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
@@ -79,11 +82,21 @@ struct QueueItem
* data is passes to the module nearly as directly as if it was using the API directly itself.
*/
-class PgSQLresult : public SQLResult
+class PgSQLresult : public SQL::Result
{
PGresult* res;
int currentrow;
int rows;
+ std::vector<std::string> colnames;
+
+ void getColNames()
+ {
+ colnames.resize(PQnfields(res));
+ for(unsigned int i=0; i < colnames.size(); i++)
+ {
+ colnames[i] = PQfname(res, i);
+ }
+ }
public:
PgSQLresult(PGresult* result) : res(result), currentrow(0)
{
@@ -97,30 +110,44 @@ class PgSQLresult : public SQLResult
PQclear(res);
}
- virtual int Rows()
+ int Rows() CXX11_OVERRIDE
{
return rows;
}
- virtual void GetCols(std::vector<std::string>& result)
+ void GetCols(std::vector<std::string>& result) CXX11_OVERRIDE
+ {
+ if (colnames.empty())
+ getColNames();
+ result = colnames;
+ }
+
+ bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
{
- result.resize(PQnfields(res));
- for(unsigned int i=0; i < result.size(); i++)
+ if (colnames.empty())
+ getColNames();
+
+ for (size_t i = 0; i < colnames.size(); ++i)
{
- result[i] = PQfname(res, i);
+ if (colnames[i] == column)
+ {
+ index = i;
+ return true;
+ }
}
+ return false;
}
- virtual SQLEntry GetValue(int row, int column)
+ SQL::Field GetValue(int row, int column)
{
char* v = PQgetvalue(res, row, column);
if (!v || PQgetisnull(res, row, column))
- return SQLEntry();
+ return SQL::Field();
- return SQLEntry(std::string(v, PQgetlength(res, row, column)));
+ return SQL::Field(std::string(v, PQgetlength(res, row, column)));
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQL::Row& result) CXX11_OVERRIDE
{
if (currentrow >= PQntuples(res))
return false;
@@ -138,7 +165,7 @@ class PgSQLresult : public SQLResult
/** SQLConn represents one SQL session.
*/
-class SQLConn : public SQLProvider, public EventHandler
+class SQLConn : public SQL::Provider, public EventHandler
{
public:
reference<ConfigTag> conf; /* The <database> entry */
@@ -148,25 +175,25 @@ class SQLConn : public SQLProvider, public EventHandler
QueueItem qinprog; /* If there is currently a query in progress */
SQLConn(Module* Creator, ConfigTag* tag)
- : SQLProvider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL, "")
+ : SQL::Provider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL, "")
{
if (!DoConnect())
{
- ServerInstance->Logs->Log("m_pgsql",DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
DelayReconnect();
}
}
- CullResult cull()
+ CullResult cull() CXX11_OVERRIDE
{
- this->SQLProvider::cull();
+ this->SQL::Provider::cull();
ServerInstance->Modules->DelService(*this);
return this->EventHandler::cull();
}
~SQLConn()
{
- SQLerror err(SQL_BAD_DBID);
+ SQL::Error err(SQL::BAD_DBID);
if (qinprog.c)
{
qinprog.c->OnError(err);
@@ -174,24 +201,25 @@ class SQLConn : public SQLProvider, public EventHandler
}
for(std::deque<QueueItem>::iterator i = queue.begin(); i != queue.end(); i++)
{
- SQLQuery* q = i->c;
+ SQL::Query* q = i->c;
q->OnError(err);
delete q;
}
}
- virtual void HandleEvent(EventType et, int errornum)
+ void OnEventHandlerRead() CXX11_OVERRIDE
{
- switch (et)
- {
- case EVENT_READ:
- case EVENT_WRITE:
- DoEvent();
- break;
+ DoEvent();
+ }
- case EVENT_ERROR:
- DelayReconnect();
- }
+ void OnEventHandlerWrite() CXX11_OVERRIDE
+ {
+ DoEvent();
+ }
+
+ void OnEventHandlerError(int errornum) CXX11_OVERRIDE
+ {
+ DelayReconnect();
}
std::string GetDSN()
@@ -242,9 +270,9 @@ class SQLConn : public SQLProvider, public EventHandler
if(this->fd <= -1)
return false;
- if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
+ if (!SocketEngine::AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
{
- ServerInstance->Logs->Log("m_pgsql",DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
return false;
}
@@ -257,17 +285,17 @@ class SQLConn : public SQLProvider, public EventHandler
switch(PQconnectPoll(sql))
{
case PGRES_POLLING_WRITING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
status = CWRITE;
return true;
case PGRES_POLLING_READING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = CREAD;
return true;
case PGRES_POLLING_FAILED:
return false;
case PGRES_POLLING_OK:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = WWRITE;
DoConnectedPoll();
default:
@@ -316,7 +344,7 @@ restart:
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
{
- SQLerror err(SQL_QREPLY_FAIL, PQresultErrorMessage(result));
+ SQL::Error err(SQL::QREPLY_FAIL, PQresultErrorMessage(result));
qinprog.c->OnError(err);
break;
}
@@ -350,17 +378,17 @@ restart:
switch(PQresetPoll(sql))
{
case PGRES_POLLING_WRITING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
status = CWRITE;
return DoPoll();
case PGRES_POLLING_READING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = CREAD;
return true;
case PGRES_POLLING_FAILED:
return false;
case PGRES_POLLING_OK:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = WWRITE;
DoConnectedPoll();
default:
@@ -386,7 +414,7 @@ restart:
}
}
- void submit(SQLQuery *req, const std::string& q)
+ void Submit(SQL::Query *req, const std::string& q) CXX11_OVERRIDE
{
if (qinprog.q.empty())
{
@@ -399,7 +427,7 @@ restart:
}
}
- void submit(SQLQuery *req, const std::string& q, const ParamL& p)
+ void Submit(SQL::Query *req, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
{
std::string res;
unsigned int param = 0;
@@ -413,22 +441,18 @@ restart:
{
std::string parm = p[param++];
std::vector<char> buffer(parm.length() * 2 + 1);
-#ifdef PGSQL_HAS_ESCAPECONN
int error;
size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
- ServerInstance->Logs->Log("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
-#else
- size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
-#endif
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
res.append(&buffer[0], escapedsize);
}
}
}
- submit(req, res);
+ Submit(req, res);
}
- void submit(SQLQuery *req, const std::string& q, const ParamM& p)
+ void Submit(SQL::Query *req, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE
{
std::string res;
for(std::string::size_type i = 0; i < q.length(); i++)
@@ -443,24 +467,20 @@ restart:
field.push_back(q[i++]);
i--;
- ParamM::const_iterator it = p.find(field);
+ SQL::ParamMap::const_iterator it = p.find(field);
if (it != p.end())
{
std::string parm = it->second;
std::vector<char> buffer(parm.length() * 2 + 1);
-#ifdef PGSQL_HAS_ESCAPECONN
int error;
size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
- ServerInstance->Logs->Log("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
-#else
- size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
-#endif
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
res.append(&buffer[0], escapedsize);
}
}
}
- submit(req, res);
+ Submit(req, res);
}
void DoQuery(const QueueItem& req)
@@ -468,7 +488,7 @@ restart:
if (status != WREAD && status != WWRITE)
{
// whoops, not connected...
- SQLerror err(SQL_BAD_CONN);
+ SQL::Error err(SQL::BAD_CONN);
req.c->OnError(err);
delete req.c;
return;
@@ -480,7 +500,7 @@ restart:
}
else
{
- SQLerror err(SQL_QSEND_FAIL, PQerrorMessage(sql));
+ SQL::Error err(SQL::QSEND_FAIL, PQerrorMessage(sql));
req.c->OnError(err);
delete req.c;
}
@@ -488,7 +508,7 @@ restart:
void Close()
{
- ServerInstance->SE->DelFd(this);
+ SocketEngine::DelFd(this);
if(sql)
{
@@ -505,25 +525,17 @@ class ModulePgSQL : public Module
ReconnectTimer* retimer;
ModulePgSQL()
+ : retimer(NULL)
{
}
- void init()
- {
- ReadConf();
-
- Implementation eventlist[] = { I_OnUnloadModule, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModulePgSQL()
+ ~ModulePgSQL()
{
- if (retimer)
- ServerInstance->Timers->DelTimer(retimer);
+ delete retimer;
ClearAllConnections();
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ReadConf();
}
@@ -534,7 +546,7 @@ class ModulePgSQL : public Module
ConfigTagList tags = ServerInstance->Config->ConfTags("database");
for(ConfigIter i = tags.first; i != tags.second; i++)
{
- if (i->second->getString("module", "pgsql") != "pgsql")
+ if (!stdalgo::string::equalsci(i->second->getString("provider"), "pgsql"))
continue;
std::string id = i->second->getString("id");
ConnMap::iterator curr = connections.find(id);
@@ -564,9 +576,9 @@ class ModulePgSQL : public Module
connections.clear();
}
- void OnUnloadModule(Module* mod)
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
{
- SQLerror err(SQL_BAD_DBID);
+ SQL::Error err(SQL::BAD_DBID);
for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
{
SQLConn* conn = i->second;
@@ -579,7 +591,7 @@ class ModulePgSQL : public Module
std::deque<QueueItem>::iterator j = conn->queue.begin();
while (j != conn->queue.end())
{
- SQLQuery* q = j->c;
+ SQL::Query* q = j->c;
if (q->creator == mod)
{
q->OnError(err);
@@ -592,16 +604,18 @@ class ModulePgSQL : public Module
}
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API", VF_VENDOR);
}
};
-void ReconnectTimer::Tick(time_t time)
+bool ReconnectTimer::Tick(time_t time)
{
mod->retimer = NULL;
mod->ReadConf();
+ delete this;
+ return false;
}
void SQLConn::DelayReconnect()
@@ -615,7 +629,7 @@ void SQLConn::DelayReconnect()
if (!mod->retimer)
{
mod->retimer = new ReconnectTimer(mod);
- ServerInstance->Timers->AddTimer(mod->retimer);
+ ServerInstance->Timers.AddTimer(mod->retimer);
}
}
}
diff --git a/src/modules/extra/m_regex_pcre.cpp b/src/modules/extra/m_regex_pcre.cpp
index cba234c8c..e8ef96c22 100644
--- a/src/modules/extra/m_regex_pcre.cpp
+++ b/src/modules/extra/m_regex_pcre.cpp
@@ -17,35 +17,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: execute("pcre-config --cflags" "PCRE_CXXFLAGS")
+/// $LinkerFlags: execute("pcre-config --libs" "PCRE_LDFLAGS" "-lpcre")
+
+/// $PackageInfo: require_system("centos") pcre-devel pkgconfig
+/// $PackageInfo: require_system("darwin") pcre pkg-config
+/// $PackageInfo: require_system("debian") libpcre3-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libpcre3-dev pkg-config
+
#include "inspircd.h"
#include <pcre.h>
-#include "m_regex.h"
-
-/* $ModDesc: Regex Provider Module for PCRE */
-/* $ModDep: m_regex.h */
-/* $CompileFlags: exec("pcre-config --cflags") */
-/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */
+#include "modules/regex.h"
#ifdef _WIN32
# pragma comment(lib, "libpcre.lib")
#endif
-class PCREException : public ModuleException
-{
-public:
- PCREException(const std::string& rx, const std::string& error, int erroffset)
- : ModuleException("Error in regex " + rx + " at offset " + ConvToStr(erroffset) + ": " + error)
- {
- }
-};
-
class PCRERegex : public Regex
{
-private:
pcre* regex;
-public:
+ public:
PCRERegex(const std::string& rx) : Regex(rx)
{
const char* error;
@@ -53,24 +46,19 @@ public:
regex = pcre_compile(rx.c_str(), 0, &error, &erroffset, NULL);
if (!regex)
{
- ServerInstance->Logs->Log("REGEX", DEBUG, "pcre_compile failed: /%s/ [%d] %s", rx.c_str(), erroffset, error);
- throw PCREException(rx, error, erroffset);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "pcre_compile failed: /%s/ [%d] %s", rx.c_str(), erroffset, error);
+ throw RegexException(rx, error, erroffset);
}
}
- virtual ~PCRERegex()
+ ~PCRERegex()
{
pcre_free(regex);
}
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
- if (pcre_exec(regex, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1)
- {
- // Bang. :D
- return true;
- }
- return false;
+ return (pcre_exec(regex, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) >= 0);
}
};
@@ -78,7 +66,7 @@ class PCREFactory : public RegexFactory
{
public:
PCREFactory(Module* m) : RegexFactory(m, "regex/pcre") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new PCRERegex(expr);
}
@@ -86,13 +74,13 @@ class PCREFactory : public RegexFactory
class ModuleRegexPCRE : public Module
{
-public:
+ public:
PCREFactory ref;
- ModuleRegexPCRE() : ref(this) {
- ServerInstance->Modules->AddService(ref);
+ ModuleRegexPCRE() : ref(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex Provider Module for PCRE", VF_VENDOR);
}
diff --git a/src/modules/extra/m_regex_posix.cpp b/src/modules/extra/m_regex_posix.cpp
index b3afd60c8..b5fddfab8 100644
--- a/src/modules/extra/m_regex_posix.cpp
+++ b/src/modules/extra/m_regex_posix.cpp
@@ -19,28 +19,15 @@
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
#include <sys/types.h>
#include <regex.h>
-/* $ModDesc: Regex Provider Module for POSIX Regular Expressions */
-/* $ModDep: m_regex.h */
-
-class POSIXRegexException : public ModuleException
-{
-public:
- POSIXRegexException(const std::string& rx, const std::string& error)
- : ModuleException("Error in regex " + rx + ": " + error)
- {
- }
-};
-
class POSIXRegex : public Regex
{
-private:
regex_t regbuf;
-public:
+ public:
POSIXRegex(const std::string& rx, bool extended) : Regex(rx)
{
int flags = (extended ? REG_EXTENDED : 0) | REG_NOSUB;
@@ -58,23 +45,18 @@ public:
error = errbuf;
delete[] errbuf;
regfree(&regbuf);
- throw POSIXRegexException(rx, error);
+ throw RegexException(rx, error);
}
}
- virtual ~POSIXRegex()
+ ~POSIXRegex()
{
regfree(&regbuf);
}
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
- if (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0)
- {
- // Bang. :D
- return true;
- }
- return false;
+ return (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0);
}
};
@@ -83,7 +65,7 @@ class PosixFactory : public RegexFactory
public:
bool extended;
PosixFactory(Module* m) : RegexFactory(m, "regex/posix") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new POSIXRegex(expr, extended);
}
@@ -92,20 +74,18 @@ class PosixFactory : public RegexFactory
class ModuleRegexPOSIX : public Module
{
PosixFactory ref;
-public:
- ModuleRegexPOSIX() : ref(this) {
- ServerInstance->Modules->AddService(ref);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+
+ public:
+ ModuleRegexPOSIX() : ref(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex Provider Module for POSIX Regular Expressions", VF_VENDOR);
}
- void OnRehash(User* u)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ref.extended = ServerInstance->Config->ConfValue("posix")->getBool("extended");
}
diff --git a/src/modules/extra/m_regex_re2.cpp b/src/modules/extra/m_regex_re2.cpp
new file mode 100644
index 000000000..4bcf287ca
--- /dev/null
+++ b/src/modules/extra/m_regex_re2.cpp
@@ -0,0 +1,86 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ * Copyright (C) 2012 ChrisTX <chris@rev-crew.info>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// $CompilerFlags: find_compiler_flags("re2" "")
+/// $LinkerFlags: find_linker_flags("re2" "-lre2")
+
+/// $PackageInfo: require_system("darwin") pkg-config re2
+/// $PackageInfo: require_system("debian" "8.0") libre2-dev pkg-config
+/// $PackageInfo: require_system("ubuntu" "15.10") libre2-dev pkg-config
+
+
+#include "inspircd.h"
+#include "modules/regex.h"
+
+// Fix warnings about the use of `long long` on C++03 and
+// shadowing on GCC.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#include <re2/re2.h>
+
+class RE2Regex : public Regex
+{
+ RE2 regexcl;
+
+ public:
+ RE2Regex(const std::string& rx) : Regex(rx), regexcl(rx, RE2::Quiet)
+ {
+ if (!regexcl.ok())
+ {
+ throw RegexException(rx, regexcl.error());
+ }
+ }
+
+ bool Matches(const std::string& text) CXX11_OVERRIDE
+ {
+ return RE2::FullMatch(text, regexcl);
+ }
+};
+
+class RE2Factory : public RegexFactory
+{
+ public:
+ RE2Factory(Module* m) : RegexFactory(m, "regex/re2") { }
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
+ {
+ return new RE2Regex(expr);
+ }
+};
+
+class ModuleRegexRE2 : public Module
+{
+ RE2Factory ref;
+
+ public:
+ ModuleRegexRE2() : ref(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Regex Provider Module for RE2", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleRegexRE2)
diff --git a/src/modules/extra/m_regex_stdlib.cpp b/src/modules/extra/m_regex_stdlib.cpp
index 204728b65..42e5c8bf1 100644
--- a/src/modules/extra/m_regex_stdlib.cpp
+++ b/src/modules/extra/m_regex_stdlib.cpp
@@ -15,32 +15,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-#include "inspircd.h"
-#include "m_regex.h"
-#include <regex>
-/* $ModDesc: Regex Provider Module for std::regex Regular Expressions */
-/* $ModConfig: <stdregex type="ecmascript">
- * Specify the Regular Expression engine to use here. Valid settings are
- * bre, ere, awk, grep, egrep, ecmascript (default if not specified)*/
-/* $CompileFlags: -std=c++11 */
-/* $ModDep: m_regex.h */
+/// $CompilerFlags: -std=c++11
-class StdRegexException : public ModuleException
-{
-public:
- StdRegexException(const std::string& rx, const std::string& error)
- : ModuleException(std::string("Error in regex ") + rx + ": " + error)
- {
- }
-};
+
+#include "inspircd.h"
+#include "modules/regex.h"
+#include <regex>
class StdRegex : public Regex
{
-private:
std::regex regexcl;
-public:
+
+ public:
StdRegex(const std::string& rx, std::regex::flag_type fltype) : Regex(rx)
{
try{
@@ -48,11 +35,11 @@ public:
}
catch(std::regex_error rxerr)
{
- throw StdRegexException(rx, rxerr.what());
+ throw RegexException(rx, rxerr.what());
}
}
-
- virtual bool Matches(const std::string& text)
+
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
return std::regex_search(text, regexcl);
}
@@ -63,7 +50,7 @@ class StdRegexFactory : public RegexFactory
public:
std::regex::flag_type regextype;
StdRegexFactory(Module* m) : RegexFactory(m, "regex/stdregex") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new StdRegex(expr, regextype);
}
@@ -73,36 +60,33 @@ class ModuleRegexStd : public Module
{
public:
StdRegexFactory ref;
- ModuleRegexStd() : ref(this) {
- ServerInstance->Modules->AddService(ref);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+ ModuleRegexStd() : ref(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex Provider Module for std::regex", VF_VENDOR);
}
-
- void OnRehash(User* u)
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* Conf = ServerInstance->Config->ConfValue("stdregex");
std::string regextype = Conf->getString("type", "ecmascript");
-
- if(regextype == "bre")
+
+ if (stdalgo::string::equalsci(regextype, "bre"))
ref.regextype = std::regex::basic;
- else if(regextype == "ere")
+ else if (stdalgo::string::equalsci(regextype, "ere"))
ref.regextype = std::regex::extended;
- else if(regextype == "awk")
+ else if (stdalgo::string::equalsci(regextype, "awk"))
ref.regextype = std::regex::awk;
- else if(regextype == "grep")
+ else if (stdalgo::string::equalsci(regextype, "grep"))
ref.regextype = std::regex::grep;
- else if(regextype == "egrep")
+ else if (stdalgo::string::equalsci(regextype, "egrep"))
ref.regextype = std::regex::egrep;
else
{
- if(regextype != "ecmascript")
+ if (!stdalgo::string::equalsci(regextype, "ecmascript"))
ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Non-existent regex engine '%s' specified. Falling back to ECMAScript.", regextype.c_str());
ref.regextype = std::regex::ECMAScript;
}
diff --git a/src/modules/extra/m_regex_tre.cpp b/src/modules/extra/m_regex_tre.cpp
index 4b9eab472..aa3f1d41e 100644
--- a/src/modules/extra/m_regex_tre.cpp
+++ b/src/modules/extra/m_regex_tre.cpp
@@ -17,29 +17,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("tre")
+/// $LinkerFlags: find_linker_flags("tre" "-ltre")
+
+/// $PackageInfo: require_system("darwin") pkg-config tre
+/// $PackageInfo: require_system("debian") libtre-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libtre-dev pkg-config
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
#include <sys/types.h>
#include <tre/regex.h>
-/* $ModDesc: Regex Provider Module for TRE Regular Expressions */
-/* $CompileFlags: pkgconfincludes("tre","tre/regex.h","") */
-/* $LinkerFlags: pkgconflibs("tre","/libtre.so","-ltre") rpath("pkg-config --libs tre") */
-/* $ModDep: m_regex.h */
-
-class TRERegexException : public ModuleException
-{
-public:
- TRERegexException(const std::string& rx, const std::string& error)
- : ModuleException("Error in regex " + rx + ": " + error)
- {
- }
-};
-
class TRERegex : public Regex
{
-private:
regex_t regbuf;
public:
@@ -60,30 +51,26 @@ public:
error = errbuf;
delete[] errbuf;
regfree(&regbuf);
- throw TRERegexException(rx, error);
+ throw RegexException(rx, error);
}
}
- virtual ~TRERegex()
+ ~TRERegex()
{
regfree(&regbuf);
}
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
- if (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0)
- {
- // Bang. :D
- return true;
- }
- return false;
+ return (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0);
}
};
-class TREFactory : public RegexFactory {
+class TREFactory : public RegexFactory
+{
public:
TREFactory(Module* m) : RegexFactory(m, "regex/tre") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new TRERegex(expr);
}
@@ -92,18 +79,15 @@ class TREFactory : public RegexFactory {
class ModuleRegexTRE : public Module
{
TREFactory trf;
-public:
- ModuleRegexTRE() : trf(this) {
- ServerInstance->Modules->AddService(trf);
- }
- Version GetVersion()
+ public:
+ ModuleRegexTRE() : trf(this)
{
- return Version("Regex Provider Module for TRE Regular Expressions", VF_VENDOR);
}
- ~ModuleRegexTRE()
+ Version GetVersion() CXX11_OVERRIDE
{
+ return Version("Regex Provider Module for TRE Regular Expressions", VF_VENDOR);
}
};
diff --git a/src/modules/extra/m_sqlite3.cpp b/src/modules/extra/m_sqlite3.cpp
index 47880c02c..b46810062 100644
--- a/src/modules/extra/m_sqlite3.cpp
+++ b/src/modules/extra/m_sqlite3.cpp
@@ -19,45 +19,51 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("sqlite3")
+/// $LinkerFlags: find_linker_flags("sqlite3" "-lsqlite3")
+
+/// $PackageInfo: require_system("centos") pkgconfig sqlite-devel
+/// $PackageInfo: require_system("darwin") pkg-config sqlite3
+/// $PackageInfo: require_system("debian") libsqlite3-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libsqlite3-dev pkg-config
#include "inspircd.h"
+#include "modules/sql.h"
+
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
#include <sqlite3.h>
-#include "sql.h"
#ifdef _WIN32
# pragma comment(lib, "sqlite3.lib")
#endif
-/* $ModDesc: sqlite3 provider */
-/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */
-/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */
-/* $NoPedantic */
-
class SQLConn;
-typedef std::map<std::string, SQLConn*> ConnMap;
+typedef insp::flat_map<std::string, SQLConn*> ConnMap;
-class SQLite3Result : public SQLResult
+class SQLite3Result : public SQL::Result
{
public:
int currentrow;
int rows;
std::vector<std::string> columns;
- std::vector<SQLEntries> fieldlists;
+ std::vector<SQL::Row> fieldlists;
SQLite3Result() : currentrow(0), rows(0)
{
}
- ~SQLite3Result()
- {
- }
-
- virtual int Rows()
+ int Rows() CXX11_OVERRIDE
{
return rows;
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQL::Row& result) CXX11_OVERRIDE
{
if (currentrow < rows)
{
@@ -72,20 +78,32 @@ class SQLite3Result : public SQLResult
}
}
- virtual void GetCols(std::vector<std::string>& result)
+ void GetCols(std::vector<std::string>& result) CXX11_OVERRIDE
{
result.assign(columns.begin(), columns.end());
}
+
+ bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
+ {
+ for (size_t i = 0; i < columns.size(); ++i)
+ {
+ if (columns[i] == column)
+ {
+ index = i;
+ return true;
+ }
+ }
+ return false;
+ }
};
-class SQLConn : public SQLProvider
+class SQLConn : public SQL::Provider
{
- private:
sqlite3* conn;
reference<ConfigTag> config;
public:
- SQLConn(Module* Parent, ConfigTag* tag) : SQLProvider(Parent, "SQL/" + tag->getString("id")), config(tag)
+ SQLConn(Module* Parent, ConfigTag* tag) : SQL::Provider(Parent, "SQL/" + tag->getString("id")), config(tag)
{
std::string host = tag->getString("hostname");
if (sqlite3_open_v2(host.c_str(), &conn, SQLITE_OPEN_READWRITE, 0) != SQLITE_OK)
@@ -93,7 +111,7 @@ class SQLConn : public SQLProvider
// Even in case of an error conn must be closed
sqlite3_close(conn);
conn = NULL;
- ServerInstance->Logs->Log("m_sqlite3",DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
}
}
@@ -106,14 +124,14 @@ class SQLConn : public SQLProvider
}
}
- void Query(SQLQuery* query, const std::string& q)
+ void Query(SQL::Query* query, const std::string& q)
{
SQLite3Result res;
sqlite3_stmt *stmt;
int err = sqlite3_prepare_v2(conn, q.c_str(), q.length(), &stmt, NULL);
if (err != SQLITE_OK)
{
- SQLerror error(SQL_QSEND_FAIL, sqlite3_errmsg(conn));
+ SQL::Error error(SQL::QSEND_FAIL, sqlite3_errmsg(conn));
query->OnError(error);
return;
}
@@ -135,7 +153,7 @@ class SQLConn : public SQLProvider
{
const char* txt = (const char*)sqlite3_column_text(stmt, i);
if (txt)
- res.fieldlists[res.rows][i] = SQLEntry(txt);
+ res.fieldlists[res.rows][i] = SQL::Field(txt);
}
res.rows++;
}
@@ -146,7 +164,7 @@ class SQLConn : public SQLProvider
}
else
{
- SQLerror error(SQL_QREPLY_FAIL, sqlite3_errmsg(conn));
+ SQL::Error error(SQL::QREPLY_FAIL, sqlite3_errmsg(conn));
query->OnError(error);
break;
}
@@ -154,13 +172,13 @@ class SQLConn : public SQLProvider
sqlite3_finalize(stmt);
}
- virtual void submit(SQLQuery* query, const std::string& q)
+ void Submit(SQL::Query* query, const std::string& q) CXX11_OVERRIDE
{
Query(query, q);
delete query;
}
- virtual void submit(SQLQuery* query, const std::string& q, const ParamL& p)
+ void Submit(SQL::Query* query, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
{
std::string res;
unsigned int param = 0;
@@ -178,10 +196,10 @@ class SQLConn : public SQLProvider
}
}
}
- submit(query, res);
+ Submit(query, res);
}
- virtual void submit(SQLQuery* query, const std::string& q, const ParamM& p)
+ void Submit(SQL::Query* query, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE
{
std::string res;
for(std::string::size_type i = 0; i < q.length(); i++)
@@ -196,7 +214,7 @@ class SQLConn : public SQLProvider
field.push_back(q[i++]);
i--;
- ParamM::const_iterator it = p.find(field);
+ SQL::ParamMap::const_iterator it = p.find(field);
if (it != p.end())
{
char* escaped = sqlite3_mprintf("%q", it->second.c_str());
@@ -205,29 +223,16 @@ class SQLConn : public SQLProvider
}
}
}
- submit(query, res);
+ Submit(query, res);
}
};
class ModuleSQLite3 : public Module
{
- private:
ConnMap conns;
public:
- ModuleSQLite3()
- {
- }
-
- void init()
- {
- ReadConf();
-
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleSQLite3()
+ ~ModuleSQLite3()
{
ClearConns();
}
@@ -243,13 +248,13 @@ class ModuleSQLite3 : public Module
conns.clear();
}
- void ReadConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ClearConns();
ConfigTagList tags = ServerInstance->Config->ConfTags("database");
for(ConfigIter i = tags.first; i != tags.second; i++)
{
- if (i->second->getString("module", "sqlite") != "sqlite")
+ if (!stdalgo::string::equalsci(i->second->getString("provider"), "sqlite"))
continue;
SQLConn* conn = new SQLConn(this, i->second);
conns.insert(std::make_pair(i->second->getString("id"), conn));
@@ -257,12 +262,7 @@ class ModuleSQLite3 : public Module
}
}
- void OnRehash(User* user)
- {
- ReadConf();
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("sqlite3 provider", VF_VENDOR);
}
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp
index 2f4acf3f0..8bd73b2bb 100644
--- a/src/modules/extra/m_ssl_gnutls.cpp
+++ b/src/modules/extra/m_ssl_gnutls.cpp
@@ -20,120 +20,98 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("gnutls")
+/// $CompilerFlags: require_version("gnutls" "1.0" "2.12") execute("libgcrypt-config --cflags" "LIBGCRYPT_CXXFLAGS")
-#include "inspircd.h"
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#include "ssl.h"
-#include "m_cap.h"
+/// $LinkerFlags: find_linker_flags("gnutls" "-lgnutls")
+/// $LinkerFlags: require_version("gnutls" "1.0" "2.12") execute("libgcrypt-config --libs" "LIBGCRYPT_LDFLAGS")
-#ifdef _WIN32
-# pragma comment(lib, "libgnutls-30.lib")
+/// $PackageInfo: require_system("centos") gnutls-devel pkgconfig
+/// $PackageInfo: require_system("darwin") gnutls pkg-config
+/// $PackageInfo: require_system("debian" "1.0" "7.99") libgcrypt11-dev
+/// $PackageInfo: require_system("debian") gnutls-bin libgnutls28-dev pkg-config
+/// $PackageInfo: require_system("ubuntu" "1.0" "13.10") libgcrypt11-dev
+/// $PackageInfo: require_system("ubuntu") gnutls-bin libgnutls-dev pkg-config
+
+#include "inspircd.h"
+#include "modules/ssl.h"
+#include <memory>
+
+// Fix warnings about the use of commas at end of enumerator lists on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+#elif defined __GNUC__
+# if __GNUC__ < 6
+# pragma GCC diagnostic ignored "-pedantic"
+# endif
#endif
-/* $ModDesc: Provides SSL support for clients */
-/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") iflt("pkg-config --modversion gnutls","2.12") exec("libgcrypt-config --cflags") */
-/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") iflt("pkg-config --modversion gnutls","2.12") exec("libgcrypt-config --libs") */
-/* $NoPedantic */
+// Fix warnings about using std::auto_ptr on C++11 or newer.
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#ifndef GNUTLS_VERSION_MAJOR
-#define GNUTLS_VERSION_MAJOR LIBGNUTLS_VERSION_MAJOR
-#define GNUTLS_VERSION_MINOR LIBGNUTLS_VERSION_MINOR
-#define GNUTLS_VERSION_PATCH LIBGNUTLS_VERSION_PATCH
-#endif
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
-// These don't exist in older GnuTLS versions
-#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 7))
-#define GNUTLS_NEW_PRIO_API
+#ifndef GNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION LIBGNUTLS_VERSION
#endif
-#if(GNUTLS_VERSION_MAJOR < 2)
-typedef gnutls_certificate_credentials_t gnutls_certificate_credentials;
-typedef gnutls_dh_params_t gnutls_dh_params;
+// Check if the GnuTLS library is at least version major.minor.patch
+#define INSPIRCD_GNUTLS_HAS_VERSION(major, minor, patch) (GNUTLS_VERSION_NUMBER >= ((major << 16) | (minor << 8) | patch))
+
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 9, 8)
+#define GNUTLS_HAS_MAC_GET_ID
+#include <gnutls/crypto.h>
#endif
-#if (GNUTLS_VERSION_MAJOR > 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR >= 12))
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
# define GNUTLS_HAS_RND
-# include <gnutls/crypto.h>
#else
# include <gcrypt.h>
#endif
-enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
-
-struct SSLConfig : public refcountbase
-{
- gnutls_certificate_credentials_t x509_cred;
- std::vector<gnutls_x509_crt_t> x509_certs;
- gnutls_x509_privkey_t x509_key;
- gnutls_dh_params_t dh_params;
-#ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_t priority;
+#ifdef _WIN32
+# pragma comment(lib, "libgnutls-30.lib")
#endif
- SSLConfig()
- : x509_cred(NULL)
- , x509_key(NULL)
- , dh_params(NULL)
-#ifdef GNUTLS_NEW_PRIO_API
- , priority(NULL)
+// These don't exist in older GnuTLS versions
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 1, 7)
+#define GNUTLS_NEW_PRIO_API
#endif
- {
- }
-
- ~SSLConfig()
- {
- ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Destroying SSLConfig %p", (void*)this);
-
- if (x509_cred)
- gnutls_certificate_free_credentials(x509_cred);
- for (unsigned int i = 0; i < x509_certs.size(); i++)
- gnutls_x509_crt_deinit(x509_certs[i]);
+enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN };
- if (x509_key)
- gnutls_x509_privkey_deinit(x509_key);
-
- if (dh_params)
- gnutls_dh_params_deinit(dh_params);
-
-#ifdef GNUTLS_NEW_PRIO_API
- if (priority)
- gnutls_priority_deinit(priority);
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
+#define INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+#define GNUTLS_NEW_CERT_CALLBACK_API
+typedef gnutls_retr2_st cert_cb_last_param_type;
+#else
+typedef gnutls_retr_st cert_cb_last_param_type;
#endif
- }
-};
-
-static reference<SSLConfig> currconf;
-
-static SSLConfig* GetSessionConfig(gnutls_session_t session);
-#if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
-static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
- const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) {
+#if INSPIRCD_GNUTLS_HAS_VERSION(3, 3, 5)
+#define INSPIRCD_GNUTLS_HAS_RECV_PACKET
+#endif
- st->type = GNUTLS_CRT_X509;
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 99, 0)
+// The second parameter of gnutls_init() has changed in 2.99.0 from gnutls_connection_end_t to unsigned int
+// (it became a general flags parameter) and the enum has been deprecated and generates a warning on use.
+typedef unsigned int inspircd_gnutls_session_init_flags_t;
#else
-static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
- const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) {
- st->cert_type = GNUTLS_CRT_X509;
- st->key_type = GNUTLS_PRIVKEY_X509;
+typedef gnutls_connection_end_t inspircd_gnutls_session_init_flags_t;
#endif
- SSLConfig* conf = GetSessionConfig(session);
- std::vector<gnutls_x509_crt_t>& x509_certs = conf->x509_certs;
- st->ncerts = x509_certs.size();
- st->cert.x509 = &x509_certs[0];
- st->key.x509 = conf->x509_key;
- st->deinit_all = 0;
- return 0;
-}
+#if INSPIRCD_GNUTLS_HAS_VERSION(3, 1, 9)
+#define INSPIRCD_GNUTLS_HAS_CORK
+#endif
-class RandGen : public HandlerBase2<void, char*, size_t>
+static Module* thismod;
+
+class RandGen
{
public:
- RandGen() {}
- void Call(char* buffer, size_t len)
+ static void Call(char* buffer, size_t len)
{
#ifdef GNUTLS_HAS_RND
gnutls_rnd(GNUTLS_RND_RANDOM, buffer, len);
@@ -143,749 +121,677 @@ class RandGen : public HandlerBase2<void, char*, size_t>
}
};
-/** Represents an SSL user's extra data
- */
-class issl_session
-{
-public:
- StreamSocket* socket;
- gnutls_session_t sess;
- issl_status status;
- reference<ssl_cert> cert;
- reference<SSLConfig> config;
-
- issl_session() : socket(NULL), sess(NULL), status(ISSL_NONE) {}
-};
-
-static SSLConfig* GetSessionConfig(gnutls_session_t sess)
+namespace GnuTLS
{
- issl_session* session = reinterpret_cast<issl_session*>(gnutls_transport_get_ptr(sess));
- return session->config;
-}
-
-class CommandStartTLS : public SplitCommand
-{
- public:
- bool enabled;
- CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS")
+ class Init
{
- enabled = true;
- works_before_reg = true;
- }
+ public:
+ Init() { gnutls_global_init(); }
+ ~Init() { gnutls_global_deinit(); }
+ };
- CmdResult HandleLocal(const std::vector<std::string> &parameters, LocalUser *user)
+ class Exception : public ModuleException
{
- if (!enabled)
- {
- user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
- return CMD_FAILURE;
- }
+ public:
+ Exception(const std::string& reason)
+ : ModuleException(reason) { }
+ };
- if (user->registered == REG_ALL)
- {
- user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
- }
- else
+ void ThrowOnError(int errcode, const char* msg)
+ {
+ if (errcode < 0)
{
- if (!user->eh.GetIOHook())
- {
- user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
- /* We need to flush the write buffer prior to adding the IOHook,
- * otherwise we'll be sending this line inside the SSL session - which
- * won't start its handshake until the client gets this line. Currently,
- * we assume the write will not block here; this is usually safe, as
- * STARTTLS is sent very early on in the registration phase, where the
- * user hasn't built up much sendq. Handling a blocked write here would
- * be very annoying.
- */
- user->eh.DoWrite();
- user->eh.AddIOHook(creator);
- creator->OnStreamSocketAccept(&user->eh, NULL, NULL);
- }
- else
- user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
+ std::string reason = msg;
+ reason.append(" :").append(gnutls_strerror(errcode));
+ throw Exception(reason);
}
-
- return CMD_FAILURE;
}
-};
-
-class ModuleSSLGnuTLS : public Module
-{
- issl_session* sessions;
- gnutls_digest_algorithm_t hash;
-
- std::string sslports;
- int dh_bits;
+ /** Used to create a gnutls_datum_t* from a std::string
+ */
+ class Datum
+ {
+ gnutls_datum_t datum;
- RandGen randhandler;
- CommandStartTLS starttls;
+ public:
+ Datum(const std::string& dat)
+ {
+ datum.data = (unsigned char*)dat.data();
+ datum.size = static_cast<unsigned int>(dat.length());
+ }
- GenericCap capHandler;
- ServiceProvider iohook;
+ const gnutls_datum_t* get() const { return &datum; }
+ };
- inline static const char* UnknownIfNULL(const char* str)
+ class Hash
{
- return str ? str : "UNKNOWN";
- }
+ gnutls_digest_algorithm_t hash;
- static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
- {
- issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
- if (session->socket->GetEventMask() & FD_READ_WILL_BLOCK)
+ public:
+ // Nothing to deallocate, constructor may throw freely
+ Hash(const std::string& hashname)
{
-#ifdef _WIN32
- gnutls_transport_set_errno(session->sess, EAGAIN);
+ // As older versions of gnutls can't do this, let's disable it where needed.
+#ifdef GNUTLS_HAS_MAC_GET_ID
+ // As gnutls_digest_algorithm_t and gnutls_mac_algorithm_t are mapped 1:1, we can do this
+ // There is no gnutls_dig_get_id() at the moment, but it may come later
+ hash = (gnutls_digest_algorithm_t)gnutls_mac_get_id(hashname.c_str());
+ if (hash == GNUTLS_DIG_UNKNOWN)
+ throw Exception("Unknown hash type " + hashname);
+
+ // Check if the user is giving us something that is a valid MAC but not digest
+ gnutls_hash_hd_t is_digest;
+ if (gnutls_hash_init(&is_digest, hash) < 0)
+ throw Exception("Unknown hash type " + hashname);
+ gnutls_hash_deinit(is_digest, NULL);
#else
- errno = EAGAIN;
+ if (stdalgo::string::equalsci(hashname, "md5"))
+ hash = GNUTLS_DIG_MD5;
+ else if (stdalgo::string::equalsci(hashname, "sha1"))
+ hash = GNUTLS_DIG_SHA1;
+#ifdef INSPIRCD_GNUTLS_ENABLE_SHA256_FINGERPRINT
+ else if (stdalgo::string::equalsci(hashname, "sha256"))
+ hash = GNUTLS_DIG_SHA256;
+#endif
+ else
+ throw Exception("Unknown hash type " + hashname);
#endif
- return -1;
}
- int rv = ServerInstance->SE->Recv(session->socket, reinterpret_cast<char *>(buffer), size, 0);
+ gnutls_digest_algorithm_t get() const { return hash; }
+ };
-#ifdef _WIN32
- if (rv < 0)
+ class DHParams
+ {
+ gnutls_dh_params_t dh_params;
+
+ DHParams()
{
- /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
- * and then set errno appropriately.
- * The gnutls library may also have a different errno variable than us, see
- * gnutls_transport_set_errno(3).
- */
- gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ ThrowOnError(gnutls_dh_params_init(&dh_params), "gnutls_dh_params_init() failed");
}
-#endif
- if (rv < (int)size)
- ServerInstance->SE->ChangeEventMask(session->socket, FD_READ_WILL_BLOCK);
- return rv;
- }
-
- static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
- {
- issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
- if (session->socket->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ public:
+ /** Import */
+ static std::auto_ptr<DHParams> Import(const std::string& dhstr)
{
-#ifdef _WIN32
- gnutls_transport_set_errno(session->sess, EAGAIN);
-#else
- errno = EAGAIN;
-#endif
- return -1;
+ std::auto_ptr<DHParams> dh(new DHParams);
+ int ret = gnutls_dh_params_import_pkcs3(dh->dh_params, Datum(dhstr).get(), GNUTLS_X509_FMT_PEM);
+ ThrowOnError(ret, "Unable to import DH params");
+ return dh;
}
- int rv = ServerInstance->SE->Send(session->socket, reinterpret_cast<const char *>(buffer), size, 0);
-
-#ifdef _WIN32
- if (rv < 0)
+ ~DHParams()
{
- /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
- * and then set errno appropriately.
- * The gnutls library may also have a different errno variable than us, see
- * gnutls_transport_set_errno(3).
- */
- gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ gnutls_dh_params_deinit(dh_params);
}
-#endif
- if (rv < (int)size)
- ServerInstance->SE->ChangeEventMask(session->socket, FD_WRITE_WILL_BLOCK);
- return rv;
- }
-
- public:
+ const gnutls_dh_params_t& get() const { return dh_params; }
+ };
- ModuleSSLGnuTLS()
- : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK)
+ class X509Key
{
-#ifndef GNUTLS_HAS_RND
- gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
-
- sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
-
- gnutls_global_init(); // This must be called once in the program
- }
+ /** Ensure that the key is deinited in case the constructor of X509Key throws
+ */
+ class RAIIKey
+ {
+ public:
+ gnutls_x509_privkey_t key;
- void init()
- {
- currconf = new SSLConfig;
- InitSSLConfig(currconf);
+ RAIIKey()
+ {
+ ThrowOnError(gnutls_x509_privkey_init(&key), "gnutls_x509_privkey_init() failed");
+ }
- ServerInstance->GenRandom = &randhandler;
+ ~RAIIKey()
+ {
+ gnutls_x509_privkey_deinit(key);
+ }
+ } key;
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnUserConnect,
- I_OnEvent, I_OnHookIO, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ public:
+ /** Import */
+ X509Key(const std::string& keystr)
+ {
+ int ret = gnutls_x509_privkey_import(key.key, Datum(keystr).get(), GNUTLS_X509_FMT_PEM);
+ ThrowOnError(ret, "Unable to import private key");
+ }
- ServerInstance->Modules->AddService(iohook);
- ServerInstance->Modules->AddService(starttls);
- }
+ gnutls_x509_privkey_t& get() { return key.key; }
+ };
- void OnRehash(User* user)
+ class X509CertList
{
- sslports.clear();
+ std::vector<gnutls_x509_crt_t> certs;
- ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
- starttls.enabled = Conf->getBool("starttls", true);
-
- if (Conf->getBool("showports", true))
+ public:
+ /** Import */
+ X509CertList(const std::string& certstr)
{
- sslports = Conf->getString("advertisedports");
- if (!sslports.empty())
- return;
+ unsigned int certcount = 3;
+ certs.resize(certcount);
+ Datum datum(certstr);
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+ int ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
{
- ListenSocket* port = ServerInstance->ports[i];
- if (port->bind_tag->getString("ssl") != "gnutls")
- continue;
-
- const std::string& portid = port->bind_desc;
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %s", portid.c_str());
-
- if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
- {
- /*
- * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
- * the IP:port in ISUPPORT.
- *
- * We used to advertise all ports seperated by a ';' char that matched the above criteria,
- * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
- * To solve this by default we now only display the first IP:port found and let the user
- * configure the exact value for the 005 token, if necessary.
- */
- sslports = portid;
- break;
- }
+ // the buffer wasn't big enough to hold all certs but gnutls changed certcount to the number of available certs,
+ // try again with a bigger buffer
+ certs.resize(certcount);
+ ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
}
- }
- }
- void OnModuleRehash(User* user, const std::string &param)
- {
- if(param != "ssl")
- return;
+ ThrowOnError(ret, "Unable to load certificates");
- reference<SSLConfig> newconf = new SSLConfig;
- try
- {
- InitSSLConfig(newconf);
+ // Resize the vector to the actual number of certs because we rely on its size being correct
+ // when deallocating the certs
+ certs.resize(certcount);
}
- catch (ModuleException& ex)
+
+ ~X509CertList()
{
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Not applying new config. %s", ex.GetReason());
- return;
+ for (std::vector<gnutls_x509_crt_t>::iterator i = certs.begin(); i != certs.end(); ++i)
+ gnutls_x509_crt_deinit(*i);
}
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Applying new config, old config is in use by %d connection(s)", currconf->GetReferenceCount()-1);
- currconf = newconf;
- }
+ gnutls_x509_crt_t* raw() { return &certs[0]; }
+ unsigned int size() const { return certs.size(); }
+ };
- void InitSSLConfig(SSLConfig* config)
+ class X509CRL : public refcountbase
{
- ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Initializing new SSLConfig %p", (void*)config);
-
- std::string keyfile;
- std::string certfile;
- std::string cafile;
- std::string crlfile;
- OnRehash(NULL);
-
- ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
-
- cafile = Conf->getString("cafile", CONFIG_PATH "/ca.pem");
- crlfile = Conf->getString("crlfile", CONFIG_PATH "/crl.pem");
- certfile = Conf->getString("certfile", CONFIG_PATH "/cert.pem");
- keyfile = Conf->getString("keyfile", CONFIG_PATH "/key.pem");
- dh_bits = Conf->getInt("dhbits");
- std::string hashname = Conf->getString("hash", "md5");
-
- // The GnuTLS manual states that the gnutls_set_default_priority()
- // call we used previously when initializing the session is the same
- // as setting the "NORMAL" priority string.
- // Thus if the setting below is not in the config we will behave exactly
- // the same as before, when the priority setting wasn't available.
- std::string priorities = Conf->getString("priority", "NORMAL");
-
- if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
- dh_bits = 1024;
-
- if (hashname == "md5")
- hash = GNUTLS_DIG_MD5;
- else if (hashname == "sha1")
- hash = GNUTLS_DIG_SHA1;
-#ifdef INSPIRCD_GNUTLS_ENABLE_SHA256_FINGERPRINT
- else if (hashname == "sha256")
- hash = GNUTLS_DIG_SHA256;
-#endif
- else
- throw ModuleException("Unknown hash type " + hashname);
-
+ class RAIICRL
+ {
+ public:
+ gnutls_x509_crl_t crl;
- int ret;
+ RAIICRL()
+ {
+ ThrowOnError(gnutls_x509_crl_init(&crl), "gnutls_x509_crl_init() failed");
+ }
- gnutls_certificate_credentials_t& x509_cred = config->x509_cred;
+ ~RAIICRL()
+ {
+ gnutls_x509_crl_deinit(crl);
+ }
+ } crl;
- ret = gnutls_certificate_allocate_credentials(&x509_cred);
- if (ret < 0)
+ public:
+ /** Import */
+ X509CRL(const std::string& crlstr)
{
- // Set to NULL because we can't be sure what value is in it and we must not try to
- // deallocate it in case of an error
- x509_cred = NULL;
- throw ModuleException("Failed to allocate certificate credentials: " + std::string(gnutls_strerror(ret)));
+ int ret = gnutls_x509_crl_import(get(), Datum(crlstr).get(), GNUTLS_X509_FMT_PEM);
+ ThrowOnError(ret, "Unable to load certificate revocation list");
}
- if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
-
- if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
+ gnutls_x509_crl_t& get() { return crl.crl; }
+ };
- FileReader reader;
-
- reader.LoadFile(certfile);
- std::string cert_string = reader.Contents();
- gnutls_datum_t cert_datum = { (unsigned char*)cert_string.data(), static_cast<unsigned int>(cert_string.length()) };
+#ifdef GNUTLS_NEW_PRIO_API
+ class Priority
+ {
+ gnutls_priority_t priority;
- reader.LoadFile(keyfile);
- std::string key_string = reader.Contents();
- gnutls_datum_t key_datum = { (unsigned char*)key_string.data(), static_cast<unsigned int>(key_string.length()) };
+ public:
+ Priority(const std::string& priorities)
+ {
+ // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
+ // If the user did not supply anything then the string is already set to "NORMAL"
+ const char* priocstr = priorities.c_str();
+ const char* prioerror;
- std::vector<gnutls_x509_crt_t>& x509_certs = config->x509_certs;
+ int ret = gnutls_priority_init(&priority, priocstr, &prioerror);
+ if (ret < 0)
+ {
+ // gnutls did not understand the user supplied string
+ throw Exception("Unable to initialize priorities to \"" + priorities + "\": " + gnutls_strerror(ret) + " Syntax error at position " + ConvToStr((unsigned int) (prioerror - priocstr)));
+ }
+ }
- // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
- unsigned int certcount = 3;
- x509_certs.resize(certcount);
- ret = gnutls_x509_crt_list_import(&x509_certs[0], &certcount, &cert_datum, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
- if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+ ~Priority()
{
- // the buffer wasn't big enough to hold all certs but gnutls updated certcount to the number of available certs, try again with a bigger buffer
- x509_certs.resize(certcount);
- ret = gnutls_x509_crt_list_import(&x509_certs[0], &certcount, &cert_datum, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ gnutls_priority_deinit(priority);
}
- if (ret <= 0)
+ void SetupSession(gnutls_session_t sess)
{
- // clear the vector so we won't call gnutls_x509_crt_deinit() on the (uninited) certs later
- x509_certs.clear();
- throw ModuleException("Unable to load GnuTLS server certificate (" + certfile + "): " + ((ret < 0) ? (std::string(gnutls_strerror(ret))) : "No certs could be read"));
+ gnutls_priority_set(sess, priority);
}
- x509_certs.resize(ret);
- gnutls_x509_privkey_t& x509_key = config->x509_key;
- if (gnutls_x509_privkey_init(&x509_key) < 0)
+ static const char* GetDefault()
{
- // Make sure the destructor does not try to deallocate this, see above
- x509_key = NULL;
- throw ModuleException("Unable to initialize private key");
+ return "NORMAL:%SERVER_PRECEDENCE:-VERS-SSL3.0";
}
- if((ret = gnutls_x509_privkey_import(x509_key, &key_datum, GNUTLS_X509_FMT_PEM)) < 0)
- throw ModuleException("Unable to load GnuTLS server private key (" + keyfile + "): " + std::string(gnutls_strerror(ret)));
+ static std::string RemoveUnknownTokens(const std::string& prio)
+ {
+ std::string ret;
+ irc::sepstream ss(prio, ':');
+ for (std::string token; ss.GetToken(token); )
+ {
+ // Save current position so we can revert later if needed
+ const std::string::size_type prevpos = ret.length();
+ // Append next token
+ if (!ret.empty())
+ ret.push_back(':');
+ ret.append(token);
+
+ gnutls_priority_t test;
+ if (gnutls_priority_init(&test, ret.c_str(), NULL) < 0)
+ {
+ // The new token broke the priority string, revert to the previously working one
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Priority string token not recognized: \"%s\"", token.c_str());
+ ret.erase(prevpos);
+ }
+ else
+ {
+ // Worked
+ gnutls_priority_deinit(test);
+ }
+ }
+ return ret;
+ }
+ };
+#else
+ /** Dummy class, used when gnutls_priority_set() is not available
+ */
+ class Priority
+ {
+ public:
+ Priority(const std::string& priorities)
+ {
+ if (priorities != GetDefault())
+ throw Exception("You've set a non-default priority string, but GnuTLS lacks support for it");
+ }
- if((ret = gnutls_certificate_set_x509_key(x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
- throw ModuleException("Unable to set GnuTLS cert/key pair: " + std::string(gnutls_strerror(ret)));
+ static void SetupSession(gnutls_session_t sess)
+ {
+ // Always set the default priorities
+ gnutls_set_default_priority(sess);
+ }
- #ifdef GNUTLS_NEW_PRIO_API
- // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
- // If the user did not supply anything then the string is already set to "NORMAL"
- const char* priocstr = priorities.c_str();
- const char* prioerror;
+ static const char* GetDefault()
+ {
+ return "NORMAL";
+ }
- gnutls_priority_t& priority = config->priority;
- if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0)
+ static std::string RemoveUnknownTokens(const std::string& prio)
{
- // gnutls did not understand the user supplied string, log and fall back to the default priorities
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
- gnutls_priority_init(&priority, "NORMAL", NULL);
+ // We don't do anything here because only NORMAL is accepted
+ return prio;
}
+ };
+#endif
- #else
- if (priorities != "NORMAL")
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
- #endif
+ class CertCredentials
+ {
+ /** DH parameters associated with these credentials
+ */
+ std::auto_ptr<DHParams> dh;
- #if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
- gnutls_certificate_client_set_retrieve_function (x509_cred, cert_callback);
- #else
- gnutls_certificate_set_retrieve_function (x509_cred, cert_callback);
- #endif
+ protected:
+ gnutls_certificate_credentials_t cred;
- gnutls_dh_params_t& dh_params = config->dh_params;
- ret = gnutls_dh_params_init(&dh_params);
- if (ret < 0)
+ public:
+ CertCredentials()
{
- // Make sure the destructor does not try to deallocate this, see above
- dh_params = NULL;
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
- return;
+ ThrowOnError(gnutls_certificate_allocate_credentials(&cred), "Cannot allocate certificate credentials");
}
- std::string dhfile = Conf->getString("dhfile");
- if (!dhfile.empty())
+ ~CertCredentials()
{
- // Try to load DH params from file
- reader.LoadFile(dhfile);
- std::string dhstring = reader.Contents();
- gnutls_datum_t dh_datum = { (unsigned char*)dhstring.data(), static_cast<unsigned int>(dhstring.length()) };
-
- if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0)
- {
- // File unreadable or GnuTLS was unhappy with the contents, generate the DH primes now
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
- GenerateDHParams(dh_params);
- }
+ gnutls_certificate_free_credentials(cred);
}
- else
+
+ /** Associates these credentials with the session
+ */
+ void SetupSession(gnutls_session_t sess)
{
- GenerateDHParams(dh_params);
+ gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred);
}
- gnutls_certificate_set_dh_params(x509_cred, dh_params);
- }
+ /** Set the given DH parameters to be used with these credentials
+ */
+ void SetDH(std::auto_ptr<DHParams>& DH)
+ {
+ dh = DH;
+ gnutls_certificate_set_dh_params(cred, dh->get());
+ }
+ };
- void GenerateDHParams(gnutls_dh_params_t dh_params)
+ class X509Credentials : public CertCredentials
{
- // Generate Diffie Hellman parameters - for use with DHE
- // kx algorithms. These should be discarded and regenerated
- // once a day, once a week or once a month. Depending on the
- // security requirements.
+ /** Private key
+ */
+ X509Key key;
- int ret;
+ /** Certificate list, presented to the peer
+ */
+ X509CertList certs;
- if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
- }
+ /** Trusted CA, may be NULL
+ */
+ std::auto_ptr<X509CertList> trustedca;
- ~ModuleSSLGnuTLS()
- {
- currconf = NULL;
+ /** Certificate revocation list, may be NULL
+ */
+ std::auto_ptr<X509CRL> crl;
- gnutls_global_deinit();
- delete[] sessions;
- ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
- }
+ static int cert_callback(gnutls_session_t session, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st);
- void OnCleanup(int target_type, void* item)
- {
- if(target_type == TYPE_USER)
+ public:
+ X509Credentials(const std::string& certstr, const std::string& keystr)
+ : key(keystr)
+ , certs(certstr)
{
- LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+ // Throwing is ok here, the destructor of Credentials is called in that case
+ int ret = gnutls_certificate_set_x509_key(cred, certs.raw(), certs.size(), key.get());
+ ThrowOnError(ret, "Unable to set cert/key pair");
- if (user && user->eh.GetIOHook() == this)
- {
- // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
- // Potentially there could be multiple SSL modules loaded at once on different ports.
- ServerInstance->Users->QuitUser(user, "SSL module unloading");
- }
+#ifdef GNUTLS_NEW_CERT_CALLBACK_API
+ gnutls_certificate_set_retrieve_function(cred, cert_callback);
+#else
+ gnutls_certificate_client_set_retrieve_function(cred, cert_callback);
+#endif
}
- }
- Version GetVersion()
- {
- return Version("Provides SSL support for clients", VF_VENDOR);
- }
+ /** Sets the trusted CA and the certificate revocation list
+ * to use when verifying certificates
+ */
+ void SetCA(std::auto_ptr<X509CertList>& certlist, std::auto_ptr<X509CRL>& CRL)
+ {
+ // Do nothing if certlist is NULL
+ if (certlist.get())
+ {
+ int ret = gnutls_certificate_set_x509_trust(cred, certlist->raw(), certlist->size());
+ ThrowOnError(ret, "gnutls_certificate_set_x509_trust() failed");
+
+ if (CRL.get())
+ {
+ ret = gnutls_certificate_set_x509_crl(cred, &CRL->get(), 1);
+ ThrowOnError(ret, "gnutls_certificate_set_x509_crl() failed");
+ }
+ trustedca = certlist;
+ crl = CRL;
+ }
+ }
+ };
- void On005Numeric(std::string &output)
+ class DataReader
{
- if (!sslports.empty())
- output.append(" SSL=" + sslports);
- if (starttls.enabled)
- output.append(" STARTTLS");
- }
+ int retval;
+#ifdef INSPIRCD_GNUTLS_HAS_RECV_PACKET
+ gnutls_packet_t packet;
- void OnHookIO(StreamSocket* user, ListenSocket* lsb)
- {
- if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
+ public:
+ DataReader(gnutls_session_t sess)
{
- /* Hook the user with our module */
- user->AddIOHook(this);
+ // Using the packet API avoids the final copy of the data which GnuTLS does if we supply
+ // our own buffer. Instead, we get the buffer containing the data from GnuTLS and copy it
+ // to the recvq directly from there in appendto().
+ retval = gnutls_record_recv_packet(sess, &packet);
}
- }
- void OnRequest(Request& request)
- {
- if (strcmp("GET_SSL_CERT", request.id) == 0)
+ void appendto(std::string& recvq)
{
- SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
- int fd = req.sock->GetFd();
- issl_session* session = &sessions[fd];
+ // Copy data from GnuTLS buffers to recvq
+ gnutls_datum_t datum;
+ gnutls_packet_get(packet, &datum, NULL);
+ recvq.append(reinterpret_cast<const char*>(datum.data), datum.size);
- req.cert = session->cert;
+ gnutls_packet_deinit(packet);
}
- else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+#else
+ char* const buffer;
+
+ public:
+ DataReader(gnutls_session_t sess)
+ : buffer(ServerInstance->GetReadBuffer())
{
- SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
- if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
- req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+ // Read data from GnuTLS buffers into ReadBuffer
+ retval = gnutls_record_recv(sess, buffer, ServerInstance->Config->NetBufferSize);
}
- }
- void InitSession(StreamSocket* user, bool me_server)
- {
- issl_session* session = &sessions[user->GetFd()];
-
- gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
- session->socket = user;
- session->config = currconf;
-
- #ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_set(session->sess, currconf->priority);
- #else
- gnutls_set_default_priority(session->sess);
- #endif
- gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, currconf->x509_cred);
- gnutls_dh_set_prime_bits(session->sess, dh_bits);
- gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
- gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
- gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
-
- if (me_server)
- gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+ void appendto(std::string& recvq)
+ {
+ // Copy data from ReadBuffer to recvq
+ recvq.append(buffer, retval);
+ }
+#endif
- Handshake(session, user);
- }
+ int ret() const { return retval; }
+ };
- void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+ class Profile
{
- issl_session* session = &sessions[user->GetFd()];
+ /** Name of this profile
+ */
+ const std::string name;
- /* For STARTTLS: Don't try and init a session on a socket that already has a session */
- if (session->sess)
- return;
+ /** X509 certificate(s) and key
+ */
+ X509Credentials x509cred;
- InitSession(user, true);
- }
+ /** The minimum length in bits for the DH prime to be accepted as a client
+ */
+ unsigned int min_dh_bits;
- void OnStreamSocketConnect(StreamSocket* user)
- {
- InitSession(user, false);
- }
+ /** Hashing algorithm to use when generating certificate fingerprints
+ */
+ Hash hash;
- void OnStreamSocketClose(StreamSocket* user)
- {
- CloseSession(&sessions[user->GetFd()]);
- }
+ /** Priorities for ciphers, compression methods, etc.
+ */
+ Priority priority;
- int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
- {
- issl_session* session = &sessions[user->GetFd()];
+ /** Rough max size of records to send
+ */
+ const unsigned int outrecsize;
- if (!session->sess)
+ /** True to request a client certificate as a server
+ */
+ const bool requestclientcert;
+
+ static std::string ReadFile(const std::string& filename)
{
- CloseSession(session);
- user->SetError("No SSL session");
- return -1;
+ FileReader reader(filename);
+ std::string ret = reader.GetString();
+ if (ret.empty())
+ throw Exception("Cannot read file " + filename);
+ return ret;
}
- if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
+ static std::string GetPrioStr(const std::string& profilename, ConfigTag* tag)
{
- // The handshake isn't finished, try to finish it.
-
- if(!Handshake(session, user))
+ // Use default priority string if this tag does not specify one
+ std::string priostr = GnuTLS::Priority::GetDefault();
+ bool found = tag->readString("priority", priostr);
+ // If the prio string isn't set in the config don't be strict about the default one because it doesn't work on all versions of GnuTLS
+ if (!tag->getBool("strictpriority", found))
{
- if (session->status != ISSL_CLOSING)
- return 0;
- return -1;
+ std::string stripped = GnuTLS::Priority::RemoveUnknownTokens(priostr);
+ if (stripped.empty())
+ {
+ // Stripping failed, act as if a prio string wasn't set
+ stripped = GnuTLS::Priority::RemoveUnknownTokens(GnuTLS::Priority::GetDefault());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Priority string for profile \"%s\" contains unknown tokens and stripping it didn't yield a working one either, falling back to \"%s\"", profilename.c_str(), stripped.c_str());
+ }
+ else if ((found) && (stripped != priostr))
+ {
+ // Prio string was set in the config and we ended up with something that works but different
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Priority string for profile \"%s\" contains unknown tokens, stripped to \"%s\"", profilename.c_str(), stripped.c_str());
+ }
+ priostr.swap(stripped);
}
+ return priostr;
}
- // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
-
- if (session->status == ISSL_HANDSHAKEN)
+ public:
+ struct Config
{
- char* buffer = ServerInstance->GetReadBuffer();
- size_t bufsiz = ServerInstance->Config->NetBufferSize;
- int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
- if (ret > 0)
- {
- recvq.append(buffer, ret);
- // Schedule a read if there is still data in the GnuTLS buffer
- if (gnutls_record_check_pending(session->sess) > 0)
- ServerInstance->SE->ChangeEventMask(user, FD_ADD_TRIAL_READ);
- return 1;
- }
- else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
- {
- return 0;
- }
- else if (ret == 0)
+ std::string name;
+
+ std::auto_ptr<X509CertList> ca;
+ std::auto_ptr<X509CRL> crl;
+
+ std::string certstr;
+ std::string keystr;
+ std::auto_ptr<DHParams> dh;
+
+ std::string priostr;
+ unsigned int mindh;
+ std::string hashstr;
+
+ unsigned int outrecsize;
+ bool requestclientcert;
+
+ Config(const std::string& profilename, ConfigTag* tag)
+ : name(profilename)
+ , certstr(ReadFile(tag->getString("certfile", "cert.pem")))
+ , keystr(ReadFile(tag->getString("keyfile", "key.pem")))
+ , dh(DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem"))))
+ , priostr(GetPrioStr(profilename, tag))
+ , mindh(tag->getUInt("mindhbits", 1024))
+ , hashstr(tag->getString("hash", "md5"))
+ , requestclientcert(tag->getBool("requestclientcert", true))
{
- user->SetError("Connection closed");
- CloseSession(session);
- return -1;
- }
- else
- {
- user->SetError(gnutls_strerror(ret));
- CloseSession(session);
- return -1;
- }
- }
- else if (session->status == ISSL_CLOSING)
- return -1;
-
- return 0;
- }
+ // Load trusted CA and revocation list, if set
+ std::string filename = tag->getString("cafile");
+ if (!filename.empty())
+ {
+ ca.reset(new X509CertList(ReadFile(filename)));
- int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
- {
- issl_session* session = &sessions[user->GetFd()];
+ filename = tag->getString("crlfile");
+ if (!filename.empty())
+ crl.reset(new X509CRL(ReadFile(filename)));
+ }
- if (!session->sess)
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ // If cork support is available outrecsize represents the (rough) max amount of data we give GnuTLS while corked
+ outrecsize = tag->getUInt("outrecsize", 2048, 512);
+#else
+ outrecsize = tag->getUInt("outrecsize", 2048, 512, 16384);
+#endif
+ }
+ };
+
+ Profile(Config& config)
+ : name(config.name)
+ , x509cred(config.certstr, config.keystr)
+ , min_dh_bits(config.mindh)
+ , hash(config.hashstr)
+ , priority(config.priostr)
+ , outrecsize(config.outrecsize)
+ , requestclientcert(config.requestclientcert)
{
- CloseSession(session);
- user->SetError("No SSL session");
- return -1;
+ x509cred.SetDH(config.dh);
+ x509cred.SetCA(config.ca, config.crl);
}
-
- if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
+ /** Set up the given session with the settings in this profile
+ */
+ void SetupSession(gnutls_session_t sess)
{
- // The handshake isn't finished, try to finish it.
- Handshake(session, user);
- if (session->status != ISSL_CLOSING)
- return 0;
- return -1;
+ priority.SetupSession(sess);
+ x509cred.SetupSession(sess);
+ gnutls_dh_set_prime_bits(sess, min_dh_bits);
+
+ // Request client certificate if enabled and we are a server, no-op if we're a client
+ if (requestclientcert)
+ gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST);
}
- int ret = 0;
+ const std::string& GetName() const { return name; }
+ X509Credentials& GetX509Credentials() { return x509cred; }
+ gnutls_digest_algorithm_t GetHash() const { return hash.get(); }
+ unsigned int GetOutgoingRecordSize() const { return outrecsize; }
+ };
+}
- if (session->status == ISSL_HANDSHAKEN)
- {
- ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
+class GnuTLSIOHook : public SSLIOHook
+{
+ private:
+ gnutls_session_t sess;
+ issl_status status;
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ size_t gbuffersize;
+#endif
- if (ret == (int)sendq.length())
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
- return 1;
- }
- else if (ret > 0)
- {
- sendq = sendq.substr(ret);
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
- return 0;
- }
- else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
- return 0;
- }
- else // (ret < 0)
- {
- user->SetError(gnutls_strerror(ret));
- CloseSession(session);
- return -1;
- }
+ void CloseSession()
+ {
+ if (this->sess)
+ {
+ gnutls_bye(this->sess, GNUTLS_SHUT_WR);
+ gnutls_deinit(this->sess);
}
-
- return 0;
+ sess = NULL;
+ certificate = NULL;
+ status = ISSL_NONE;
}
- bool Handshake(issl_session* session, StreamSocket* user)
+ // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+ int Handshake(StreamSocket* user)
{
- int ret = gnutls_handshake(session->sess);
+ int ret = gnutls_handshake(this->sess);
if (ret < 0)
{
if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
{
// Handshake needs resuming later, read() or write() would have blocked.
+ this->status = ISSL_HANDSHAKING;
- if(gnutls_record_get_direction(session->sess) == 0)
+ if (gnutls_record_get_direction(this->sess) == 0)
{
// gnutls_handshake() wants to read() again.
- session->status = ISSL_HANDSHAKING_READ;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
}
else
{
// gnutls_handshake() wants to write() again.
- session->status = ISSL_HANDSHAKING_WRITE;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
}
+
+ return 0;
}
else
{
user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
- CloseSession(session);
- session->status = ISSL_CLOSING;
+ CloseSession();
+ return -1;
}
-
- return false;
}
else
{
// Change the seesion state
- session->status = ISSL_HANDSHAKEN;
+ this->status = ISSL_HANDSHAKEN;
- VerifyCertificate(session,user);
+ VerifyCertificate();
// Finish writing, if any left
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
-
- return true;
- }
- }
-
- void OnUserConnect(LocalUser* user)
- {
- if (user->eh.GetIOHook() == this)
- {
- if (sessions[user->eh.GetFd()].sess)
- {
- const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
- std::string cipher = UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
- cipher.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
- cipher.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
-
- ssl_cert* cert = sessions[user->eh.GetFd()].cert;
- if (cert->fingerprint.empty())
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), cipher.c_str());
- else
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
- " and your SSL fingerprint is %s", user->nick.c_str(), cipher.c_str(), cert->fingerprint.c_str());
- }
- }
- }
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
- void CloseSession(issl_session* session)
- {
- if (session->sess)
- {
- gnutls_bye(session->sess, GNUTLS_SHUT_WR);
- gnutls_deinit(session->sess);
+ return 1;
}
- session->socket = NULL;
- session->sess = NULL;
- session->cert = NULL;
- session->status = ISSL_NONE;
- session->config = NULL;
}
- void VerifyCertificate(issl_session* session, StreamSocket* user)
+ void VerifyCertificate()
{
- if (!session->sess || !user)
- return;
-
- unsigned int status;
+ unsigned int certstatus;
const gnutls_datum_t* cert_list;
int ret;
unsigned int cert_list_size;
gnutls_x509_crt_t cert;
- char name[MAXBUF];
- unsigned char digest[MAXBUF];
+ char str[512];
+ unsigned char digest[512];
size_t digest_size = sizeof(digest);
- size_t name_size = sizeof(name);
+ size_t name_size = sizeof(str);
ssl_cert* certinfo = new ssl_cert;
- session->cert = certinfo;
+ this->certificate = certinfo;
/* This verification function uses the trusted CAs in the credentials
* structure. So you must have installed one or more CA certificates.
*/
- ret = gnutls_certificate_verify_peers2(session->sess, &status);
+ ret = gnutls_certificate_verify_peers2(this->sess, &certstatus);
if (ret < 0)
{
@@ -893,16 +799,16 @@ class ModuleSSLGnuTLS : public Module
return;
}
- certinfo->invalid = (status & GNUTLS_CERT_INVALID);
- certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
- certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
- certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
+ certinfo->invalid = (certstatus & GNUTLS_CERT_INVALID);
+ certinfo->unknownsigner = (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND);
+ certinfo->revoked = (certstatus & GNUTLS_CERT_REVOKED);
+ certinfo->trusted = !(certstatus & GNUTLS_CERT_SIGNER_NOT_CA);
/* Up to here the process is the same for X.509 certificates and
* OpenPGP keys. From now on X.509 certificates are assumed. This can
* be easily extended to work with openpgp keys as well.
*/
- if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
+ if (gnutls_certificate_type_get(this->sess) != GNUTLS_CRT_X509)
{
certinfo->error = "No X509 keys sent";
return;
@@ -916,7 +822,7 @@ class ModuleSSLGnuTLS : public Module
}
cert_list_size = 0;
- cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
+ cert_list = gnutls_certificate_get_peers(this->sess, &cert_list_size);
if (cert_list == NULL)
{
certinfo->error = "No certificate was found";
@@ -934,31 +840,31 @@ class ModuleSSLGnuTLS : public Module
goto info_done_dealloc;
}
- if (gnutls_x509_crt_get_dn(cert, name, &name_size) == 0)
+ if (gnutls_x509_crt_get_dn(cert, str, &name_size) == 0)
{
std::string& dn = certinfo->dn;
- dn = name;
+ dn = str;
// Make sure there are no chars in the string that we consider invalid
if (dn.find_first_of("\r\n") != std::string::npos)
dn.clear();
}
- name_size = sizeof(name);
- if (gnutls_x509_crt_get_issuer_dn(cert, name, &name_size) == 0)
+ name_size = sizeof(str);
+ if (gnutls_x509_crt_get_issuer_dn(cert, str, &name_size) == 0)
{
std::string& issuer = certinfo->issuer;
- issuer = name;
+ issuer = str;
if (issuer.find_first_of("\r\n") != std::string::npos)
issuer.clear();
}
- if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
+ if ((ret = gnutls_x509_crt_get_fingerprint(cert, GetProfile().GetHash(), digest, &digest_size)) < 0)
{
certinfo->error = gnutls_strerror(ret);
}
else
{
- certinfo->fingerprint = irc::hex(digest, digest_size);
+ certinfo->fingerprint = BinToHex(digest, digest_size);
}
/* Beware here we do not check for errors.
@@ -972,15 +878,522 @@ info_done_dealloc:
gnutls_x509_crt_deinit(cert);
}
- void OnEvent(Event& ev)
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_HANDSHAKEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
+ }
+
+ CloseSession();
+ sock->SetError("No SSL session");
+ return -1;
+ }
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ int FlushBuffer(StreamSocket* sock)
+ {
+ // If GnuTLS has some data buffered, write it
+ if (gbuffersize)
+ return HandleWriteRet(sock, gnutls_record_uncork(this->sess, 0));
+ return 1;
+ }
+#endif
+
+ int HandleWriteRet(StreamSocket* sock, int ret)
+ {
+ if (ret > 0)
+ {
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ gbuffersize -= ret;
+ if (gbuffersize)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+#endif
+ return ret;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else // (ret < 0)
+ {
+ sock->SetError(gnutls_strerror(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
+ static const char* UnknownIfNULL(const char* str)
+ {
+ return str ? str : "UNKNOWN";
+ }
+
+ static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
+ {
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0);
+
+#ifdef _WIN32
+ if (rv < 0)
+ {
+ /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+ * and then set errno appropriately.
+ * The gnutls library may also have a different errno variable than us, see
+ * gnutls_transport_set_errno(3).
+ */
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ }
+#endif
+
+ if (rv < (int)size)
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ return rv;
+ }
+
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ static ssize_t VectorPush(gnutls_transport_ptr_t transportptr, const giovec_t* iov, int iovcnt)
+ {
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(transportptr);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ // Cast the giovec_t to iovec not to IOVector so the correct function is called on Windows
+ int ret = SocketEngine::WriteV(sock, reinterpret_cast<const iovec*>(iov), iovcnt);
+#ifdef _WIN32
+ // See the function above for more info about the usage of gnutls_transport_set_errno() on Windows
+ if (ret < 0)
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+#endif
+
+ int size = 0;
+ for (int i = 0; i < iovcnt; i++)
+ size += iov[i].iov_len;
+
+ if (ret < size)
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ return ret;
+ }
+
+#else // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
+ {
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0);
+
+#ifdef _WIN32
+ if (rv < 0)
+ {
+ /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+ * and then set errno appropriately.
+ * The gnutls library may also have a different errno variable than us, see
+ * gnutls_transport_set_errno(3).
+ */
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ }
+#endif
+
+ if (rv < (int)size)
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ return rv;
+ }
+#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+
+ public:
+ GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags)
+ : SSLIOHook(hookprov)
+ , sess(NULL)
+ , status(ISSL_NONE)
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ , gbuffersize(0)
+#endif
+ {
+ gnutls_init(&sess, flags);
+ gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(sock));
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ gnutls_transport_set_vec_push_function(sess, VectorPush);
+#else
+ gnutls_transport_set_push_function(sess, gnutls_push_wrapper);
+#endif
+ gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper);
+ GetProfile().SetupSession(sess);
+
+ sock->AddIOHook(this);
+ Handshake(sock);
+ }
+
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
+ {
+ CloseSession();
+ }
+
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
+
+ // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
+ {
+ GnuTLS::DataReader reader(sess);
+ int ret = reader.ret();
+ if (ret > 0)
+ {
+ reader.appendto(recvq);
+ // Schedule a read if there is still data in the GnuTLS buffer
+ if (gnutls_record_check_pending(sess) > 0)
+ SocketEngine::ChangeEventMask(user, FD_ADD_TRIAL_READ);
+ return 1;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+ {
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ user->SetError("Connection closed");
+ CloseSession();
+ return -1;
+ }
+ else
+ {
+ user->SetError(gnutls_strerror(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+ }
+
+ int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
+
+ // Session is ready for transferring application data
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ while (true)
+ {
+ // If there is something in the GnuTLS buffer try to send() it
+ int ret = FlushBuffer(user);
+ if (ret <= 0)
+ return ret; // Couldn't flush entire buffer, retry later (or close on error)
+
+ // GnuTLS buffer is empty, if the sendq is empty as well then break to set FD_WANT_NO_WRITE
+ if (sendq.empty())
+ break;
+
+ // GnuTLS buffer is empty but sendq is not, begin sending data from the sendq
+ gnutls_record_cork(this->sess);
+ while ((!sendq.empty()) && (gbuffersize < GetProfile().GetOutgoingRecordSize()))
+ {
+ const StreamSocket::SendQueue::Element& elem = sendq.front();
+ gbuffersize += elem.length();
+ ret = gnutls_record_send(this->sess, elem.data(), elem.length());
+ if (ret < 0)
+ {
+ CloseSession();
+ return -1;
+ }
+ sendq.pop_front();
+ }
+ }
+#else
+ int ret = 0;
+
+ while (!sendq.empty())
+ {
+ FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
+ ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length()));
+
+ if (ret <= 0)
+ return ret;
+ else if (ret < (int)buffer.length())
+ {
+ sendq.erase_front(ret);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+
+ // Wrote entire record, continue sending
+ sendq.pop_front();
+ }
+#endif
+
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
+ return 1;
+ }
+
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
+ {
+ if (!IsHandshakeDone())
+ return;
+ out.append(UnknownIfNULL(gnutls_protocol_get_name(gnutls_protocol_get_version(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
+ }
+
+ bool GetServerName(std::string& out) const CXX11_OVERRIDE
+ {
+ std::vector<char> nameBuffer;
+ size_t nameLength = 0;
+ unsigned int nameType = GNUTLS_NAME_DNS;
+
+ // First, determine the size of the hostname.
+ if (gnutls_server_name_get(sess, &nameBuffer[0], &nameLength, &nameType, 0) != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ return false;
+
+ // Then retrieve the hostname.
+ nameBuffer.resize(nameLength);
+ if (gnutls_server_name_get(sess, &nameBuffer[0], &nameLength, &nameType, 0) != GNUTLS_E_SUCCESS)
+ return false;
+
+ out.append(&nameBuffer[0]);
+ return true;
+ }
+
+ GnuTLS::Profile& GetProfile();
+ bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st)
+{
+#ifndef GNUTLS_NEW_CERT_CALLBACK_API
+ st->type = GNUTLS_CRT_X509;
+#else
+ st->cert_type = GNUTLS_CRT_X509;
+ st->key_type = GNUTLS_PRIVKEY_X509;
+#endif
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess));
+ GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod))->GetProfile().GetX509Credentials();
+
+ st->ncerts = cred.certs.size();
+ st->cert.x509 = cred.certs.raw();
+ st->key.x509 = cred.key.get();
+ st->deinit_all = 0;
+
+ return 0;
+}
+
+class GnuTLSIOHookProvider : public IOHookProvider
+{
+ GnuTLS::Profile profile;
+
+ public:
+ GnuTLSIOHookProvider(Module* mod, GnuTLS::Profile::Config& config)
+ : IOHookProvider(mod, "ssl/" + config.name, IOHookProvider::IOH_SSL)
+ , profile(config)
+ {
+ ServerInstance->Modules->AddService(*this);
+ }
+
+ ~GnuTLSIOHookProvider()
+ {
+ ServerInstance->Modules->DelService(*this);
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ new GnuTLSIOHook(this, sock, GNUTLS_SERVER);
+ }
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
{
- if (starttls.enabled)
- capHandler.HandleEvent(ev);
+ new GnuTLSIOHook(this, sock, GNUTLS_CLIENT);
+ }
+
+ GnuTLS::Profile& GetProfile() { return profile; }
+};
+
+GnuTLS::Profile& GnuTLSIOHook::GetProfile()
+{
+ IOHookProvider* hookprov = prov;
+ return static_cast<GnuTLSIOHookProvider*>(hookprov)->GetProfile();
+}
+
+class ModuleSSLGnuTLS : public Module
+{
+ typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
+
+ // First member of the class, gets constructed first and destructed last
+ GnuTLS::Init libinit;
+ ProfileList profiles;
+
+ void ReadProfiles()
+ {
+ // First, store all profiles in a new, temporary container. If no problems occur, swap the two
+ // containers; this way if something goes wrong we can go back and continue using the current profiles,
+ // avoiding unpleasant situations where no new SSL connections are possible.
+ ProfileList newprofiles;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
+ {
+ // No <sslprofile> tags found, create a profile named "gnutls" from settings in the <gnutls> block
+ const std::string defname = "gnutls";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <gnutls> tag");
+
+ try
+ {
+ GnuTLS::Profile::Config profileconfig(defname, tag);
+ newprofiles.push_back(new GnuTLSIOHookProvider(this, profileconfig));
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+ }
+ }
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ if (!stdalgo::string::equalsci(tag->getString("provider"), "gnutls"))
+ continue;
+
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
+
+ reference<GnuTLSIOHookProvider> prov;
+ try
+ {
+ GnuTLS::Profile::Config profileconfig(name, tag);
+ prov = new GnuTLSIOHookProvider(this, profileconfig);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
+
+ newprofiles.push_back(prov);
+ }
+
+ // New profiles are ok, begin using them
+ // Old profiles are deleted when their refcount drops to zero
+ for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
+ {
+ GnuTLSIOHookProvider& prov = **i;
+ ServerInstance->Modules.DelService(prov);
+ }
+
+ profiles.swap(newprofiles);
+ }
+
+ public:
+ ModuleSSLGnuTLS()
+ {
+#ifndef GNUTLS_HAS_RND
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+ thismod = this;
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "GnuTLS lib version %s module was compiled for " GNUTLS_VERSION, gnutls_check_version(NULL));
+ ReadProfiles();
+ ServerInstance->GenRandom = RandGen::Call;
+ }
+
+ void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
+ {
+ if(param != "ssl")
+ return;
+
+ try
+ {
+ ReadProfiles();
+ }
+ catch (ModuleException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
+ }
+ }
+
+ ~ModuleSSLGnuTLS()
+ {
+ ServerInstance->GenRandom = &InspIRCd::DefaultGenRandom;
+ }
+
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+ {
+ if (type == ExtensionItem::EXT_USER)
+ {
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+
+ if ((user) && (user->eh.GetModHook(this)))
+ {
+ // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users->QuitUser(user, "SSL module unloading");
+ }
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support for clients", VF_VENDOR);
}
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- if ((user->eh.GetIOHook() == this) && (sessions[user->eh.GetFd()].status != ISSL_HANDSHAKEN))
+ const GnuTLSIOHook* const iohook = static_cast<GnuTLSIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/extra/m_ssl_mbedtls.cpp b/src/modules/extra/m_ssl_mbedtls.cpp
new file mode 100644
index 000000000..75b25fbc4
--- /dev/null
+++ b/src/modules/extra/m_ssl_mbedtls.cpp
@@ -0,0 +1,969 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// $LinkerFlags: -lmbedtls
+
+/// $PackageInfo: require_system("darwin") mbedtls
+/// $PackageInfo: require_system("debian" "9.0") libmbedtls-dev
+/// $PackageInfo: require_system("ubuntu" "16.04") libmbedtls-dev
+
+
+#include "inspircd.h"
+#include "modules/ssl.h"
+
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/dhm.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/error.h>
+#include <mbedtls/md.h>
+#include <mbedtls/pk.h>
+#include <mbedtls/ssl.h>
+#include <mbedtls/ssl_ciphersuites.h>
+#include <mbedtls/version.h>
+#include <mbedtls/x509.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/x509_crl.h>
+
+#ifdef INSPIRCD_MBEDTLS_LIBRARY_DEBUG
+#include <mbedtls/debug.h>
+#endif
+
+namespace mbedTLS
+{
+ class Exception : public ModuleException
+ {
+ public:
+ Exception(const std::string& reason)
+ : ModuleException(reason) { }
+ };
+
+ std::string ErrorToString(int errcode)
+ {
+ char buf[256];
+ mbedtls_strerror(errcode, buf, sizeof(buf));
+ return buf;
+ }
+
+ void ThrowOnError(int errcode, const char* msg)
+ {
+ if (errcode != 0)
+ {
+ std::string reason = msg;
+ reason.append(" :").append(ErrorToString(errcode));
+ throw Exception(reason);
+ }
+ }
+
+ template <typename T, void (*init)(T*), void (*deinit)(T*)>
+ class RAIIObj
+ {
+ T obj;
+
+ public:
+ RAIIObj()
+ {
+ init(&obj);
+ }
+
+ ~RAIIObj()
+ {
+ deinit(&obj);
+ }
+
+ T* get() { return &obj; }
+ const T* get() const { return &obj; }
+ };
+
+ typedef RAIIObj<mbedtls_entropy_context, mbedtls_entropy_init, mbedtls_entropy_free> Entropy;
+
+ class CTRDRBG : private RAIIObj<mbedtls_ctr_drbg_context, mbedtls_ctr_drbg_init, mbedtls_ctr_drbg_free>
+ {
+ public:
+ bool Seed(Entropy& entropy)
+ {
+ return (mbedtls_ctr_drbg_seed(get(), mbedtls_entropy_func, entropy.get(), NULL, 0) == 0);
+ }
+
+ void SetupConf(mbedtls_ssl_config* conf)
+ {
+ mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, get());
+ }
+ };
+
+ class DHParams : public RAIIObj<mbedtls_dhm_context, mbedtls_dhm_init, mbedtls_dhm_free>
+ {
+ public:
+ void set(const std::string& dhstr)
+ {
+ // Last parameter is buffer size, must include the terminating null
+ int ret = mbedtls_dhm_parse_dhm(get(), reinterpret_cast<const unsigned char*>(dhstr.c_str()), dhstr.size()+1);
+ ThrowOnError(ret, "Unable to import DH params");
+ }
+ };
+
+ class X509Key : public RAIIObj<mbedtls_pk_context, mbedtls_pk_init, mbedtls_pk_free>
+ {
+ public:
+ /** Import */
+ X509Key(const std::string& keystr)
+ {
+ int ret = mbedtls_pk_parse_key(get(), reinterpret_cast<const unsigned char*>(keystr.c_str()), keystr.size()+1, NULL, 0);
+ ThrowOnError(ret, "Unable to import private key");
+ }
+ };
+
+ class Ciphersuites
+ {
+ std::vector<int> list;
+
+ public:
+ Ciphersuites(const std::string& str)
+ {
+ // mbedTLS uses the ciphersuite format "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256" internally.
+ // This is a bit verbose, so we make life a bit simpler for admins by not requiring them to supply the static parts.
+ irc::sepstream ss(str, ':');
+ for (std::string token; ss.GetToken(token); )
+ {
+ // Prepend "TLS-" if not there
+ if (token.compare(0, 4, "TLS-", 4))
+ token.insert(0, "TLS-");
+
+ const int id = mbedtls_ssl_get_ciphersuite_id(token.c_str());
+ if (!id)
+ throw Exception("Unknown ciphersuite " + token);
+ list.push_back(id);
+ }
+ list.push_back(0);
+ }
+
+ const int* get() const { return &list.front(); }
+ bool empty() const { return (list.size() <= 1); }
+ };
+
+ class Curves
+ {
+ std::vector<mbedtls_ecp_group_id> list;
+
+ public:
+ Curves(const std::string& str)
+ {
+ irc::sepstream ss(str, ':');
+ for (std::string token; ss.GetToken(token); )
+ {
+ const mbedtls_ecp_curve_info* curve = mbedtls_ecp_curve_info_from_name(token.c_str());
+ if (!curve)
+ throw Exception("Unknown curve " + token);
+ list.push_back(curve->grp_id);
+ }
+ list.push_back(MBEDTLS_ECP_DP_NONE);
+ }
+
+ const mbedtls_ecp_group_id* get() const { return &list.front(); }
+ bool empty() const { return (list.size() <= 1); }
+ };
+
+ class X509CertList : public RAIIObj<mbedtls_x509_crt, mbedtls_x509_crt_init, mbedtls_x509_crt_free>
+ {
+ public:
+ /** Import or create empty */
+ X509CertList(const std::string& certstr, bool allowempty = false)
+ {
+ if ((allowempty) && (certstr.empty()))
+ return;
+ int ret = mbedtls_x509_crt_parse(get(), reinterpret_cast<const unsigned char*>(certstr.c_str()), certstr.size()+1);
+ ThrowOnError(ret, "Unable to load certificates");
+ }
+
+ bool empty() const { return (get()->raw.p != NULL); }
+ };
+
+ class X509CRL : public RAIIObj<mbedtls_x509_crl, mbedtls_x509_crl_init, mbedtls_x509_crl_free>
+ {
+ public:
+ X509CRL(const std::string& crlstr)
+ {
+ if (crlstr.empty())
+ return;
+ int ret = mbedtls_x509_crl_parse(get(), reinterpret_cast<const unsigned char*>(crlstr.c_str()), crlstr.size()+1);
+ ThrowOnError(ret, "Unable to load CRL");
+ }
+ };
+
+ class X509Credentials
+ {
+ /** Private key
+ */
+ X509Key key;
+
+ /** Certificate list, presented to the peer
+ */
+ X509CertList certs;
+
+ public:
+ X509Credentials(const std::string& certstr, const std::string& keystr)
+ : key(keystr)
+ , certs(certstr)
+ {
+ // Verify that one of the certs match the private key
+ bool found = false;
+ for (mbedtls_x509_crt* cert = certs.get(); cert; cert = cert->next)
+ {
+ if (mbedtls_pk_check_pair(&cert->pk, key.get()) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ throw Exception("Public/private key pair does not match");
+ }
+
+ mbedtls_pk_context* getkey() { return key.get(); }
+ mbedtls_x509_crt* getcerts() { return certs.get(); }
+ };
+
+ class Context
+ {
+ mbedtls_ssl_config conf;
+
+#ifdef INSPIRCD_MBEDTLS_LIBRARY_DEBUG
+ static void DebugLogFunc(void* userptr, int level, const char* file, int line, const char* msg)
+ {
+ // Remove trailing \n
+ size_t len = strlen(msg);
+ if ((len > 0) && (msg[len-1] == '\n'))
+ len--;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s:%d %.*s", file, line, len, msg);
+ }
+#endif
+
+ public:
+ Context(CTRDRBG& ctrdrbg, unsigned int endpoint)
+ {
+ mbedtls_ssl_config_init(&conf);
+#ifdef INSPIRCD_MBEDTLS_LIBRARY_DEBUG
+ mbedtls_debug_set_threshold(INT_MAX);
+ mbedtls_ssl_conf_dbg(&conf, DebugLogFunc, NULL);
+#endif
+
+ // TODO: check ret of mbedtls_ssl_config_defaults
+ mbedtls_ssl_config_defaults(&conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ ctrdrbg.SetupConf(&conf);
+ }
+
+ ~Context()
+ {
+ mbedtls_ssl_config_free(&conf);
+ }
+
+ void SetMinDHBits(unsigned int mindh)
+ {
+ mbedtls_ssl_conf_dhm_min_bitlen(&conf, mindh);
+ }
+
+ void SetDHParams(DHParams& dh)
+ {
+ mbedtls_ssl_conf_dh_param_ctx(&conf, dh.get());
+ }
+
+ void SetX509CertAndKey(X509Credentials& x509cred)
+ {
+ mbedtls_ssl_conf_own_cert(&conf, x509cred.getcerts(), x509cred.getkey());
+ }
+
+ void SetCiphersuites(const Ciphersuites& ciphersuites)
+ {
+ mbedtls_ssl_conf_ciphersuites(&conf, ciphersuites.get());
+ }
+
+ void SetCurves(const Curves& curves)
+ {
+ mbedtls_ssl_conf_curves(&conf, curves.get());
+ }
+
+ void SetVersion(int minver, int maxver)
+ {
+ // SSL v3 support cannot be enabled
+ if (minver)
+ mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, minver);
+ if (maxver)
+ mbedtls_ssl_conf_max_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, maxver);
+ }
+
+ void SetCA(X509CertList& certs, X509CRL& crl)
+ {
+ mbedtls_ssl_conf_ca_chain(&conf, certs.get(), crl.get());
+ }
+
+ void SetOptionalVerifyCert()
+ {
+ mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+ }
+
+ const mbedtls_ssl_config* GetConf() const { return &conf; }
+ };
+
+ class Hash
+ {
+ const mbedtls_md_info_t* md;
+
+ /** Buffer where cert hashes are written temporarily
+ */
+ mutable std::vector<unsigned char> buf;
+
+ public:
+ Hash(std::string hashstr)
+ {
+ std::transform(hashstr.begin(), hashstr.end(), hashstr.begin(), ::toupper);
+ md = mbedtls_md_info_from_string(hashstr.c_str());
+ if (!md)
+ throw Exception("Unknown hash: " + hashstr);
+
+ buf.resize(mbedtls_md_get_size(md));
+ }
+
+ std::string hash(const unsigned char* input, size_t length) const
+ {
+ mbedtls_md(md, input, length, &buf.front());
+ return BinToHex(&buf.front(), buf.size());
+ }
+ };
+
+ class Profile
+ {
+ /** Name of this profile
+ */
+ const std::string name;
+
+ X509Credentials x509cred;
+
+ /** Ciphersuites to use
+ */
+ Ciphersuites ciphersuites;
+
+ /** Curves accepted for use in ECDHE and in the peer's end-entity certificate
+ */
+ Curves curves;
+
+ Context serverctx;
+ Context clientctx;
+
+ DHParams dhparams;
+
+ X509CertList cacerts;
+
+ X509CRL crl;
+
+ /** Hashing algorithm to use when generating certificate fingerprints
+ */
+ Hash hash;
+
+ /** Rough max size of records to send
+ */
+ const unsigned int outrecsize;
+
+ public:
+ struct Config
+ {
+ const std::string name;
+
+ CTRDRBG& ctrdrbg;
+
+ const std::string certstr;
+ const std::string keystr;
+ const std::string dhstr;
+
+ const std::string ciphersuitestr;
+ const std::string curvestr;
+ const unsigned int mindh;
+ const std::string hashstr;
+
+ std::string crlstr;
+ std::string castr;
+
+ const int minver;
+ const int maxver;
+ const unsigned int outrecsize;
+ const bool requestclientcert;
+
+ Config(const std::string& profilename, ConfigTag* tag, CTRDRBG& ctr_drbg)
+ : name(profilename)
+ , ctrdrbg(ctr_drbg)
+ , certstr(ReadFile(tag->getString("certfile", "cert.pem")))
+ , keystr(ReadFile(tag->getString("keyfile", "key.pem")))
+ , dhstr(ReadFile(tag->getString("dhfile", "dhparams.pem")))
+ , ciphersuitestr(tag->getString("ciphersuites"))
+ , curvestr(tag->getString("curves"))
+ , mindh(tag->getUInt("mindhbits", 2048))
+ , hashstr(tag->getString("hash", "sha256"))
+ , castr(tag->getString("cafile"))
+ , minver(tag->getUInt("minver", 0))
+ , maxver(tag->getUInt("maxver", 0))
+ , outrecsize(tag->getUInt("outrecsize", 2048, 512, 16384))
+ , requestclientcert(tag->getBool("requestclientcert", true))
+ {
+ if (!castr.empty())
+ {
+ castr = ReadFile(castr);
+ crlstr = tag->getString("crlfile");
+ if (!crlstr.empty())
+ crlstr = ReadFile(crlstr);
+ }
+ }
+ };
+
+ Profile(Config& config)
+ : name(config.name)
+ , x509cred(config.certstr, config.keystr)
+ , ciphersuites(config.ciphersuitestr)
+ , curves(config.curvestr)
+ , serverctx(config.ctrdrbg, MBEDTLS_SSL_IS_SERVER)
+ , clientctx(config.ctrdrbg, MBEDTLS_SSL_IS_CLIENT)
+ , cacerts(config.castr, true)
+ , crl(config.crlstr)
+ , hash(config.hashstr)
+ , outrecsize(config.outrecsize)
+ {
+ serverctx.SetX509CertAndKey(x509cred);
+ clientctx.SetX509CertAndKey(x509cred);
+ clientctx.SetMinDHBits(config.mindh);
+
+ if (!ciphersuites.empty())
+ {
+ serverctx.SetCiphersuites(ciphersuites);
+ clientctx.SetCiphersuites(ciphersuites);
+ }
+
+ if (!curves.empty())
+ {
+ serverctx.SetCurves(curves);
+ clientctx.SetCurves(curves);
+ }
+
+ serverctx.SetVersion(config.minver, config.maxver);
+ clientctx.SetVersion(config.minver, config.maxver);
+
+ if (!config.dhstr.empty())
+ {
+ dhparams.set(config.dhstr);
+ serverctx.SetDHParams(dhparams);
+ }
+
+ clientctx.SetOptionalVerifyCert();
+ clientctx.SetCA(cacerts, crl);
+ // The default for servers is to not request a client certificate from the peer
+ if (config.requestclientcert)
+ {
+ serverctx.SetOptionalVerifyCert();
+ serverctx.SetCA(cacerts, crl);
+ }
+ }
+
+ static std::string ReadFile(const std::string& filename)
+ {
+ FileReader reader(filename);
+ std::string ret = reader.GetString();
+ if (ret.empty())
+ throw Exception("Cannot read file " + filename);
+ return ret;
+ }
+
+ /** Set up the given session with the settings in this profile
+ */
+ void SetupClientSession(mbedtls_ssl_context* sess)
+ {
+ mbedtls_ssl_setup(sess, clientctx.GetConf());
+ }
+
+ void SetupServerSession(mbedtls_ssl_context* sess)
+ {
+ mbedtls_ssl_setup(sess, serverctx.GetConf());
+ }
+
+ const std::string& GetName() const { return name; }
+ X509Credentials& GetX509Credentials() { return x509cred; }
+ unsigned int GetOutgoingRecordSize() const { return outrecsize; }
+ const Hash& GetHash() const { return hash; }
+ };
+}
+
+class mbedTLSIOHook : public SSLIOHook
+{
+ enum Status
+ {
+ ISSL_NONE,
+ ISSL_HANDSHAKING,
+ ISSL_HANDSHAKEN
+ };
+
+ mbedtls_ssl_context sess;
+ Status status;
+
+ void CloseSession()
+ {
+ if (status == ISSL_NONE)
+ return;
+
+ mbedtls_ssl_close_notify(&sess);
+ mbedtls_ssl_free(&sess);
+ certificate = NULL;
+ status = ISSL_NONE;
+ }
+
+ // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+ int Handshake(StreamSocket* sock)
+ {
+ int ret = mbedtls_ssl_handshake(&sess);
+ if (ret == 0)
+ {
+ // Change the seesion state
+ this->status = ISSL_HANDSHAKEN;
+
+ VerifyCertificate();
+
+ // Finish writing, if any left
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+
+ return 1;
+ }
+
+ this->status = ISSL_HANDSHAKING;
+ if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ return 0;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+
+ sock->SetError("Handshake Failed - " + mbedTLS::ErrorToString(ret));
+ CloseSession();
+ return -1;
+ }
+
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_HANDSHAKEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
+ }
+
+ CloseSession();
+ sock->SetError("No SSL session");
+ return -1;
+ }
+
+ void VerifyCertificate()
+ {
+ this->certificate = new ssl_cert;
+ const mbedtls_x509_crt* const cert = mbedtls_ssl_get_peer_cert(&sess);
+ if (!cert)
+ {
+ certificate->error = "No client certificate sent";
+ return;
+ }
+
+ // If there is a certificate we can always generate a fingerprint
+ certificate->fingerprint = GetProfile().GetHash().hash(cert->raw.p, cert->raw.len);
+
+ // At this point mbedTLS verified the cert already, we just need to check the results
+ const uint32_t flags = mbedtls_ssl_get_verify_result(&sess);
+ if (flags == 0xFFFFFFFF)
+ {
+ certificate->error = "Internal error during verification";
+ return;
+ }
+
+ if (flags == 0)
+ {
+ // Verification succeeded
+ certificate->trusted = true;
+ }
+ else
+ {
+ // Verification failed
+ certificate->trusted = false;
+ if ((flags & MBEDTLS_X509_BADCERT_EXPIRED) || (flags & MBEDTLS_X509_BADCERT_FUTURE))
+ certificate->error = "Not activated, or expired certificate";
+ }
+
+ certificate->unknownsigner = (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED);
+ certificate->revoked = (flags & MBEDTLS_X509_BADCERT_REVOKED);
+ certificate->invalid = ((flags & MBEDTLS_X509_BADCERT_BAD_KEY) || (flags & MBEDTLS_X509_BADCERT_BAD_MD) || (flags & MBEDTLS_X509_BADCERT_BAD_PK));
+
+ GetDNString(&cert->subject, certificate->dn);
+ GetDNString(&cert->issuer, certificate->issuer);
+ }
+
+ static void GetDNString(const mbedtls_x509_name* x509name, std::string& out)
+ {
+ char buf[512];
+ const int ret = mbedtls_x509_dn_gets(buf, sizeof(buf), x509name);
+ if (ret <= 0)
+ return;
+
+ out.assign(buf, ret);
+ }
+
+ static int Pull(void* userptr, unsigned char* buffer, size_t size)
+ {
+ StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr);
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+ return MBEDTLS_ERR_SSL_WANT_READ;
+
+ const int ret = SocketEngine::Recv(sock, reinterpret_cast<char*>(buffer), size, 0);
+ if (ret < (int)size)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ if ((ret == -1) && (SocketEngine::IgnoreError()))
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ return ret;
+ }
+
+ static int Push(void* userptr, const unsigned char* buffer, size_t size)
+ {
+ StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr);
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ return MBEDTLS_ERR_SSL_WANT_WRITE;
+
+ const int ret = SocketEngine::Send(sock, buffer, size, 0);
+ if (ret < (int)size)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ if ((ret == -1) && (SocketEngine::IgnoreError()))
+ return MBEDTLS_ERR_SSL_WANT_WRITE;
+ }
+ return ret;
+ }
+
+ public:
+ mbedTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool isserver)
+ : SSLIOHook(hookprov)
+ , status(ISSL_NONE)
+ {
+ mbedtls_ssl_init(&sess);
+ if (isserver)
+ GetProfile().SetupServerSession(&sess);
+ else
+ GetProfile().SetupClientSession(&sess);
+
+ mbedtls_ssl_set_bio(&sess, reinterpret_cast<void*>(sock), Push, Pull, NULL);
+
+ sock->AddIOHook(this);
+ Handshake(sock);
+ }
+
+ void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ CloseSession();
+ }
+
+ int OnStreamSocketRead(StreamSocket* sock, std::string& recvq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(sock);
+ if (prepret <= 0)
+ return prepret;
+
+ // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
+ char* const readbuf = ServerInstance->GetReadBuffer();
+ const size_t readbufsize = ServerInstance->Config->NetBufferSize;
+ int ret = mbedtls_ssl_read(&sess, reinterpret_cast<unsigned char*>(readbuf), readbufsize);
+ if (ret > 0)
+ {
+ recvq.append(readbuf, ret);
+
+ // Schedule a read if there is still data in the mbedTLS buffer
+ if (mbedtls_ssl_get_bytes_avail(&sess) > 0)
+ SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_READ);
+ return 1;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ);
+ return 0;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ sock->SetError("Connection closed");
+ CloseSession();
+ return -1;
+ }
+ else // error or MBEDTLS_ERR_SSL_CLIENT_RECONNECT which we treat as an error
+ {
+ sock->SetError(mbedTLS::ErrorToString(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
+ int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(sock);
+ if (prepret <= 0)
+ return prepret;
+
+ // Session is ready for transferring application data
+ while (!sendq.empty())
+ {
+ FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
+ int ret = mbedtls_ssl_write(&sess, reinterpret_cast<const unsigned char*>(buffer.data()), buffer.length());
+ if (ret == (int)buffer.length())
+ {
+ // Wrote entire record, continue sending
+ sendq.pop_front();
+ }
+ else if (ret > 0)
+ {
+ sendq.erase_front(ret);
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ sock->SetError("Connection closed");
+ CloseSession();
+ return -1;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ);
+ return 0;
+ }
+ else
+ {
+ sock->SetError(mbedTLS::ErrorToString(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
+ SocketEngine::ChangeEventMask(sock, FD_WANT_NO_WRITE);
+ return 1;
+ }
+
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
+ {
+ if (!IsHandshakeDone())
+ return;
+ out.append(mbedtls_ssl_get_version(&sess)).push_back('-');
+
+ // All mbedTLS ciphersuite names currently begin with "TLS-" which provides no useful information so skip it, but be prepared if it changes
+ const char* const ciphersuitestr = mbedtls_ssl_get_ciphersuite(&sess);
+ const char prefix[] = "TLS-";
+ unsigned int skip = sizeof(prefix)-1;
+ if (strncmp(ciphersuitestr, prefix, sizeof(prefix)-1))
+ skip = 0;
+ out.append(ciphersuitestr + skip);
+ }
+
+ bool GetServerName(std::string& out) const CXX11_OVERRIDE
+ {
+ // TODO: Implement SNI support.
+ return false;
+ }
+
+ mbedTLS::Profile& GetProfile();
+ bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+class mbedTLSIOHookProvider : public IOHookProvider
+{
+ mbedTLS::Profile profile;
+
+ public:
+ mbedTLSIOHookProvider(Module* mod, mbedTLS::Profile::Config& config)
+ : IOHookProvider(mod, "ssl/" + config.name, IOHookProvider::IOH_SSL)
+ , profile(config)
+ {
+ ServerInstance->Modules->AddService(*this);
+ }
+
+ ~mbedTLSIOHookProvider()
+ {
+ ServerInstance->Modules->DelService(*this);
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ new mbedTLSIOHook(this, sock, true);
+ }
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ new mbedTLSIOHook(this, sock, false);
+ }
+
+ mbedTLS::Profile& GetProfile() { return profile; }
+};
+
+mbedTLS::Profile& mbedTLSIOHook::GetProfile()
+{
+ IOHookProvider* hookprov = prov;
+ return static_cast<mbedTLSIOHookProvider*>(hookprov)->GetProfile();
+}
+
+class ModuleSSLmbedTLS : public Module
+{
+ typedef std::vector<reference<mbedTLSIOHookProvider> > ProfileList;
+
+ mbedTLS::Entropy entropy;
+ mbedTLS::CTRDRBG ctr_drbg;
+ ProfileList profiles;
+
+ void ReadProfiles()
+ {
+ // First, store all profiles in a new, temporary container. If no problems occur, swap the two
+ // containers; this way if something goes wrong we can go back and continue using the current profiles,
+ // avoiding unpleasant situations where no new SSL connections are possible.
+ ProfileList newprofiles;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
+ {
+ // No <sslprofile> tags found, create a profile named "mbedtls" from settings in the <mbedtls> block
+ const std::string defname = "mbedtls";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <mbedtls> tag");
+
+ try
+ {
+ mbedTLS::Profile::Config profileconfig(defname, tag, ctr_drbg);
+ newprofiles.push_back(new mbedTLSIOHookProvider(this, profileconfig));
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+ }
+ }
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ if (!stdalgo::string::equalsci(tag->getString("provider"), "mbedtls"))
+ continue;
+
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
+
+ reference<mbedTLSIOHookProvider> prov;
+ try
+ {
+ mbedTLS::Profile::Config profileconfig(name, tag, ctr_drbg);
+ prov = new mbedTLSIOHookProvider(this, profileconfig);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
+
+ newprofiles.push_back(prov);
+ }
+
+ // New profiles are ok, begin using them
+ // Old profiles are deleted when their refcount drops to zero
+ for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
+ {
+ mbedTLSIOHookProvider& prov = **i;
+ ServerInstance->Modules.DelService(prov);
+ }
+
+ profiles.swap(newprofiles);
+ }
+
+ public:
+ void init() CXX11_OVERRIDE
+ {
+ char verbuf[16]; // Should be at least 9 bytes in size
+ mbedtls_version_get_string(verbuf);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "mbedTLS lib version %s module was compiled for " MBEDTLS_VERSION_STRING, verbuf);
+
+ if (!ctr_drbg.Seed(entropy))
+ throw ModuleException("CTR DRBG seed failed");
+ ReadProfiles();
+ }
+
+ void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
+ {
+ if (param != "ssl")
+ return;
+
+ try
+ {
+ ReadProfiles();
+ }
+ catch (ModuleException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
+ }
+ }
+
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+ {
+ if (type != ExtensionItem::EXT_USER)
+ return;
+
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+ if ((user) && (user->eh.GetModHook(this)))
+ {
+ // User is using SSL, they're a local user, and they're using our IOHook.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users.QuitUser(user, "SSL module unloading");
+ }
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ const mbedTLSIOHook* const iohook = static_cast<mbedTLSIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support via mbedTLS (PolarSSL)", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleSSLmbedTLS)
diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp
index f2189f257..5f61c71a9 100644
--- a/src/modules/extra/m_ssl_openssl.cpp
+++ b/src/modules/extra/m_ssl_openssl.cpp
@@ -21,852 +21,1050 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- /* HACK: This prevents OpenSSL on OS X 10.7 and later from spewing deprecation
- * warnings for every single function call. As far as I (SaberUK) know, Apple
- * have no plans to remove OpenSSL so this warning just causes needless spam.
- */
-#ifdef __APPLE__
-# define __AVAILABILITYMACROS__
-# define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
-#endif
-
+/// $CompilerFlags: find_compiler_flags("openssl")
+/// $LinkerFlags: find_linker_flags("openssl" "-lssl -lcrypto")
+
+/// $PackageInfo: require_system("centos") openssl-devel pkgconfig
+/// $PackageInfo: require_system("darwin") openssl pkg-config
+/// $PackageInfo: require_system("debian") libssl-dev openssl pkg-config
+/// $PackageInfo: require_system("ubuntu") libssl-dev openssl pkg-config
+
+
#include "inspircd.h"
+#include "iohook.h"
+#include "modules/ssl.h"
+
+// Ignore OpenSSL deprecation warnings on OS X Lion and newer.
+#if defined __APPLE__
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/dh.h>
-#include "ssl.h"
#ifdef _WIN32
# pragma comment(lib, "ssleay32.lib")
# pragma comment(lib, "libeay32.lib")
-# undef MAX_DESCRIPTORS
-# define MAX_DESCRIPTORS 10000
#endif
// Compatibility layer to allow OpenSSL 1.0 to use the 1.1 API.
#if ((defined LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L))
+
+// BIO is opaque in OpenSSL 1.1 but the access API does not exist in 1.0.
+# define BIO_get_data(BIO) BIO->ptr
+# define BIO_set_data(BIO, VALUE) BIO->ptr = VALUE;
+# define BIO_set_init(BIO, VALUE) BIO->init = VALUE;
+
+// These functions have been renamed in OpenSSL 1.1.
+# define OpenSSL_version SSLeay_version
# define X509_getm_notAfter X509_get_notAfter
# define X509_getm_notBefore X509_get_notBefore
# define OPENSSL_init_ssl(OPTIONS, SETTINGS) \
SSL_library_init(); \
SSL_load_error_strings();
-#endif
-/* $ModDesc: Provides SSL support for clients */
+// These macros have been renamed in OpenSSL 1.1.
+# define OPENSSL_VERSION SSLEAY_VERSION
-/* $LinkerFlags: if("USE_FREEBSD_BASE_SSL") -lssl -lcrypto */
-/* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
-/* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
-
-/* $NoPedantic */
-
-
-class ModuleSSLOpenSSL;
+#else
+# define INSPIRCD_OPENSSL_OPAQUE_BIO
+#endif
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
static bool SelfSigned = false;
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-static ModuleSSLOpenSSL* opensslmod = NULL;
-#endif
+static int exdataindex;
char* get_error()
{
return ERR_error_string(ERR_get_error(), NULL);
}
-static int error_callback(const char *str, size_t len, void *u);
+static int OnVerify(int preverify_ok, X509_STORE_CTX* ctx);
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
-/** Represents an SSL user's extra data
- */
-class issl_session
+namespace OpenSSL
{
-public:
- SSL* sess;
- issl_status status;
- reference<ssl_cert> cert;
-
- bool outbound;
- bool data_to_write;
-
- issl_session()
- : sess(NULL)
- , status(ISSL_NONE)
+ class Exception : public ModuleException
{
- outbound = false;
- data_to_write = false;
- }
-};
-
-static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
-{
- /* XXX: This will allow self signed certificates.
- * In the future if we want an option to not allow this,
- * we can just return preverify_ok here, and openssl
- * will boot off self-signed and invalid peer certs.
- */
- int ve = X509_STORE_CTX_get_error(ctx);
-
- SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+ public:
+ Exception(const std::string& reason)
+ : ModuleException(reason) { }
+ };
- return 1;
-}
+ class DHParams
+ {
+ DH* dh;
-class ModuleSSLOpenSSL : public Module
-{
- issl_session* sessions;
+ public:
+ DHParams(const std::string& filename)
+ {
+ BIO* dhpfile = BIO_new_file(filename.c_str(), "r");
+ if (dhpfile == NULL)
+ throw Exception("Couldn't open DH file " + filename);
- SSL_CTX* ctx;
- SSL_CTX* clictx;
+ dh = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
+ BIO_free(dhpfile);
- long ctx_options;
- long clictx_options;
+ if (!dh)
+ throw Exception("Couldn't read DH params from file " + filename);
+ }
- std::string sslports;
- bool use_sha;
+ ~DHParams()
+ {
+ DH_free(dh);
+ }
- ServiceProvider iohook;
+ DH* get()
+ {
+ return dh;
+ }
+ };
- static void SetContextOptions(SSL_CTX* ctx, long defoptions, const std::string& ctxname, ConfigTag* tag)
+ class Context
{
- long setoptions = tag->getInt(ctxname + "setoptions");
- // User-friendly config options for setting context options
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
- if (tag->getBool("cipherserverpref"))
- setoptions |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+ SSL_CTX* const ctx;
+ long ctx_options;
+
+ public:
+ Context(SSL_CTX* context)
+ : ctx(context)
+ {
+ // Sane default options for OpenSSL see https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
+ // and when choosing a cipher, use the server's preferences instead of the client preferences.
+ long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE;
+ // Only turn options on if they exist
+#ifdef SSL_OP_SINGLE_ECDH_USE
+ opts |= SSL_OP_SINGLE_ECDH_USE;
#endif
-#ifdef SSL_OP_NO_COMPRESSION
- if (!tag->getBool("compression", true))
- setoptions |= SSL_OP_NO_COMPRESSION;
+#ifdef SSL_OP_NO_TICKET
+ opts |= SSL_OP_NO_TICKET;
#endif
- if (!tag->getBool("sslv3", true))
- setoptions |= SSL_OP_NO_SSLv3;
- if (!tag->getBool("tlsv1", true))
- setoptions |= SSL_OP_NO_TLSv1;
- long clearoptions = tag->getInt(ctxname + "clearoptions");
- ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Setting OpenSSL %s context options, default: %ld set: %ld clear: %ld", ctxname.c_str(), defoptions, setoptions, clearoptions);
+ ctx_options = SSL_CTX_set_options(ctx, opts);
- // Clear everything
- SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
-
- // Set the default options and what is in the conf
- SSL_CTX_set_options(ctx, defoptions | setoptions);
- long final = SSL_CTX_clear_options(ctx, clearoptions);
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "OpenSSL %s context options: %ld", ctxname.c_str(), final);
- }
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
- void SetupECDH(ConfigTag* tag)
- {
- std::string curvename = tag->getString("ecdhcurve", "prime256v1");
- if (curvename.empty())
- return;
+ long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ mode |= SSL_MODE_RELEASE_BUFFERS;
+#endif
+ SSL_CTX_set_mode(ctx, mode);
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ SSL_CTX_set_info_callback(ctx, StaticSSLInfoCallback);
+ }
- int nid = OBJ_sn2nid(curvename.c_str());
- if (nid == 0)
+ ~Context()
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unknown curve: \"%s\"", curvename.c_str());
- return;
+ SSL_CTX_free(ctx);
}
- EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
- if (!eckey)
+ bool SetDH(DHParams& dh)
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unable to create EC key object");
- return;
+ ERR_clear_error();
+ return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
}
- ERR_clear_error();
- if (SSL_CTX_set_tmp_ecdh(ctx, eckey) < 0)
+#ifndef OPENSSL_NO_ECDH
+ void SetECDH(const std::string& curvename)
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set ECDH parameters");
- ERR_print_errors_cb(error_callback, this);
- }
+ int nid = OBJ_sn2nid(curvename.c_str());
+ if (nid == 0)
+ throw Exception("Unknown curve: " + curvename);
- EC_KEY_free(eckey);
- }
+ EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
+ if (!eckey)
+ throw Exception("Unable to create EC key object");
+
+ ERR_clear_error();
+ bool ret = (SSL_CTX_set_tmp_ecdh(ctx, eckey) >= 0);
+ EC_KEY_free(eckey);
+ if (!ret)
+ throw Exception("Couldn't set ECDH parameters");
+ }
#endif
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- static void SSLInfoCallback(const SSL* ssl, int where, int rc)
- {
- int fd = SSL_get_fd(const_cast<SSL*>(ssl));
- issl_session& session = opensslmod->sessions[fd];
+ bool SetCiphers(const std::string& ciphers)
+ {
+ ERR_clear_error();
+ return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
+ }
- if ((where & SSL_CB_HANDSHAKE_START) && (session.status == ISSL_OPEN))
+ bool SetCerts(const std::string& filename)
{
- // The other side is trying to renegotiate, kill the connection and change status
- // to ISSL_NONE so CheckRenego() closes the session
- session.status = ISSL_NONE;
- ServerInstance->SE->Shutdown(fd, 2);
+ ERR_clear_error();
+ return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
}
- }
- bool CheckRenego(StreamSocket* sock, issl_session* session)
- {
- if (session->status != ISSL_NONE)
- return true;
+ bool SetPrivateKey(const std::string& filename)
+ {
+ ERR_clear_error();
+ return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
+ }
- ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Session %p killed, attempted to renegotiate", (void*)session->sess);
- CloseSession(session);
- sock->SetError("Renegotiation is not allowed");
- return false;
- }
-#endif
+ bool SetCA(const std::string& filename)
+ {
+ ERR_clear_error();
+ return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
+ }
- public:
+ void SetCRL(const std::string& crlfile, const std::string& crlpath, const std::string& crlmode)
+ {
+ if (crlfile.empty() && crlpath.empty())
+ return;
- ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
- {
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- opensslmod = this;
-#endif
- sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+ /* Set CRL mode */
+ unsigned long crlflags = X509_V_FLAG_CRL_CHECK;
+ if (stdalgo::string::equalsci(crlmode, "chain"))
+ {
+ crlflags |= X509_V_FLAG_CRL_CHECK_ALL;
+ }
+ else if (!stdalgo::string::equalsci(crlmode, "leaf"))
+ {
+ throw ModuleException("Unknown mode '" + crlmode + "'; expected either 'chain' (default) or 'leaf'");
+ }
- /* Global SSL library initialization*/
- OPENSSL_init_ssl(0, NULL);
+ /* Load CRL files */
+ X509_STORE* store = SSL_CTX_get_cert_store(ctx);
+ if (!store)
+ {
+ throw ModuleException("Unable to get X509_STORE from SSL context; this should never happen");
+ }
+ ERR_clear_error();
+ if (!X509_STORE_load_locations(store,
+ crlfile.empty() ? NULL : crlfile.c_str(),
+ crlpath.empty() ? NULL : crlpath.c_str()))
+ {
+ int err = ERR_get_error();
+ throw ModuleException("Unable to load CRL file '" + crlfile + "' or CRL path '" + crlpath + "': '" + (err ? ERR_error_string(err, NULL) : "unknown") + "'");
+ }
- /* Build our SSL contexts:
- * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
- */
- ctx = SSL_CTX_new( SSLv23_server_method() );
- clictx = SSL_CTX_new( SSLv23_client_method() );
+ /* Set CRL mode */
+ if (X509_STORE_set_flags(store, crlflags) != 1)
+ {
+ throw ModuleException("Unable to set X509 CRL flags");
+ }
+ }
- SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
- SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ long GetDefaultContextOptions() const
+ {
+ return ctx_options;
+ }
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
- SSL_CTX_set_session_cache_mode(clictx, SSL_SESS_CACHE_OFF);
+ long SetRawContextOptions(long setoptions, long clearoptions)
+ {
+ // Clear everything
+ SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
- long opts = SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE;
- // Only turn options on if they exist
-#ifdef SSL_OP_SINGLE_ECDH_USE
- opts |= SSL_OP_SINGLE_ECDH_USE;
-#endif
-#ifdef SSL_OP_NO_TICKET
- opts |= SSL_OP_NO_TICKET;
-#endif
+ // Set the default options and what is in the conf
+ SSL_CTX_set_options(ctx, ctx_options | setoptions);
+ return SSL_CTX_clear_options(ctx, clearoptions);
+ }
- ctx_options = SSL_CTX_set_options(ctx, opts);
- clictx_options = SSL_CTX_set_options(clictx, opts);
- }
+ void SetVerifyCert()
+ {
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ }
- void init()
- {
- // Needs the flag as it ignores a plain /rehash
- OnModuleRehash(NULL,"ssl");
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- ServerInstance->Modules->AddService(iohook);
- }
+ SSL* CreateServerSession()
+ {
+ SSL* sess = SSL_new(ctx);
+ SSL_set_accept_state(sess); // Act as server
+ return sess;
+ }
- void OnHookIO(StreamSocket* user, ListenSocket* lsb)
- {
- if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
+ SSL* CreateClientSession()
{
- /* Hook the user with our module */
- user->AddIOHook(this);
+ SSL* sess = SSL_new(ctx);
+ SSL_set_connect_state(sess); // Act as client
+ return sess;
}
- }
+ };
- void OnRehash(User* user)
+ class Profile
{
- sslports.clear();
+ /** Name of this profile
+ */
+ const std::string name;
+
+ /** DH parameters in use
+ */
+ DHParams dh;
- ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
+ /** OpenSSL makes us have two contexts, one for servers and one for clients
+ */
+ Context ctx;
+ Context clictx;
+
+ /** Digest to use when generating fingerprints
+ */
+ const EVP_MD* digest;
+
+ /** Last error, set by error_callback()
+ */
+ std::string lasterr;
+
+ /** True if renegotiations are allowed, false if not
+ */
+ const bool allowrenego;
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- // Set the callback if we are not allowing renegotiations, unset it if we do
- if (Conf->getBool("renegotiation", true))
+ /** Rough max size of records to send
+ */
+ const unsigned int outrecsize;
+
+ static int error_callback(const char* str, size_t len, void* u)
{
- SSL_CTX_set_info_callback(ctx, NULL);
- SSL_CTX_set_info_callback(clictx, NULL);
+ Profile* profile = reinterpret_cast<Profile*>(u);
+ profile->lasterr = std::string(str, len - 1);
+ return 0;
}
- else
+
+ /** Set raw OpenSSL context (SSL_CTX) options from a config tag
+ * @param ctxname Name of the context, client or server
+ * @param tag Config tag defining this profile
+ * @param context Context object to manipulate
+ */
+ void SetContextOptions(const std::string& ctxname, ConfigTag* tag, Context& context)
{
- SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
- SSL_CTX_set_info_callback(clictx, SSLInfoCallback);
- }
+ long setoptions = tag->getInt(ctxname + "setoptions", 0);
+ long clearoptions = tag->getInt(ctxname + "clearoptions", 0);
+#ifdef SSL_OP_NO_COMPRESSION
+ if (!tag->getBool("compression", false)) // Disable compression by default
+ setoptions |= SSL_OP_NO_COMPRESSION;
#endif
+ // Disable TLSv1.0 by default.
+ if (!tag->getBool("tlsv1", false))
+ setoptions |= SSL_OP_NO_TLSv1;
- if (Conf->getBool("showports", true))
- {
- sslports = Conf->getString("advertisedports");
- if (!sslports.empty())
- return;
+ if (!setoptions && !clearoptions)
+ return; // Nothing to do
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
- {
- ListenSocket* port = ServerInstance->ports[i];
- if (port->bind_tag->getString("ssl") != "openssl")
- continue;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Setting %s %s context options, default: %ld set: %ld clear: %ld", name.c_str(), ctxname.c_str(), ctx.GetDefaultContextOptions(), setoptions, clearoptions);
+ long final = context.SetRawContextOptions(setoptions, clearoptions);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "%s %s context options: %ld", name.c_str(), ctxname.c_str(), final);
+ }
+
+ public:
+ Profile(const std::string& profilename, ConfigTag* tag)
+ : name(profilename)
+ , dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "dhparams.pem")))
+ , ctx(SSL_CTX_new(SSLv23_server_method()))
+ , clictx(SSL_CTX_new(SSLv23_client_method()))
+ , allowrenego(tag->getBool("renegotiation")) // Disallow by default
+ , outrecsize(tag->getUInt("outrecsize", 2048, 512, 16384))
+ {
+ if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
+ throw Exception("Couldn't set DH parameters");
- const std::string& portid = port->bind_desc;
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
+ std::string hash = tag->getString("hash", "md5");
+ digest = EVP_get_digestbyname(hash.c_str());
+ if (digest == NULL)
+ throw Exception("Unknown hash type " + hash);
- if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+ std::string ciphers = tag->getString("ciphers");
+ if (!ciphers.empty())
+ {
+ if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers)))
{
- /*
- * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
- * the IP:port in ISUPPORT.
- *
- * We used to advertise all ports seperated by a ';' char that matched the above criteria,
- * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
- * To solve this by default we now only display the first IP:port found and let the user
- * configure the exact value for the 005 token, if necessary.
- */
- sslports = portid;
- break;
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr);
}
}
- }
- }
-
- void OnModuleRehash(User* user, const std::string &param)
- {
- if (param != "ssl")
- return;
-
- std::string keyfile;
- std::string certfile;
- std::string cafile;
- std::string dhfile;
- OnRehash(user);
- ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
+#ifndef OPENSSL_NO_ECDH
+ std::string curvename = tag->getString("ecdhcurve", "prime256v1");
+ if (!curvename.empty())
+ ctx.SetECDH(curvename);
+#endif
- cafile = conf->getString("cafile", CONFIG_PATH "/ca.pem");
- certfile = conf->getString("certfile", CONFIG_PATH "/cert.pem");
- keyfile = conf->getString("keyfile", CONFIG_PATH "/key.pem");
- dhfile = conf->getString("dhfile", CONFIG_PATH "/dhparams.pem");
- std::string hash = conf->getString("hash", "md5");
- if (hash != "sha1" && hash != "md5")
- throw ModuleException("Unknown hash type " + hash);
- use_sha = (hash == "sha1");
+ SetContextOptions("server", tag, ctx);
+ SetContextOptions("client", tag, clictx);
- if (conf->getBool("customcontextoptions"))
- {
- SetContextOptions(ctx, ctx_options, "server", conf);
- SetContextOptions(clictx, clictx_options, "client", conf);
- }
+ /* Load our keys and certificates
+ * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
+ */
+ std::string filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("certfile", "cert.pem"));
+ if ((!ctx.SetCerts(filename)) || (!clictx.SetCerts(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't read certificate file: " + lasterr);
+ }
- std::string ciphers = conf->getString("ciphers", "");
+ filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("keyfile", "key.pem"));
+ if ((!ctx.SetPrivateKey(filename)) || (!clictx.SetPrivateKey(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't read key file: " + lasterr);
+ }
- if (!ciphers.empty())
- {
- ERR_clear_error();
- if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
+ // Load the CAs we trust
+ filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("cafile", "ca.pem"));
+ if ((!ctx.SetCA(filename)) || (!clictx.SetCA(filename)))
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't set cipher list to %s.", ciphers.c_str());
ERR_print_errors_cb(error_callback, this);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", filename.c_str(), lasterr.c_str());
}
+
+ // Load the CRLs.
+ std::string crlfile = tag->getString("crlfile");
+ std::string crlpath = tag->getString("crlpath");
+ std::string crlmode = tag->getString("crlmode", "chain");
+ ctx.SetCRL(crlfile, crlpath, crlmode);
+
+ clictx.SetVerifyCert();
+ if (tag->getBool("requestclientcert", true))
+ ctx.SetVerifyCert();
}
- /* Load our keys and certificates
- * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
- */
- ERR_clear_error();
- if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
+ const std::string& GetName() const { return name; }
+ SSL* CreateServerSession() { return ctx.CreateServerSession(); }
+ SSL* CreateClientSession() { return clictx.CreateClientSession(); }
+ const EVP_MD* GetDigest() { return digest; }
+ bool AllowRenegotiation() const { return allowrenego; }
+ unsigned int GetOutgoingRecordSize() const { return outrecsize; }
+ };
+
+ namespace BIOMethod
+ {
+ static int create(BIO* bio)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
+ BIO_set_init(bio, 1);
+ return 1;
}
- ERR_clear_error();
- if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
+ static int destroy(BIO* bio)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
+ // XXX: Dummy function to avoid a memory leak in OpenSSL.
+ // The memory leak happens in BIO_free() (bio_lib.c) when the destroy func of the BIO is NULL.
+ // This is fixed in OpenSSL but some distros still ship the unpatched version hence we provide this workaround.
+ return 1;
}
- /* Load the CAs we trust*/
- ERR_clear_error();
- if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
+ static long ctrl(BIO* bio, int cmd, long num, void* ptr)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", cafile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
+ if (cmd == BIO_CTRL_FLUSH)
+ return 1;
+ return 0;
}
-#ifdef _WIN32
- BIO* dhpfile = BIO_new_file(dhfile.c_str(), "r");
-#else
- FILE* dhpfile = fopen(dhfile.c_str(), "r");
-#endif
- DH* ret;
+ static int read(BIO* bio, char* buf, int len);
+ static int write(BIO* bio, const char* buf, int len);
- if (dhpfile == NULL)
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+ static BIO_METHOD* alloc()
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
- throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
+ BIO_METHOD* meth = BIO_meth_new(100 | BIO_TYPE_SOURCE_SINK, "inspircd");
+ BIO_meth_set_write(meth, OpenSSL::BIOMethod::write);
+ BIO_meth_set_read(meth, OpenSSL::BIOMethod::read);
+ BIO_meth_set_ctrl(meth, OpenSSL::BIOMethod::ctrl);
+ BIO_meth_set_create(meth, OpenSSL::BIOMethod::create);
+ BIO_meth_set_destroy(meth, OpenSSL::BIOMethod::destroy);
+ return meth;
}
- else
- {
-#ifdef _WIN32
- ret = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
- BIO_free(dhpfile);
+#endif
+ }
+}
+
+// BIO_METHOD is opaque in OpenSSL 1.1 so we can't do this.
+// See OpenSSL::BIOMethod::alloc for the new method.
+#ifndef INSPIRCD_OPENSSL_OPAQUE_BIO
+static BIO_METHOD biomethods =
+{
+ (100 | BIO_TYPE_SOURCE_SINK),
+ "inspircd",
+ OpenSSL::BIOMethod::write,
+ OpenSSL::BIOMethod::read,
+ NULL, // puts
+ NULL, // gets
+ OpenSSL::BIOMethod::ctrl,
+ OpenSSL::BIOMethod::create,
+ OpenSSL::BIOMethod::destroy, // destroy, does nothing, see function body for more info
+ NULL // callback_ctrl
+};
#else
- ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
+static BIO_METHOD* biomethods;
#endif
- ERR_clear_error();
- if (ret)
+static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
+{
+ /* XXX: This will allow self signed certificates.
+ * In the future if we want an option to not allow this,
+ * we can just return preverify_ok here, and openssl
+ * will boot off self-signed and invalid peer certs.
+ */
+ int ve = X509_STORE_CTX_get_error(ctx);
+
+ SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+
+ return 1;
+}
+
+class OpenSSLIOHook : public SSLIOHook
+{
+ private:
+ SSL* sess;
+ issl_status status;
+ bool data_to_write;
+
+ // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+ int Handshake(StreamSocket* user)
+ {
+ ERR_clear_error();
+ int ret = SSL_do_handshake(sess);
+ if (ret < 0)
+ {
+ int err = SSL_get_error(sess, ret);
+
+ if (err == SSL_ERROR_WANT_READ)
{
- if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
- {
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
- ERR_print_errors_cb(error_callback, this);
- }
- DH_free(ret);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ this->status = ISSL_HANDSHAKING;
+ return 0;
+ }
+ else if (err == SSL_ERROR_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ this->status = ISSL_HANDSHAKING;
+ return 0;
}
else
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s.", dhfile.c_str());
+ CloseSession();
+ return -1;
}
}
+ else if (ret > 0)
+ {
+ // Handshake complete.
+ VerifyCertificate();
-#ifndef _WIN32
- fclose(dhpfile);
-#endif
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
- SetupECDH(conf);
-#endif
- }
-
- void On005Numeric(std::string &output)
- {
- if (!sslports.empty())
- output.append(" SSL=" + sslports);
- }
+ status = ISSL_OPEN;
- ~ModuleSSLOpenSSL()
- {
- SSL_CTX_free(ctx);
- SSL_CTX_free(clictx);
- delete[] sessions;
- }
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
- void OnUserConnect(LocalUser* user)
- {
- if (user->eh.GetIOHook() == this)
+ return 1;
+ }
+ else if (ret == 0)
{
- if (sessions[user->eh.GetFd()].sess)
- {
- if (!sessions[user->eh.GetFd()].cert->fingerprint.empty())
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
- " and your SSL fingerprint is %s", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess), sessions[user->eh.GetFd()].cert->fingerprint.c_str());
- else
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess));
- }
+ CloseSession();
}
+ return -1;
}
- void OnCleanup(int target_type, void* item)
+ void CloseSession()
{
- if (target_type == TYPE_USER)
+ if (sess)
{
- LocalUser* user = IS_LOCAL((User*)item);
-
- if (user && user->eh.GetIOHook() == this)
- {
- // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
- // Potentially there could be multiple SSL modules loaded at once on different ports.
- ServerInstance->Users->QuitUser(user, "SSL module unloading");
- }
+ SSL_shutdown(sess);
+ SSL_free(sess);
}
+ sess = NULL;
+ certificate = NULL;
+ status = ISSL_NONE;
}
- Version GetVersion()
+ void VerifyCertificate()
{
- return Version("Provides SSL support for clients", VF_VENDOR);
- }
+ X509* cert;
+ ssl_cert* certinfo = new ssl_cert;
+ this->certificate = certinfo;
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
- void OnRequest(Request& request)
- {
- if (strcmp("GET_SSL_CERT", request.id) == 0)
+ cert = SSL_get_peer_certificate(sess);
+
+ if (!cert)
{
- SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
- int fd = req.sock->GetFd();
- issl_session* session = &sessions[fd];
+ certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+ return;
+ }
+
+ certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK);
- req.cert = session->cert;
+ if (!SelfSigned)
+ {
+ certinfo->unknownsigner = false;
+ certinfo->trusted = true;
}
- else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+ else
{
- SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
- if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
- req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+ certinfo->unknownsigner = true;
+ certinfo->trusted = false;
}
- }
- void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- {
- int fd = user->GetFd();
-
- issl_session* session = &sessions[fd];
+ char buf[512];
+ X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
+ certinfo->dn = buf;
+ // Make sure there are no chars in the string that we consider invalid
+ if (certinfo->dn.find_first_of("\r\n") != std::string::npos)
+ certinfo->dn.clear();
- session->sess = SSL_new(ctx);
- session->status = ISSL_NONE;
- session->outbound = false;
- session->data_to_write = false;
+ X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
+ certinfo->issuer = buf;
+ if (certinfo->issuer.find_first_of("\r\n") != std::string::npos)
+ certinfo->issuer.clear();
- if (session->sess == NULL)
- return;
+ if (!X509_digest(cert, GetProfile().GetDigest(), md, &n))
+ {
+ certinfo->error = "Out of memory generating fingerprint";
+ }
+ else
+ {
+ certinfo->fingerprint = BinToHex(md, n);
+ }
- if (SSL_set_fd(session->sess, fd) == 0)
+ if ((ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()) == 0))
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
- return;
+ certinfo->error = "Not activated, or expired certificate";
}
- Handshake(user, session);
+ X509_free(cert);
}
- void OnStreamSocketConnect(StreamSocket* user)
+ void SSLInfoCallback(int where, int rc)
{
- int fd = user->GetFd();
- /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
- if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
- return;
+ if ((where & SSL_CB_HANDSHAKE_START) && (status == ISSL_OPEN))
+ {
+ if (GetProfile().AllowRenegotiation())
+ return;
- issl_session* session = &sessions[fd];
+ // The other side is trying to renegotiate, kill the connection and change status
+ // to ISSL_NONE so CheckRenego() closes the session
+ status = ISSL_NONE;
+ BIO* bio = SSL_get_rbio(sess);
+ EventHandler* eh = static_cast<StreamSocket*>(BIO_get_data(bio));
+ SocketEngine::Shutdown(eh, 2);
+ }
+ }
- session->sess = SSL_new(clictx);
- session->status = ISSL_NONE;
- session->outbound = true;
- session->data_to_write = false;
+ bool CheckRenego(StreamSocket* sock)
+ {
+ if (status != ISSL_NONE)
+ return true;
- if (session->sess == NULL)
- return;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Session %p killed, attempted to renegotiate", (void*)sess);
+ CloseSession();
+ sock->SetError("Renegotiation is not allowed");
+ return false;
+ }
- if (SSL_set_fd(session->sess, fd) == 0)
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_OPEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
- return;
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
}
- Handshake(user, session);
+ CloseSession();
+ return -1;
}
- void OnStreamSocketClose(StreamSocket* user)
+ // Calls our private SSLInfoCallback()
+ friend void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
+
+ public:
+ OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, SSL* session)
+ : SSLIOHook(hookprov)
+ , sess(session)
+ , status(ISSL_NONE)
+ , data_to_write(false)
{
- int fd = user->GetFd();
- /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
- if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
- return;
+ // Create BIO instance and store a pointer to the socket in it which will be used by the read and write functions
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+ BIO* bio = BIO_new(biomethods);
+#else
+ BIO* bio = BIO_new(&biomethods);
+#endif
+ BIO_set_data(bio, sock);
+ SSL_set_bio(sess, bio, bio);
- CloseSession(&sessions[fd]);
+ SSL_set_ex_data(sess, exdataindex, this);
+ sock->AddIOHook(this);
+ Handshake(sock);
}
- int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
{
- int fd = user->GetFd();
- /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
- if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
- return -1;
-
- issl_session* session = &sessions[fd];
-
- if (!session->sess)
- {
- CloseSession(session);
- return -1;
- }
-
- if (session->status == ISSL_HANDSHAKING)
- {
- // The handshake isn't finished and it wants to read, try to finish it.
- if (!Handshake(user, session))
- {
- // Couldn't resume handshake.
- if (session->status == ISSL_NONE)
- return -1;
- return 0;
- }
- }
+ CloseSession();
+ }
- // If we resumed the handshake then session->status will be ISSL_OPEN
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
- if (session->status == ISSL_OPEN)
+ // If we resumed the handshake then this->status will be ISSL_OPEN
{
ERR_clear_error();
char* buffer = ServerInstance->GetReadBuffer();
size_t bufsiz = ServerInstance->Config->NetBufferSize;
- int ret = SSL_read(session->sess, buffer, bufsiz);
+ int ret = SSL_read(sess, buffer, bufsiz);
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- if (!CheckRenego(user, session))
+ if (!CheckRenego(user))
return -1;
-#endif
if (ret > 0)
{
recvq.append(buffer, ret);
-
int mask = 0;
// Schedule a read if there is still data in the OpenSSL buffer
- if (SSL_pending(session->sess) > 0)
+ if (SSL_pending(sess) > 0)
mask |= FD_ADD_TRIAL_READ;
- if (session->data_to_write)
+ if (data_to_write)
mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
if (mask != 0)
- ServerInstance->SE->ChangeEventMask(user, mask);
+ SocketEngine::ChangeEventMask(user, mask);
return 1;
}
else if (ret == 0)
{
// Client closed connection.
- CloseSession(session);
+ CloseSession();
user->SetError("Connection closed");
return -1;
}
- else if (ret < 0)
+ else // if (ret < 0)
{
- int err = SSL_get_error(session->sess, ret);
+ int err = SSL_get_error(sess, ret);
if (err == SSL_ERROR_WANT_READ)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
return 0;
}
else if (err == SSL_ERROR_WANT_WRITE)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
return 0;
}
else
{
- CloseSession(session);
+ CloseSession();
return -1;
}
}
}
-
- return 0;
}
- int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
+ int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
{
- int fd = user->GetFd();
-
- issl_session* session = &sessions[fd];
-
- if (!session->sess)
- {
- CloseSession(session);
- return -1;
- }
-
- session->data_to_write = true;
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
- if (session->status == ISSL_HANDSHAKING)
- {
- if (!Handshake(user, session))
- {
- // Couldn't resume handshake.
- if (session->status == ISSL_NONE)
- return -1;
- return 0;
- }
- }
+ data_to_write = true;
- if (session->status == ISSL_OPEN)
+ // Session is ready for transferring application data
+ while (!sendq.empty())
{
ERR_clear_error();
- int ret = SSL_write(session->sess, buffer.data(), buffer.size());
+ FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
+ int ret = SSL_write(sess, buffer.data(), buffer.size());
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- if (!CheckRenego(user, session))
+ if (!CheckRenego(user))
return -1;
-#endif
if (ret == (int)buffer.length())
{
- session->data_to_write = false;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- return 1;
+ // Wrote entire record, continue sending
+ sendq.pop_front();
}
else if (ret > 0)
{
- buffer = buffer.substr(ret);
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ sendq.erase_front(ret);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
else if (ret == 0)
{
- CloseSession(session);
+ CloseSession();
return -1;
}
- else if (ret < 0)
+ else // if (ret < 0)
{
- int err = SSL_get_error(session->sess, ret);
+ int err = SSL_get_error(sess, ret);
if (err == SSL_ERROR_WANT_WRITE)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
else if (err == SSL_ERROR_WANT_READ)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
return 0;
}
else
{
- CloseSession(session);
+ CloseSession();
return -1;
}
}
}
- return 0;
+
+ data_to_write = false;
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ return 1;
}
- bool Handshake(StreamSocket* user, issl_session* session)
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
{
- int ret;
+ if (!IsHandshakeDone())
+ return;
+ out.append(SSL_get_version(sess)).push_back('-');
+ out.append(SSL_get_cipher(sess));
+ }
- ERR_clear_error();
- if (session->outbound)
- ret = SSL_connect(session->sess);
- else
- ret = SSL_accept(session->sess);
+ bool GetServerName(std::string& out) const CXX11_OVERRIDE
+ {
+ const char* name = SSL_get_servername(sess, TLSEXT_NAMETYPE_host_name);
+ if (!name)
+ return false;
- if (ret < 0)
+ out.append(name);
+ return true;
+ }
+
+ bool IsHandshakeDone() const { return (status == ISSL_OPEN); }
+ OpenSSL::Profile& GetProfile();
+};
+
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
+{
+ OpenSSLIOHook* hook = static_cast<OpenSSLIOHook*>(SSL_get_ex_data(ssl, exdataindex));
+ hook->SSLInfoCallback(where, rc);
+}
+
+static int OpenSSL::BIOMethod::write(BIO* bio, const char* buffer, int size)
+{
+ BIO_clear_retry_flags(bio);
+
+ StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+ // Writes blocked earlier, don't retry syscall
+ BIO_set_retry_write(bio);
+ return -1;
+ }
+
+ int ret = SocketEngine::Send(sock, buffer, size, 0);
+ if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+ {
+ // Blocked, set retry flag for OpenSSL
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ BIO_set_retry_write(bio);
+ }
+
+ return ret;
+}
+
+static int OpenSSL::BIOMethod::read(BIO* bio, char* buffer, int size)
+{
+ BIO_clear_retry_flags(bio);
+
+ StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+ {
+ // Reads blocked earlier, don't retry syscall
+ BIO_set_retry_read(bio);
+ return -1;
+ }
+
+ int ret = SocketEngine::Recv(sock, buffer, size, 0);
+ if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+ {
+ // Blocked, set retry flag for OpenSSL
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ BIO_set_retry_read(bio);
+ }
+
+ return ret;
+}
+
+class OpenSSLIOHookProvider : public IOHookProvider
+{
+ OpenSSL::Profile profile;
+
+ public:
+ OpenSSLIOHookProvider(Module* mod, const std::string& profilename, ConfigTag* tag)
+ : IOHookProvider(mod, "ssl/" + profilename, IOHookProvider::IOH_SSL)
+ , profile(profilename, tag)
+ {
+ ServerInstance->Modules->AddService(*this);
+ }
+
+ ~OpenSSLIOHookProvider()
+ {
+ ServerInstance->Modules->DelService(*this);
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ new OpenSSLIOHook(this, sock, profile.CreateServerSession());
+ }
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ new OpenSSLIOHook(this, sock, profile.CreateClientSession());
+ }
+
+ OpenSSL::Profile& GetProfile() { return profile; }
+};
+
+OpenSSL::Profile& OpenSSLIOHook::GetProfile()
+{
+ IOHookProvider* hookprov = prov;
+ return static_cast<OpenSSLIOHookProvider*>(hookprov)->GetProfile();
+}
+
+class ModuleSSLOpenSSL : public Module
+{
+ typedef std::vector<reference<OpenSSLIOHookProvider> > ProfileList;
+
+ ProfileList profiles;
+
+ void ReadProfiles()
+ {
+ ProfileList newprofiles;
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
{
- int err = SSL_get_error(session->sess, ret);
+ // Create a default profile named "openssl"
+ const std::string defname = "openssl";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found, using settings from the <openssl> tag");
- if (err == SSL_ERROR_WANT_READ)
+ try
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- session->status = ISSL_HANDSHAKING;
- return true;
+ newprofiles.push_back(new OpenSSLIOHookProvider(this, defname, tag));
}
- else if (err == SSL_ERROR_WANT_WRITE)
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
- session->status = ISSL_HANDSHAKING;
- return true;
- }
- else
+ catch (OpenSSL::Exception& ex)
{
- CloseSession(session);
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
}
-
- return false;
}
- else if (ret > 0)
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- // Handshake complete.
- VerifyCertificate(session, user);
+ ConfigTag* tag = i->second;
+ if (!stdalgo::string::equalsci(tag->getString("provider"), "openssl"))
+ continue;
- session->status = ISSL_OPEN;
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+ reference<OpenSSLIOHookProvider> prov;
+ try
+ {
+ prov = new OpenSSLIOHookProvider(this, name, tag);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
- return true;
+ newprofiles.push_back(prov);
}
- else if (ret == 0)
+
+ for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
{
- CloseSession(session);
+ OpenSSLIOHookProvider& prov = **i;
+ ServerInstance->Modules.DelService(prov);
}
- return false;
+
+ profiles.swap(newprofiles);
}
- void CloseSession(issl_session* session)
+ public:
+ ModuleSSLOpenSSL()
{
- if (session->sess)
- {
- SSL_shutdown(session->sess);
- SSL_free(session->sess);
- }
+ // Initialize OpenSSL
+ OPENSSL_init_ssl(0, NULL);
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+ biomethods = OpenSSL::BIOMethod::alloc();
+ }
- session->sess = NULL;
- session->status = ISSL_NONE;
- session->cert = NULL;
+ ~ModuleSSLOpenSSL()
+ {
+ BIO_meth_free(biomethods);
+#endif
}
- void VerifyCertificate(issl_session* session, StreamSocket* user)
+ void init() CXX11_OVERRIDE
{
- if (!session->sess || !user)
- return;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OpenSSL lib version \"%s\" module was compiled for \"" OPENSSL_VERSION_TEXT "\"", OpenSSL_version(OPENSSL_VERSION));
- X509* cert;
- ssl_cert* certinfo = new ssl_cert;
- session->cert = certinfo;
- unsigned int n;
- unsigned char md[EVP_MAX_MD_SIZE];
- const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5();
+ // Register application specific data
+ char exdatastr[] = "inspircd";
+ exdataindex = SSL_get_ex_new_index(0, exdatastr, NULL, NULL, NULL);
+ if (exdataindex < 0)
+ throw ModuleException("Failed to register application specific data");
- cert = SSL_get_peer_certificate((SSL*)session->sess);
+ ReadProfiles();
+ }
- if (!cert)
- {
- certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+ void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
+ {
+ if (param != "ssl")
return;
- }
-
- certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
- if (!SelfSigned)
+ try
{
- certinfo->unknownsigner = false;
- certinfo->trusted = true;
+ ReadProfiles();
}
- else
+ catch (ModuleException& ex)
{
- certinfo->unknownsigner = true;
- certinfo->trusted = false;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
}
+ }
- char buf[512];
- X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
- certinfo->dn = buf;
- // Make sure there are no chars in the string that we consider invalid
- if (certinfo->dn.find_first_of("\r\n") != std::string::npos)
- certinfo->dn.clear();
-
- X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
- certinfo->issuer = buf;
- if (certinfo->issuer.find_first_of("\r\n") != std::string::npos)
- certinfo->issuer.clear();
-
- if (!X509_digest(cert, digest, md, &n))
- {
- certinfo->error = "Out of memory generating fingerprint";
- }
- else
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+ {
+ if (type == ExtensionItem::EXT_USER)
{
- certinfo->fingerprint = irc::hex(md, n);
- }
+ LocalUser* user = IS_LOCAL((User*)item);
- if ((ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()) == 0))
- {
- certinfo->error = "Not activated, or expired certificate";
+ if ((user) && (user->eh.GetModHook(this)))
+ {
+ // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users->QuitUser(user, "SSL module unloading");
+ }
}
+ }
- X509_free(cert);
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ const OpenSSLIOHook* const iohook = static_cast<OpenSSLIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
}
-};
-static int error_callback(const char *str, size_t len, void *u)
-{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
-
- //
- // XXX: Remove this line, it causes valgrind warnings...
- //
- // MD_update(&m, buf, j);
- //
- //
- // ... ONLY JOKING! :-)
- //
-
- return 0;
-}
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support for clients", VF_VENDOR);
+ }
+};
MODULE_INIT(ModuleSSLOpenSSL)
diff --git a/src/modules/extra/m_sslrehashsignal.cpp b/src/modules/extra/m_sslrehashsignal.cpp
new file mode 100644
index 000000000..fea32326a
--- /dev/null
+++ b/src/modules/extra/m_sslrehashsignal.cpp
@@ -0,0 +1,64 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+static volatile sig_atomic_t signaled;
+
+class ModuleSSLRehashSignal : public Module
+{
+ private:
+ static void SignalHandler(int)
+ {
+ signaled = 1;
+ }
+
+ public:
+ ~ModuleSSLRehashSignal()
+ {
+ signal(SIGUSR1, SIG_DFL);
+ }
+
+ void init()
+ {
+ signal(SIGUSR1, SignalHandler);
+ }
+
+ void OnBackgroundTimer(time_t)
+ {
+ if (!signaled)
+ return;
+
+ const std::string feedbackmsg = "Got SIGUSR1, reloading SSL credentials";
+ ServerInstance->SNO->WriteGlobalSno('a', feedbackmsg);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, feedbackmsg);
+
+ const std::string str = "ssl";
+ FOREACH_MOD(OnModuleRehash, (NULL, str));
+ signaled = 0;
+ }
+
+ Version GetVersion()
+ {
+ return Version("Reloads SSL credentials on SIGUSR1", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleSSLRehashSignal)
diff --git a/src/modules/hash.h b/src/modules/hash.h
deleted file mode 100644
index f7bf85e20..000000000
--- a/src/modules/hash.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef HASH_H
-#define HASH_H
-
-#include "modules.h"
-
-class HashProvider : public DataProvider
-{
- public:
- const unsigned int out_size;
- const unsigned int block_size;
- HashProvider(Module* mod, const std::string& Name, int osiz, int bsiz)
- : DataProvider(mod, Name), out_size(osiz), block_size(bsiz) {}
- virtual std::string sum(const std::string& data) = 0;
- inline std::string hexsum(const std::string& data)
- {
- return BinToHex(sum(data));
- }
-
- inline std::string b64sum(const std::string& data)
- {
- return BinToBase64(sum(data), NULL, 0);
- }
-
- /** Allows the IVs for the hash to be specified. As the choice of initial IV is
- * important for the security of a hash, this should not be used except to
- * maintain backwards compatability. This also allows you to change the hex
- * sequence from its default of "0123456789abcdef", which does not improve the
- * strength of the output, but helps confuse those attempting to implement it.
- *
- * Example:
- * \code
- * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC };
- * std::string result = Hash.sumIV(iv, "fedcba9876543210", "data");
- * \endcode
- */
- virtual std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata) = 0;
-
- /** HMAC algorithm, RFC 2104 */
- std::string hmac(const std::string& key, const std::string& msg)
- {
- std::string hmac1, hmac2;
- std::string kbuf = key.length() > block_size ? sum(key) : key;
- kbuf.resize(block_size);
-
- for (size_t n = 0; n < block_size; n++)
- {
- hmac1.push_back(static_cast<char>(kbuf[n] ^ 0x5C));
- hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36));
- }
- hmac2.append(msg);
- hmac1.append(sum(hmac2));
- return sum(hmac1);
- }
-};
-
-#endif
-
diff --git a/src/modules/httpd.h b/src/modules/httpd.h
deleted file mode 100644
index 56fd22da0..000000000
--- a/src/modules/httpd.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "base.h"
-
-#ifndef HTTPD_H
-#define HTTPD_H
-
-#include <string>
-#include <sstream>
-#include <map>
-
-/** A modifyable list of HTTP header fields
- */
-class HTTPHeaders
-{
- protected:
- std::map<std::string,std::string> headers;
- public:
-
- /** Set the value of a header
- * Sets the value of the named header. If the header is already present, it will be replaced
- */
- void SetHeader(const std::string &name, const std::string &data)
- {
- headers[name] = data;
- }
-
- /** Set the value of a header, only if it doesn't exist already
- * Sets the value of the named header. If the header is already present, it will NOT be updated
- */
- void CreateHeader(const std::string &name, const std::string &data)
- {
- if (!IsSet(name))
- SetHeader(name, data);
- }
-
- /** Remove the named header
- */
- void RemoveHeader(const std::string &name)
- {
- headers.erase(name);
- }
-
- /** Remove all headers
- */
- void Clear()
- {
- headers.clear();
- }
-
- /** Get the value of a header
- * @return The value of the header, or an empty string
- */
- std::string GetHeader(const std::string &name)
- {
- std::map<std::string,std::string>::iterator it = headers.find(name);
- if (it == headers.end())
- return std::string();
-
- return it->second;
- }
-
- /** Check if the given header is specified
- * @return true if the header is specified
- */
- bool IsSet(const std::string &name)
- {
- std::map<std::string,std::string>::iterator it = headers.find(name);
- return (it != headers.end());
- }
-
- /** Get all headers, formatted by the HTTP protocol
- * @return Returns all headers, formatted according to the HTTP protocol. There is no request terminator at the end
- */
- std::string GetFormattedHeaders()
- {
- std::string re;
-
- for (std::map<std::string,std::string>::iterator i = headers.begin(); i != headers.end(); i++)
- re += i->first + ": " + i->second + "\r\n";
-
- return re;
- }
-};
-
-class HttpServerSocket;
-
-/** This class represents a HTTP request.
- */
-class HTTPRequest : public Event
-{
- protected:
- std::string type;
- std::string document;
- std::string ipaddr;
- std::string postdata;
-
- public:
-
- HTTPHeaders *headers;
- int errorcode;
-
- /** A socket pointer, which you must return in your HTTPDocument class
- * if you reply to this request.
- */
- HttpServerSocket* sock;
-
- /** Initialize HTTPRequest.
- * This constructor is called by m_httpd.so to initialize the class.
- * @param request_type The request type, e.g. GET, POST, HEAD
- * @param uri The URI, e.g. /page
- * @param hdr The headers sent with the request
- * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply.
- * @param ip The IP address making the web request.
- * @param pdata The post data (content after headers) received with the request, up to Content-Length in size
- */
- HTTPRequest(Module* me, const std::string &eventid, const std::string &request_type, const std::string &uri,
- HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata)
- : Event(me, eventid), type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(socket)
- {
- }
-
- /** Get the post data (request content).
- * All post data will be returned, including carriage returns and linefeeds.
- * @return The postdata
- */
- std::string& GetPostData()
- {
- return postdata;
- }
-
- /** Get the request type.
- * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec.
- * @return The request type, e.g. GET, POST, HEAD
- */
- std::string& GetType()
- {
- return type;
- }
-
- /** Get URI.
- * The URI string (URL minus hostname and scheme) will be provided by this function.
- * @return The URI being requested
- */
- std::string& GetURI()
- {
- return document;
- }
-
- /** Get IP address of requester.
- * The requesting system's ip address will be returned.
- * @return The IP address as a string
- */
- std::string& GetIP()
- {
- return ipaddr;
- }
-};
-
-/** You must return a HTTPDocument to the httpd module by using the Request class.
- * When you initialize this class you may initialize it with all components required to
- * form a valid HTTP response, including document data, headers, and a response code.
- */
-class HTTPDocumentResponse : public Request
-{
- public:
- std::stringstream* document;
- int responsecode;
- HTTPHeaders headers;
- HTTPRequest& src;
-
- /** Initialize a HTTPRequest ready for sending to m_httpd.so.
- * @param opaque The socket pointer you obtained from the HTTPRequest at an earlier time
- * @param doc A stringstream containing the document body
- * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you
- * based upon the response code.
- * @param extra Any extra headers to include with the defaults, seperated by carriage return and linefeed.
- */
- HTTPDocumentResponse(Module* me, HTTPRequest& req, std::stringstream* doc, int response)
- : Request(me, req.source, "HTTP-DOC"), document(doc), responsecode(response), src(req)
- {
- }
-};
-
-#endif
-
diff --git a/src/modules/m_abbreviation.cpp b/src/modules/m_abbreviation.cpp
index 1e8f71176..f5065c73f 100644
--- a/src/modules/m_abbreviation.cpp
+++ b/src/modules/m_abbreviation.cpp
@@ -19,49 +19,43 @@
#include "inspircd.h"
-/* $ModDesc: Provides the ability to abbreviate commands a-la BBC BASIC keywords. */
+enum
+{
+ // InspIRCd-specific.
+ ERR_AMBIGUOUSCOMMAND = 420
+};
class ModuleAbbreviation : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnPreCommand, this);
- }
-
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_FIRST);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ability to abbreviate commands a-la BBC BASIC keywords.",VF_VENDOR);
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
/* Command is already validated, has a length of 0, or last character is not a . */
if (validated || command.empty() || *command.rbegin() != '.')
return MOD_RES_PASSTHRU;
- /* Whack the . off the end */
- command.erase(command.end() - 1);
-
/* Look for any command that starts with the same characters, if it does, replace the command string with it */
- size_t clen = command.length();
+ size_t clen = command.length() - 1;
std::string foundcommand, matchlist;
bool foundmatch = false;
- for (Commandtable::iterator n = ServerInstance->Parser->cmdlist.begin(); n != ServerInstance->Parser->cmdlist.end(); ++n)
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator n = commands.begin(); n != commands.end(); ++n)
{
- if (n->first.length() < clen)
- continue;
-
- if (command == n->first.substr(0, clen))
+ if (!command.compare(0, clen, n->first, 0, clen))
{
if (matchlist.length() > 450)
{
- user->WriteNumeric(420, "%s :Ambiguous abbreviation and too many possible matches.", user->nick.c_str());
+ user->WriteNumeric(ERR_AMBIGUOUSCOMMAND, "Ambiguous abbreviation and too many possible matches.");
return MOD_RES_DENY;
}
@@ -79,16 +73,11 @@ class ModuleAbbreviation : public Module
/* Ambiguous command, list the matches */
if (!matchlist.empty())
{
- user->WriteNumeric(420, "%s :Ambiguous abbreviation, possible matches: %s%s", user->nick.c_str(), foundcommand.c_str(), matchlist.c_str());
+ user->WriteNumeric(ERR_AMBIGUOUSCOMMAND, InspIRCd::Format("Ambiguous abbreviation, possible matches: %s%s", foundcommand.c_str(), matchlist.c_str()));
return MOD_RES_DENY;
}
- if (foundcommand.empty())
- {
- /* No match, we have to put the . back again so that the invalid command numeric looks correct. */
- command += '.';
- }
- else
+ if (!foundcommand.empty())
{
command = foundcommand;
}
diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp
index 32fc80b64..6f27ecc09 100644
--- a/src/modules/m_alias.cpp
+++ b/src/modules/m_alias.cpp
@@ -22,15 +22,13 @@
#include "inspircd.h"
-/* $ModDesc: Provides aliases of commands. */
-
/** An alias definition
*/
class Alias
{
public:
/** The text of the alias command */
- irc::string AliasedCommand;
+ std::string AliasedCommand;
/** Text to replace with */
std::string ReplaceFormat;
@@ -44,9 +42,6 @@ class Alias
/** Requires oper? */
bool OperOnly;
- /* is case sensitive params */
- bool CaseSensitive;
-
/* whether or not it may be executed via fantasy (default OFF) */
bool ChannelCommand;
@@ -59,25 +54,29 @@ class Alias
class ModuleAlias : public Module
{
- private:
-
- char fprefix;
+ std::string fprefix;
/* We cant use a map, there may be multiple aliases with the same name.
* We can, however, use a fancy invention: the multimap. Maps a key to one or more values.
* -- w00t
- */
- std::multimap<irc::string, Alias> Aliases;
+ */
+ typedef insp::flat_multimap<std::string, Alias, irc::insensitive_swo> AliasMap;
+
+ AliasMap Aliases;
/* whether or not +B users are allowed to use fantasy commands */
bool AllowBots;
+ UserModeReference botmode;
+
+ // Whether we are actively executing an alias.
+ bool active;
- virtual void ReadAliases()
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy");
AllowBots = fantasy->getBool("allowbots", false);
- std::string fpre = fantasy->getString("prefix", "!");
- fprefix = fpre.empty() ? '!' : fpre[0];
+ fprefix = fantasy->getString("prefix", "!", 1, ServerInstance->Config->Limits.MaxLine);
Aliases.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("alias");
@@ -85,8 +84,8 @@ class ModuleAlias : public Module
{
ConfigTag* tag = i->second;
Alias a;
- std::string aliastext = tag->getString("text");
- a.AliasedCommand = aliastext.c_str();
+ a.AliasedCommand = tag->getString("text");
+ std::transform(a.AliasedCommand.begin(), a.AliasedCommand.end(), a.AliasedCommand.begin(), ::toupper);
tag->readString("replace", a.ReplaceFormat, true);
a.RequiredNick = tag->getString("requires");
a.ULineOnly = tag->getBool("uline");
@@ -94,25 +93,16 @@ class ModuleAlias : public Module
a.UserCommand = tag->getBool("usercommand", true);
a.OperOnly = tag->getBool("operonly");
a.format = tag->getString("format");
- a.CaseSensitive = tag->getBool("matchcase");
Aliases.insert(std::make_pair(a.AliasedCommand, a));
}
}
- public:
-
- void init()
- {
- ReadAliases();
- Implementation eventlist[] = { I_OnPreCommand, I_OnRehash, I_OnUserMessage };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleAlias()
+ ModuleAlias()
+ : botmode(this, "bot")
{
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides aliases of commands.", VF_VENDOR);
}
@@ -142,10 +132,22 @@ class ModuleAlias : public Module
return word;
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ std::string CreateRFCMessage(const std::string& command, CommandBase::Params& parameters)
{
- std::multimap<irc::string, Alias>::iterator i, upperbound;
+ std::string message(command);
+ for (CommandBase::Params::const_iterator iter = parameters.begin(); iter != parameters.end();)
+ {
+ const std::string& parameter = *iter++;
+ message.push_back(' ');
+ if (iter == parameters.end() && (parameter.empty() || parameter.find(' ') != std::string::npos))
+ message.push_back(':');
+ message.append(parameter);
+ }
+ return message;
+ }
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+ {
/* If theyre not registered yet, we dont want
* to know.
*/
@@ -153,19 +155,17 @@ class ModuleAlias : public Module
return MOD_RES_PASSTHRU;
/* We dont have any commands looking like this? Stop processing. */
- i = Aliases.find(command.c_str());
- if (i == Aliases.end())
+ std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(command);
+ if (iters.first == iters.second)
return MOD_RES_PASSTHRU;
- /* Avoid iterating on to different aliases if no patterns match. */
- upperbound = Aliases.upper_bound(command.c_str());
- irc::string c = command.c_str();
/* The parameters for the command in their original form, with the command stripped off */
- std::string compare = original_line.substr(command.length());
+ std::string original_line = CreateRFCMessage(command, parameters);
+ std::string compare(original_line, command.length());
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
- while (i != upperbound)
+ for (AliasMap::iterator i = iters.first; i != iters.second; ++i)
{
if (i->second.UserCommand)
{
@@ -174,120 +174,107 @@ class ModuleAlias : public Module
return MOD_RES_DENY;
}
}
-
- i++;
}
// If we made it here, no aliases actually matched.
return MOD_RES_PASSTHRU;
}
- virtual void OnUserMessage(User *user, void *dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (target_type != TYPE_CHANNEL)
+ // Don't echo anything which is caused by an alias.
+ if (active)
+ details.echo = false;
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+ {
+ if ((target.type != MessageTarget::TYPE_CHANNEL) || (details.type != MSG_PRIVMSG))
{
return;
}
// fcommands are only for local users. Spanningtree will send them back out as their original cmd.
- if (!user || !IS_LOCAL(user))
+ if (!IS_LOCAL(user))
{
return;
}
/* Stop here if the user is +B and allowbot is set to no. */
- if (!AllowBots && user->IsModeSet('B'))
+ if (!AllowBots && user->IsModeSet(botmode))
{
return;
}
- Channel *c = (Channel *)dest;
+ Channel *c = target.Get<Channel>();
std::string scommand;
// text is like "!moo cows bite me", we want "!moo" first
- irc::spacesepstream ss(text);
+ irc::spacesepstream ss(details.text);
ss.GetToken(scommand);
- irc::string fcommand = scommand.c_str();
- if (fcommand.empty())
+ if (scommand.size() <= fprefix.size())
{
return; // wtfbbq
}
// we don't want to touch non-fantasy stuff
- if (*fcommand.c_str() != fprefix)
+ if (scommand.compare(0, fprefix.size(), fprefix) != 0)
{
return;
}
// nor do we give a shit about the prefix
- fcommand.erase(fcommand.begin());
-
- std::multimap<irc::string, Alias>::iterator i = Aliases.find(fcommand);
+ scommand.erase(0, fprefix.size());
- if (i == Aliases.end())
+ std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(scommand);
+ if (iters.first == iters.second)
return;
- /* Avoid iterating on to other aliases if no patterns match */
- std::multimap<irc::string, Alias>::iterator upperbound = Aliases.upper_bound(fcommand);
-
-
/* The parameters for the command in their original form, with the command stripped off */
- std::string compare = text.substr(fcommand.length() + 1);
+ std::string compare(details.text, scommand.length() + fprefix.size());
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
- while (i != upperbound)
+ for (AliasMap::iterator i = iters.first; i != iters.second; ++i)
{
if (i->second.ChannelCommand)
{
- // We use substr(1) here to remove the fantasy prefix
- if (DoAlias(user, c, &(i->second), compare, text.substr(1)))
+ // We use substr here to remove the fantasy prefix
+ if (DoAlias(user, c, &(i->second), compare, details.text.substr(fprefix.size())))
return;
}
-
- i++;
}
}
int DoAlias(User *user, Channel *c, Alias *a, const std::string& compare, const std::string& safe)
{
- User *u = NULL;
-
/* Does it match the pattern? */
if (!a->format.empty())
{
- if (a->CaseSensitive)
- {
- if (!InspIRCd::Match(compare, a->format, rfc_case_sensitive_map))
- return 0;
- }
- else
- {
- if (!InspIRCd::Match(compare, a->format))
- return 0;
- }
+ if (!InspIRCd::Match(compare, a->format))
+ return 0;
}
- if ((a->OperOnly) && (!IS_OPER(user)))
+ if ((a->OperOnly) && (!user->IsOper()))
return 0;
if (!a->RequiredNick.empty())
{
- u = ServerInstance->FindNick(a->RequiredNick);
+ User* u = ServerInstance->FindNick(a->RequiredNick);
if (!u)
{
- user->WriteNumeric(401, ""+user->nick+" "+a->RequiredNick+" :is currently unavailable. Please try again later.");
+ user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick, "is currently unavailable. Please try again later.");
return 1;
}
- }
- if ((u != NULL) && (!a->RequiredNick.empty()) && (a->ULineOnly))
- {
- if (!ServerInstance->ULine(u->server))
+
+ if ((a->ULineOnly) && (!u->server->IsULine()))
{
- ServerInstance->SNO->WriteToSnoMask('a', "NOTICE -- Service "+a->RequiredNick+" required by alias "+std::string(a->AliasedCommand.c_str())+" is not on a u-lined server, possibly underhanded antics detected!");
- user->WriteNumeric(401, ""+user->nick+" "+a->RequiredNick+" :is an imposter! Please inform an IRC operator as soon as possible.");
+ ServerInstance->SNO->WriteToSnoMask('a', "NOTICE -- Service "+a->RequiredNick+" required by alias "+a->AliasedCommand+" is not on a u-lined server, possibly underhanded antics detected!");
+ user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick, "is an imposter! Please inform an IRC operator as soon as possible.");
return 1;
}
}
@@ -298,7 +285,7 @@ class ModuleAlias : public Module
if (crlf == std::string::npos)
{
- DoCommand(a->ReplaceFormat, user, c, safe);
+ DoCommand(a->ReplaceFormat, user, c, safe, a);
return 1;
}
else
@@ -307,16 +294,16 @@ class ModuleAlias : public Module
std::string scommand;
while (commands.GetToken(scommand))
{
- DoCommand(scommand, user, c, safe);
+ DoCommand(scommand, user, c, safe, a);
}
return 1;
}
}
- void DoCommand(const std::string& newline, User* user, Channel *chan, const std::string &original_line)
+ void DoCommand(const std::string& newline, User* user, Channel *chan, const std::string &original_line, Alias* a)
{
std::string result;
- result.reserve(MAXBUF);
+ result.reserve(newline.length());
for (unsigned int i = 0; i < newline.length(); i++)
{
char c = newline[i];
@@ -324,37 +311,42 @@ class ModuleAlias : public Module
{
if (isdigit(newline[i+1]))
{
- int len = ((i + 2 < newline.length()) && (newline[i+2] == '-')) ? 3 : 2;
+ size_t len = ((i + 2 < newline.length()) && (newline[i+2] == '-')) ? 3 : 2;
std::string var = newline.substr(i, len);
result.append(GetVar(var, original_line));
i += len - 1;
}
- else if (newline.substr(i, 5) == "$nick")
+ else if (!newline.compare(i, 5, "$nick", 5))
{
result.append(user->nick);
i += 4;
}
- else if (newline.substr(i, 5) == "$host")
+ else if (!newline.compare(i, 5, "$host", 5))
{
- result.append(user->host);
+ result.append(user->GetRealHost());
i += 4;
}
- else if (newline.substr(i, 5) == "$chan")
+ else if (!newline.compare(i, 5, "$chan", 5))
{
if (chan)
result.append(chan->name);
i += 4;
}
- else if (newline.substr(i, 6) == "$ident")
+ else if (!newline.compare(i, 6, "$ident", 6))
{
result.append(user->ident);
i += 5;
}
- else if (newline.substr(i, 6) == "$vhost")
+ else if (!newline.compare(i, 6, "$vhost", 6))
{
- result.append(user->dhost);
+ result.append(user->GetDisplayedHost());
i += 5;
}
+ else if (!newline.compare(i, 12, "$requirement", 12))
+ {
+ result.append(a->RequiredNick);
+ i += 11;
+ }
else
result.push_back(c);
}
@@ -363,27 +355,25 @@ class ModuleAlias : public Module
}
irc::tokenstream ss(result);
- std::vector<std::string> pars;
+ CommandBase::Params pars;
std::string command, token;
- ss.GetToken(command);
- while (ss.GetToken(token) && (pars.size() <= MAXPARAMETERS))
+ ss.GetMiddle(command);
+ while (ss.GetTrailing(token))
{
pars.push_back(token);
}
- ServerInstance->Parser->CallHandler(command, pars, user);
- }
- virtual void OnRehash(User* user)
- {
- ReadAliases();
- }
+ active = true;
+ ServerInstance->Parser.CallHandler(command, pars, user);
+ active = false;
+ }
- virtual void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
// Prioritise after spanningtree so that channel aliases show the alias before the effects.
Module* linkmod = ServerInstance->Modules->Find("m_spanningtree.so");
- ServerInstance->Modules->SetPriority(this, I_OnUserMessage, PRIORITY_AFTER, &linkmod);
+ ServerInstance->Modules->SetPriority(this, I_OnUserPostMessage, PRIORITY_AFTER, linkmod);
}
};
diff --git a/src/modules/m_allowinvite.cpp b/src/modules/m_allowinvite.cpp
index 08a5f542a..41edbc855 100644
--- a/src/modules/m_allowinvite.cpp
+++ b/src/modules/m_allowinvite.cpp
@@ -19,36 +19,22 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for channel mode +A, allowing /invite freely on a channel and extban A to deny specific users it */
-
-class AllowInvite : public SimpleChannelModeHandler
-{
- public:
- AllowInvite(Module* Creator) : SimpleChannelModeHandler(Creator, "allowinvite", 'A') { }
-};
-
class ModuleAllowInvite : public Module
{
- AllowInvite ni;
+ SimpleChannelModeHandler ni;
public:
- ModuleAllowInvite() : ni(this)
+ ModuleAllowInvite()
+ : ni(this, "allowinvite", 'A')
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(ni);
- Implementation eventlist[] = { I_OnUserPreInvite, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["EXTBAN"].push_back('A');
}
- virtual void On005Numeric(std::string &output)
- {
- ServerInstance->AddExtBanChar('A');
- }
-
- virtual ModResult OnUserPreInvite(User* user,User* dest,Channel* channel, time_t timeout)
+ ModResult OnUserPreInvite(User* user,User* dest,Channel* channel, time_t timeout) CXX11_OVERRIDE
{
if (IS_LOCAL(user))
{
@@ -56,10 +42,10 @@ class ModuleAllowInvite : public Module
if (res == MOD_RES_DENY)
{
// Matching extban, explicitly deny /invite
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You are banned from using INVITE", user->nick.c_str(), channel->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You are banned from using INVITE");
return res;
}
- if (channel->IsModeSet('A') || res == MOD_RES_ALLOW)
+ if (channel->IsModeSet(ni) || res == MOD_RES_ALLOW)
{
// Explicitly allow /invite
return MOD_RES_ALLOW;
@@ -69,11 +55,7 @@ class ModuleAllowInvite : public Module
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleAllowInvite()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +A, allowing /invite freely on a channel and extban A to deny specific users it",VF_VENDOR);
}
diff --git a/src/modules/m_alltime.cpp b/src/modules/m_alltime.cpp
index 38ae4b254..486ad1c18 100644
--- a/src/modules/m_alltime.cpp
+++ b/src/modules/m_alltime.cpp
@@ -21,38 +21,32 @@
#include "inspircd.h"
-/* $ModDesc: Display timestamps from all servers connected to the network */
-
class CommandAlltime : public Command
{
public:
CommandAlltime(Module* Creator) : Command(Creator, "ALLTIME", 0)
{
flags_needed = 'o';
- translation.push_back(TR_END);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- char fmtdate[64];
- time_t now = ServerInstance->Time();
- strftime(fmtdate, sizeof(fmtdate), "%Y-%m-%d %H:%M:%S", gmtime(&now));
+ const std::string fmtdate = InspIRCd::TimeString(ServerInstance->Time(), "%Y-%m-%d %H:%M:%S", true);
- std::string msg = ":" + ServerInstance->Config->ServerName + " NOTICE " + user->nick + " :System time is " + fmtdate + " (" + ConvToStr(ServerInstance->Time()) + ") on " + ServerInstance->Config->ServerName;
+ std::string msg = "System time is " + fmtdate + " (" + ConvToStr(ServerInstance->Time()) + ") on " + ServerInstance->Config->ServerName;
- user->SendText(msg);
+ user->WriteRemoteNotice(msg);
/* we want this routed out! */
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_OPT_BCAST;
}
};
-
class Modulealltime : public Module
{
CommandAlltime mycommand;
@@ -62,16 +56,7 @@ class Modulealltime : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(mycommand);
- }
-
- virtual ~Modulealltime()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Display timestamps from all servers connected to the network", VF_OPTCOMMON | VF_VENDOR);
}
diff --git a/src/modules/m_anticaps.cpp b/src/modules/m_anticaps.cpp
new file mode 100644
index 000000000..6cb9b940b
--- /dev/null
+++ b/src/modules/m_anticaps.cpp
@@ -0,0 +1,304 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2017 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/exemption.h"
+
+enum AntiCapsMethod
+{
+ ACM_BAN,
+ ACM_BLOCK,
+ ACM_MUTE,
+ ACM_KICK,
+ ACM_KICK_BAN
+};
+
+class AntiCapsSettings
+{
+ public:
+ const AntiCapsMethod method;
+ const uint16_t minlen;
+ const uint8_t percent;
+
+ AntiCapsSettings(const AntiCapsMethod& Method, const uint16_t& MinLen, const uint8_t& Percent)
+ : method(Method)
+ , minlen(MinLen)
+ , percent(Percent)
+ {
+ }
+};
+
+class AntiCapsMode : public ParamMode<AntiCapsMode, SimpleExtItem<AntiCapsSettings> >
+{
+ private:
+ bool ParseMethod(irc::sepstream& stream, AntiCapsMethod& method)
+ {
+ std::string methodstr;
+ if (!stream.GetToken(methodstr))
+ return false;
+
+ if (irc::equals(methodstr, "ban"))
+ method = ACM_BAN;
+ else if (irc::equals(methodstr, "block"))
+ method = ACM_BLOCK;
+ else if (irc::equals(methodstr, "mute"))
+ method = ACM_MUTE;
+ else if (irc::equals(methodstr, "kick"))
+ method = ACM_KICK;
+ else if (irc::equals(methodstr, "kickban"))
+ method = ACM_KICK_BAN;
+ else
+ return false;
+
+ return true;
+ }
+
+ bool ParseMinimumLength(irc::sepstream& stream, uint16_t& minlen)
+ {
+ std::string minlenstr;
+ if (!stream.GetToken(minlenstr))
+ return false;
+
+ uint16_t result = ConvToNum<uint16_t>(minlenstr);
+ if (result < 1 || result > ServerInstance->Config->Limits.MaxLine)
+ return false;
+
+ minlen = result;
+ return true;
+ }
+
+ bool ParsePercent(irc::sepstream& stream, uint8_t& percent)
+ {
+ std::string percentstr;
+ if (!stream.GetToken(percentstr))
+ return false;
+
+ uint8_t result = ConvToNum<uint8_t>(percentstr);
+ if (result < 1 || result > 100)
+ return false;
+
+ percent = result;
+ return true;
+ }
+
+ public:
+ AntiCapsMode(Module* Creator)
+ : ParamMode<AntiCapsMode, SimpleExtItem<AntiCapsSettings> >(Creator, "anticaps", 'B')
+ {
+ }
+
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
+ {
+ irc::sepstream stream(parameter, ':');
+ AntiCapsMethod method;
+ uint16_t minlen;
+ uint8_t percent;
+
+ // Attempt to parse the method.
+ if (!ParseMethod(stream, method) || !ParseMinimumLength(stream, minlen) || !ParsePercent(stream, percent))
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, "Invalid anticaps mode parameter. Syntax: <ban|block|mute|kick|kickban>:{minlen}:{percent}."));
+ return MODEACTION_DENY;
+ }
+
+ ext.set(channel, new AntiCapsSettings(method, minlen, percent));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const AntiCapsSettings* acs, std::string& out)
+ {
+ switch (acs->method)
+ {
+ case ACM_BAN:
+ out.append("ban");
+ break;
+ case ACM_BLOCK:
+ out.append("block");
+ break;
+ case ACM_MUTE:
+ out.append("mute");
+ break;
+ case ACM_KICK:
+ out.append("kick");
+ break;
+ case ACM_KICK_BAN:
+ out.append("kickban");
+ break;
+ default:
+ out.append("unknown~");
+ out.append(ConvToStr(acs->method));
+ break;
+ }
+ out.push_back(':');
+ out.append(ConvToStr(acs->minlen));
+ out.push_back(':');
+ out.append(ConvToStr(acs->percent));
+ }
+};
+
+class ModuleAntiCaps : public Module
+{
+ private:
+ CheckExemption::EventProvider exemptionprov;
+ std::bitset<UCHAR_MAX> uppercase;
+ std::bitset<UCHAR_MAX> lowercase;
+ AntiCapsMode mode;
+
+ void CreateBan(Channel* channel, User* user, bool mute)
+ {
+ std::string banmask(mute ? "m:" : "");
+ banmask.append("*!*@");
+ banmask.append(user->GetDisplayedHost());
+
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), banmask);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, channel, NULL, changelist);
+ }
+
+ void InformUser(Channel* channel, User* user, const std::string& message)
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, channel, message + " and was blocked.");
+ }
+
+ public:
+ ModuleAntiCaps()
+ : exemptionprov(this)
+ , mode(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("anticaps");
+
+ uppercase.reset();
+ const std::string upper = tag->getString("uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ for (std::string::const_iterator iter = upper.begin(); iter != upper.end(); ++iter)
+ uppercase.set(static_cast<unsigned char>(*iter));
+
+ lowercase.reset();
+ const std::string lower = tag->getString("lowercase", "abcdefghijklmnopqrstuvwxyz");
+ for (std::string::const_iterator iter = lower.begin(); iter != lower.end(); ++iter)
+ lowercase.set(static_cast<unsigned char>(*iter));
+ }
+
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+ {
+ // We only want to operate on messages from local users.
+ if (!IS_LOCAL(user))
+ return MOD_RES_PASSTHRU;
+
+ // The mode can only be applied to channels.
+ if (target.type != MessageTarget::TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
+
+ // We only act if the channel has the mode set.
+ Channel* channel = target.Get<Channel>();
+ if (!channel->IsModeSet(&mode))
+ return MOD_RES_PASSTHRU;
+
+ // If the user is exempt from anticaps then we don't need
+ // to do anything else.
+ ModResult result = CheckExemption::Call(exemptionprov, user, channel, "anticaps");
+ if (result == MOD_RES_ALLOW)
+ return MOD_RES_PASSTHRU;
+
+ // If the message is a CTCP then we skip it unless it is
+ // an ACTION in which case we just check against the body.
+ std::string ctcpname;
+ std::string msgbody(details.text);
+ if (details.IsCTCP(ctcpname, msgbody))
+ {
+ // If the CTCP is not an action then skip it.
+ if (!irc::equals(ctcpname, "ACTION"))
+ return MOD_RES_PASSTHRU;
+ }
+
+ // Retrieve the anticaps config. This should never be
+ // null but its better to be safe than sorry.
+ AntiCapsSettings* config = mode.ext.get(channel);
+ if (!config)
+ return MOD_RES_PASSTHRU;
+
+ // If the message is shorter than the minimum length then
+ // we don't need to do anything else.
+ size_t length = msgbody.length();
+ if (length < config->minlen)
+ return MOD_RES_PASSTHRU;
+
+ // Count the characters to see how many upper case and
+ // ignored (non upper or lower) characters there are.
+ size_t upper = 0;
+ for (std::string::const_iterator iter = msgbody.begin(); iter != msgbody.end(); ++iter)
+ {
+ unsigned char chr = static_cast<unsigned char>(*iter);
+ if (uppercase.test(chr))
+ upper += 1;
+ else if (!lowercase.test(chr))
+ length -= 1;
+ }
+
+ // If the message was entirely symbols then the message
+ // can't contain any upper case letters.
+ if (length == 0)
+ return MOD_RES_PASSTHRU;
+
+ // Calculate the percentage.
+ double percent = round((upper * 100) / length);
+ if (percent < config->percent)
+ return MOD_RES_PASSTHRU;
+
+ const std::string message = InspIRCd::Format("Your message exceeded the %d%% upper case character threshold for %s",
+ config->percent, channel->name.c_str());
+
+ switch (config->method)
+ {
+ case ACM_BAN:
+ InformUser(channel, user, message);
+ CreateBan(channel, user, false);
+ break;
+
+ case ACM_BLOCK:
+ InformUser(channel, user, message);
+ break;
+
+ case ACM_MUTE:
+ InformUser(channel, user, message);
+ CreateBan(channel, user, true);
+ break;
+
+ case ACM_KICK:
+ channel->KickUser(ServerInstance->FakeClient, user, message);
+ break;
+
+ case ACM_KICK_BAN:
+ CreateBan(channel, user, false);
+ channel->KickUser(ServerInstance->FakeClient, user, message);
+ break;
+ }
+ return MOD_RES_DENY;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for punishing users that send capitalised messages.", VF_COMMON|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleAntiCaps)
diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp
index 2a8edb9d4..8485f1d7a 100644
--- a/src/modules/m_auditorium.cpp
+++ b/src/modules/m_auditorium.cpp
@@ -21,56 +21,58 @@
#include "inspircd.h"
+#include "modules/exemption.h"
-/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */
-
-class AuditoriumMode : public ModeHandler
+class AuditoriumMode : public SimpleChannelModeHandler
{
public:
- AuditoriumMode(Module* Creator) : ModeHandler(Creator, "auditorium", 'u', PARAM_NONE, MODETYPE_CHANNEL)
+ AuditoriumMode(Module* Creator) : SimpleChannelModeHandler(Creator, "auditorium", 'u')
{
- levelrequired = OP_VALUE;
+ ranktoset = ranktounset = OP_VALUE;
}
+};
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- if (channel->IsModeSet(this) == adding)
- return MODEACTION_DENY;
- channel->SetMode(this, adding);
- return MODEACTION_ALLOW;
- }
+class ModuleAuditorium;
+
+namespace
+{
+
+/** Hook handler for join client protocol events.
+ * This allows us to block join protocol events completely, including all associated messages (e.g. MODE, away-notify AWAY).
+ * This is not the same as OnUserJoin() because that runs only when a real join happens but this runs also when a module
+ * such as delayjoin or hostcycle generates a join.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+ ModuleAuditorium* const parentmod;
+ bool active;
+
+ public:
+ JoinHook(ModuleAuditorium* mod);
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE;
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE;
};
+}
+
class ModuleAuditorium : public Module
{
- private:
+ CheckExemption::EventProvider exemptionprov;
AuditoriumMode aum;
bool OpsVisible;
bool OpsCanSee;
bool OperCanSee;
- public:
- ModuleAuditorium() : aum(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(aum);
+ JoinHook joinhook;
- OnRehash(NULL);
-
- Implementation eventlist[] = {
- I_OnUserJoin, I_OnUserPart, I_OnUserKick,
- I_OnBuildNeighborList, I_OnNamesListItem, I_OnSendWhoLine,
- I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleAuditorium()
+ public:
+ ModuleAuditorium()
+ : exemptionprov(this)
+ , aum(this)
+ , joinhook(this)
{
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("auditorium");
OpsVisible = tag->getBool("opvisible");
@@ -78,7 +80,7 @@ class ModuleAuditorium : public Module
OperCanSee = tag->getBool("opercansee", true);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list", VF_VENDOR);
}
@@ -89,7 +91,7 @@ class ModuleAuditorium : public Module
if (!memb->chan->IsModeSet(&aum))
return true;
- ModResult res = ServerInstance->OnCheckExemption(memb->user, memb->chan, "auditorium-vis");
+ ModResult res = CheckExemption::Call(exemptionprov, memb->user, memb->chan, "auditorium-vis");
return res.check(OpsVisible && memb->getRank() >= OP_VALUE);
}
@@ -105,26 +107,23 @@ class ModuleAuditorium : public Module
return true;
// Can you see the list by permission?
- ModResult res = ServerInstance->OnCheckExemption(issuer,memb->chan,"auditorium-see");
+ ModResult res = CheckExemption::Call(exemptionprov, issuer, memb->chan, "auditorium-see");
if (res.check(OpsCanSee && memb->chan->GetPrefixValue(issuer) >= OP_VALUE))
return true;
return false;
}
- void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- // Some module already hid this from being displayed, don't bother
- if (nick.empty())
- return;
-
if (IsVisible(memb))
- return;
+ return MOD_RES_PASSTHRU;
if (CanSee(issuer, memb))
- return;
+ return MOD_RES_PASSTHRU;
- nick.clear();
+ // Don't display this user in the NAMES list
+ return MOD_RES_DENY;
}
/** Build CUList for showing this join/part/kick */
@@ -133,43 +132,40 @@ class ModuleAuditorium : public Module
if (IsVisible(memb))
return;
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
if (IS_LOCAL(i->first) && !CanSee(i->first, memb))
excepts.insert(i->first);
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
{
BuildExcept(memb, excepts);
}
- void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE
{
BuildExcept(memb, excepts);
}
- void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
+ void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
{
- BuildExcept(memb, excepts);
- }
-
- void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
- {
- UCListIter i = include.begin();
- while (i != include.end())
+ for (IncludeChanList::iterator i = include.begin(); i != include.end(); )
{
- Channel* c = *i++;
- Membership* memb = c->GetUser(source);
- if (!memb || IsVisible(memb))
+ Membership* memb = *i;
+ if (IsVisible(memb))
+ {
+ ++i;
continue;
+ }
+
// this channel should not be considered when listing my neighbors
- include.erase(c);
+ i = include.erase(i);
// however, that might hide me from ops that can see me...
- const UserMembList* users = c->GetUsers();
- for(UserMembCIter j = users->begin(); j != users->end(); j++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for(Channel::MemberMap::const_iterator j = users.begin(); j != users.end(); ++j)
{
if (IS_LOCAL(j->first) && CanSee(j->first, memb))
exception[j->first] = true;
@@ -177,18 +173,37 @@ class ModuleAuditorium : public Module
}
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+ ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- Channel* channel = ServerInstance->FindChan(params[0]);
- if (!channel)
- return;
- Membership* memb = channel->GetUser(user);
- if ((!memb) || (IsVisible(memb)))
- return;
+ if (!memb)
+ return MOD_RES_PASSTHRU;
+ if (IsVisible(memb))
+ return MOD_RES_PASSTHRU;
if (CanSee(source, memb))
- return;
- line.clear();
+ return MOD_RES_PASSTHRU;
+ return MOD_RES_DENY;
}
};
+JoinHook::JoinHook(ModuleAuditorium* mod)
+ : ClientProtocol::EventHook(mod, "JOIN", 10)
+ , parentmod(mod)
+{
+}
+
+void JoinHook::OnEventInit(const ClientProtocol::Event& ev)
+{
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ active = !parentmod->IsVisible(join.GetMember());
+}
+
+ModResult JoinHook::OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist)
+{
+ if (!active)
+ return MOD_RES_PASSTHRU;
+
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ return ((parentmod->CanSee(user, join.GetMember())) ? MOD_RES_PASSTHRU : MOD_RES_DENY);
+}
+
MODULE_INIT(ModuleAuditorium)
diff --git a/src/modules/m_autoop.cpp b/src/modules/m_autoop.cpp
index 0c0e8f579..a09d0d9e4 100644
--- a/src/modules/m_autoop.cpp
+++ b/src/modules/m_autoop.cpp
@@ -19,9 +19,7 @@
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +w channel mode, autoop list */
+#include "listmode.h"
/** Handles +w channel mode
*/
@@ -30,36 +28,31 @@ class AutoOpList : public ListModeBase
public:
AutoOpList(Module* Creator) : ListModeBase(Creator, "autoop", 'w', "End of Channel Access List", 910, 911, true)
{
- levelrequired = OP_VALUE;
+ ranktoset = ranktounset = OP_VALUE;
tidy = false;
}
- ModeHandler* FindMode(const std::string& mid)
+ PrefixMode* FindMode(const std::string& mid)
{
if (mid.length() == 1)
- return ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL);
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->name == mid)
- return mh;
- }
- return NULL;
+ return ServerInstance->Modes->FindPrefixMode(mid[0]);
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mid, MODETYPE_CHANNEL);
+ return mh ? mh->IsPrefixMode() : NULL;
}
- ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
+ ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE
{
std::string::size_type pos = parameter.find(':');
if (pos == 0 || pos == std::string::npos)
return adding ? MOD_RES_DENY : MOD_RES_PASSTHRU;
unsigned int mylevel = channel->GetPrefixValue(source);
- std::string mid = parameter.substr(0, pos);
- ModeHandler* mh = FindMode(mid);
+ std::string mid(parameter, 0, pos);
+ PrefixMode* mh = FindMode(mid);
- if (adding && (!mh || !mh->GetPrefixRank()))
+ if (adding && !mh)
{
- source->WriteNumeric(415, "%s %s :Cannot find prefix mode '%s' for autoop",
- source->nick.c_str(), mid.c_str(), mid.c_str());
+ source->WriteNumeric(ERR_UNKNOWNMODE, mid, InspIRCd::Format("Cannot find prefix mode '%s' for autoop", mid.c_str()));
return MOD_RES_DENY;
}
else if (!mh)
@@ -68,10 +61,9 @@ class AutoOpList : public ListModeBase
std::string dummy;
if (mh->AccessCheck(source, channel, dummy, true) == MOD_RES_DENY)
return MOD_RES_DENY;
- if (mh->GetLevelRequired() > mylevel)
+ if (mh->GetLevelRequired(true) > mylevel)
{
- source->WriteNumeric(482, "%s %s :You must be able to set mode '%s' to include it in an autoop",
- source->nick.c_str(), channel->name.c_str(), mid.c_str());
+ source->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, InspIRCd::Format("You must be able to set mode '%s' to include it in an autoop", mid.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
@@ -82,62 +74,42 @@ class ModuleAutoOp : public Module
{
AutoOpList mh;
-public:
+ public:
ModuleAutoOp() : mh(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(mh);
- mh.DoImplements(this);
-
- Implementation list[] = { I_OnPostJoin, };
- ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
- }
-
- void OnPostJoin(Membership *memb)
+ void OnPostJoin(Membership *memb) CXX11_OVERRIDE
{
if (!IS_LOCAL(memb->user))
return;
- modelist* list = mh.extItem.get(memb->chan);
+ ListModeBase::ModeList* list = mh.GetList(memb->chan);
if (list)
{
- std::string modeline("+");
- std::vector<std::string> modechange;
- modechange.push_back(memb->chan->name);
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ Modes::ChangeList changelist;
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
std::string::size_type colon = it->mask.find(':');
if (colon == std::string::npos)
continue;
if (memb->chan->CheckBan(memb->user, it->mask.substr(colon+1)))
{
- ModeHandler* given = mh.FindMode(it->mask.substr(0, colon));
- if (given && given->GetPrefixRank())
- modeline.push_back(given->GetModeChar());
+ PrefixMode* given = mh.FindMode(it->mask.substr(0, colon));
+ if (given)
+ changelist.push_add(given, memb->user->nick);
}
}
- modechange.push_back(modeline);
- for(std::string::size_type i = modeline.length(); i > 1; --i) // we use "i > 1" instead of "i" so we skip the +
- modechange.push_back(memb->user->nick);
- if(modechange.size() >= 3)
- ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
}
}
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- mh.DoSyncChannel(chan, proto, opaque);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
mh.DoRehash();
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the +w channel mode", VF_VENDOR);
}
diff --git a/src/modules/m_banexception.cpp b/src/modules/m_banexception.cpp
index 7531c5c12..3905cdf6a 100644
--- a/src/modules/m_banexception.cpp
+++ b/src/modules/m_banexception.cpp
@@ -22,10 +22,7 @@
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +e channel mode */
-/* $ModDep: ../../include/u_listmode.h */
+#include "listmode.h"
/* Written by Om<om@inspircd.org>, April 2005. */
/* Rewritten to use the listmode utility by Om, December 2005 */
@@ -49,85 +46,63 @@ class ModuleBanException : public Module
{
BanException be;
-public:
+ public:
ModuleBanException() : be(this)
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(be);
-
- be.DoImplements(this);
- Implementation list[] = { I_OnRehash, I_On005Numeric, I_OnExtBanCheck, I_OnCheckChannelBan };
- ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
+ tokens["EXCEPTS"] = ConvToStr(be.GetModeChar());
}
- void On005Numeric(std::string &output)
+ ModResult OnExtBanCheck(User *user, Channel *chan, char type) CXX11_OVERRIDE
{
- output.append(" EXCEPTS=e");
- }
+ ListModeBase::ModeList* list = be.GetList(chan);
+ if (!list)
+ return MOD_RES_PASSTHRU;
- ModResult OnExtBanCheck(User *user, Channel *chan, char type)
- {
- if (chan != NULL)
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
- modelist *list = be.extItem.get(chan);
-
- if (!list)
- return MOD_RES_PASSTHRU;
+ if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
+ continue;
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ if (chan->CheckBan(user, it->mask.substr(2)))
{
- if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
- continue;
-
- if (chan->CheckBan(user, it->mask.substr(2)))
- {
- // They match an entry on the list, so let them pass this.
- return MOD_RES_ALLOW;
- }
+ // They match an entry on the list, so let them pass this.
+ return MOD_RES_ALLOW;
}
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckChannelBan(User* user, Channel* chan)
+ ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
{
- if (chan)
+ ListModeBase::ModeList* list = be.GetList(chan);
+ if (!list)
{
- modelist *list = be.extItem.get(chan);
-
- if (!list)
- {
- // No list, proceed normally
- return MOD_RES_PASSTHRU;
- }
+ // No list, proceed normally
+ return MOD_RES_PASSTHRU;
+ }
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
+ {
+ if (chan->CheckBan(user, it->mask))
{
- if (chan->CheckBan(user, it->mask))
- {
- // They match an entry on the list, so let them in.
- return MOD_RES_ALLOW;
- }
+ // They match an entry on the list, so let them in.
+ return MOD_RES_ALLOW;
}
}
return MOD_RES_PASSTHRU;
}
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- be.DoSyncChannel(chan, proto, opaque);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
be.DoRehash();
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the +e channel mode", VF_VENDOR);
}
diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp
index 1d35c2934..5ec75f13c 100644
--- a/src/modules/m_banredirect.cpp
+++ b/src/modules/m_banredirect.cpp
@@ -23,9 +23,7 @@
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */
+#include "listmode.h"
/* Originally written by Om, January 2009
*/
@@ -43,18 +41,20 @@ class BanRedirectEntry
};
typedef std::vector<BanRedirectEntry> BanRedirectList;
-typedef std::deque<std::string> StringDeque;
class BanRedirect : public ModeWatcher
{
+ ChanModeReference ban;
public:
SimpleExtItem<BanRedirectList> extItem;
- BanRedirect(Module* parent) : ModeWatcher(parent, 'b', MODETYPE_CHANNEL),
- extItem("banredirect", parent)
+ BanRedirect(Module* parent)
+ : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
+ , ban(parent, "ban")
+ , extItem("banredirect", ExtensionItem::EXT_CHANNEL, parent)
{
}
- bool BeforeMode(User* source, User* dest, Channel* channel, std::string &param, bool adding, ModeType type)
+ bool BeforeMode(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE
{
/* nick!ident@host -> nick!ident@host
* nick!ident@host#chan -> nick!ident@host#chan
@@ -63,14 +63,13 @@ class BanRedirect : public ModeWatcher
* nick#chan -> nick!*@*#chan
*/
- if(channel && (type == MODETYPE_CHANNEL) && param.length())
+ if ((channel) && !param.empty())
{
BanRedirectList* redirects;
std::string mask[4];
enum { NICK, IDENT, HOST, CHAN } current = NICK;
std::string::iterator start_pos = param.begin();
- long maxbans = channel->GetMaxBans();
if (param.length() >= 2 && param[1] == ':')
return true;
@@ -78,9 +77,12 @@ class BanRedirect : public ModeWatcher
if (param.find('#') == std::string::npos)
return true;
- if(adding && (channel->bans.size() > static_cast<unsigned>(maxbans)))
+ ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+ unsigned int maxbans = banlm->GetLimit(channel);
+ ListModeBase::ModeList* list = banlm->GetList(channel);
+ if ((list) && (adding) && (maxbans <= list->size()))
{
- source->WriteNumeric(478, "%s %s :Channel ban list for %s is full (maximum entries for this channel is %ld)", source->nick.c_str(), channel->name.c_str(), channel->name.c_str(), maxbans);
+ source->WriteNumeric(ERR_BANLISTFULL, channel->name, InspIRCd::Format("Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), maxbans));
return false;
}
@@ -89,23 +91,25 @@ class BanRedirect : public ModeWatcher
switch(*curr)
{
case '!':
+ if (current != NICK)
+ break;
mask[current].assign(start_pos, curr);
current = IDENT;
start_pos = curr+1;
break;
case '@':
+ if (current != IDENT)
+ break;
mask[current].assign(start_pos, curr);
current = HOST;
start_pos = curr+1;
break;
case '#':
- /* bug #921: don't barf when redirecting to ## channels */
- if (current != CHAN)
- {
- mask[current].assign(start_pos, curr);
- current = CHAN;
- start_pos = curr;
- }
+ if (current == CHAN)
+ break;
+ mask[current].assign(start_pos, curr);
+ current = CHAN;
+ start_pos = curr;
break;
}
}
@@ -144,27 +148,27 @@ class BanRedirect : public ModeWatcher
{
if (adding && IS_LOCAL(source))
{
- if (!ServerInstance->IsChannel(mask[CHAN].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(mask[CHAN]))
{
- source->WriteNumeric(403, "%s %s :Invalid channel name in redirection (%s)", source->nick.c_str(), channel->name.c_str(), mask[CHAN].c_str());
+ source->WriteNumeric(ERR_NOSUCHCHANNEL, channel->name, InspIRCd::Format("Invalid channel name in redirection (%s)", mask[CHAN].c_str()));
return false;
}
Channel *c = ServerInstance->FindChan(mask[CHAN]);
if (!c)
{
- source->WriteNumeric(690, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),mask[CHAN].c_str());
+ source->WriteNumeric(690, InspIRCd::Format("Target channel %s must exist to be set as a redirect.", mask[CHAN].c_str()));
return false;
}
else if (adding && c->GetPrefixValue(source) < OP_VALUE)
{
- source->WriteNumeric(690, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(), mask[CHAN].c_str());
+ source->WriteNumeric(690, InspIRCd::Format("You must be opped on %s to set it as a redirect.", mask[CHAN].c_str()));
return false;
}
- if (assign(channel->name) == mask[CHAN])
+ if (irc::equals(channel->name, mask[CHAN]))
{
- source->WriteNumeric(690, "%s %s :You cannot set a ban redirection to the channel the ban is on", source->nick.c_str(), channel->name.c_str());
+ source->WriteNumeric(690, channel->name, "You cannot set a ban redirection to the channel the ban is on");
return false;
}
}
@@ -183,7 +187,7 @@ class BanRedirect : public ModeWatcher
for (BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); ++redir)
{
// Mimic the functionality used when removing the mode
- if ((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str())))
+ if (irc::equals(redir->targetchan, mask[CHAN]) && irc::equals(redir->banmask, param))
{
// Make sure the +b handler will still set the right ban
param.append(mask[CHAN]);
@@ -210,8 +214,7 @@ class BanRedirect : public ModeWatcher
for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
{
- /* Ugly as fuck */
- if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str())))
+ if ((irc::equals(redir->targetchan, mask[CHAN])) && (irc::equals(redir->banmask, param)))
{
redirects->erase(redir);
@@ -239,61 +242,42 @@ class ModuleBanRedirect : public Module
{
BanRedirect re;
bool nofollow;
+ ChanModeReference limitmode;
+ ChanModeReference redirectmode;
public:
ModuleBanRedirect()
- : re(this)
+ : re(this)
+ , nofollow(false)
+ , limitmode(this, "limit")
+ , redirectmode(this, "redirect")
{
- nofollow = false;
- }
-
-
- void init()
- {
- if(!ServerInstance->Modes->AddModeWatcher(&re))
- throw ModuleException("Could not add mode watcher");
-
- ServerInstance->Modules->AddService(re.extItem);
- Implementation list[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
}
- virtual void OnCleanup(int target_type, void* item)
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
{
- if(target_type == TYPE_CHANNEL)
+ if (type == ExtensionItem::EXT_CHANNEL)
{
Channel* chan = static_cast<Channel*>(item);
BanRedirectList* redirects = re.extItem.get(chan);
if(redirects)
{
- irc::modestacker modestack(false);
- StringDeque stackresult;
- std::vector<std::string> mode_junk;
- mode_junk.push_back(chan->name);
+ ModeHandler* ban = ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL);
+ Modes::ChangeList changelist;
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
- {
- modestack.Push('b', i->targetchan.insert(0, i->banmask));
- }
+ changelist.push_remove(ban, i->targetchan.insert(0, i->banmask));
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
- {
- modestack.PushPlus();
- modestack.Push('b', i->banmask);
- }
+ changelist.push_add(ban, i->banmask);
- while(modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist, ModeParser::MODE_LOCALONLY);
}
}
}
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
@@ -337,19 +321,19 @@ class ModuleBanRedirect : public Module
std::string destlimit;
if (destchan)
- destlimit = destchan->GetModeParameter('l');
+ destlimit = destchan->GetModeParameter(limitmode);
- if(destchan && ServerInstance->Modules->Find("m_redirect.so") && destchan->IsModeSet('L') && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
+ if(destchan && destchan->IsModeSet(redirectmode) && !destlimit.empty() && (destchan->GetUserCounter() >= ConvToNum<size_t>(destlimit)))
{
- user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (You are banned)");
return MOD_RES_DENY;
}
else
{
- user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
- user->WriteNumeric(470, "%s %s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", user->nick.c_str(), chan->name.c_str(), redir->targetchan.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (You are banned)");
+ user->WriteNumeric(470, chan->name, redir->targetchan, "You are banned from this channel, so you are automatically transferred to the redirected channel.");
nofollow = true;
- Channel::JoinUser(user, redir->targetchan.c_str(), false, "", false, ServerInstance->Time());
+ Channel::JoinUser(user, redir->targetchan);
nofollow = false;
return MOD_RES_DENY;
}
@@ -360,14 +344,7 @@ class ModuleBanRedirect : public Module
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleBanRedirect()
- {
- /* XXX is this the best place to do this? */
- if (!ServerInstance->Modes->DelModeWatcher(&re))
- ServerInstance->Logs->Log("m_banredirect.so", DEBUG, "Failed to delete modewatcher!");
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows an extended ban (+b) syntax redirecting banned users to another channel", VF_COMMON|VF_VENDOR);
}
diff --git a/src/modules/m_bcrypt.cpp b/src/modules/m_bcrypt.cpp
new file mode 100644
index 000000000..59ee1556c
--- /dev/null
+++ b/src/modules/m_bcrypt.cpp
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// $CompilerFlags: -Ivendor_directory("bcrypt")
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+#include <crypt_blowfish.c>
+
+class BCryptProvider : public HashProvider
+{
+ private:
+ std::string Salt()
+ {
+ char entropy[16];
+ for (unsigned int i = 0; i < sizeof(entropy); ++i)
+ entropy[i] = ServerInstance->GenRandomInt(0xFF);
+
+ char salt[32];
+ if (!_crypt_gensalt_blowfish_rn("$2a$", rounds, entropy, sizeof(entropy), salt, sizeof(salt)))
+ throw ModuleException("Could not generate salt - this should never happen");
+
+ return salt;
+ }
+
+ public:
+ unsigned int rounds;
+
+ std::string Generate(const std::string& data, const std::string& salt)
+ {
+ char hash[64];
+ _crypt_blowfish_rn(data.c_str(), salt.c_str(), hash, sizeof(hash));
+ return hash;
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ return Generate(data, Salt());
+ }
+
+ bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+ {
+ std::string ret = Generate(input, hash);
+ if (ret.empty())
+ return false;
+
+ if (ret == hash)
+ return true;
+ return false;
+ }
+
+ std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+ {
+ return raw;
+ }
+
+ BCryptProvider(Module* parent)
+ : HashProvider(parent, "bcrypt", 60)
+ , rounds(10)
+ {
+ }
+};
+
+class ModuleBCrypt : public Module
+{
+ BCryptProvider bcrypt;
+
+ public:
+ ModuleBCrypt() : bcrypt(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("bcrypt");
+ bcrypt.rounds = conf->getUInt("rounds", 10, 1);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Implements bcrypt hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleBCrypt)
diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp
index be861447f..c4af2fd7f 100644
--- a/src/modules/m_blockamsg.cpp
+++ b/src/modules/m_blockamsg.cpp
@@ -23,8 +23,6 @@
#include "inspircd.h"
-/* $ModDesc: Attempt to block /amsg, at least some of the irritating mIRC scripts. */
-
enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOPERS, IBLOCK_SILENT };
/* IBLOCK_NOTICE - Send a notice to the user informing them of what happened.
* IBLOCK_NOTICEOPERS - Send a notice to the user informing them and send an oper notice.
@@ -37,64 +35,53 @@ enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOP
*/
class BlockedMessage
{
-public:
+ public:
std::string message;
- irc::string target;
+ std::string target;
time_t sent;
- BlockedMessage(const std::string &msg, const irc::string &tgt, time_t when)
- : message(msg), target(tgt), sent(when)
+ BlockedMessage(const std::string& msg, const std::string& tgt, time_t when)
+ : message(msg), target(tgt), sent(when)
{
}
};
class ModuleBlockAmsg : public Module
{
- int ForgetDelay;
+ unsigned int ForgetDelay;
BlockAction action;
SimpleExtItem<BlockedMessage> blockamsg;
public:
- ModuleBlockAmsg() : blockamsg("blockamsg", this)
- {
- }
-
- void init()
- {
- this->OnRehash(NULL);
- ServerInstance->Modules->AddService(blockamsg);
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleBlockAmsg()
+ ModuleBlockAmsg()
+ : blockamsg("blockamsg", ExtensionItem::EXT_USER, this)
{
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Attempt to block /amsg, at least some of the irritating mIRC scripts.",VF_VENDOR);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("blockamsg");
- ForgetDelay = tag->getInt("delay", -1);
+ ForgetDelay = tag->getDuration("delay", 3);
std::string act = tag->getString("action");
- if(act == "notice")
+ if (stdalgo::string::equalsci(act, "notice"))
action = IBLOCK_NOTICE;
- else if(act == "noticeopers")
+ else if (stdalgo::string::equalsci(act, "noticeopers"))
action = IBLOCK_NOTICEOPERS;
- else if(act == "silent")
+ else if (stdalgo::string::equalsci(act, "silent"))
action = IBLOCK_SILENT;
- else if(act == "kill")
+ else if (stdalgo::string::equalsci(act, "kill"))
action = IBLOCK_KILL;
else
action = IBLOCK_KILLOPERS;
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
// Don't do anything with unregistered users
if (user->registered != REG_ALL)
@@ -102,33 +89,24 @@ class ModuleBlockAmsg : public Module
if ((validated) && (parameters.size() >= 2) && ((command == "PRIVMSG") || (command == "NOTICE")))
{
- // parameters[0] should have the target(s) in it.
- // I think it will be faster to first check if there are any commas, and if there are then try and parse it out.
- // Most messages have a single target so...
+ // parameters[0] is the target list, count how many channels are there
+ unsigned int targets = 0;
+ // Is the first target a channel?
+ if (*parameters[0].c_str() == '#')
+ targets = 1;
- int targets = 1;
- int userchans = 0;
-
- if(*parameters[0].c_str() != '#')
+ for (const char* c = parameters[0].c_str(); *c; c++)
{
- // Decrement if the first target wasn't a channel.
- targets--;
- }
-
- for(const char* c = parameters[0].c_str(); *c; c++)
- if((*c == ',') && *(c+1) && (*(c+1) == '#'))
+ if ((*c == ',') && (*(c+1) == '#'))
targets++;
+ }
/* targets should now contain the number of channel targets the msg/notice was pointed at.
* If the msg/notice was a PM there should be no channel targets and 'targets' should = 0.
* We don't want to block PMs so...
*/
- if(targets == 0)
- {
+ if (targets == 0)
return MOD_RES_PASSTHRU;
- }
-
- userchans = user->chans.size();
// Check that this message wasn't already sent within a few seconds.
BlockedMessage* m = blockamsg.get(user);
@@ -138,30 +116,30 @@ class ModuleBlockAmsg : public Module
// OR
// The number of target channels is equal to the number of channels the sender is on..a little suspicious.
// Check it's more than 1 too, or else users on one channel would have fun.
- if((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == userchans)))
+ if ((m && (m->message == parameters[1]) && (!irc::equals(m->target, parameters[0])) && ForgetDelay && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == user->chans.size())))
{
// Block it...
- if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
+ if (action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
ServerInstance->SNO->WriteToSnoMask('a', "%s had an /amsg or /ame denied", user->nick.c_str());
- if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
+ if (action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
ServerInstance->Users->QuitUser(user, "Attempted to global message (/amsg or /ame)");
- else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
- user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) denied", user->nick.c_str());
+ else if (action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
+ user->WriteNotice("Global message (/amsg or /ame) denied");
return MOD_RES_DENY;
}
- if(m)
+ if (m)
{
// If there's already a BlockedMessage allocated, use it.
m->message = parameters[1];
- m->target = parameters[0].c_str();
+ m->target = parameters[0];
m->sent = ServerInstance->Time();
}
else
{
- m = new BlockedMessage(parameters[1], parameters[0].c_str(), ServerInstance->Time());
+ m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time());
blockamsg.set(user, m);
}
}
@@ -169,5 +147,4 @@ class ModuleBlockAmsg : public Module
}
};
-
MODULE_INIT(ModuleBlockAmsg)
diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp
index 7146ee068..fa780427c 100644
--- a/src/modules/m_blockcaps.cpp
+++ b/src/modules/m_blockcaps.cpp
@@ -21,83 +21,79 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides support to block all-CAPS channel messages and notices */
-
-
-/** Handles the +B channel mode
- */
-class BlockCaps : public SimpleChannelModeHandler
-{
- public:
- BlockCaps(Module* Creator) : SimpleChannelModeHandler(Creator, "blockcaps", 'B') { }
-};
+#include "modules/exemption.h"
class ModuleBlockCAPS : public Module
{
- BlockCaps bc;
- int percent;
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler bc;
+ unsigned int percent;
unsigned int minlen;
- char capsmap[256];
-public:
+ std::bitset<UCHAR_MAX> lowercase;
+ std::bitset<UCHAR_MAX> uppercase;
- ModuleBlockCAPS() : bc(this)
- {
- }
-
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(bc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void On005Numeric(std::string &output)
+public:
+ ModuleBlockCAPS()
+ : exemptionprov(this)
+ , bc(this, "blockcaps", 'B')
{
- ServerInstance->AddExtBanChar('B');
}
- virtual void OnRehash(User* user)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ReadConf();
+ tokens["EXTBAN"].push_back('B');
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (target_type == TYPE_CHANNEL)
+ if (target.type == MessageTarget::TYPE_CHANNEL)
{
- if ((!IS_LOCAL(user)) || (text.length() < minlen) || (text == "\1ACTION\1") || (text == "\1ACTION"))
+ if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- Channel* c = (Channel*)dest;
- ModResult res = ServerInstance->OnCheckExemption(user,c,"blockcaps");
+ Channel* c = target.Get<Channel>();
+ ModResult res = CheckExemption::Call(exemptionprov, user, c, "blockcaps");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet('B')))
+ if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet(bc)))
{
- int caps = 0;
- const char* actstr = "\1ACTION ";
- int act = 0;
+ // If the message is a CTCP then we skip it unless it is
+ // an ACTION in which case we just check against the body.
+ std::string ctcpname;
+ std::string message(details.text);
+ if (details.IsCTCP(ctcpname, message))
+ {
+ // If the CTCP is not an action then skip it.
+ if (!irc::equals(ctcpname, "ACTION"))
+ return MOD_RES_PASSTHRU;
+ }
- for (std::string::iterator i = text.begin(); i != text.end(); i++)
+ // If the message is shorter than the minimum length
+ // then we don't need to do anything else.
+ size_t length = message.length();
+ if (length < minlen)
+ return MOD_RES_PASSTHRU;
+
+ // Count the characters to see how many upper case and
+ // ignored (non upper or lower) characters there are.
+ size_t upper = 0;
+ for (std::string::const_iterator iter = message.begin(); iter != message.end(); ++iter)
{
- /* Smart fix for suggestion from Jobe, ignore CTCP ACTION (part of /ME) */
- if (*actstr && *i == *actstr++ && act != -1)
- {
- act++;
- continue;
- }
- else
- act = -1;
-
- caps += capsmap[(unsigned char)*i];
+ unsigned char chr = static_cast<unsigned char>(*iter);
+ if (uppercase.test(chr))
+ upper += 1;
+ else if (!lowercase.test(chr))
+ length -= 1;
}
- if ( ((caps*100)/(int)text.length()) >= percent )
+
+ // Calculate the percentage which is upper case. If the
+ // message was entirely symbols then it can't contain
+ // any upper case letters.
+ if (length > 0 && round((upper * 100) / length) >= percent)
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Your message cannot contain more than %d%% capital letters if it's longer than %d characters", user->nick.c_str(), c->name.c_str(), percent, minlen);
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, InspIRCd::Format("Your message cannot contain %d%% or more capital letters if it's longer than %d characters", percent, minlen));
return MOD_RES_DENY;
}
}
@@ -105,37 +101,24 @@ public:
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- void ReadConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("blockcaps");
- percent = tag->getInt("percent", 100);
- minlen = tag->getInt("minlen", 1);
- std::string hmap = tag->getString("capsmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
- memset(capsmap, 0, sizeof(capsmap));
- for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
- capsmap[(unsigned char)*n] = 1;
- if (percent < 1 || percent > 100)
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "<blockcaps:percent> out of range, setting to default of 100.");
- percent = 100;
- }
- if (minlen < 1 || minlen > MAXBUF-1)
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "<blockcaps:minlen> out of range, setting to default of 1.");
- minlen = 1;
- }
- }
-
- virtual ~ModuleBlockCAPS()
- {
+ percent = tag->getUInt("percent", 100, 1, 100);
+ minlen = tag->getUInt("minlen", 1, 1, ServerInstance->Config->Limits.MaxLine);
+
+ lowercase.reset();
+ const std::string lower = tag->getString("lowercase", "abcdefghijklmnopqrstuvwxyz");
+ for (std::string::const_iterator iter = lower.begin(); iter != lower.end(); ++iter)
+ lowercase.set(static_cast<unsigned char>(*iter));
+
+ uppercase.reset();
+ const std::string upper = tag->getString("uppercase", tag->getString("capsmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ for (std::string::const_iterator iter = upper.begin(); iter != upper.end(); ++iter)
+ uppercase.set(static_cast<unsigned char>(*iter));
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support to block all-CAPS channel messages and notices", VF_VENDOR);
}
diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp
index 3cc01b4c0..43d0826dd 100644
--- a/src/modules/m_blockcolor.cpp
+++ b/src/modules/m_blockcolor.cpp
@@ -22,51 +22,38 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +c to block color */
-
-/** Handles the +c channel mode
- */
-class BlockColor : public SimpleChannelModeHandler
-{
- public:
- BlockColor(Module* Creator) : SimpleChannelModeHandler(Creator, "blockcolor", 'c') { }
-};
+#include "modules/exemption.h"
class ModuleBlockColor : public Module
{
- BlockColor bc;
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler bc;
public:
- ModuleBlockColor() : bc(this)
+ ModuleBlockColor()
+ : exemptionprov(this)
+ , bc(this, "blockcolor", 'c')
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(bc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["EXTBAN"].push_back('c');
}
- virtual void On005Numeric(std::string &output)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('c');
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ if ((target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user)))
{
- Channel* c = (Channel*)dest;
- ModResult res = ServerInstance->OnCheckExemption(user,c,"blockcolor");
+ Channel* c = target.Get<Channel>();
+ ModResult res = CheckExemption::Call(exemptionprov, user, c, "blockcolor");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet('c')))
+ if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet(bc)))
{
- for (std::string::iterator i = text.begin(); i != text.end(); i++)
+ for (std::string::iterator i = details.text.begin(); i != details.text.end(); i++)
{
switch (*i)
{
@@ -76,7 +63,7 @@ class ModuleBlockColor : public Module
case 21:
case 22:
case 31:
- user->WriteNumeric(404, "%s %s :Can't send colors to channel (+c set)",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send colors to channel (+c set)");
return MOD_RES_DENY;
break;
}
@@ -86,16 +73,7 @@ class ModuleBlockColor : public Module
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual ~ModuleBlockColor()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +c to block color",VF_VENDOR);
}
diff --git a/src/modules/m_botmode.cpp b/src/modules/m_botmode.cpp
index b29c58240..1931666d1 100644
--- a/src/modules/m_botmode.cpp
+++ b/src/modules/m_botmode.cpp
@@ -20,51 +20,36 @@
#include "inspircd.h"
+#include "modules/whois.h"
-/* $ModDesc: Provides user mode +B to mark the user as a bot */
-
-/** Handles user mode +B
- */
-class BotMode : public SimpleUserModeHandler
+enum
{
- public:
- BotMode(Module* Creator) : SimpleUserModeHandler(Creator, "bot", 'B') { }
+ // From UnrealIRCd.
+ RPL_WHOISBOT = 335
};
-class ModuleBotMode : public Module
+class ModuleBotMode : public Module, public Whois::EventListener
{
- BotMode bm;
+ SimpleUserModeHandler bm;
public:
ModuleBotMode()
- : bm(this)
- {
- }
-
- void init()
+ : Whois::EventListener(this)
+ , bm(this, "bot", 'B')
{
- ServerInstance->Modules->AddService(bm);
- Implementation eventlist[] = { I_OnWhois };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleBotMode()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides user mode +B to mark the user as a bot",VF_VENDOR);
}
- virtual void OnWhois(User* src, User* dst)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (dst->IsModeSet('B'))
+ if (whois.GetTarget()->IsModeSet(bm))
{
- ServerInstance->SendWhoisLine(src, dst, 335, src->nick+" "+dst->nick+" :is a bot on "+ServerInstance->Config->Network);
+ whois.SendLine(RPL_WHOISBOT, "is a bot on " + ServerInstance->Config->Network);
}
}
-
};
-
MODULE_INIT(ModuleBotMode)
diff --git a/src/modules/m_callerid.cpp b/src/modules/m_callerid.cpp
index 2df6d7af0..f50bdeedf 100644
--- a/src/modules/m_callerid.cpp
+++ b/src/modules/m_callerid.cpp
@@ -22,20 +22,33 @@
#include "inspircd.h"
-/* $ModDesc: Implementation of callerid, usermode +g, /accept */
+enum
+{
+ RPL_ACCEPTLIST = 281,
+ RPL_ENDOFACCEPT = 282,
+ ERR_ACCEPTFULL = 456,
+ ERR_ACCEPTEXIST = 457,
+ ERR_ACCEPTNOT = 458,
+ ERR_TARGUMODEG = 716,
+ RPL_TARGNOTIFY = 717,
+ RPL_UMODEGMSG = 718
+};
class callerid_data
{
public:
+ typedef insp::flat_set<User*> UserSet;
+ typedef std::vector<callerid_data*> CallerIdDataSet;
+
time_t lastnotify;
/** Users I accept messages from
*/
- std::set<User*> accepting;
+ UserSet accepting;
/** Users who list me as accepted
*/
- std::list<callerid_data *> wholistsme;
+ CallerIdDataSet wholistsme;
callerid_data() : lastnotify(0) { }
@@ -43,7 +56,7 @@ class callerid_data
{
std::ostringstream oss;
oss << lastnotify;
- for (std::set<User*>::const_iterator i = accepting.begin(); i != accepting.end(); ++i)
+ for (UserSet::const_iterator i = accepting.begin(); i != accepting.end(); ++i)
{
User* u = *i;
// Encode UIDs.
@@ -56,21 +69,29 @@ class callerid_data
struct CallerIDExtInfo : public ExtensionItem
{
CallerIDExtInfo(Module* parent)
- : ExtensionItem("callerid_data", parent)
+ : ExtensionItem("callerid_data", ExtensionItem::EXT_USER, parent)
{
}
- std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
{
- callerid_data* dat = static_cast<callerid_data*>(item);
- return dat->ToString(format);
+ std::string ret;
+ if (format != FORMAT_NETWORK)
+ {
+ callerid_data* dat = static_cast<callerid_data*>(item);
+ ret = dat->ToString(format);
+ }
+ return ret;
}
- void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
{
+ if (format == FORMAT_NETWORK)
+ return;
+
void* old = get_raw(container);
if (old)
- this->free(old);
+ this->free(NULL, old);
callerid_data* dat = new callerid_data;
set_raw(container, dat);
@@ -81,11 +102,8 @@ struct CallerIDExtInfo : public ExtensionItem
while (s.GetToken(tok))
{
- if (tok.empty())
- continue;
-
User *u = ServerInstance->FindNick(tok);
- if ((u) && (u->registered == REG_ALL) && (!u->quitting) && (!IS_SERVER(u)))
+ if ((u) && (u->registered == REG_ALL) && (!u->quitting))
{
if (dat->accepting.insert(u).second)
{
@@ -107,39 +125,52 @@ struct CallerIDExtInfo : public ExtensionItem
return dat;
}
- void free(void* item)
+ void free(Extensible* container, void* item) CXX11_OVERRIDE
{
callerid_data* dat = static_cast<callerid_data*>(item);
// We need to walk the list of users on our accept list, and remove ourselves from their wholistsme.
- for (std::set<User *>::iterator it = dat->accepting.begin(); it != dat->accepting.end(); it++)
+ for (callerid_data::UserSet::iterator it = dat->accepting.begin(); it != dat->accepting.end(); ++it)
{
callerid_data *targ = this->get(*it, false);
if (!targ)
{
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)");
continue; // shouldn't happen, but oh well.
}
- std::list<callerid_data*>::iterator it2 = std::find(targ->wholistsme.begin(), targ->wholistsme.end(), dat);
- if (it2 != targ->wholistsme.end())
- targ->wholistsme.erase(it2);
- else
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
+ if (!stdalgo::vector::swaperase(targ->wholistsme, dat))
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
}
delete dat;
}
};
-class User_g : public SimpleUserModeHandler
-{
-public:
- User_g(Module* Creator) : SimpleUserModeHandler(Creator, "callerid", 'g') { }
-};
-
class CommandAccept : public Command
{
+ /** Pair: first is the target, second is true to add, false to remove
+ */
+ typedef std::pair<User*, bool> ACCEPTAction;
+
+ static ACCEPTAction GetTargetAndAction(std::string& tok, User* cmdfrom = NULL)
+ {
+ bool remove = (tok[0] == '-');
+ if ((remove) || (tok[0] == '+'))
+ tok.erase(tok.begin());
+
+ User* target;
+ if (!cmdfrom || !IS_LOCAL(cmdfrom))
+ target = ServerInstance->FindNick(tok);
+ else
+ target = ServerInstance->FindNickOnly(tok);
+
+ if ((!target) || (target->registered != REG_ALL) || (target->quitting))
+ target = NULL;
+
+ return std::make_pair(target, !remove);
+ }
+
public:
CallerIDExtInfo extInfo;
unsigned int maxaccepts;
@@ -148,42 +179,21 @@ public:
{
allow_empty_last_param = false;
syntax = "*|(+|-)<nick>[,(+|-)<nick> ...]";
- TRANSLATE2(TR_CUSTOM, TR_END);
+ TRANSLATE1(TR_CUSTOM);
}
- virtual void EncodeParameter(std::string& parameter, int index)
+ void EncodeParameter(std::string& parameter, unsigned int index) CXX11_OVERRIDE
{
- if (index != 0)
+ // Send lists as-is (part of 2.0 compat)
+ if (parameter.find(',') != std::string::npos)
return;
- std::string out;
- irc::commasepstream nicks(parameter);
- std::string tok;
- while (nicks.GetToken(tok))
- {
- if (tok == "*")
- {
- continue; // Drop list requests, since remote servers ignore them anyway.
- }
- if (!out.empty())
- out.append(",");
- bool dash = false;
- if (tok[0] == '-')
- {
- dash = true;
- tok.erase(0, 1); // Remove the dash.
- }
- else if (tok[0] == '+')
- tok.erase(0, 1);
- User* u = ServerInstance->FindNick(tok);
- if ((!u) || (u->registered != REG_ALL) || (u->quitting) || (IS_SERVER(u)))
- continue;
+ // Convert a [+|-]<nick> into a [-]<uuid>
+ ACCEPTAction action = GetTargetAndAction(parameter);
+ if (!action.first)
+ return;
- if (dash)
- out.append("-");
- out.append(u->uuid);
- }
- parameter = out;
+ parameter = (action.second ? "" : "-") + action.first->uuid;
}
/** Will take any number of nicks (up to MaxTargets), which can be seperated by commas.
@@ -191,56 +201,60 @@ public:
* /accept nick1,nick2,nick3,*
* to add 3 nicks and then show your list
*/
- CmdResult Handle(const std::vector<std::string> &parameters, User* user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+ if (CommandParser::LoopCall(user, this, parameters, 0))
return CMD_SUCCESS;
+
/* Even if callerid mode is not set, we let them manage their ACCEPT list so that if they go +g they can
* have a list already setup. */
- const std::string& tok = parameters[0];
-
- if (tok == "*")
+ if (parameters[0] == "*")
{
- if (IS_LOCAL(user))
- ListAccept(user);
+ ListAccept(user);
return CMD_SUCCESS;
}
- else if (tok[0] == '-')
+
+ std::string tok = parameters[0];
+ ACCEPTAction action = GetTargetAndAction(tok, user);
+ if (!action.first)
{
- User* whotoremove;
- if (IS_LOCAL(user))
- whotoremove = ServerInstance->FindNickOnly(tok.substr(1));
- else
- whotoremove = ServerInstance->FindNick(tok.substr(1));
-
- if (whotoremove)
- return (RemoveAccept(user, whotoremove) ? CMD_SUCCESS : CMD_FAILURE);
- else
- return CMD_FAILURE;
+ user->WriteNumeric(Numerics::NoSuchNick(tok));
+ return CMD_FAILURE;
}
+
+ if ((!IS_LOCAL(user)) && (!IS_LOCAL(action.first)))
+ // Neither source nor target is local, forward the command to the server of target
+ return CMD_SUCCESS;
+
+ // The second item in the pair is true if the first char is a '+' (or nothing), false if it's a '-'
+ if (action.second)
+ return (AddAccept(user, action.first) ? CMD_SUCCESS : CMD_FAILURE);
else
- {
- const std::string target = (tok[0] == '+' ? tok.substr(1) : tok);
- User* whotoadd;
- if (IS_LOCAL(user))
- whotoadd = ServerInstance->FindNickOnly(target);
- else
- whotoadd = ServerInstance->FindNick(target);
-
- if ((whotoadd) && (whotoadd->registered == REG_ALL) && (!whotoadd->quitting) && (!IS_SERVER(whotoadd)))
- return (AddAccept(user, whotoadd) ? CMD_SUCCESS : CMD_FAILURE);
- else
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), tok.c_str());
- return CMD_FAILURE;
- }
- }
+ return (RemoveAccept(user, action.first) ? CMD_SUCCESS : CMD_FAILURE);
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- return ROUTE_BROADCAST;
+ // There is a list in parameters[0] in two cases:
+ // Either when the source is remote, this happens because 2.0 servers send comma seperated uuid lists,
+ // we don't split those but broadcast them, as before.
+ //
+ // Or if the source is local then LoopCall() runs OnPostCommand() after each entry in the list,
+ // meaning the linking module has sent an ACCEPT already for each entry in the list to the
+ // appropiate server and the ACCEPT with the list of nicks (this) doesn't need to be sent anywhere.
+ if ((!IS_LOCAL(user)) && (parameters[0].find(',') != std::string::npos))
+ return ROUTE_BROADCAST;
+
+ // Find the target
+ std::string targetstring = parameters[0];
+ ACCEPTAction action = GetTargetAndAction(targetstring, user);
+ if (!action.first)
+ // Target is a "*" or source is local and the target is a list of nicks
+ return ROUTE_LOCALONLY;
+
+ // Route to the server of the target
+ return ROUTE_UNICAST(action.first->server);
}
void ListAccept(User* user)
@@ -248,10 +262,10 @@ public:
callerid_data* dat = extInfo.get(user, false);
if (dat)
{
- for (std::set<User*>::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
- user->WriteNumeric(281, "%s %s", user->nick.c_str(), (*i)->nick.c_str());
+ for (callerid_data::UserSet::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
+ user->WriteNumeric(RPL_ACCEPTLIST, (*i)->nick);
}
- user->WriteNumeric(282, "%s :End of ACCEPT list", user->nick.c_str());
+ user->WriteNumeric(RPL_ENDOFACCEPT, "End of ACCEPT list");
}
bool AddAccept(User* user, User* whotoadd)
@@ -260,12 +274,12 @@ public:
callerid_data* dat = extInfo.get(user, true);
if (dat->accepting.size() >= maxaccepts)
{
- user->WriteNumeric(456, "%s :Accept list is full (limit is %d)", user->nick.c_str(), maxaccepts);
+ user->WriteNumeric(ERR_ACCEPTFULL, InspIRCd::Format("Accept list is full (limit is %d)", maxaccepts));
return false;
}
if (!dat->accepting.insert(whotoadd).second)
{
- user->WriteNumeric(457, "%s %s :is already on your accept list", user->nick.c_str(), whotoadd->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTEXIST, whotoadd->nick, "is already on your accept list");
return false;
}
@@ -273,7 +287,7 @@ public:
callerid_data *targ = extInfo.get(whotoadd, true);
targ->wholistsme.push_back(dat);
- user->WriteServ("NOTICE %s :%s is now on your accept list", user->nick.c_str(), whotoadd->nick.c_str());
+ user->WriteNotice(whotoadd->nick + " is now on your accept list");
return true;
}
@@ -283,48 +297,39 @@ public:
callerid_data* dat = extInfo.get(user, false);
if (!dat)
{
- user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTNOT, whotoremove->nick, "is not on your accept list");
return false;
}
- std::set<User*>::iterator i = dat->accepting.find(whotoremove);
- if (i == dat->accepting.end())
+ if (!dat->accepting.erase(whotoremove))
{
- user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTNOT, whotoremove->nick, "is not on your accept list");
return false;
}
- dat->accepting.erase(i);
-
// Look up their list to remove me.
callerid_data *dat2 = extInfo.get(whotoremove, false);
if (!dat2)
{
// How the fuck is this possible.
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)");
return false;
}
- std::list<callerid_data*>::iterator it = std::find(dat2->wholistsme.begin(), dat2->wholistsme.end(), dat);
- if (it != dat2->wholistsme.end())
- // Found me!
- dat2->wholistsme.erase(it);
- else
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
+ if (!stdalgo::vector::swaperase(dat2->wholistsme, dat))
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
- user->WriteServ("NOTICE %s :%s is no longer on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+ user->WriteNotice(whotoremove->nick + " is no longer on your accept list");
return true;
}
};
class ModuleCallerID : public Module
{
-private:
CommandAccept cmd;
- User_g myumode;
+ SimpleUserModeHandler myumode;
// Configuration variables:
- bool operoverride; // Operators can override callerid.
bool tracknick; // Allow ACCEPT entries to update with nick changes.
unsigned int notify_cooldown; // Seconds between notifications.
@@ -339,74 +344,59 @@ private:
return;
// Iterate over the list of people who accept me, and remove all entries
- for (std::list<callerid_data *>::iterator it = userdata->wholistsme.begin(); it != userdata->wholistsme.end(); it++)
+ for (callerid_data::CallerIdDataSet::iterator it = userdata->wholistsme.begin(); it != userdata->wholistsme.end(); ++it)
{
callerid_data *dat = *(it);
// Find me on their callerid list
- std::set<User *>::iterator it2 = dat->accepting.find(who);
-
- if (it2 != dat->accepting.end())
- dat->accepting.erase(it2);
- else
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
+ if (!dat->accepting.erase(who))
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
}
userdata->wholistsme.clear();
}
public:
- ModuleCallerID() : cmd(this), myumode(this)
+ ModuleCallerID()
+ : cmd(this)
+ , myumode(this, "callerid", 'g')
{
}
- void init()
- {
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(myumode);
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.extInfo);
-
- Implementation eventlist[] = { I_OnRehash, I_OnUserPostNick, I_OnUserQuit, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleCallerID()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implementation of callerid, usermode +g, /accept", VF_COMMON | VF_VENDOR);
}
- virtual void On005Numeric(std::string& output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output += " CALLERID=g";
+ tokens["ACCEPT"] = ConvToStr(cmd.maxaccepts);
+ tokens["CALLERID"] = ConvToStr(myumode.GetModeChar());
}
- ModResult PreText(User* user, User* dest, std::string& text)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (!dest->IsModeSet('g') || (user == dest))
+ if (!IS_LOCAL(user) || target.type != MessageTarget::TYPE_USER)
return MOD_RES_PASSTHRU;
- if (operoverride && IS_OPER(user))
+ User* dest = target.Get<User>();
+ if (!dest->IsModeSet(myumode) || (user == dest))
return MOD_RES_PASSTHRU;
- callerid_data* dat = cmd.extInfo.get(dest, true);
- std::set<User*>::iterator i = dat->accepting.find(user);
+ if (user->HasPrivPermission("users/callerid-override"))
+ return MOD_RES_PASSTHRU;
- if (i == dat->accepting.end())
+ callerid_data* dat = cmd.extInfo.get(dest, true);
+ if (!dat->accepting.count(user))
{
time_t now = ServerInstance->Time();
/* +g and *not* accepted */
- user->WriteNumeric(716, "%s %s :is in +g mode (server-side ignore).", user->nick.c_str(), dest->nick.c_str());
+ user->WriteNumeric(ERR_TARGUMODEG, dest->nick, "is in +g mode (server-side ignore).");
if (now > (dat->lastnotify + (time_t)notify_cooldown))
{
- user->WriteNumeric(717, "%s %s :has been informed that you messaged them.", user->nick.c_str(), dest->nick.c_str());
- dest->SendText(":%s 718 %s %s %s@%s :is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
- ServerInstance->Config->ServerName.c_str(), dest->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), user->nick.c_str());
+ user->WriteNumeric(RPL_TARGNOTIFY, dest->nick, "has been informed that you messaged them.");
+ dest->WriteRemoteNumeric(RPL_UMODEGMSG, user->nick, InspIRCd::Format("%s@%s", user->ident.c_str(), user->GetDisplayedHost().c_str()), InspIRCd::Format("is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
+ user->nick.c_str()));
dat->lastnotify = now;
}
return MOD_RES_DENY;
@@ -414,43 +404,30 @@ public:
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
- {
- if (IS_LOCAL(user) && target_type == TYPE_USER)
- return PreText(user, (User*)dest, text);
-
- return MOD_RES_PASSTHRU;
- }
-
- virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
- {
- if (IS_LOCAL(user) && target_type == TYPE_USER)
- return PreText(user, (User*)dest, text);
-
- return MOD_RES_PASSTHRU;
- }
-
- void OnUserPostNick(User* user, const std::string& oldnick)
+ void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
{
if (!tracknick)
RemoveFromAllAccepts(user);
}
- void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
{
RemoveFromAllAccepts(user);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("callerid");
- cmd.maxaccepts = tag->getInt("maxaccepts", 16);
- operoverride = tag->getBool("operoverride");
+ cmd.maxaccepts = tag->getUInt("maxaccepts", 16);
tracknick = tag->getBool("tracknick");
- notify_cooldown = tag->getInt("cooldown", 60);
+ notify_cooldown = tag->getDuration("cooldown", 60);
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ // Want to be after modules like silence or services_account
+ ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
}
};
MODULE_INIT(ModuleCallerID)
-
-
diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp
index ae9e824f4..922061757 100644
--- a/src/modules/m_cap.cpp
+++ b/src/modules/m_cap.cpp
@@ -1,8 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -19,151 +18,439 @@
#include "inspircd.h"
-#include "m_cap.h"
+#include "modules/reload.h"
+#include "modules/cap.h"
-/* $ModDesc: Provides the CAP negotiation mechanism seen in ratbox-derived ircds */
+enum
+{
+ // From IRCv3 capability-negotiation-3.1.
+ ERR_INVALIDCAPCMD = 410
+};
-/*
-CAP LS
-:alfred.staticbox.net CAP * LS :multi-prefix sasl
-CAP REQ :multi-prefix
-:alfred.staticbox.net CAP * ACK :multi-prefix
-CAP CLEAR
-:alfred.staticbox.net CAP * ACK :-multi-prefix
-CAP REQ :multi-prefix
-:alfred.staticbox.net CAP * ACK :multi-prefix
-CAP LIST
-:alfred.staticbox.net CAP * LIST :multi-prefix
-CAP END
-*/
-
-/** Handle /CAP
- */
-class CommandCAP : public Command
+namespace Cap
{
- public:
- LocalIntExt reghold;
- CommandCAP (Module* mod) : Command(mod, "CAP", 1),
- reghold("CAP_REGHOLD", mod)
+ class ManagerImpl;
+}
+
+static Cap::ManagerImpl* managerimpl;
+
+class Cap::ManagerImpl : public Cap::Manager, public ReloadModule::EventListener
+{
+ /** Stores the cap state of a module being reloaded
+ */
+ struct CapModData
{
- works_before_reg = true;
+ struct Data
+ {
+ std::string name;
+ std::vector<std::string> users;
+
+ Data(Capability* cap)
+ : name(cap->GetName())
+ {
+ }
+ };
+ std::vector<Data> caps;
+ };
+
+ typedef insp::flat_map<std::string, Capability*, irc::insensitive_swo> CapMap;
+
+ ExtItem capext;
+ CapMap caps;
+ Events::ModuleEventProvider& evprov;
+
+ static bool CanRequest(LocalUser* user, Ext usercaps, Capability* cap, bool adding)
+ {
+ const bool hascap = ((usercaps & cap->GetMask()) != 0);
+ if (hascap == adding)
+ return true;
+
+ return cap->OnRequest(user, adding);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ Capability::Bit AllocateBit() const
{
- irc::string subcommand = parameters[0].c_str();
+ Capability::Bit used = 0;
+ for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ used |= cap->GetMask();
+ }
- if (subcommand == "REQ")
+ for (unsigned int i = 0; i < MAX_CAPS; i++)
{
- if (parameters.size() < 2)
- return CMD_FAILURE;
+ Capability::Bit bit = (1 << i);
+ if (!(used & bit))
+ return bit;
+ }
+ throw ModuleException("Too many caps");
+ }
+
+ void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnReloadModuleSave()");
+ if (mod == creator)
+ return;
- CapEvent Data(creator, user, CapEvent::CAPEVENT_REQ);
+ CapModData* capmoddata = new CapModData;
+ cd.add(this, capmoddata);
- // tokenize the input into a nice list of requested caps
- std::string cap_;
- irc::spacesepstream cap_stream(parameters[1]);
+ for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ // Only save users of caps that belong to the module being reloaded
+ if (cap->creator != mod)
+ continue;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Module being reloaded implements cap %s, saving cap users", cap->GetName().c_str());
+ capmoddata->caps.push_back(CapModData::Data(cap));
+ CapModData::Data& capdata = capmoddata->caps.back();
- while (cap_stream.GetToken(cap_))
+ // Populate list with uuids of users who are using the cap
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
{
- // Whilst the handling of extraneous spaces is not currently defined in the CAP specification
- // every single other implementation ignores extraneous spaces. Lets copy them for
- // compatibility purposes.
- trim(cap_);
- if (!cap_.empty())
- Data.wanted.push_back(cap_);
+ LocalUser* user = *j;
+ if (cap->get(user))
+ capdata.users.push_back(user->uuid);
}
+ }
+ }
- reghold.set(user, 1);
- Data.Send();
-
- if (Data.wanted.empty())
+ void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
+ {
+ CapModData* capmoddata = static_cast<CapModData*>(data);
+ for (std::vector<CapModData::Data>::const_iterator i = capmoddata->caps.begin(); i != capmoddata->caps.end(); ++i)
+ {
+ const CapModData::Data& capdata = *i;
+ Capability* cap = ManagerImpl::Find(capdata.name);
+ if (!cap)
{
- user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), parameters[1].c_str());
- return CMD_SUCCESS;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s is no longer available after reload", capdata.name.c_str());
+ continue;
}
- // HACK: reset all of the caps which were enabled on this user because a cap request is atomic.
- for (std::vector<std::pair<GenericCap*, int> >::iterator iter = Data.changed.begin(); iter != Data.changed.end(); ++iter)
- iter->first->ext.set(user, iter->second);
+ // Set back the cap for all users who were using it before the reload
+ for (std::vector<std::string>::const_iterator j = capdata.users.begin(); j != capdata.users.end(); ++j)
+ {
+ const std::string& uuid = *j;
+ User* user = ServerInstance->FindUUID(uuid);
+ if (!user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone when trying to restore cap %s", uuid.c_str(), capdata.name.c_str());
+ continue;
+ }
- user->WriteServ("CAP %s NAK :%s", user->nick.c_str(), parameters[1].c_str());
+ cap->set(user, true);
+ }
}
- else if (subcommand == "END")
+ delete capmoddata;
+ }
+
+ public:
+ ManagerImpl(Module* mod, Events::ModuleEventProvider& evprovref)
+ : Cap::Manager(mod)
+ , ReloadModule::EventListener(mod)
+ , capext(mod)
+ , evprov(evprovref)
+ {
+ managerimpl = this;
+ }
+
+ ~ManagerImpl()
+ {
+ for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
{
- reghold.set(user, 0);
+ Capability* cap = i->second;
+ cap->Unregister();
}
- else if ((subcommand == "LS") || (subcommand == "LIST"))
- {
- CapEvent Data(creator, user, subcommand == "LS" ? CapEvent::CAPEVENT_LS : CapEvent::CAPEVENT_LIST);
+ }
+
+ void AddCap(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // No-op if the cap is already registered.
+ // This allows modules to call SetActive() on a cap without checking if it's active first.
+ if (cap->IsRegistered())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Registering cap %s", cap->GetName().c_str());
+ cap->bit = AllocateBit();
+ cap->extitem = &capext;
+ caps.insert(std::make_pair(cap->GetName(), cap));
+ ServerInstance->Modules.AddReferent("cap/" + cap->GetName(), cap);
+
+ FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, true));
+ }
+
+ void DelCap(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // No-op if the cap is not registered, see AddCap() above
+ if (!cap->IsRegistered())
+ return;
- reghold.set(user, 1);
- Data.Send();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
- std::string Result;
- if (Data.wanted.size() > 0)
- Result = irc::stringjoiner(" ", Data.wanted, 0, Data.wanted.size() - 1).GetJoined();
+ // Fire the event first so modules can still see who is using the cap which is being unregistered
+ FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, false));
- user->WriteServ("CAP %s %s :%s", user->nick.c_str(), subcommand.c_str(), Result.c_str());
+ // Turn off the cap for all users
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ cap->set(user, false);
}
- else if (subcommand == "CLEAR")
+
+ ServerInstance->Modules.DelReferent(cap);
+ cap->Unregister();
+ caps.erase(cap->GetName());
+ }
+
+ Capability* Find(const std::string& capname) const CXX11_OVERRIDE
+ {
+ CapMap::const_iterator it = caps.find(capname);
+ if (it != caps.end())
+ return it->second;
+ return NULL;
+ }
+
+ void NotifyValueChange(Capability* cap) CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s changed value", cap->GetName().c_str());
+ FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapValueChange, (cap));
+ }
+
+ Protocol GetProtocol(LocalUser* user) const
+ {
+ return ((capext.get(user) & CAP_302_BIT) ? CAP_302 : CAP_LEGACY);
+ }
+
+ void Set302Protocol(LocalUser* user)
+ {
+ capext.set(user, capext.get(user) | CAP_302_BIT);
+ }
+
+ bool HandleReq(LocalUser* user, const std::string& reqlist)
+ {
+ Ext usercaps = capext.get(user);
+ irc::spacesepstream ss(reqlist);
+ for (std::string capname; ss.GetToken(capname); )
{
- CapEvent Data(creator, user, CapEvent::CAPEVENT_CLEAR);
+ bool remove = (capname[0] == '-');
+ if (remove)
+ capname.erase(capname.begin());
- reghold.set(user, 1);
- Data.Send();
+ Capability* cap = ManagerImpl::Find(capname);
+ if ((!cap) || (!CanRequest(user, usercaps, cap, !remove)))
+ return false;
- std::string Result;
- if (!Data.ack.empty())
- Result = irc::stringjoiner(" ", Data.ack, 0, Data.ack.size() - 1).GetJoined();
- user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), Result.c_str());
+ if (remove)
+ usercaps = cap->DelFromMask(usercaps);
+ else
+ usercaps = cap->AddToMask(usercaps);
}
- else
+
+ capext.set(user, usercaps);
+ return true;
+ }
+
+ void HandleList(std::string& out, LocalUser* user, bool show_all, bool show_values, bool minus_prefix = false) const
+ {
+ Ext show_caps = (show_all ? ~0 : capext.get(user));
+
+ for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
{
- user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s %s :Invalid CAP subcommand", user->nick.c_str(), subcommand.empty() ? "*" : subcommand.c_str());
- return CMD_FAILURE;
+ Capability* cap = i->second;
+ if (!(show_caps & cap->GetMask()))
+ continue;
+
+ if ((show_all) && (!cap->OnList(user)))
+ continue;
+
+ if (minus_prefix)
+ out.push_back('-');
+ out.append(cap->GetName());
+
+ if (show_values)
+ {
+ const std::string* capvalue = cap->GetValue(user);
+ if ((capvalue) && (!capvalue->empty()) && (capvalue->find(' ') == std::string::npos))
+ {
+ out.push_back('=');
+ out.append(*capvalue, 0, MAX_VALUE_LENGTH);
+ }
+ }
+ out.push_back(' ');
}
+ }
- return CMD_SUCCESS;
+ void HandleClear(LocalUser* user, std::string& result)
+ {
+ HandleList(result, user, false, false, true);
+ capext.unset(user);
}
};
-class ModuleCAP : public Module
+Cap::ExtItem::ExtItem(Module* mod)
+ : LocalIntExt("caps", ExtensionItem::EXT_USER, mod)
+{
+}
+
+std::string Cap::ExtItem::serialize(SerializeFormat format, const Extensible* container, void* item) const
+{
+ std::string ret;
+ // XXX: Cast away the const because IS_LOCAL() doesn't handle it
+ LocalUser* user = IS_LOCAL(const_cast<User*>(static_cast<const User*>(container)));
+ if ((format == FORMAT_NETWORK) || (!user))
+ return ret;
+
+ // List requested caps
+ managerimpl->HandleList(ret, user, false, false);
+
+ // Serialize cap protocol version. If building a human-readable string append a new token, otherwise append only a single character indicating the version.
+ Protocol protocol = managerimpl->GetProtocol(user);
+ if (format == FORMAT_USER)
+ ret.append("capversion=3.");
+ else if (!ret.empty())
+ ret.erase(ret.length()-1);
+
+ if (protocol == CAP_302)
+ ret.push_back('2');
+ else
+ ret.push_back('1');
+
+ return ret;
+}
+
+void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format == FORMAT_NETWORK)
+ return;
+
+ LocalUser* user = IS_LOCAL(static_cast<User*>(container));
+ if (!user)
+ return; // Can't happen
+
+ // Process the cap protocol version which is a single character at the end of the serialized string
+ const char verchar = *value.rbegin();
+ if (verchar == '2')
+ managerimpl->Set302Protocol(user);
+
+ // Remove the version indicator from the string passed to HandleReq
+ std::string caplist(value, 0, value.size()-1);
+ managerimpl->HandleReq(user, caplist);
+}
+
+class CapMessage : public Cap::MessageBase
{
- CommandCAP cmd;
public:
- ModuleCAP()
- : cmd(this)
+ CapMessage(LocalUser* user, const std::string& subcmd, const std::string& result)
+ : Cap::MessageBase(subcmd)
{
+ SetUser(user);
+ PushParamRef(result);
}
+};
+
+class CommandCap : public SplitCommand
+{
+ Events::ModuleEventProvider evprov;
+ Cap::ManagerImpl manager;
+ ClientProtocol::EventProvider protoevprov;
- void init()
+ void DisplayResult(LocalUser* user, const std::string& subcmd, std::string& result)
{
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.reghold);
+ if (*result.rbegin() == ' ')
+ result.erase(result.end()-1);
+ DisplayResult2(user, subcmd, result);
+ }
- Implementation eventlist[] = { I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ void DisplayResult2(LocalUser* user, const std::string& subcmd, const std::string& result)
+ {
+ CapMessage msg(user, subcmd, result);
+ ClientProtocol::Event ev(protoevprov, msg);
+ user->Send(ev);
}
- ModResult OnCheckReady(LocalUser* user)
+ public:
+ LocalIntExt holdext;
+
+ CommandCap(Module* mod)
+ : SplitCommand(mod, "CAP", 1)
+ , evprov(mod, "event/cap")
+ , manager(mod, evprov)
+ , protoevprov(mod, name)
+ , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
{
- /* Users in CAP state get held until CAP END */
- if (cmd.reghold.get(user))
- return MOD_RES_DENY;
+ works_before_reg = true;
+ }
+
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if (user->registered != REG_ALL)
+ holdext.set(user, 1);
+
+ std::string subcommand(parameters[0].length(), ' ');
+ std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
+
+ if (subcommand == "REQ")
+ {
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
- return MOD_RES_PASSTHRU;
+ const std::string replysubcmd = (manager.HandleReq(user, parameters[1]) ? "ACK" : "NAK");
+ DisplayResult2(user, replysubcmd, parameters[1]);
+ }
+ else if (subcommand == "END")
+ {
+ holdext.unset(user);
+ }
+ else if ((subcommand == "LS") || (subcommand == "LIST"))
+ {
+ const bool is_ls = (subcommand.length() == 2);
+ if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
+ manager.Set302Protocol(user);
+
+ std::string result;
+ // Show values only if supports v3.2 and doing LS
+ manager.HandleList(result, user, is_ls, ((is_ls) && (manager.GetProtocol(user) != Cap::CAP_LEGACY)));
+ DisplayResult(user, subcommand, result);
+ }
+ else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
+ {
+ std::string result;
+ manager.HandleClear(user, result);
+ DisplayResult(user, "ACK", result);
+ }
+ else
+ {
+ user->WriteNumeric(ERR_INVALIDCAPCMD, subcommand.empty() ? "*" : subcommand, "Invalid CAP subcommand");
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
}
+};
- ~ModuleCAP()
+class ModuleCap : public Module
+{
+ CommandCap cmd;
+
+ public:
+ ModuleCap()
+ : cmd(this)
{
}
- Version GetVersion()
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- return Version("Client CAP extension support", VF_VENDOR);
+ return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
}
-};
-MODULE_INIT(ModuleCAP)
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for CAP capability negotiation", VF_VENDOR);
+ }
+};
+MODULE_INIT(ModuleCap)
diff --git a/src/modules/m_cap.h b/src/modules/m_cap.h
deleted file mode 100644
index 23cf8cf69..000000000
--- a/src/modules/m_cap.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef M_CAP_H
-#define M_CAP_H
-
-class GenericCap;
-
-class CapEvent : public Event
-{
- public:
- enum CapEventType
- {
- CAPEVENT_REQ,
- CAPEVENT_LS,
- CAPEVENT_LIST,
- CAPEVENT_CLEAR
- };
-
- CapEventType type;
- std::vector<std::string> wanted;
- std::vector<std::string> ack;
- std::vector<std::pair<GenericCap*, int> > changed; // HACK: clean this up before 2.2
- User* user;
- CapEvent(Module* sender, User* u, CapEventType capevtype) : Event(sender, "cap_request"), type(capevtype), user(u) {}
-};
-
-class GenericCap
-{
- public:
- LocalIntExt ext;
- const std::string cap;
- GenericCap(Module* parent, const std::string &Cap) : ext("cap_" + Cap, parent), cap(Cap)
- {
- ServerInstance->Modules->AddService(ext);
- }
-
- void HandleEvent(Event& ev)
- {
- if (ev.id != "cap_request")
- return;
-
- CapEvent *data = static_cast<CapEvent*>(&ev);
- if (data->type == CapEvent::CAPEVENT_REQ)
- {
- for (std::vector<std::string>::iterator it = data->wanted.begin(); it != data->wanted.end(); ++it)
- {
- if (it->empty())
- continue;
- bool enablecap = ((*it)[0] != '-');
- if (((enablecap) && (*it == cap)) || (*it == "-" + cap))
- {
- // we can handle this, so ACK it, and remove it from the wanted list
- data->ack.push_back(*it);
- data->wanted.erase(it);
- data->changed.push_back(std::make_pair(this, ext.set(data->user, enablecap ? 1 : 0)));
- break;
- }
- }
- }
- else if (data->type == CapEvent::CAPEVENT_LS)
- {
- data->wanted.push_back(cap);
- }
- else if (data->type == CapEvent::CAPEVENT_LIST)
- {
- if (ext.get(data->user))
- data->wanted.push_back(cap);
- }
- else if (data->type == CapEvent::CAPEVENT_CLEAR)
- {
- data->ack.push_back("-" + cap);
- ext.set(data->user, 0);
- }
- }
-};
-
-#endif
diff --git a/src/modules/m_cban.cpp b/src/modules/m_cban.cpp
index fb78e41b2..d1fb23620 100644
--- a/src/modules/m_cban.cpp
+++ b/src/modules/m_cban.cpp
@@ -22,48 +22,42 @@
#include "inspircd.h"
#include "xline.h"
+#include "modules/stats.h"
-/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */
+enum
+{
+ // InspIRCd-specific.
+ ERR_BADCHANNEL = 926
+};
/** Holds a CBAN item
*/
class CBan : public XLine
{
-public:
- irc::string matchtext;
+private:
+ std::string matchtext;
+public:
CBan(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& ch)
: XLine(s_time, d, src, re, "CBAN")
- {
- this->matchtext = ch.c_str();
- }
-
- ~CBan()
+ , matchtext(ch)
{
}
// XXX I shouldn't have to define this
- bool Matches(User *u)
+ bool Matches(User* u) CXX11_OVERRIDE
{
return false;
}
- bool Matches(const std::string &s)
+ bool Matches(const std::string& s) CXX11_OVERRIDE
{
- if (matchtext == s)
- return true;
- return false;
- }
-
- void DisplayExpiry()
- {
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired CBan %s (set by %s %ld seconds ago)",
- this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
+ return irc::equals(matchtext, s);
}
- const char* Displayable()
+ const std::string& Displayable() CXX11_OVERRIDE
{
- return matchtext.c_str();
+ return matchtext;
}
};
@@ -76,12 +70,12 @@ class CBanFactory : public XLineFactory
/** Generate a CBAN
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
return new CBan(set_time, duration, source, reason, xline_specific_mask);
}
- bool AutoApplyToUserList(XLine *x)
+ bool AutoApplyToUserList(XLine* x) CXX11_OVERRIDE
{
return false; // No, we apply to channels.
}
@@ -95,10 +89,9 @@ class CommandCBan : public Command
CommandCBan(Module* Creator) : Command(Creator, "CBAN", 1, 3)
{
flags_needed = 'o'; this->syntax = "<channel> [<duration> :<reason>]";
- TRANSLATE4(TR_TEXT,TR_TEXT,TR_TEXT,TR_END);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
/* syntax: CBAN #channel time :reason goes here */
/* 'time' is a human-readable timestring, like 2d3h2s. */
@@ -111,14 +104,14 @@ class CommandCBan : public Command
}
else
{
- user->WriteServ("NOTICE %s :*** CBan %s not found in list, try /stats C.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** CBan " + parameters[0] + " not found in list, try /stats C.");
return CMD_FAILURE;
}
}
else
{
// Adding - XXX todo make this respect <insane> tag perhaps..
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
const char *reason = (parameters.size() > 2) ? parameters[2].c_str() : "No reason supplied";
CBan* r = new CBan(ServerInstance->Time(), duration, user->nick.c_str(), reason, parameters[0].c_str());
@@ -131,21 +124,21 @@ class CommandCBan : public Command
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteGlobalSno('x', "%s added timed CBan for %s, expires on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), reason);
}
}
else
{
delete r;
- user->WriteServ("NOTICE %s :*** CBan for %s already exists", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** CBan for " + parameters[0] + " already exists");
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
if (IS_LOCAL(user))
return ROUTE_LOCALONLY; // spanningtree will send ADDLINE
@@ -154,61 +147,58 @@ class CommandCBan : public Command
}
};
-class ModuleCBan : public Module
+class ModuleCBan : public Module, public Stats::EventListener
{
CommandCBan mycommand;
CBanFactory f;
public:
- ModuleCBan() : mycommand(this)
+ ModuleCBan()
+ : Stats::EventListener(this)
+ , mycommand(this)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->XLines->RegisterFactory(&f);
-
- ServerInstance->Modules->AddService(mycommand);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnStats };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleCBan()
+ ~ModuleCBan()
{
ServerInstance->XLines->DelAll("CBAN");
ServerInstance->XLines->UnregisterFactory(&f);
}
- virtual ModResult OnStats(char symbol, User* user, string_list &out)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'C')
+ if (stats.GetSymbol() != 'C')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("CBAN", 210, user, out);
+ ServerInstance->XLines->InvokeStats("CBAN", 210, stats);
return MOD_RES_DENY;
}
- virtual ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
XLine *rl = ServerInstance->XLines->MatchesLine("CBAN", cname);
if (rl)
{
// Channel is banned.
- user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick.c_str(), cname, rl->reason.c_str());
+ user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is CBANed: %s", cname.c_str(), rl->reason.c_str()));
ServerInstance->SNO->WriteGlobalSno('a', "%s tried to join %s which is CBANed (%s)",
- user->nick.c_str(), cname, rl->reason.c_str());
+ user->nick.c_str(), cname.c_str(), rl->reason.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Gives /cban, aka C:lines. Think Q:lines, for channels.", VF_COMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleCBan)
-
diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp
index 65b965df2..fedc0f713 100644
--- a/src/modules/m_censor.cpp
+++ b/src/modules/m_censor.cpp
@@ -20,72 +20,41 @@
*/
-/* $ModDesc: Provides user and channel +G mode */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
#include "inspircd.h"
-#include <iostream>
-
-typedef std::map<irc::string,irc::string> censor_t;
+#include "modules/exemption.h"
-/** Handles usermode +G
- */
-class CensorUser : public SimpleUserModeHandler
-{
- public:
- CensorUser(Module* Creator) : SimpleUserModeHandler(Creator, "u_censor", 'G') { }
-};
-
-/** Handles channel mode +G
- */
-class CensorChannel : public SimpleChannelModeHandler
-{
- public:
- CensorChannel(Module* Creator) : SimpleChannelModeHandler(Creator, "censor", 'G') { }
-};
+typedef insp::flat_map<std::string, std::string, irc::insensitive_swo> censor_t;
class ModuleCensor : public Module
{
+ CheckExemption::EventProvider exemptionprov;
censor_t censors;
- CensorUser cu;
- CensorChannel cc;
+ SimpleUserModeHandler cu;
+ SimpleChannelModeHandler cc;
public:
- ModuleCensor() : cu(this), cc(this) { }
-
- void init()
- {
- /* Read the configuration file on startup.
- */
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cu);
- ServerInstance->Modules->AddService(cc);
- Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual ~ModuleCensor()
+ ModuleCensor()
+ : exemptionprov(this)
+ , cu(this, "u_censor", 'G')
+ , cc(this, "censor", 'G')
{
}
// format of a config entry is <badword text="shit" replace="poo">
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
bool active = false;
- if (target_type == TYPE_USER)
- active = ((User*)dest)->IsModeSet('G');
- else if (target_type == TYPE_CHANNEL)
+ if (target.type == MessageTarget::TYPE_USER)
+ active = target.Get<User>()->IsModeSet(cu);
+ else if (target.type == MessageTarget::TYPE_CHANNEL)
{
- active = ((Channel*)dest)->IsModeSet('G');
- Channel* c = (Channel*)dest;
- ModResult res = ServerInstance->OnCheckExemption(user,c,"censor");
+ Channel* c = target.Get<Channel>();
+ active = c->IsModeSet(cc);
+ ModResult res = CheckExemption::Call(exemptionprov, user, c, "censor");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
@@ -94,30 +63,25 @@ class ModuleCensor : public Module
if (!active)
return MOD_RES_PASSTHRU;
- irc::string text2 = text.c_str();
for (censor_t::iterator index = censors.begin(); index != censors.end(); index++)
{
- if (text2.find(index->first) != irc::string::npos)
+ size_t censorpos;
+ while ((censorpos = irc::find(details.text, index->first)) != std::string::npos)
{
if (index->second.empty())
{
- user->WriteNumeric(ERR_WORDFILTERED, "%s %s %s :Your message contained a censored word, and was blocked", user->nick.c_str(), ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name.c_str() : ((User*)dest)->nick.c_str()), index->first.c_str());
+ const std::string targname = target.type == MessageTarget::TYPE_CHANNEL ? target.Get<Channel>()->name : target.Get<User>()->nick;
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, targname, "Your message contained a censored word (" + index->first + "), and was blocked");
return MOD_RES_DENY;
}
- SearchAndReplace(text2, index->first, index->second);
+ details.text.replace(censorpos, index->first.size(), index->second);
}
}
- text = text2.c_str();
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
/*
* reload our config file on rehash - we must destroy and re-allocate the classes
@@ -129,14 +93,16 @@ class ModuleCensor : public Module
for (ConfigIter i = badwords.first; i != badwords.second; ++i)
{
ConfigTag* tag = i->second;
- std::string str = tag->getString("text");
- irc::string pattern(str.c_str());
- str = tag->getString("replace");
- censors[pattern] = irc::string(str.c_str());
+ const std::string text = tag->getString("text");
+ if (text.empty())
+ continue;
+
+ const std::string replace = tag->getString("replace");
+ censors[text] = replace;
}
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides user and channel +G mode",VF_VENDOR);
}
diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp
index 13f1c1fb5..8d911a5b4 100644
--- a/src/modules/m_cgiirc.cpp
+++ b/src/modules/m_cgiirc.cpp
@@ -24,406 +24,365 @@
#include "inspircd.h"
-#include "xline.h"
+#include "modules/ssl.h"
+#include "modules/whois.h"
-/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
+enum
+{
+ // InspIRCd-specific.
+ RPL_WHOISGATEWAY = 350
+};
-enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
+// We need this method up here so that it can be accessed from anywhere
+static void ChangeIP(LocalUser* user, const irc::sockets::sockaddrs& sa)
+{
+ // Set the users IP address and make sure they are in the right clone pool.
+ ServerInstance->Users->RemoveCloneCounts(user);
+ user->SetClientIP(sa);
+ ServerInstance->Users->AddClone(user);
+ if (user->quitting)
+ return;
+
+ // Recheck the connect class.
+ user->MyClass = NULL;
+ user->SetClass();
+ user->CheckClass();
+ if (user->quitting)
+ return;
+
+ // Check if this user matches any XLines.
+ user->CheckLines(true);
+ if (user->quitting)
+ return;
+}
+
+// Encapsulates information about an ident host.
+class IdentHost
+{
+ private:
+ std::string hostmask;
+ std::string newident;
+ public:
+ IdentHost(const std::string& mask, const std::string& ident)
+ : hostmask(mask)
+ , newident(ident)
+ {
+ }
-/** Holds a CGI site's details
- */
-class CGIhost
+ const std::string& GetIdent() const
+ {
+ return newident;
+ }
+
+ bool Matches(LocalUser* user) const
+ {
+ if (!InspIRCd::Match(user->GetRealHost(), hostmask, ascii_case_insensitive_map))
+ return false;
+
+ return InspIRCd::MatchCIDR(user->GetIPString(), hostmask, ascii_case_insensitive_map);
+ }
+};
+
+// Encapsulates information about a WebIRC host.
+class WebIRCHost
{
-public:
+ private:
std::string hostmask;
- CGItype type;
+ std::string fingerprint;
std::string password;
+ std::string passhash;
- CGIhost(const std::string &mask, CGItype t, const std::string &spassword)
- : hostmask(mask), type(t), password(spassword)
+ public:
+ WebIRCHost(const std::string& mask, const std::string& fp, const std::string& pass, const std::string& hash)
+ : hostmask(mask)
+ , fingerprint(fp)
+ , password(pass)
+ , passhash(hash)
{
}
+
+ bool Matches(LocalUser* user, const std::string& pass) const
+ {
+ // Did the user send a valid password?
+ if (!password.empty() && !ServerInstance->PassCompare(user, password, pass, passhash))
+ return false;
+
+ // Does the user have a valid fingerprint?
+ const std::string fp = SSLClientCert::GetFingerprint(&user->eh);
+ if (!fingerprint.empty() && fp != fingerprint)
+ return false;
+
+ // Does the user's hostname match our hostmask?
+ if (InspIRCd::Match(user->GetRealHost(), hostmask, ascii_case_insensitive_map))
+ return true;
+
+ // Does the user's IP address match our hostmask?
+ return InspIRCd::MatchCIDR(user->GetIPString(), hostmask, ascii_case_insensitive_map);
+ }
};
-typedef std::vector<CGIhost> CGIHostlist;
/*
* WEBIRC
* This is used for the webirc method of CGIIRC auth, and is (really) the best way to do these things.
- * Syntax: WEBIRC password client hostname ip
- * Where password is a shared key, client is the name of the "client" and version (e.g. cgiirc), hostname
+ * Syntax: WEBIRC password gateway hostname ip
+ * Where password is a shared key, gateway is the name of the WebIRC gateway and version (e.g. cgiirc), hostname
* is the resolved host of the client issuing the command and IP is the real IP of the client.
*
* How it works:
* To tie in with the rest of cgiirc module, and to avoid race conditions, /webirc is only processed locally
* and simply sets metadata on the user, which is later decoded on full connect to give something meaningful.
*/
-class CommandWebirc : public Command
+class CommandWebIRC : public SplitCommand
{
public:
+ std::vector<WebIRCHost> hosts;
bool notify;
+ StringExtItem gateway;
StringExtItem realhost;
StringExtItem realip;
- LocalStringExt webirc_hostname;
- LocalStringExt webirc_ip;
-
- CGIHostlist Hosts;
- CommandWebirc(Module* Creator)
- : Command(Creator, "WEBIRC", 4),
- realhost("cgiirc_realhost", Creator), realip("cgiirc_realip", Creator),
- webirc_hostname("cgiirc_webirc_hostname", Creator), webirc_ip("cgiirc_webirc_ip", Creator)
- {
- allow_empty_last_param = false;
- works_before_reg = true;
- this->syntax = "password client hostname ip";
- }
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
- {
- if(user->registered == REG_ALL)
- return CMD_FAILURE;
-
- irc::sockets::sockaddrs ipaddr;
- if (!irc::sockets::aptosa(parameters[3], 0, ipaddr))
- {
- IS_LOCAL(user)->CommandFloodPenalty += 5000;
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC but gave an invalid IP address.", user->GetFullRealHost().c_str());
- return CMD_FAILURE;
- }
-
- for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
- {
- if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
- {
- if(iter->type == WEBIRC && parameters[0] == iter->password)
- {
- realhost.set(user, user->host);
- realip.set(user, user->GetIPString());
-
- // Check if we're happy with the provided hostname. If it's problematic then use the IP instead.
- bool host_ok = (parameters[2].length() < 64) && (parameters[2].find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-") == std::string::npos);
- const std::string& newhost = (host_ok ? parameters[2] : parameters[3]);
-
- if (notify)
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick.c_str(), user->host.c_str(), newhost.c_str(), user->host.c_str());
-
- webirc_hostname.set(user, newhost);
- webirc_ip.set(user, parameters[3]);
- return CMD_SUCCESS;
- }
- }
- }
-
- IS_LOCAL(user)->CommandFloodPenalty += 5000;
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
- return CMD_FAILURE;
- }
-};
-
-/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
- */
-class CGIResolver : public Resolver
-{
- std::string typ;
- std::string theiruid;
- LocalIntExt& waiting;
- bool notify;
- public:
- CGIResolver(Module* me, bool NotifyOpers, const std::string &source, LocalUser* u,
- const std::string &type, bool &cached, LocalIntExt& ext)
- : Resolver(source, DNS_QUERY_PTR4, cached, me), typ(type), theiruid(u->uuid),
- waiting(ext), notify(NotifyOpers)
+ CommandWebIRC(Module* Creator)
+ : SplitCommand(Creator, "WEBIRC", 4)
+ , gateway("cgiirc_gateway", ExtensionItem::EXT_USER, Creator)
+ , realhost("cgiirc_realhost", ExtensionItem::EXT_USER, Creator)
+ , realip("cgiirc_realip", ExtensionItem::EXT_USER, Creator)
{
+ allow_empty_last_param = false;
+ works_before_reg = true;
+ this->syntax = "password gateway hostname ip";
}
- virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
- /* Check the user still exists */
- User* them = ServerInstance->FindUUID(theiruid);
- if ((them) && (!them->quitting))
+ if (user->registered == REG_ALL || realhost.get(user))
+ return CMD_FAILURE;
+
+ irc::sockets::sockaddrs ipaddr;
+ if (!irc::sockets::aptosa(parameters[3], user->client_sa.port(), ipaddr))
{
- if (notify)
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), result.c_str(), typ.c_str());
-
- if (result.length() > 64)
- return;
- them->host = result;
- them->dhost = result;
- them->InvalidateCache();
- them->CheckLines(true);
+ user->CommandFloodPenalty += 5000;
+ WriteLog("Connecting user %s (%s) tried to use WEBIRC but gave an invalid IP address.",
+ user->uuid.c_str(), user->GetIPString().c_str());
+ return CMD_FAILURE;
}
- }
- virtual void OnError(ResolverError e, const std::string &errormessage)
- {
- if (!notify)
- return;
-
- User* them = ServerInstance->FindUUID(theiruid);
- if ((them) && (!them->quitting))
+ for (std::vector<WebIRCHost>::const_iterator iter = hosts.begin(); iter != hosts.end(); ++iter)
{
- ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick.c_str(), them->host.c_str(), typ.c_str());
+ // If we don't match the host then skip to the next host.
+ if (!iter->Matches(user, parameters[0]))
+ continue;
+
+ // The user matched a WebIRC block!
+ gateway.set(user, parameters[1]);
+ realhost.set(user, user->GetRealHost());
+ realip.set(user, user->GetIPString());
+
+ WriteLog("Connecting user %s is using a WebIRC gateway; changing their IP from %s to %s.",
+ user->uuid.c_str(), user->GetIPString().c_str(), parameters[3].c_str());
+
+ // Set the IP address sent via WEBIRC. We ignore the hostname and lookup
+ // instead do our own DNS lookups because of unreliable gateways.
+ ChangeIP(user, ipaddr);
+ return CMD_SUCCESS;
}
+
+ user->CommandFloodPenalty += 5000;
+ WriteLog("Connecting user %s (%s) tried to use WEBIRC but didn't match any configured WebIRC hosts.",
+ user->uuid.c_str(), user->GetIPString().c_str());
+ return CMD_FAILURE;
}
- virtual ~CGIResolver()
+ void WriteLog(const char* message, ...) CUSTOM_PRINTF(2, 3)
{
- User* them = ServerInstance->FindUUID(theiruid);
- if (!them)
- return;
- int count = waiting.get(them);
- if (count)
- waiting.set(them, count - 1);
+ std::string buffer;
+ VAFORMAT(buffer, message, message);
+
+ // If we are sending a snotice then the message will already be
+ // written to the logfile.
+ if (notify)
+ ServerInstance->SNO->WriteGlobalSno('w', buffer);
+ else
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, buffer);
}
};
-class ModuleCgiIRC : public Module
+class ModuleCgiIRC : public Module, public Whois::EventListener
{
- CommandWebirc cmd;
- LocalIntExt waiting;
-
- static void RecheckClass(LocalUser* user)
- {
- user->MyClass = NULL;
- user->SetClass();
- user->CheckClass();
- }
+ private:
+ CommandWebIRC cmd;
+ std::vector<IdentHost> hosts;
- static void ChangeIP(LocalUser* user, const std::string& newip)
+ static bool ParseIdent(LocalUser* user, irc::sockets::sockaddrs& out)
{
- ServerInstance->Users->RemoveCloneCounts(user);
- const std::string oldip(user->GetIPString());
- user->SetClientIP(newip.c_str());
- user->InvalidateCache();
- if (user->host == oldip)
- user->host = user->GetIPString();
- if (user->dhost == oldip)
- user->dhost = user->GetIPString();
- ServerInstance->Users->AddLocalClone(user);
- ServerInstance->Users->AddGlobalClone(user);
- }
-
- void HandleIdentOrPass(LocalUser* user, const std::string& newip, bool was_pass)
- {
- cmd.realhost.set(user, user->host);
- cmd.realip.set(user, user->GetIPString());
- user->host = user->dhost = user->GetIPString();
- ChangeIP(user, newip);
- user->InvalidateCache();
- RecheckClass(user);
- // Don't create the resolver if the core couldn't put the user in a connect class or when dns is disabled
- if (user->quitting || ServerInstance->Config->NoUserDns)
- return;
-
- try
+ const char* ident = NULL;
+ if (user->ident.length() == 8)
+ {
+ // The ident is an IPv4 address encoded in hexadecimal with two characters
+ // per address segment.
+ ident = user->ident.c_str();
+ }
+ else if (user->ident.length() == 9 && user->ident[0] == '~')
{
- bool cached;
- CGIResolver* r = new CGIResolver(this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), cached, waiting);
- waiting.set(user, waiting.get(user) + 1);
- ServerInstance->AddResolver(r, cached);
+ // The same as above but m_ident got to this user before we did. Strip the
+ // ident prefix and continue as normal.
+ ident = user->ident.c_str() + 1;
}
- catch (...)
+ else
{
- if (cmd.notify)
- ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname!", user->nick.c_str(), user->host.c_str());
+ // The user either does not have an IPv4 in their ident or the gateway server
+ // is also running an identd. In the latter case there isn't really a lot we
+ // can do so we just assume that the client in question is not connecting via
+ // an ident gateway.
+ return false;
}
+
+ // Try to convert the IP address to a string. If this fails then the user
+ // does not have an IPv4 address in their ident.
+ errno = 0;
+ unsigned long address = strtoul(ident, NULL, 16);
+ if (errno)
+ return false;
+
+ out.in4.sin_family = AF_INET;
+ out.in4.sin_addr.s_addr = htonl(address);
+ return true;
}
-public:
- ModuleCgiIRC() : cmd(this), waiting("cgiirc-delay", this)
+ public:
+ ModuleCgiIRC()
+ : Whois::EventListener(this)
+ , cmd(this)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- OnRehash(NULL);
- ServiceProvider* providerlist[] = { &cmd, &cmd.realhost, &cmd.realip, &cmd.webirc_hostname, &cmd.webirc_ip, &waiting };
- ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
-
- Implementation eventlist[] = { I_OnRehash, I_OnUserRegister, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ServerInstance->SNO->EnableSnomask('w', "CGIIRC");
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- cmd.Hosts.clear();
-
- // Do we send an oper notice when a CGI:IRC has their host changed?
- cmd.notify = ServerInstance->Config->ConfValue("cgiirc")->getBool("opernotice", true);
+ std::vector<IdentHost> identhosts;
+ std::vector<WebIRCHost> webirchosts;
ConfigTagList tags = ServerInstance->Config->ConfTags("cgihost");
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- std::string hostmask = tag->getString("mask"); // An allowed CGI:IRC host
- std::string type = tag->getString("type"); // What type of user-munging we do on this host.
- std::string password = tag->getString("password");
- if(hostmask.length())
+ // Ensure that we have the <cgihost:mask> parameter.
+ const std::string mask = tag->getString("mask");
+ if (mask.empty())
+ throw ModuleException("<cgihost:mask> is a mandatory field, at " + tag->getTagLocation());
+
+ // Determine what lookup type this host uses.
+ const std::string type = tag->getString("type");
+ if (stdalgo::string::equalsci(type, "ident"))
+ {
+ // The IP address should be looked up from the hex IP address.
+ const std::string newident = tag->getString("newident", "gateway", ServerInstance->IsIdent);
+ identhosts.push_back(IdentHost(mask, newident));
+ }
+ else if (stdalgo::string::equalsci(type, "webirc"))
{
- if (type == "webirc" && password.empty())
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
- }
- else
- {
- CGItype cgitype;
- if (type == "pass")
- cgitype = PASS;
- else if (type == "ident")
- cgitype = IDENT;
- else if (type == "passfirst")
- cgitype = PASSFIRST;
- else if (type == "webirc")
- cgitype = WEBIRC;
- else
- {
- cgitype = PASS;
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
- }
-
- cmd.Hosts.push_back(CGIhost(hostmask, cgitype, password));
- }
+ // The IP address will be received via the WEBIRC command.
+ const std::string fingerprint = tag->getString("fingerprint");
+ const std::string password = tag->getString("password");
+
+ // WebIRC blocks require a password.
+ if (fingerprint.empty() && password.empty())
+ throw ModuleException("When using <cgihost type=\"webirc\"> either the fingerprint or password field is required, at " + tag->getTagLocation());
+
+ webirchosts.push_back(WebIRCHost(mask, fingerprint, password, tag->getString("hash")));
}
else
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
- continue;
+ throw ModuleException(type + " is an invalid <cgihost:mask> type, at " + tag->getTagLocation());
}
}
+
+ // The host configuration was valid so we can apply it.
+ hosts.swap(identhosts);
+ cmd.hosts.swap(webirchosts);
+
+ // Do we send an oper notice when a m_cgiirc client has their IP changed?
+ cmd.notify = ServerInstance->Config->ConfValue("cgiirc")->getBool("opernotice", true);
}
- ModResult OnCheckReady(LocalUser *user)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
- if (waiting.get(user))
- return MOD_RES_DENY;
-
- std::string *webirc_ip = cmd.webirc_ip.get(user);
- if (!webirc_ip)
+ // If <connect:webirc> is not set then we have nothing to do.
+ const std::string webirc = myclass->config->getString("webirc");
+ if (webirc.empty())
return MOD_RES_PASSTHRU;
- std::string* webirc_hostname = cmd.webirc_hostname.get(user);
- user->host = user->dhost = (webirc_hostname ? *webirc_hostname : user->GetIPString());
-
- ChangeIP(user, *webirc_ip);
- user->InvalidateCache();
-
- RecheckClass(user);
- if (user->quitting)
- return MOD_RES_DENY;
-
- user->CheckLines(true);
- if (user->quitting)
+ // If the user is not connecting via a WebIRC gateway then they
+ // cannot match this connect class.
+ const std::string* gateway = cmd.gateway.get(user);
+ if (!gateway)
return MOD_RES_DENY;
- cmd.webirc_hostname.unset(user);
- cmd.webirc_ip.unset(user);
-
- return MOD_RES_PASSTHRU;
+ // If the gateway matches the <connect:webirc> constraint then
+ // allow the check to continue. Otherwise, reject it.
+ return InspIRCd::Match(*gateway, webirc) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
- for(CGIHostlist::iterator iter = cmd.Hosts.begin(); iter != cmd.Hosts.end(); iter++)
- {
- if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
- {
- // Deal with it...
- if(iter->type == PASS)
- {
- CheckPass(user); // We do nothing if it fails so...
- user->CheckLines(true);
- }
- else if(iter->type == PASSFIRST && !CheckPass(user))
- {
- // If the password lookup failed, try the ident
- CheckIdent(user); // If this fails too, do nothing
- user->CheckLines(true);
- }
- else if(iter->type == IDENT)
- {
- CheckIdent(user); // Nothing on failure.
- user->CheckLines(true);
- }
- else if(iter->type == IDENTFIRST && !CheckIdent(user))
- {
- // If the ident lookup fails, try the password.
- CheckPass(user);
- user->CheckLines(true);
- }
- else if(iter->type == WEBIRC)
- {
- // We don't need to do anything here
- }
- return MOD_RES_PASSTHRU;
- }
- }
- return MOD_RES_PASSTHRU;
- }
+ // There is no need to check for gateways if one is already being used.
+ if (cmd.realhost.get(user))
+ return MOD_RES_PASSTHRU;
- bool CheckPass(LocalUser* user)
- {
- if(IsValidHost(user->password))
+ for (std::vector<IdentHost>::const_iterator iter = hosts.begin(); iter != hosts.end(); ++iter)
{
- HandleIdentOrPass(user, user->password, true);
- user->password.clear();
- return true;
- }
-
- return false;
- }
-
- bool CheckIdent(LocalUser* user)
- {
- const char* ident;
- in_addr newip;
+ // If we don't match the host then skip to the next host.
+ if (!iter->Matches(user))
+ continue;
- if (user->ident.length() == 8)
- ident = user->ident.c_str();
- else if (user->ident.length() == 9 && user->ident[0] == '~')
- ident = user->ident.c_str() + 1;
- else
- return false;
+ // We have matched an <cgihost> block! Try to parse the encoded IPv4 address
+ // out of the ident.
+ irc::sockets::sockaddrs address(user->client_sa);
+ if (!ParseIdent(user, address))
+ return MOD_RES_PASSTHRU;
- errno = 0;
- unsigned long ipaddr = strtoul(ident, NULL, 16);
- if (errno)
- return false;
- newip.s_addr = htonl(ipaddr);
- std::string newipstr(inet_ntoa(newip));
+ // Store the hostname and IP of the gateway for later use.
+ cmd.realhost.set(user, user->GetRealHost());
+ cmd.realip.set(user, user->GetIPString());
- user->ident = "~cgiirc";
- HandleIdentOrPass(user, newipstr, false);
+ const std::string& newident = iter->GetIdent();
+ cmd.WriteLog("Connecting user %s is using an ident gateway; changing their IP from %s to %s and their ident from %s to %s.",
+ user->uuid.c_str(), user->GetIPString().c_str(), address.addr().c_str(), user->ident.c_str(), newident.c_str());
- return true;
+ user->ChangeIdent(newident);
+ ChangeIP(user, address);
+ break;
+ }
+ return MOD_RES_PASSTHRU;
}
- bool IsValidHost(const std::string &host)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if(!host.size() || host.size() > 64)
- return false;
-
- for(unsigned int i = 0; i < host.size(); i++)
- {
- if( ((host[i] >= '0') && (host[i] <= '9')) ||
- ((host[i] >= 'A') && (host[i] <= 'Z')) ||
- ((host[i] >= 'a') && (host[i] <= 'z')) ||
- ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) ||
- ((host[i] == '.') && (i > 0) && (i+1 < host.size())) )
+ if (!whois.IsSelfWhois() && !whois.GetSource()->HasPrivPermission("users/auspex"))
+ return;
- continue;
- else
- return false;
- }
+ // If these fields are not set then the client is not using a gateway.
+ const std::string* realhost = cmd.realhost.get(whois.GetTarget());
+ const std::string* realip = cmd.realip.get(whois.GetTarget());
+ if (!realhost || !realip)
+ return;
- return true;
+ const std::string* gateway = cmd.gateway.get(whois.GetTarget());
+ if (gateway)
+ whois.SendLine(RPL_WHOISGATEWAY, *realhost, *realip, "is connected via the " + *gateway + " WebIRC gateway");
+ else
+ whois.SendLine(RPL_WHOISGATEWAY, *realhost, *realip, "is connected via an ident gateway");
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Change user's hosts connecting from known CGI:IRC hosts",VF_VENDOR);
+ return Version("Enables forwarding the real IP address of a user from a gateway to the IRC server", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleCgiIRC)
diff --git a/src/modules/m_chancreate.cpp b/src/modules/m_chancreate.cpp
index 997a92648..6cf4af235 100644
--- a/src/modules/m_chancreate.cpp
+++ b/src/modules/m_chancreate.cpp
@@ -21,26 +21,20 @@
#include "inspircd.h"
-/* $ModDesc: Provides snomasks 'j' and 'J', to which notices about newly created channels are sent */
-
class ModuleChanCreate : public Module
{
- private:
public:
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->SNO->EnableSnomask('j', "CHANCREATE");
- Implementation eventlist[] = { I_OnUserJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides snomasks 'j' and 'J', to which notices about newly created channels are sent",VF_VENDOR);
}
-
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except) CXX11_OVERRIDE
{
if ((created) && (IS_LOCAL(memb->user)))
{
diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp
index 651e659b5..903aab33a 100644
--- a/src/modules/m_chanfilter.cpp
+++ b/src/modules/m_chanfilter.cpp
@@ -23,97 +23,107 @@
*/
-/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
#include "inspircd.h"
-#include "u_listmode.h"
+#include "listmode.h"
+#include "modules/exemption.h"
+
+enum
+{
+ // InspIRCd-specific.
+ ERR_ALREADYCHANFILTERED = 937,
+ ERR_NOSUCHCHANFILTER = 938,
+ ERR_CHANFILTERFULL = 939
+};
/** Handles channel mode +g
*/
class ChanFilter : public ListModeBase
{
public:
+ unsigned long maxlen;
+
ChanFilter(Module* Creator) : ListModeBase(Creator, "filter", 'g', "End of channel spamfilter list", 941, 940, false, "chanfilter") { }
- virtual bool ValidateParam(User* user, Channel* chan, std::string &word)
+ bool ValidateParam(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- if ((word.length() > 35) || (word.empty()))
+ if (word.length() > maxlen)
{
- user->WriteNumeric(935, "%s %s %s :word is too %s for censor list",user->nick.c_str(), chan->name.c_str(), word.c_str(), (word.empty() ? "short" : "long"));
+ user->WriteNumeric(Numerics::InvalidModeParameter(chan, this, word, "Word is too long for the spamfilter list"));
return false;
}
return true;
}
- virtual bool TellListTooLong(User* user, Channel* chan, std::string &word)
+ void TellListTooLong(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- user->WriteNumeric(939, "%s %s %s :Channel spamfilter list is full", user->nick.c_str(), chan->name.c_str(), word.c_str());
- return true;
+ user->WriteNumeric(ERR_CHANFILTERFULL, chan->name, word, "Channel spamfilter list is full");
}
- virtual void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
+ void TellAlreadyOnList(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- user->WriteNumeric(937, "%s %s :The word %s is already on the spamfilter list",user->nick.c_str(), chan->name.c_str(), word.c_str());
+ user->WriteNumeric(ERR_ALREADYCHANFILTERED, chan->name, InspIRCd::Format("The word %s is already on the spamfilter list", word.c_str()));
}
- virtual void TellNotSet(User* user, Channel* chan, std::string &word)
+ void TellNotSet(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- user->WriteNumeric(938, "%s %s :No such spamfilter word is set",user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_NOSUCHCHANFILTER, chan->name, "No such spamfilter word is set");
}
};
class ModuleChanFilter : public Module
{
+ CheckExemption::EventProvider exemptionprov;
ChanFilter cf;
bool hidemask;
+ bool notifyuser;
public:
ModuleChanFilter()
- : cf(this)
+ : exemptionprov(this)
+ , cf(this)
{
}
- void init()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(cf);
-
- cf.DoImplements(this);
- Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice, I_OnSyncChannel };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
- }
-
- virtual void OnRehash(User* user)
- {
- hidemask = ServerInstance->Config->ConfValue("chanfilter")->getBool("hidemask");
+ ConfigTag* tag = ServerInstance->Config->ConfValue("chanfilter");
+ hidemask = tag->getBool("hidemask");
+ cf.maxlen = tag->getUInt("maxlen", 35, 10, 100);
+ notifyuser = tag->getBool("notifyuser", true);
cf.DoRehash();
}
- virtual ModResult ProcessMessages(User* user,Channel* chan,std::string &text)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- ModResult res = ServerInstance->OnCheckExemption(user,chan,"filter");
+ if (target.type != MessageTarget::TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
+
+ Channel* chan = target.Get<Channel>();
+ ModResult res = CheckExemption::Call(exemptionprov, user, chan, "filter");
if (!IS_LOCAL(user) || res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- modelist* list = cf.extItem.get(chan);
+ ListModeBase::ModeList* list = cf.GetList(chan);
if (list)
{
- for (modelist::iterator i = list->begin(); i != list->end(); i++)
+ for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); i++)
{
- if (InspIRCd::Match(text, i->mask))
+ if (InspIRCd::Match(details.text, i->mask))
{
+ if (!notifyuser)
+ {
+ details.echo_original = true;
+ return MOD_RES_DENY;
+ }
+
if (hidemask)
- user->WriteNumeric(404, "%s %s :Cannot send to channel (your message contained a censored word)",user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (your message contained a censored word)");
else
- user->WriteNumeric(404, "%s %s %s :Cannot send to channel (your message contained a censored word)",user->nick.c_str(), chan->name.c_str(), i->mask.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (your message contained a censored word: " + i->mask + ")");
return MOD_RES_DENY;
}
}
@@ -122,32 +132,14 @@ class ModuleChanFilter : public Module
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ Version GetVersion() CXX11_OVERRIDE
{
- if (target_type == TYPE_CHANNEL)
- {
- return ProcessMessages(user,(Channel*)dest,text);
- }
- return MOD_RES_PASSTHRU;
- }
-
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
+ // We don't send any link data if the length is 35 for compatibility with the 2.0 branch.
+ std::string maxfilterlen;
+ if (cf.maxlen != 35)
+ maxfilterlen.assign(ConvToStr(cf.maxlen));
- virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- cf.DoSyncChannel(chan, proto, opaque);
- }
-
- virtual Version GetVersion()
- {
- return Version("Provides channel-specific censor lists (like mode +G but varies from channel to channel)", VF_VENDOR);
- }
-
- virtual ~ModuleChanFilter()
- {
+ return Version("Provides channel-specific censor lists (like mode +G but varies from channel to channel)", VF_VENDOR, maxfilterlen);
}
};
diff --git a/src/modules/m_chanhistory.cpp b/src/modules/m_chanhistory.cpp
index 08f316578..fe4bd9477 100644
--- a/src/modules/m_chanhistory.cpp
+++ b/src/modules/m_chanhistory.cpp
@@ -18,24 +18,34 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides channel history for a given number of lines */
+#include "modules/ircv3_servertime.h"
+#include "modules/ircv3_batch.h"
struct HistoryItem
{
time_t ts;
- std::string line;
- HistoryItem(const std::string& Line) : ts(ServerInstance->Time()), line(Line) {}
+ std::string text;
+ std::string sourcemask;
+
+ HistoryItem(User* source, const std::string& Text)
+ : ts(ServerInstance->Time())
+ , text(Text)
+ , sourcemask(source->GetFullHost())
+ {
+ }
};
struct HistoryList
{
std::deque<HistoryItem> lines;
unsigned int maxlen, maxtime;
- HistoryList(unsigned int len, unsigned int time) : maxlen(len), maxtime(time) {}
+ std::string param;
+
+ HistoryList(unsigned int len, unsigned int time, const std::string& oparam)
+ : maxlen(len), maxtime(time), param(oparam) { }
};
-class HistoryMode : public ModeHandler
+class HistoryMode : public ParamMode<HistoryMode, SimpleExtItem<HistoryList> >
{
bool IsValidDuration(const std::string& duration)
{
@@ -52,113 +62,114 @@ class HistoryMode : public ModeHandler
}
public:
- SimpleExtItem<HistoryList> ext;
unsigned int maxlines;
- HistoryMode(Module* Creator) : ModeHandler(Creator, "history", 'H', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("history", Creator) { }
+ HistoryMode(Module* Creator)
+ : ParamMode<HistoryMode, SimpleExtItem<HistoryList> >(Creator, "history", 'H')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if (colon == std::string::npos)
{
- std::string::size_type colon = parameter.find(':');
- if (colon == std::string::npos)
- return MODEACTION_DENY;
-
- std::string duration = parameter.substr(colon+1);
- if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
- return MODEACTION_DENY;
-
- unsigned int len = ConvToInt(parameter.substr(0, colon));
- int time = ServerInstance->Duration(duration);
- if (len == 0 || time < 0)
- return MODEACTION_DENY;
- if (len > maxlines && IS_LOCAL(source))
- return MODEACTION_DENY;
- if (len > maxlines)
- len = maxlines;
- if (parameter == channel->GetModeParameter(this))
- return MODEACTION_DENY;
-
- HistoryList* history = ext.get(channel);
- if (history)
- {
- // Shrink the list if the new line number limit is lower than the old one
- if (len < history->lines.size())
- history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
+ }
- history->maxlen = len;
- history->maxtime = time;
- }
- else
- {
- ext.set(channel, new HistoryList(len, time));
- }
- channel->SetModeParam('H', parameter);
+ std::string duration(parameter, colon+1);
+ if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
+ }
+
+ unsigned int len = ConvToInt(parameter.substr(0, colon));
+ unsigned int time = InspIRCd::Duration(duration);
+ if (len == 0 || (len > maxlines && IS_LOCAL(source)))
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
+ }
+ if (len > maxlines)
+ len = maxlines;
+
+ HistoryList* history = ext.get(channel);
+ if (history)
+ {
+ // Shrink the list if the new line number limit is lower than the old one
+ if (len < history->lines.size())
+ history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
+
+ history->maxlen = len;
+ history->maxtime = time;
+ history->param = parameter;
}
else
{
- if (!channel->IsModeSet('H'))
- return MODEACTION_DENY;
- ext.unset(channel);
- channel->SetModeParam('H', "");
+ ext.set(channel, new HistoryList(len, time, parameter));
}
return MODEACTION_ALLOW;
}
+
+ void SerializeParam(Channel* chan, const HistoryList* history, std::string& out)
+ {
+ out.append(history->param);
+ }
};
class ModuleChanHistory : public Module
{
HistoryMode m;
bool sendnotice;
+ UserModeReference botmode;
bool dobots;
- public:
- ModuleChanHistory() : m(this)
- {
- }
+ IRCv3::Batch::CapReference batchcap;
+ IRCv3::Batch::API batchmanager;
+ IRCv3::Batch::Batch batch;
+ IRCv3::ServerTime::API servertimemanager;
- void init()
+ public:
+ ModuleChanHistory()
+ : m(this)
+ , botmode(this, "bot")
+ , batchcap(this)
+ , batchmanager(this)
+ , batch("chathistory")
+ , servertimemanager(this)
{
- ServerInstance->Modules->AddService(m);
- ServerInstance->Modules->AddService(m.ext);
-
- Implementation eventlist[] = { I_OnPostJoin, I_OnUserMessage, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- void OnRehash(User*)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("chanhistory");
- m.maxlines = tag->getInt("maxlines", 50);
+ m.maxlines = tag->getUInt("maxlines", 50, 1);
sendnotice = tag->getBool("notice", true);
dobots = tag->getBool("bots", true);
}
- void OnUserMessage(User* user,void* dest,int target_type, const std::string &text, char status, const CUList&)
+ void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
{
- if (target_type == TYPE_CHANNEL && status == 0)
+ if ((target.type == MessageTarget::TYPE_CHANNEL) && (target.status == 0) && (details.type == MSG_PRIVMSG))
{
- Channel* c = (Channel*)dest;
+ Channel* c = target.Get<Channel>();
HistoryList* list = m.ext.get(c);
if (list)
{
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, ":%s PRIVMSG %s :%s",
- user->GetFullHost().c_str(), c->name.c_str(), text.c_str());
- list->lines.push_back(HistoryItem(buf));
+ list->lines.push_back(HistoryItem(user, details.text));
if (list->lines.size() > list->maxlen)
list->lines.pop_front();
}
}
}
- void OnPostJoin(Membership* memb)
+ void OnPostJoin(Membership* memb) CXX11_OVERRIDE
{
- if (IS_REMOTE(memb->user))
+ LocalUser* localuser = IS_LOCAL(memb->user);
+ if (!localuser)
return;
- if (!dobots && ServerInstance->Modules->Find("m_botmode.so") && memb->user->IsModeSet('B'))
+ if (memb->user->IsModeSet(botmode) && !dobots)
return;
HistoryList* list = m.ext.get(memb->chan);
@@ -168,20 +179,38 @@ class ModuleChanHistory : public Module
if (list->maxtime)
mintime = ServerInstance->Time() - list->maxtime;
- if (sendnotice)
+ if ((sendnotice) && (!batchcap.get(localuser)))
+ {
+ std::string message("Replaying up to " + ConvToStr(list->maxlen) + " lines of pre-join history");
+ if (list->maxtime > 0)
+ message.append(" spanning up to " + ConvToStr(list->maxtime) + " seconds");
+ memb->WriteNotice(message);
+ }
+
+ if (batchmanager)
{
- memb->user->WriteServ("NOTICE %s :Replaying up to %d lines of pre-join history spanning up to %d seconds",
- memb->chan->name.c_str(), list->maxlen, list->maxtime);
+ batchmanager->Start(batch);
+ batch.GetBatchStartMessage().PushParamRef(memb->chan->name);
}
for(std::deque<HistoryItem>::iterator i = list->lines.begin(); i != list->lines.end(); ++i)
{
- if (i->ts >= mintime)
- memb->user->Write(i->line);
+ const HistoryItem& item = *i;
+ if (item.ts >= mintime)
+ {
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, item.sourcemask, memb->chan, item.text);
+ if (servertimemanager)
+ servertimemanager->Set(msg, item.ts);
+ batch.AddToBatch(msg);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+ }
}
+
+ if (batchmanager)
+ batchmanager->End(batch);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel history replayed on join", VF_VENDOR);
}
diff --git a/src/modules/m_chanlog.cpp b/src/modules/m_chanlog.cpp
index 6dbc0e7a8..85e7ca2eb 100644
--- a/src/modules/m_chanlog.cpp
+++ b/src/modules/m_chanlog.cpp
@@ -20,31 +20,16 @@
#include "inspircd.h"
-/* $ModDesc: Logs snomask output to channel(s). */
-
class ModuleChanLog : public Module
{
- private:
/*
* Multimap so people can redirect a snomask to multiple channels.
*/
- typedef std::multimap<char, std::string> ChanLogTargets;
+ typedef insp::flat_multimap<char, std::string> ChanLogTargets;
ChanLogTargets logstreams;
public:
- void init()
- {
- Implementation eventlist[] = { I_OnRehash, I_OnSendSnotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
- }
-
- virtual ~ModuleChanLog()
- {
- }
-
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string snomasks;
std::string channel;
@@ -59,100 +44,45 @@ class ModuleChanLog : public Module
if (channel.empty() || snomasks.empty())
{
- ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Malformed chanlog tag, ignoring");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Malformed chanlog tag, ignoring");
continue;
}
for (std::string::const_iterator it = snomasks.begin(); it != snomasks.end(); it++)
{
logstreams.insert(std::make_pair(*it, channel));
- ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Logging %c to %s", *it, channel.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Logging %c to %s", *it, channel.c_str());
}
}
}
- virtual ModResult OnSendSnotice(char &sno, std::string &desc, const std::string &msg)
+ ModResult OnSendSnotice(char &sno, std::string &desc, const std::string &msg) CXX11_OVERRIDE
{
std::pair<ChanLogTargets::const_iterator, ChanLogTargets::const_iterator> itpair = logstreams.equal_range(sno);
if (itpair.first == itpair.second)
return MOD_RES_PASSTHRU;
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "\2%s\2: %s", desc.c_str(), msg.c_str());
+ const std::string snotice = "\2" + desc + "\2: " + msg;
for (ChanLogTargets::const_iterator it = itpair.first; it != itpair.second; ++it)
{
Channel *c = ServerInstance->FindChan(it->second);
if (c)
{
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), buf);
- ServerInstance->PI->SendChannelPrivmsg(c, 0, buf);
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->Config->ServerName, c, snotice);
+ c->Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
+ ServerInstance->PI->SendMessage(c, 0, snotice);
}
}
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Logs snomask output to channel(s).", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleChanLog)
-
-
-
-
-
-
-
-
-
-/*
- * This is for the "old" chanlog module which intercepted messages going to the logfile..
- * I don't consider it all that useful, and it's quite dangerous if setup incorrectly, so
- * this is defined out but left intact in case someone wants to develop it further someday.
- *
- * -- w00t (aug 23rd, 2008)
- */
-#define OLD_CHANLOG 0
-
-#if OLD_CHANLOG
-class ChannelLogStream : public LogStream
-{
- private:
- std::string channel;
-
- public:
- ChannelLogStream(int loglevel, const std::string &chan) : LogStream(loglevel), channel(chan)
- {
- }
-
- virtual void OnLog(int loglevel, const std::string &type, const std::string &msg)
- {
- Channel *c = ServerInstance->FindChan(channel);
- static bool Logging = false;
-
- if (loglevel < this->loglvl)
- return;
-
- if (Logging)
- return;
-
- if (c)
- {
- Logging = true; // this avoids (rare chance) loops with logging server IO on networks
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "\2%s\2: %s", type.c_str(), msg.c_str());
-
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), buf);
- ServerInstance->PI->SendChannelPrivmsg(c, 0, buf);
- Logging = false;
- }
- }
-};
-#endif
-
diff --git a/src/modules/m_channames.cpp b/src/modules/m_channames.cpp
index 325e8fee1..d0d122b43 100644
--- a/src/modules/m_channames.cpp
+++ b/src/modules/m_channames.cpp
@@ -19,83 +19,77 @@
#include "inspircd.h"
-/* $ModDesc: Implements config tags which allow changing characters allowed in channel names */
-
static std::bitset<256> allowedmap;
-class NewIsChannelHandler : public HandlerBase2<bool, const char*, size_t>
+class NewIsChannelHandler
{
public:
- NewIsChannelHandler() { }
- virtual ~NewIsChannelHandler() { }
- virtual bool Call(const char*, size_t);
+ static bool Call(const std::string&);
};
-bool NewIsChannelHandler::Call(const char* c, size_t max)
+bool NewIsChannelHandler::Call(const std::string& channame)
{
- /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
- if (!c || *c++ != '#')
+ if (channame.empty() || channame.length() > ServerInstance->Config->Limits.ChanMax || channame[0] != '#')
+ return false;
+
+ for (std::string::const_iterator c = channame.begin(); c != channame.end(); ++c)
+ {
+ unsigned int i = *c & 0xFF;
+ if (!allowedmap[i])
return false;
+ }
- while (*c && --max)
- {
- unsigned int i = *c++ & 0xFF;
- if (!allowedmap[i])
- return false;
- }
- // a name of exactly max length will have max = 1 here; the null does not trigger --max
- return max;
+ return true;
}
class ModuleChannelNames : public Module
{
- private:
- NewIsChannelHandler myhandler;
- caller2<bool, const char*, size_t> rememberer;
+ TR1NS::function<bool(const std::string&)> rememberer;
bool badchan;
+ ChanModeReference permchannelmode;
public:
- ModuleChannelNames() : rememberer(ServerInstance->IsChannel), badchan(false)
+ ModuleChannelNames()
+ : rememberer(ServerInstance->IsChannel)
+ , badchan(false)
+ , permchannelmode(this, "permanent")
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- ServerInstance->IsChannel = &myhandler;
- Implementation eventlist[] = { I_OnRehash, I_OnUserKick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+ ServerInstance->IsChannel = NewIsChannelHandler::Call;
}
void ValidateChans()
{
+ Modes::ChangeList removepermchan;
+
badchan = true;
- std::vector<Channel*> chanvec;
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); ++i)
- {
- if (!ServerInstance->IsChannel(i->second->name.c_str(), MAXBUF))
- chanvec.push_back(i->second);
- }
- std::vector<Channel*>::reverse_iterator c2 = chanvec.rbegin();
- while (c2 != chanvec.rend())
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
{
- Channel* c = *c2++;
- if (c->IsModeSet('P') && c->GetUserCounter())
- {
- std::vector<std::string> modes;
- modes.push_back(c->name);
- modes.push_back("-P");
+ Channel* c = i->second;
+ // Move iterator before we begin kicking
+ ++i;
+ if (ServerInstance->IsChannel(c->name))
+ continue; // The name of this channel is still valid
- ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
+ if (c->IsModeSet(permchannelmode) && c->GetUserCounter())
+ {
+ removepermchan.clear();
+ removepermchan.push_remove(*permchannelmode);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, removepermchan);
}
- const UserMembList* users = c->GetUsers();
- for(UserMembCIter j = users->begin(); j != users->end(); )
+
+ Channel::MemberMap& users = c->userlist;
+ for (Channel::MemberMap::iterator j = users.begin(); j != users.end(); )
{
if (IS_LOCAL(j->first))
{
// KickUser invalidates the iterator
- UserMembCIter it = j++;
- c->KickUser(ServerInstance->FakeClient, it->first, "Channel name no longer valid");
+ Channel::MemberMap::iterator it = j++;
+ c->KickUser(ServerInstance->FakeClient, it, "Channel name no longer valid");
}
else
++j;
@@ -104,7 +98,7 @@ class ModuleChannelNames : public Module
badchan = false;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("channames");
std::string denyToken = tag->getString("denyrange");
@@ -134,24 +128,25 @@ class ModuleChannelNames : public Module
ValidateChans();
}
- virtual void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list)
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list) CXX11_OVERRIDE
{
if (badchan)
{
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
if (i->first != memb->user)
except_list.insert(i->first);
}
}
- virtual ~ModuleChannelNames()
+ CullResult cull() CXX11_OVERRIDE
{
ServerInstance->IsChannel = rememberer;
ValidateChans();
+ return Module::cull();
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements config tags which allow changing characters allowed in channel names", VF_VENDOR);
}
diff --git a/src/modules/m_channelban.cpp b/src/modules/m_channelban.cpp
index 6eec486ea..ffb43eef1 100644
--- a/src/modules/m_channelban.cpp
+++ b/src/modules/m_channelban.cpp
@@ -20,50 +20,31 @@
#include "inspircd.h"
-/* $ModDesc: Implements extban +b j: - matching channel bans */
-
class ModuleBadChannelExtban : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleBadChannelExtban()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Extban 'j' - channel status/join ban", VF_OPTCOMMON|VF_VENDOR);
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'j') && (mask[1] == ':'))
{
- std::string rm = mask.substr(2);
+ std::string rm(mask, 2);
char status = 0;
- ModeHandler* mh = ServerInstance->Modes->FindPrefix(rm[0]);
+ const PrefixMode* const mh = ServerInstance->Modes->FindPrefix(rm[0]);
if (mh)
{
- rm = mask.substr(3);
+ rm.assign(mask, 3, std::string::npos);
status = mh->GetModeChar();
}
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
- if (InspIRCd::Match((**i).name, rm))
+ if (InspIRCd::Match((*i)->chan->name, rm))
{
- if (status)
- {
- Membership* memb = (**i).GetUser(user);
- if (memb && memb->hasMode(status))
- return MOD_RES_DENY;
- }
- else
+ if ((!status) || ((*i)->HasMode(mh)))
return MOD_RES_DENY;
}
}
@@ -71,12 +52,10 @@ class ModuleBadChannelExtban : public Module
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('j');
+ tokens["EXTBAN"].push_back('j');
}
};
-
MODULE_INIT(ModuleBadChannelExtban)
-
diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp
deleted file mode 100644
index affd0c8d6..000000000
--- a/src/modules/m_chanprotect.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006-2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/* $ModDesc: Provides channel modes +a and +q */
-
-#define PROTECT_VALUE 40000
-#define FOUNDER_VALUE 50000
-
-struct ChanProtectSettings
-{
- bool DeprivSelf;
- bool DeprivOthers;
- bool FirstInGetsFounder;
- bool booting;
- ChanProtectSettings() : booting(true) {}
-};
-
-static ChanProtectSettings settings;
-
-/** Handles basic operation of +qa channel modes
- */
-class FounderProtectBase
-{
- private:
- const std::string type;
- const char mode;
- const int list;
- const int end;
- public:
- FounderProtectBase(char Mode, const std::string &mtype, int l, int e) :
- type(mtype), mode(Mode), list(l), end(e)
- {
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- const UserMembList* cl = channel->GetUsers();
- std::vector<std::string> mode_junk;
- mode_junk.push_back(channel->name);
- irc::modestacker modestack(false);
- std::deque<std::string> stackresult;
-
- for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
- {
- if (i->second->hasMode(mode))
- {
- if (stack)
- stack->Push(mode, i->first->nick);
- else
- modestack.Push(mode, i->first->nick);
- }
- }
-
- if (stack)
- return;
-
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- void DisplayList(User* user, Channel* channel)
- {
- const UserMembList* cl = channel->GetUsers();
- for (UserMembCIter i = cl->begin(); i != cl->end(); ++i)
- {
- if (i->second->hasMode(mode))
- {
- user->WriteServ("%d %s %s %s", list, user->nick.c_str(), channel->name.c_str(), i->first->nick.c_str());
- }
- }
- user->WriteServ("%d %s %s :End of channel %s list", end, user->nick.c_str(), channel->name.c_str(), type.c_str());
- }
-
- bool CanRemoveOthers(User* u1, Channel* c)
- {
- Membership* m1 = c->GetUser(u1);
- return (settings.DeprivOthers && m1 && m1->hasMode(mode));
- }
-};
-
-/** Abstraction of FounderProtectBase for channel mode +q
- */
-class ChanFounder : public ModeHandler, public FounderProtectBase
-{
- public:
- ChanFounder(Module* Creator)
- : ModeHandler(Creator, "founder", 'q', PARAM_ALWAYS, MODETYPE_CHANNEL),
- FounderProtectBase('q', "founder", 386, 387)
- {
- ModeHandler::list = true;
- levelrequired = FOUNDER_VALUE;
- m_paramtype = TR_NICK;
- }
-
- void setPrefix(int pfx)
- {
- prefix = pfx;
- }
-
- unsigned int GetPrefixRank()
- {
- return FOUNDER_VALUE;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- FounderProtectBase::RemoveMode(channel, stack);
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
- {
- }
-
- ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
- {
- User* theuser = ServerInstance->FindNick(parameter);
- // remove own privs?
- if (source == theuser && !adding && settings.DeprivSelf)
- return MOD_RES_ALLOW;
-
- if (!adding && FounderProtectBase::CanRemoveOthers(source, channel))
- {
- return MOD_RES_PASSTHRU;
- }
- else
- {
- source->WriteNumeric(468, "%s %s :Only servers may set channel mode +q", source->nick.c_str(), channel->name.c_str());
- return MOD_RES_DENY;
- }
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
-
- void DisplayList(User* user, Channel* channel)
- {
- FounderProtectBase::DisplayList(user,channel);
- }
-};
-
-/** Abstraction of FounderProtectBase for channel mode +a
- */
-class ChanProtect : public ModeHandler, public FounderProtectBase
-{
- public:
- ChanProtect(Module* Creator)
- : ModeHandler(Creator, "admin", 'a', PARAM_ALWAYS, MODETYPE_CHANNEL),
- FounderProtectBase('a',"protected user", 388, 389)
- {
- ModeHandler::list = true;
- levelrequired = PROTECT_VALUE;
- m_paramtype = TR_NICK;
- }
-
- void setPrefix(int pfx)
- {
- prefix = pfx;
- }
-
-
- unsigned int GetPrefixRank()
- {
- return PROTECT_VALUE;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- FounderProtectBase::RemoveMode(channel, stack);
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
- {
- }
-
- ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
- {
- User* theuser = ServerInstance->FindNick(parameter);
- // source has +q
- if (channel->GetPrefixValue(source) > PROTECT_VALUE)
- return MOD_RES_ALLOW;
-
- // removing own privs?
- if (source == theuser && !adding && settings.DeprivSelf)
- return MOD_RES_ALLOW;
-
- if (!adding && FounderProtectBase::CanRemoveOthers(source, channel))
- {
- return MOD_RES_PASSTHRU;
- }
- else
- {
- source->WriteNumeric(482, "%s %s :You are not a channel founder", source->nick.c_str(), channel->name.c_str());
- return MOD_RES_DENY;
- }
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
-
- void DisplayList(User* user, Channel* channel)
- {
- FounderProtectBase::DisplayList(user, channel);
- }
-
-};
-
-class ModuleChanProtect : public Module
-{
- ChanProtect cp;
- ChanFounder cf;
- public:
- ModuleChanProtect() : cp(this), cf(this)
- {
- }
-
- void init()
- {
- /* Load config stuff */
- LoadSettings();
- settings.booting = false;
-
- ServerInstance->Modules->AddService(cf);
- ServerInstance->Modules->AddService(cp);
-
- Implementation eventlist[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void LoadSettings()
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("chanprotect");
-
- settings.FirstInGetsFounder = tag->getBool("noservices");
-
- std::string qpre = tag->getString("qprefix");
- char QPrefix = qpre.empty() ? 0 : qpre[0];
-
- std::string apre = tag->getString("aprefix");
- char APrefix = apre.empty() ? 0 : apre[0];
-
- if ((APrefix && QPrefix) && APrefix == QPrefix)
- throw ModuleException("What the smeg, why are both your +q and +a prefixes the same character?");
-
- if (settings.booting)
- {
- if (APrefix && ServerInstance->Modes->FindPrefix(APrefix) && ServerInstance->Modes->FindPrefix(APrefix) != &cp)
- throw ModuleException("Looks like the +a prefix you picked for m_chanprotect is already in use. Pick another.");
-
- if (QPrefix && ServerInstance->Modes->FindPrefix(QPrefix) && ServerInstance->Modes->FindPrefix(QPrefix) != &cf)
- throw ModuleException("Looks like the +q prefix you picked for m_chanprotect is already in use. Pick another.");
-
- cp.setPrefix(APrefix);
- cf.setPrefix(QPrefix);
- }
- settings.DeprivSelf = tag->getBool("deprotectself", true);
- settings.DeprivOthers = tag->getBool("deprotectothers", true);
- }
-
- ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
- {
- // if the user is the first user into the channel, mark them as the founder, but only if
- // the config option for it is set
-
- if (settings.FirstInGetsFounder && !chan)
- privs += 'q';
-
- return MOD_RES_PASSTHRU;
- }
-
- Version GetVersion()
- {
- return Version("Founder and Protect modes (+qa)", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleChanProtect)
diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp
index 5063368b8..f820f2bd0 100644
--- a/src/modules/m_check.cpp
+++ b/src/modules/m_check.cpp
@@ -20,16 +20,112 @@
*/
-/* $ModDesc: Provides the /CHECK command to retrieve information on a user, channel, hostname or IP address */
-
#include "inspircd.h"
+#include "listmode.h"
+
+enum
+{
+ RPL_CHECK = 802
+};
+
+class CheckContext
+{
+ User* const user;
+ const std::string& target;
+
+ public:
+ CheckContext(User* u, const std::string& targetstr)
+ : user(u)
+ , target(targetstr)
+ {
+ Write("START", target);
+ }
+
+ ~CheckContext()
+ {
+ Write("END", target);
+ }
+
+ void Write(const std::string& type, const std::string& text)
+ {
+ user->WriteRemoteNumeric(RPL_CHECK, type, text);
+ }
+
+ User* GetUser() const { return user; }
+
+ void DumpListMode(const ListModeBase::ModeList* list)
+ {
+ if (!list)
+ return;
+
+ CheckContext::List modelist(*this, "modelist");
+ for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+ modelist.Add(i->mask);
+
+ modelist.Flush();
+ }
+
+ void DumpExt(Extensible* ext)
+ {
+ CheckContext::List extlist(*this, "metadata");
+ for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); ++i)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_USER, ext, i->second);
+ if (!value.empty())
+ Write("meta:" + item->name, value);
+ else if (!item->name.empty())
+ extlist.Add(item->name);
+ }
+
+ extlist.Flush();
+ }
+
+ class List : public Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>
+ {
+ public:
+ List(CheckContext& context, const char* checktype)
+ : Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>(Numeric::WriteRemoteNumericSink(context.GetUser()), RPL_CHECK, false, (IS_LOCAL(context.GetUser()) ? context.GetUser()->nick.length() : ServerInstance->Config->Limits.NickMax) + strlen(checktype) + 1)
+ {
+ GetNumeric().push(checktype).push(std::string());
+ }
+ };
+};
/** Handle /CHECK
*/
class CommandCheck : public Command
{
+ UserModeReference snomaskmode;
+
+ std::string GetSnomasks(User* user)
+ {
+ std::string ret;
+ if (snomaskmode)
+ ret = snomaskmode->GetUserParameter(user);
+
+ if (ret.empty())
+ ret = "+";
+ return ret;
+ }
+
+ static std::string GetAllowedOperOnlyModes(LocalUser* user, ModeType modetype)
+ {
+ std::string ret;
+ const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes.GetModes(modetype);
+ for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
+ {
+ const ModeHandler* const mh = i->second;
+ if ((mh->NeedsOper()) && (user->HasModePermission(mh)))
+ ret.push_back(mh->GetModeChar());
+ }
+ return ret;
+ }
+
public:
- CommandCheck(Module* parent) : Command(parent,"CHECK", 1)
+ CommandCheck(Module* parent)
+ : Command(parent,"CHECK", 1)
+ , snomaskmode(parent, "snomask")
{
flags_needed = 'o'; syntax = "<nickname>|<ip>|<hostmask>|<channel> <server>";
}
@@ -44,176 +140,137 @@ class CommandCheck : public Command
return ret;
}
- void dumpExt(User* user, const std::string& checkstr, Extensible* ext)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- std::stringstream dumpkeys;
- for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_USER, ext, i->second);
- if (!value.empty())
- user->SendText(checkstr + " meta:" + item->name + " " + value);
- else if (!item->name.empty())
- dumpkeys << " " << item->name;
- }
- if (!dumpkeys.str().empty())
- user->SendText(checkstr + " metadata", dumpkeys);
- }
-
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
- {
- if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName.c_str())
+ if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
return CMD_SUCCESS;
User *targuser;
Channel *targchan;
- std::string checkstr;
std::string chliststr;
- checkstr = ":" + ServerInstance->Config->ServerName + " 304 " + user->nick + " :CHECK";
-
targuser = ServerInstance->FindNick(parameters[0]);
targchan = ServerInstance->FindChan(parameters[0]);
/*
* Syntax of a /check reply:
- * :server.name 304 target :CHECK START <target>
- * :server.name 304 target :CHECK <field> <value>
- * :server.name 304 target :CHECK END
+ * :server.name 802 target START <target>
+ * :server.name 802 target <field> :<value>
+ * :server.name 802 target END <target>
*/
- user->SendText(checkstr + " START " + parameters[0]);
+ // Constructor sends START, destructor sends END
+ CheckContext context(user, parameters[0]);
if (targuser)
{
LocalUser* loctarg = IS_LOCAL(targuser);
/* /check on a user */
- user->SendText(checkstr + " nuh " + targuser->GetFullHost());
- user->SendText(checkstr + " realnuh " + targuser->GetFullRealHost());
- user->SendText(checkstr + " realname " + targuser->fullname);
- user->SendText(checkstr + " modes +" + targuser->FormatModes());
- user->SendText(checkstr + " snomasks +" + targuser->FormatNoticeMasks());
- user->SendText(checkstr + " server " + targuser->server);
- user->SendText(checkstr + " uid " + targuser->uuid);
- user->SendText(checkstr + " signon " + timestring(targuser->signon));
- user->SendText(checkstr + " nickts " + timestring(targuser->age));
+ context.Write("nuh", targuser->GetFullHost());
+ context.Write("realnuh", targuser->GetFullRealHost());
+ context.Write("realname", targuser->GetRealName());
+ context.Write("modes", targuser->GetModeLetters());
+ context.Write("snomasks", GetSnomasks(targuser));
+ context.Write("server", targuser->server->GetName());
+ context.Write("uid", targuser->uuid);
+ context.Write("signon", timestring(targuser->signon));
+ context.Write("nickts", timestring(targuser->age));
if (loctarg)
- user->SendText(checkstr + " lastmsg " + timestring(targuser->idle_lastmsg));
+ context.Write("lastmsg", timestring(loctarg->idle_lastmsg));
- if (IS_AWAY(targuser))
+ if (targuser->IsAway())
{
/* user is away */
- user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime));
- user->SendText(checkstr + " awaymsg " + targuser->awaymsg);
+ context.Write("awaytime", timestring(targuser->awaytime));
+ context.Write("awaymsg", targuser->awaymsg);
}
- if (IS_OPER(targuser))
+ if (targuser->IsOper())
{
OperInfo* oper = targuser->oper;
/* user is an oper of type ____ */
- user->SendText(checkstr + " opertype " + oper->NameStr());
+ context.Write("opertype", oper->name);
if (loctarg)
{
- std::string umodes;
- std::string cmodes;
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
- if (mh && mh->NeedsOper() && loctarg->HasModePermission(c, MODETYPE_USER))
- umodes.push_back(c);
- mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->NeedsOper() && loctarg->HasModePermission(c, MODETYPE_CHANNEL))
- cmodes.push_back(c);
- }
- user->SendText(checkstr + " modeperms user=" + umodes + " channel=" + cmodes);
- std::string opcmds;
- for(std::set<std::string>::iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); i++)
- {
- opcmds.push_back(' ');
- opcmds.append(*i);
- }
- std::stringstream opcmddump(opcmds);
- user->SendText(checkstr + " commandperms", opcmddump);
- std::string privs;
- for(std::set<std::string>::iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); i++)
- {
- privs.push_back(' ');
- privs.append(*i);
- }
- std::stringstream privdump(privs);
- user->SendText(checkstr + " permissions", privdump);
+ std::string umodes = GetAllowedOperOnlyModes(loctarg, MODETYPE_USER);
+ std::string cmodes = GetAllowedOperOnlyModes(loctarg, MODETYPE_CHANNEL);
+ context.Write("modeperms", "user=" + umodes + " channel=" + cmodes);
+
+ CheckContext::List opcmdlist(context, "commandperms");
+ opcmdlist.Add(oper->AllowedOperCommands.ToString());
+ opcmdlist.Flush();
+ CheckContext::List privlist(context, "permissions");
+ opcmdlist.Add(oper->AllowedPrivs.ToString());
+ privlist.Flush();
}
}
if (loctarg)
{
- user->SendText(checkstr + " clientaddr " + irc::sockets::satouser(loctarg->client_sa));
- user->SendText(checkstr + " serveraddr " + irc::sockets::satouser(loctarg->server_sa));
+ context.Write("clientaddr", loctarg->client_sa.str());
+ context.Write("serveraddr", loctarg->server_sa.str());
std::string classname = loctarg->GetClass()->name;
if (!classname.empty())
- user->SendText(checkstr + " connectclass " + classname);
+ context.Write("connectclass", classname);
}
else
- user->SendText(checkstr + " onip " + targuser->GetIPString());
+ context.Write("onip", targuser->GetIPString());
- for (UCListIter i = targuser->chans.begin(); i != targuser->chans.end(); i++)
+ CheckContext::List chanlist(context, "onchans");
+ for (User::ChanList::iterator i = targuser->chans.begin(); i != targuser->chans.end(); i++)
{
- Channel* c = *i;
- chliststr.append(c->GetPrefixChar(targuser)).append(c->name).append(" ");
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ chliststr.push_back(prefix);
+ chliststr.append(c->name);
+ chanlist.Add(chliststr);
+ chliststr.clear();
}
- std::stringstream dump(chliststr);
+ chanlist.Flush();
- user->SendText(checkstr + " onchans", dump);
-
- dumpExt(user, checkstr, targuser);
+ context.DumpExt(targuser);
}
else if (targchan)
{
/* /check on a channel */
- user->SendText(checkstr + " timestamp " + timestring(targchan->age));
+ context.Write("timestamp", timestring(targchan->age));
if (!targchan->topic.empty())
{
/* there is a topic, assume topic related information exists */
- user->SendText(checkstr + " topic " + targchan->topic);
- user->SendText(checkstr + " topic_setby " + targchan->setby);
- user->SendText(checkstr + " topic_setat " + timestring(targchan->topicset));
+ context.Write("topic", targchan->topic);
+ context.Write("topic_setby", targchan->setby);
+ context.Write("topic_setat", timestring(targchan->topicset));
}
- user->SendText(checkstr + " modes " + targchan->ChanModes(true));
- user->SendText(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter()));
+ context.Write("modes", targchan->ChanModes(true));
+ context.Write("membercount", ConvToStr(targchan->GetUserCounter()));
/* now the ugly bit, spool current members of a channel. :| */
- const UserMembList *ulist= targchan->GetUsers();
+ const Channel::MemberMap& ulist = targchan->GetUsers();
/* note that unlike /names, we do NOT check +i vs in the channel */
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
- char tmpbuf[MAXBUF];
/*
- * Unlike Asuka, I define a clone as coming from the same host. --w00t
- */
- snprintf(tmpbuf, MAXBUF, "%-3lu %s%s (%s@%s) %s ", ServerInstance->Users->GlobalCloneCount(i->first), targchan->GetAllPrefixChars(i->first), i->first->nick.c_str(), i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
- user->SendText(checkstr + " member " + tmpbuf);
+ * Unlike Asuka, I define a clone as coming from the same host. --w00t
+ */
+ const UserManager::CloneCounts& clonecount = ServerInstance->Users->GetCloneCounts(i->first);
+ context.Write("member", InspIRCd::Format("%-3u %s%s (%s@%s) %s ", clonecount.global,
+ i->second->GetAllPrefixChars().c_str(), i->first->nick.c_str(),
+ i->first->ident.c_str(), i->first->GetDisplayedHost().c_str(), i->first->GetRealName().c_str()));
}
- irc::modestacker modestack(true);
- for(BanList::iterator b = targchan->bans.begin(); b != targchan->bans.end(); ++b)
- {
- modestack.Push('b', b->data);
- }
- std::vector<std::string> stackresult;
- std::vector<TranslateType> dummy;
- while (modestack.GetStackedLine(stackresult))
- {
- creator->ProtoSendMode(user, TYPE_CHANNEL, targchan, stackresult, dummy);
- stackresult.clear();
- }
- FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(targchan,creator,user));
- dumpExt(user, checkstr, targchan);
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
+ context.DumpListMode((*i)->GetList(targchan));
+
+ context.DumpExt(targchan);
}
else
{
@@ -221,73 +278,46 @@ class CommandCheck : public Command
long x = 0;
/* hostname or other */
- for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); a++)
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator a = users.begin(); a != users.end(); ++a)
{
- if (InspIRCd::Match(a->second->host, parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->dhost, parameters[0], ascii_case_insensitive_map))
+ if (InspIRCd::Match(a->second->GetRealHost(), parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->GetDisplayedHost(), parameters[0], ascii_case_insensitive_map))
{
/* host or vhost matches mask */
- user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
+ context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->GetRealName());
}
/* IP address */
else if (InspIRCd::MatchCIDR(a->second->GetIPString(), parameters[0]))
{
/* same IP. */
- user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
+ context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->GetRealName());
}
}
- user->SendText(checkstr + " matches " + ConvToStr(x));
+ context.Write("matches", ConvToStr(x));
}
- user->SendText(checkstr + " END " + parameters[0]);
-
+ // END is sent by the CheckContext destructor
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (parameters.size() > 1)
+ if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
return ROUTE_OPT_UCAST(parameters[1]);
return ROUTE_LOCALONLY;
}
};
-
class ModuleCheck : public Module
{
- private:
CommandCheck mycommand;
public:
ModuleCheck() : mycommand(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(mycommand);
- }
-
- ~ModuleCheck()
- {
- }
-
- void ProtoSendMode(void* uv, TargetTypeFlags, void*, const std::vector<std::string>& result, const std::vector<TranslateType>&)
- {
- User* user = (User*)uv;
- std::string checkstr(":");
- checkstr.append(ServerInstance->Config->ServerName);
- checkstr.append(" 304 ");
- checkstr.append(user->nick);
- checkstr.append(" :CHECK modelist");
- for(unsigned int i=0; i < result.size(); i++)
- {
- checkstr.append(" ");
- checkstr.append(result[i]);
- }
- user->SendText(checkstr);
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("CHECK command, view user, channel, IP address or hostname information", VF_VENDOR|VF_OPTCOMMON);
}
diff --git a/src/modules/m_chghost.cpp b/src/modules/m_chghost.cpp
index 6aaed7831..6e498a8be 100644
--- a/src/modules/m_chghost.cpp
+++ b/src/modules/m_chghost.cpp
@@ -21,38 +21,35 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the CHGHOST command */
-
/** Handle /CHGHOST
*/
class CommandChghost : public Command
{
- private:
- char* hostmap;
public:
- CommandChghost(Module* Creator, char* hmap) : Command(Creator,"CHGHOST", 2), hostmap(hmap)
+ std::bitset<UCHAR_MAX> hostmap;
+
+ CommandChghost(Module* Creator)
+ : Command(Creator,"CHGHOST", 2)
{
allow_empty_last_param = false;
flags_needed = 'o';
syntax = "<nick> <newhost>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- const char* x = parameters[1].c_str();
-
- if (parameters[1].length() > 63)
+ if (parameters[1].length() > ServerInstance->Config->Limits.MaxHost)
{
- user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick.c_str());
+ user->WriteNotice("*** CHGHOST: Host too long");
return CMD_FAILURE;
}
- for (; *x; x++)
+ for (std::string::const_iterator x = parameters[1].begin(); x != parameters[1].end(); x++)
{
- if (!hostmap[(unsigned char)*x])
+ if (!hostmap.test(static_cast<unsigned char>(*x)))
{
- user->WriteServ("NOTICE "+user->nick+" :*** CHGHOST: Invalid characters in hostname");
+ user->WriteNotice("*** CHGHOST: Invalid characters in hostname");
return CMD_FAILURE;
}
}
@@ -60,30 +57,27 @@ class CommandChghost : public Command
User* dest = ServerInstance->FindNick(parameters[0]);
// Allow services to change the host of unregistered users
- if ((!dest) || ((dest->registered != REG_ALL) && (!ServerInstance->ULine(user->server))))
+ if ((!dest) || ((dest->registered != REG_ALL) && (!user->server->IsULine())))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
- if ((dest->ChangeDisplayedHost(parameters[1].c_str())) && (!ServerInstance->ULine(user->server)))
+ if ((dest->ChangeDisplayedHost(parameters[1])) && (!user->server->IsULine()))
{
// fix by brain - ulines set hosts silently
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost);
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->GetDisplayedHost());
}
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -91,38 +85,26 @@ class CommandChghost : public Command
class ModuleChgHost : public Module
{
CommandChghost cmd;
- char hostmap[256];
- public:
- ModuleChgHost() : cmd(this, hostmap)
- {
- }
- void init()
+ public:
+ ModuleChgHost()
+ : cmd(this)
{
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
- memset(hostmap, 0, sizeof(hostmap));
+ cmd.hostmap.reset();
for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
- hostmap[(unsigned char)*n] = 1;
- }
-
- ~ModuleChgHost()
- {
+ cmd.hostmap.set(static_cast<unsigned char>(*n));
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the CHGHOST command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleChgHost)
diff --git a/src/modules/m_chgident.cpp b/src/modules/m_chgident.cpp
index 2112e45a3..9a2d3b2ea 100644
--- a/src/modules/m_chgident.cpp
+++ b/src/modules/m_chgident.cpp
@@ -22,8 +22,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the CHGIDENT command */
-
/** Handle /CHGIDENT
*/
class CommandChgident : public Command
@@ -34,52 +32,48 @@ class CommandChgident : public Command
allow_empty_last_param = false;
flags_needed = 'o';
syntax = "<nick> <newident>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* dest = ServerInstance->FindNick(parameters[0]);
if ((!dest) || (dest->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
if (parameters[1].length() > ServerInstance->Config->Limits.IdentMax)
{
- user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick.c_str());
+ user->WriteNotice("*** CHGIDENT: Ident is too long");
return CMD_FAILURE;
}
- if (!ServerInstance->IsIdent(parameters[1].c_str()))
+ if (!ServerInstance->IsIdent(parameters[1]))
{
- user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick.c_str());
+ user->WriteNotice("*** CHGIDENT: Invalid characters in ident");
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
- dest->ChangeIdent(parameters[1].c_str());
+ dest->ChangeIdent(parameters[1]);
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGIDENT to change %s's ident to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str());
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
-
class ModuleChgIdent : public Module
{
CommandChgident cmd;
@@ -89,21 +83,10 @@ public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleChgIdent()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the CHGIDENT command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleChgIdent)
-
diff --git a/src/modules/m_chgname.cpp b/src/modules/m_chgname.cpp
index 73ae3d487..aedd75d94 100644
--- a/src/modules/m_chgname.cpp
+++ b/src/modules/m_chgname.cpp
@@ -20,8 +20,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the CHGNAME command */
-
/** Handle /CHGNAME
*/
class CommandChgname : public Command
@@ -31,51 +29,47 @@ class CommandChgname : public Command
{
allow_empty_last_param = false;
flags_needed = 'o';
- syntax = "<nick> <newname>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ syntax = "<nick> <new real name>";
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* dest = ServerInstance->FindNick(parameters[0]);
if ((!dest) || (dest->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
if (parameters[1].empty())
{
- user->WriteServ("NOTICE %s :*** CHGNAME: GECOS must be specified", user->nick.c_str());
+ user->WriteNotice("*** CHGNAME: Real name must be specified");
return CMD_FAILURE;
}
- if (parameters[1].length() > ServerInstance->Config->Limits.MaxGecos)
+ if (parameters[1].length() > ServerInstance->Config->Limits.MaxReal)
{
- user->WriteServ("NOTICE %s :*** CHGNAME: GECOS too long", user->nick.c_str());
+ user->WriteNotice("*** CHGNAME: Real name is too long");
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
- dest->ChangeName(parameters[1].c_str());
- ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGNAME to change %s's GECOS to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->fullname.c_str());
+ dest->ChangeRealName(parameters[1]);
+ ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGNAME to change %s's real name to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->GetRealName().c_str());
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
-
class ModuleChgName : public Module
{
CommandChgname cmd;
@@ -85,20 +79,10 @@ public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleChgName()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the CHGNAME command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleChgName)
diff --git a/src/modules/m_classban.cpp b/src/modules/m_classban.cpp
new file mode 100644
index 000000000..066834079
--- /dev/null
+++ b/src/modules/m_classban.cpp
@@ -0,0 +1,47 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Johanna Abrahamsson <johanna-a@mjao.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ModuleClassBan : public Module
+{
+ public:
+ ModResult OnCheckBan(User* user, Channel* c, const std::string& mask) CXX11_OVERRIDE
+ {
+ LocalUser* localUser = IS_LOCAL(user);
+ if ((localUser) && (mask.length() > 2) && (mask[0] == 'n') && (mask[1] == ':'))
+ {
+ if (InspIRCd::Match(localUser->GetClass()->name, mask.substr(2)))
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["EXTBAN"].push_back('n');
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Class 'n' - Connection class ban", VF_VENDOR | VF_OPTCOMMON);
+ }
+};
+
+MODULE_INIT(ModuleClassBan)
diff --git a/src/modules/m_clearchan.cpp b/src/modules/m_clearchan.cpp
new file mode 100644
index 000000000..016d28737
--- /dev/null
+++ b/src/modules/m_clearchan.cpp
@@ -0,0 +1,218 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+
+class CommandClearChan : public Command
+{
+ public:
+ Channel* activechan;
+
+ CommandClearChan(Module* Creator)
+ : Command(Creator, "CLEARCHAN", 1, 3)
+ {
+ syntax = "<channel> [<KILL|KICK|G|Z>] [<reason>]";
+ flags_needed = 'o';
+
+ // Stop the linking mod from forwarding ENCAP'd CLEARCHAN commands, see below why
+ force_manual_route = true;
+ }
+
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ Channel* chan = activechan = ServerInstance->FindChan(parameters[0]);
+ if (!chan)
+ {
+ user->WriteNotice("The channel " + parameters[0] + " does not exist.");
+ return CMD_FAILURE;
+ }
+
+ // See what method the oper wants to use, default to KILL
+ std::string method("KILL");
+ if (parameters.size() > 1)
+ {
+ method = parameters[1];
+ std::transform(method.begin(), method.end(), method.begin(), ::toupper);
+ }
+
+ XLineFactory* xlf = NULL;
+ bool kick = (method == "KICK");
+ if ((!kick) && (method != "KILL"))
+ {
+ if ((method != "Z") && (method != "G"))
+ {
+ user->WriteNotice("Invalid method for clearing " + chan->name);
+ return CMD_FAILURE;
+ }
+
+ xlf = ServerInstance->XLines->GetFactory(method);
+ if (!xlf)
+ return CMD_FAILURE;
+ }
+
+ const std::string reason = parameters.size() > 2 ? parameters.back() : "Clearing " + chan->name;
+
+ if (!user->server->IsSilentULine())
+ ServerInstance->SNO->WriteToSnoMask((IS_LOCAL(user) ? 'a' : 'A'), user->nick + " has cleared \002" + chan->name + "\002 (" + method + "): " + reason);
+
+ user->WriteNotice("Clearing \002" + chan->name + "\002 (" + method + "): " + reason);
+
+ {
+ // Route this command manually so it is sent before the QUITs we are about to generate.
+ // The idea is that by the time our QUITs reach the next hop, it has already removed all their
+ // clients from the channel, meaning victims on other servers won't see the victims on this
+ // server quitting.
+ CommandBase::Params eparams;
+ eparams.push_back(chan->name);
+ eparams.push_back(method);
+ eparams.push_back(":");
+ eparams.back().append(reason);
+ ServerInstance->PI->BroadcastEncap(this->name, eparams, user, user);
+ }
+
+ // Attach to the appropriate hook so we're able to hide the QUIT/KICK messages
+ Implementation hook = (kick ? I_OnUserKick : I_OnBuildNeighborList);
+ ServerInstance->Modules->Attach(hook, creator);
+
+ std::string mask;
+ // Now remove all local non-opers from the channel
+ Channel::MemberMap& users = chan->userlist;
+ for (Channel::MemberMap::iterator i = users.begin(); i != users.end(); )
+ {
+ User* curr = i->first;
+ const Channel::MemberMap::iterator currit = i;
+ ++i;
+
+ if (!IS_LOCAL(curr) || curr->IsOper())
+ continue;
+
+ // If kicking users, remove them and skip the QuitUser()
+ if (kick)
+ {
+ chan->KickUser(ServerInstance->FakeClient, currit, reason);
+ continue;
+ }
+
+ // If we are banning users then create the XLine and add it
+ if (xlf)
+ {
+ XLine* xline;
+ try
+ {
+ mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->GetRealHost());
+ xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
+ }
+ catch (ModuleException&)
+ {
+ // Nothing, move on to the next user
+ continue;
+ }
+
+ if (!ServerInstance->XLines->AddLine(xline, user))
+ delete xline;
+ }
+
+ ServerInstance->Users->QuitUser(curr, reason);
+ }
+
+ ServerInstance->Modules->Detach(hook, creator);
+ if (xlf)
+ ServerInstance->XLines->ApplyLines();
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleClearChan : public Module
+{
+ CommandClearChan cmd;
+
+ public:
+ ModuleClearChan()
+ : cmd(this)
+ {
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ // Only attached while we are working; don't react to events otherwise
+ ServerInstance->Modules->DetachAll(this);
+ }
+
+ void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
+ {
+ bool found = false;
+ for (IncludeChanList::iterator i = include.begin(); i != include.end(); ++i)
+ {
+ if ((*i)->chan == cmd.activechan)
+ {
+ // Don't show the QUIT to anyone in the channel by default
+ include.erase(i);
+ found = true;
+ break;
+ }
+ }
+
+ const Channel::MemberMap& users = cmd.activechan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ LocalUser* curr = IS_LOCAL(i->first);
+ if (!curr)
+ continue;
+
+ if (curr->IsOper())
+ {
+ // If another module has removed the channel we're working on from the list of channels
+ // to consider for sending the QUIT to then don't add exceptions for opers, because the
+ // module before us doesn't want them to see it or added the exceptions already.
+ // If there is a value for this oper in excepts already, this won't overwrite it.
+ if (found)
+ exception.insert(std::make_pair(curr, true));
+ continue;
+ }
+ else if (!include.empty() && curr->chans.size() > 1)
+ {
+ // This is a victim and potentially has another common channel with the user quitting,
+ // add a negative exception overwriting the previous value, if any.
+ exception[curr] = false;
+ }
+ }
+ }
+
+ void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) CXX11_OVERRIDE
+ {
+ // Hide the KICK from all non-opers
+ User* leaving = memb->user;
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* curr = i->first;
+ if ((IS_LOCAL(curr)) && (!curr->IsOper()) && (curr != leaving))
+ excepts.insert(curr);
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Adds /CLEARCHAN that allows opers to masskick, masskill or mass-G/ZLine users on a channel", VF_VENDOR|VF_OPTCOMMON);
+ }
+};
+
+MODULE_INIT(ModuleClearChan)
diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp
index f4cfdb54f..b9ff085c3 100644
--- a/src/modules/m_cloaking.cpp
+++ b/src/modules/m_cloaking.cpp
@@ -24,16 +24,10 @@
#include "inspircd.h"
-#include "hash.h"
-
-/* $ModDesc: Provides masking of user hostnames */
+#include "modules/hash.h"
enum CloakMode
{
- /** 1.2-compatible host-based cloak */
- MODE_COMPAT_HOST,
- /** 1.2-compatible IP-only cloak */
- MODE_COMPAT_IPONLY,
/** 2.0 cloak of "half" of the hostname plus the full IP hash */
MODE_HALF_CLOAK,
/** 2.0 cloak of IP hash, split at 2 common CIDR range points */
@@ -48,19 +42,22 @@ static const char base32[] = "0123456789abcdefghijklmnopqrstuv";
class CloakUser : public ModeHandler
{
public:
+ bool active;
LocalStringExt ext;
-
std::string debounce_uid;
time_t debounce_ts;
int debounce_count;
CloakUser(Module* source)
- : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER),
- ext("cloaked_host", source), debounce_ts(0), debounce_count(0)
+ : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER)
+ , active(false)
+ , ext("cloaked_host", ExtensionItem::EXT_USER, source)
+ , debounce_ts(0)
+ , debounce_count(0)
{
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
LocalUser* user = IS_LOCAL(dest);
@@ -70,7 +67,9 @@ class CloakUser : public ModeHandler
*/
if (!user)
{
- dest->SetMode('x',adding);
+ // Remote setters broadcast mode before host while local setters do the opposite, so this takes that into account
+ active = IS_LOCAL(source) ? adding : !adding;
+ dest->SetMode(this, adding);
return MODEACTION_ALLOW;
}
@@ -87,7 +86,7 @@ class CloakUser : public ModeHandler
debounce_ts = ServerInstance->Time();
}
- if (adding == user->IsModeSet('x'))
+ if (adding == user->IsModeSet(this))
return MODEACTION_DENY;
/* don't allow this user to spam modechanges */
@@ -97,7 +96,7 @@ class CloakUser : public ModeHandler
if (adding)
{
// assume this is more correct
- if (user->registered != REG_ALL && user->host != user->dhost)
+ if (user->registered != REG_ALL && user->GetRealHost() != user->GetDisplayedHost())
return MODEACTION_DENY;
std::string* cloak = ext.get(user);
@@ -110,8 +109,8 @@ class CloakUser : public ModeHandler
}
if (cloak)
{
- user->ChangeDisplayedHost(cloak->c_str());
- user->SetMode('x',true);
+ user->ChangeDisplayedHost(*cloak);
+ user->SetMode(this, true);
return MODEACTION_ALLOW;
}
else
@@ -122,12 +121,11 @@ class CloakUser : public ModeHandler
/* User is removing the mode, so restore their real host
* and make it match the displayed one.
*/
- user->SetMode('x',false);
- user->ChangeDisplayedHost(user->host.c_str());
+ user->SetMode(this, false);
+ user->ChangeDisplayedHost(user->GetRealHost().c_str());
return MODEACTION_ALLOW;
}
}
-
};
class CommandCloak : public Command
@@ -139,7 +137,7 @@ class CommandCloak : public Command
syntax = "<host>";
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
};
class ModuleCloaking : public Module
@@ -151,24 +149,15 @@ class ModuleCloaking : public Module
std::string prefix;
std::string suffix;
std::string key;
- unsigned int compatkey[4];
- const char* xtab[4];
+ unsigned int domainparts;
dynamic_reference<HashProvider> Hash;
- ModuleCloaking() : cu(this), mode(MODE_OPAQUE), ck(this), Hash(this, "hash/md5")
- {
- }
-
- void init()
+ ModuleCloaking()
+ : cu(this)
+ , mode(MODE_OPAQUE)
+ , ck(this)
+ , Hash(this, "hash/md5")
{
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(cu);
- ServerInstance->Modules->AddService(ck);
- ServerInstance->Modules->AddService(cu.ext);
-
- Implementation eventlist[] = { I_OnRehash, I_OnCheckBan, I_OnUserConnect, I_OnChangeHost };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
/** This function takes a domain name string and returns just the last two domain parts,
@@ -182,7 +171,7 @@ class ModuleCloaking : public Module
*/
std::string LastTwoDomainParts(const std::string &host)
{
- int dots = 0;
+ unsigned int dots = 0;
std::string::size_type splitdot = host.length();
for (std::string::size_type x = host.length() - 1; x; --x)
@@ -192,7 +181,7 @@ class ModuleCloaking : public Module
splitdot = x;
dots++;
}
- if (dots >= 3)
+ if (dots >= domainparts)
break;
}
@@ -208,7 +197,7 @@ class ModuleCloaking : public Module
* @param id A unique ID for this type of item (to make it unique if the item matches)
* @param len The length of the output. Maximum for MD5 is 16 characters.
*/
- std::string SegmentCloak(const std::string& item, char id, int len)
+ std::string SegmentCloak(const std::string& item, char id, size_t len)
{
std::string input;
input.reserve(key.length() + 3 + item.length());
@@ -217,8 +206,8 @@ class ModuleCloaking : public Module
input.append(1, '\0'); // null does not terminate a C++ string
input.append(item);
- std::string rv = Hash->sum(input).substr(0,len);
- for(int i=0; i < len; i++)
+ std::string rv = Hash->GenerateRaw(input).substr(0,len);
+ for(size_t i = 0; i < len; i++)
{
// this discards 3 bits per byte. We have an
// overabundance of bits in the hash output, doesn't
@@ -228,70 +217,13 @@ class ModuleCloaking : public Module
return rv;
}
- std::string CompatCloak4(const char* ip)
- {
- irc::sepstream seps(ip, '.');
- std::string octet[4];
- int i[4];
-
- for (int j = 0; j < 4; j++)
- {
- seps.GetToken(octet[j]);
- i[j] = atoi(octet[j].c_str());
- }
-
- octet[3] = octet[0] + "." + octet[1] + "." + octet[2] + "." + octet[3];
- octet[2] = octet[0] + "." + octet[1] + "." + octet[2];
- octet[1] = octet[0] + "." + octet[1];
-
- /* Reset the Hash module and send it our IV */
-
- std::string rv;
-
- /* Send the Hash module a different hex table for each octet group's Hash sum */
- for (int k = 0; k < 4; k++)
- {
- rv.append(Hash->sumIV(compatkey, xtab[(compatkey[k]+i[k]) % 4], octet[k]).substr(0,6));
- if (k < 3)
- rv.append(".");
- }
- /* Stick them all together */
- return rv;
- }
-
- std::string CompatCloak6(const char* ip)
- {
- std::vector<std::string> hashies;
- std::string item;
- int rounds = 0;
-
- /* Reset the Hash module and send it our IV */
-
- for (const char* input = ip; *input; input++)
- {
- item += *input;
- if (item.length() > 7)
- {
- hashies.push_back(Hash->sumIV(compatkey, xtab[(compatkey[0]+rounds) % 4], item).substr(0,8));
- item.clear();
- }
- rounds++;
- }
- if (!item.empty())
- {
- hashies.push_back(Hash->sumIV(compatkey, xtab[(compatkey[0]+rounds) % 4], item).substr(0,8));
- }
- /* Stick them all together */
- return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined();
- }
-
std::string SegmentIP(const irc::sockets::sockaddrs& ip, bool full)
{
std::string bindata;
- int hop1, hop2, hop3;
- int len1, len2;
+ size_t hop1, hop2, hop3;
+ size_t len1, len2;
std::string rv;
- if (ip.sa.sa_family == AF_INET6)
+ if (ip.family() == AF_INET6)
{
bindata = std::string((const char*)ip.in6.sin6_addr.s6_addr, 16);
hop1 = 8;
@@ -335,24 +267,22 @@ class ModuleCloaking : public Module
}
else
{
- char buf[50];
- if (ip.sa.sa_family == AF_INET6)
+ if (ip.family() == AF_INET6)
{
- snprintf(buf, 50, ".%02x%02x.%02x%02x%s",
+ rv.append(InspIRCd::Format(".%02x%02x.%02x%02x%s",
ip.in6.sin6_addr.s6_addr[2], ip.in6.sin6_addr.s6_addr[3],
- ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str());
+ ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str()));
}
else
{
const unsigned char* ip4 = (const unsigned char*)&ip.in4.sin_addr;
- snprintf(buf, 50, ".%d.%d%s", ip4[1], ip4[0], suffix.c_str());
+ rv.append(InspIRCd::Format(".%d.%d%s", ip4[1], ip4[0], suffix.c_str()));
}
- rv.append(buf);
}
return rv;
}
- ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
+ ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
{
LocalUser* lu = IS_LOCAL(user);
if (!lu)
@@ -361,17 +291,16 @@ class ModuleCloaking : public Module
OnUserConnect(lu);
std::string* cloak = cu.ext.get(user);
/* Check if they have a cloaked host, but are not using it */
- if (cloak && *cloak != user->dhost)
+ if (cloak && *cloak != user->GetDisplayedHost())
{
- char cmask[MAXBUF];
- snprintf(cmask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), cloak->c_str());
- if (InspIRCd::Match(cmask,mask))
+ const std::string cloakMask = user->nick + "!" + user->ident + "@" + *cloak;
+ if (InspIRCd::Match(cloakMask, mask))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
/* Needs to be after m_banexception etc. */
ServerInstance->Modules->SetPriority(this, I_OnCheckBan, PRIORITY_LAST);
@@ -379,34 +308,39 @@ class ModuleCloaking : public Module
// this unsets umode +x on every host change. If we are actually doing a +x
// mode change, we will call SetMode back to true AFTER the host change is done.
- void OnChangeHost(User* u, const std::string& host)
+ void OnChangeHost(User* u, const std::string& host) CXX11_OVERRIDE
{
- if(u->IsModeSet('x'))
+ if (u->IsModeSet(cu) && !cu.active)
{
- u->SetMode('x', false);
- u->WriteServ("MODE %s -x", u->nick.c_str());
+ u->SetMode(cu, false);
+
+ if (!IS_LOCAL(u))
+ return;
+ Modes::ChangeList modechangelist;
+ modechangelist.push_remove(&cu);
+ ClientProtocol::Events::Mode modeevent(ServerInstance->FakeClient, NULL, u, modechangelist);
+ static_cast<LocalUser*>(u)->Send(modeevent);
}
+ cu.active = false;
}
- ~ModuleCloaking()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
std::string testcloak = "broken";
if (Hash)
{
switch (mode)
{
- case MODE_COMPAT_HOST:
- testcloak = prefix + "-" + Hash->sumIV(compatkey, xtab[0], "*").substr(0,10);
- break;
- case MODE_COMPAT_IPONLY:
- testcloak = Hash->sumIV(compatkey, xtab[0], "*").substr(0,10);
- break;
case MODE_HALF_CLOAK:
- testcloak = prefix + SegmentCloak("*", 3, 8) + suffix;
+ // Use old cloaking verification to stay compatible with 2.0
+ // But verify domainparts when use 3.0-only features
+ if (domainparts == 3)
+ testcloak = prefix + SegmentCloak("*", 3, 8) + suffix;
+ else
+ {
+ irc::sockets::sockaddrs sa;
+ testcloak = GenCloak(sa, "", testcloak + ConvToStr(domainparts));
+ }
break;
case MODE_OPAQUE:
testcloak = prefix + SegmentCloak("*", 4, 8) + suffix;
@@ -415,82 +349,26 @@ class ModuleCloaking : public Module
return Version("Provides masking of user hostnames", VF_COMMON|VF_VENDOR, testcloak);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("cloak");
prefix = tag->getString("prefix");
suffix = tag->getString("suffix", ".IP");
std::string modestr = tag->getString("mode");
- if (modestr == "compat-host")
- mode = MODE_COMPAT_HOST;
- else if (modestr == "compat-ip")
- mode = MODE_COMPAT_IPONLY;
- else if (modestr == "half")
+ if (stdalgo::string::equalsci(modestr, "half"))
+ {
mode = MODE_HALF_CLOAK;
- else if (modestr == "full")
+ domainparts = tag->getUInt("domainparts", 3, 1, 10);
+ }
+ else if (stdalgo::string::equalsci(modestr, "full"))
mode = MODE_OPAQUE;
else
- throw ModuleException("Bad value for <cloak:mode>; must be one of compat-host, compat-ip, half, full");
-
- if (mode == MODE_COMPAT_HOST || mode == MODE_COMPAT_IPONLY)
- {
- bool lowercase = tag->getBool("lowercase");
-
- /* These are *not* using the need_positive parameter of ReadInteger -
- * that will limit the valid values to only the positive values in a
- * signed int. Instead, accept any value that fits into an int and
- * cast it to an unsigned int. That will, a bit oddly, give us the full
- * spectrum of an unsigned integer. - Special
- *
- * We must limit the keys or else we get different results on
- * amd64/x86 boxes. - psychon */
- const unsigned int limit = 0x80000000;
- compatkey[0] = (unsigned int) tag->getInt("key1");
- compatkey[1] = (unsigned int) tag->getInt("key2");
- compatkey[2] = (unsigned int) tag->getInt("key3");
- compatkey[3] = (unsigned int) tag->getInt("key4");
-
- if (!lowercase)
- {
- xtab[0] = "F92E45D871BCA630";
- xtab[1] = "A1B9D80C72E653F4";
- xtab[2] = "1ABC078934DEF562";
- xtab[3] = "ABCDEF5678901234";
- }
- else
- {
- xtab[0] = "f92e45d871bca630";
- xtab[1] = "a1b9d80c72e653f4";
- xtab[2] = "1abc078934def562";
- xtab[3] = "abcdef5678901234";
- }
+ throw ModuleException("Bad value for <cloak:mode>; must be half or full");
- if (prefix.empty())
- prefix = ServerInstance->Config->Network;
-
- if (!compatkey[0] || !compatkey[1] || !compatkey[2] || !compatkey[3] ||
- compatkey[0] >= limit || compatkey[1] >= limit || compatkey[2] >= limit || compatkey[3] >= limit)
- {
- std::string detail;
- if (!compatkey[0] || compatkey[0] >= limit)
- detail = "<cloak:key1> is not valid, it may be set to a too high/low value, or it may not exist.";
- else if (!compatkey[1] || compatkey[1] >= limit)
- detail = "<cloak:key2> is not valid, it may be set to a too high/low value, or it may not exist.";
- else if (!compatkey[2] || compatkey[2] >= limit)
- detail = "<cloak:key3> is not valid, it may be set to a too high/low value, or it may not exist.";
- else if (!compatkey[3] || compatkey[3] >= limit)
- detail = "<cloak:key4> is not valid, it may be set to a too high/low value, or it may not exist.";
-
- throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED! - " + detail);
- }
- }
- else
- {
- key = tag->getString("key");
- if (key.empty() || key == "secret")
- throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
- }
+ key = tag->getString("key");
+ if (key.empty() || key == "secret")
+ throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
}
std::string GenCloak(const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host)
@@ -502,29 +380,6 @@ class ModuleCloaking : public Module
switch (mode)
{
- case MODE_COMPAT_HOST:
- {
- if (!host_is_ip)
- {
- std::string tail = LastTwoDomainParts(host);
-
- // xtab is not used here due to a bug in 1.2 cloaking
- chost = prefix + "-" + Hash->sumIV(compatkey, "0123456789abcdef", host).substr(0,8) + tail;
-
- /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
- * according to the DNS RFC) then they get cloaked as an IP.
- */
- if (chost.length() <= 64)
- break;
- }
- // fall through to IP cloak
- }
- case MODE_COMPAT_IPONLY:
- if (ip.sa.sa_family == AF_INET6)
- chost = CompatCloak6(ipstr.c_str());
- else
- chost = CompatCloak4(ipstr.c_str());
- break;
case MODE_HALF_CLOAK:
{
if (!host_is_ip)
@@ -540,17 +395,21 @@ class ModuleCloaking : public Module
return chost;
}
- void OnUserConnect(LocalUser* dest)
+ void OnUserConnect(LocalUser* dest) CXX11_OVERRIDE
{
std::string* cloak = cu.ext.get(dest);
if (cloak)
return;
- cu.ext.set(dest, GenCloak(dest->client_sa, dest->GetIPString(), dest->host));
+ // TODO: decide how we are going to cloak AF_UNIX hostnames.
+ if (dest->client_sa.family() != AF_INET && dest->client_sa.family() != AF_INET6)
+ return;
+
+ cu.ext.set(dest, GenCloak(dest->client_sa, dest->GetIPString(), dest->GetRealHost()));
}
};
-CmdResult CommandCloak::Handle(const std::vector<std::string> &parameters, User *user)
+CmdResult CommandCloak::Handle(User* user, const Params& parameters)
{
ModuleCloaking* mod = (ModuleCloaking*)(Module*)creator;
irc::sockets::sockaddrs sa;
@@ -561,7 +420,7 @@ CmdResult CommandCloak::Handle(const std::vector<std::string> &parameters, User
else
cloak = mod->GenCloak(sa, "", parameters[0]);
- user->WriteServ("NOTICE %s :*** Cloak for %s is %s", user->nick.c_str(), parameters[0].c_str(), cloak.c_str());
+ user->WriteNotice("*** Cloak for " + parameters[0] + " is " + cloak);
return CMD_SUCCESS;
}
diff --git a/src/modules/m_clones.cpp b/src/modules/m_clones.cpp
index 92b1bda78..06956c564 100644
--- a/src/modules/m_clones.cpp
+++ b/src/modules/m_clones.cpp
@@ -20,74 +20,84 @@
#include "inspircd.h"
+#include "modules/ircv3_batch.h"
-/* $ModDesc: Provides the /CLONES command to retrieve information on clones. */
+enum
+{
+ // InspIRCd-specific.
+ RPL_CLONES = 399
+};
-/** Handle /CLONES
- */
-class CommandClones : public Command
+class CommandClones : public SplitCommand
{
+ private:
+ IRCv3::Batch::API batchmanager;
+ IRCv3::Batch::Batch batch;
+
public:
- CommandClones(Module* Creator) : Command(Creator,"CLONES", 1)
+ CommandClones(Module* Creator)
+ : SplitCommand(Creator,"CLONES", 1)
+ , batchmanager(Creator)
+ , batch("inspircd.org/clones")
{
- flags_needed = 'o'; syntax = "<limit>";
+ flags_needed = 'o';
+ syntax = "<limit>";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
+ unsigned int limit = ConvToNum<unsigned int>(parameters[0]);
- std::string clonesstr = "304 " + user->nick + " :CLONES";
-
- unsigned long limit = atoi(parameters[0].c_str());
+ // Syntax of a CLONES reply:
+ // :irc.example.com BATCH +<id> inspircd.org/clones :<min-count>
+ // @batch=<id> :irc.example.com 399 <client> <local-count> <remote-count> <cidr-mask>
+ /// :irc.example.com BATCH :-<id>
- /*
- * Syntax of a /clones reply:
- * :server.name 304 target :CLONES START
- * :server.name 304 target :CLONES <count> <ip>
- * :server.name 304 target :CLONES END
- */
-
- user->WriteServ(clonesstr + " START");
+ if (batchmanager)
+ {
+ batchmanager->Start(batch);
+ batch.GetBatchStartMessage().PushParam(ConvToStr(limit));
+ }
- /* hostname or other */
- // XXX I really don't like marking global_clones public for this. at all. -- w00t
- for (clonemap::iterator x = ServerInstance->Users->global_clones.begin(); x != ServerInstance->Users->global_clones.end(); x++)
+ const UserManager::CloneMap& clonemap = ServerInstance->Users->GetCloneMap();
+ for (UserManager::CloneMap::const_iterator i = clonemap.begin(); i != clonemap.end(); ++i)
{
- if (x->second >= limit)
- user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + x->first.str());
+ const UserManager::CloneCounts& counts = i->second;
+ if (counts.global < limit)
+ continue;
+
+ Numeric::Numeric numeric(RPL_CLONES);
+ numeric.push(counts.local);
+ numeric.push(counts.global);
+ numeric.push(i->first.str());
+
+ ClientProtocol::Messages::Numeric numericmsg(numeric, user);
+ batch.AddToBatch(numericmsg);
+ user->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
}
- user->WriteServ(clonesstr + " END");
+ if (batchmanager)
+ batchmanager->End(batch);
return CMD_SUCCESS;
}
};
-
class ModuleClones : public Module
{
- private:
- CommandClones cmd;
public:
- ModuleClones() : cmd(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
+ CommandClones cmd;
- virtual ~ModuleClones()
+ public:
+ ModuleClones()
+ : cmd(this)
{
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the /CLONES command to retrieve information on clones.", VF_VENDOR);
}
-
-
};
MODULE_INIT(ModuleClones)
diff --git a/src/modules/m_close.cpp b/src/modules/m_close.cpp
index 8b0ea3417..b0b45b4b6 100644
--- a/src/modules/m_close.cpp
+++ b/src/modules/m_close.cpp
@@ -20,8 +20,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides /CLOSE functionality */
-
/** Handle /CLOSE
*/
class CommandClose : public Command
@@ -30,15 +28,19 @@ class CommandClose : public Command
/* Command 'close', needs operator */
CommandClose(Module* Creator) : Command(Creator,"CLOSE", 0)
{
- flags_needed = 'o'; }
+ flags_needed = 'o';
+ }
- CmdResult Handle (const std::vector<std::string> &parameters, User *src)
+ CmdResult Handle(User* src, const Params& parameters) CXX11_OVERRIDE
{
std::map<std::string,int> closed;
- for (LocalUserList::const_iterator u = ServerInstance->Users->local_users.begin(); u != ServerInstance->Users->local_users.end(); ++u)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u = list.begin(); u != list.end(); )
{
+ // Quitting the user removes it from the list
LocalUser* user = *u;
+ ++u;
if (user->registered != REG_ALL)
{
ServerInstance->Users->QuitUser(user, "Closing all unknown connections per request");
@@ -50,13 +52,14 @@ class CommandClose : public Command
int total = 0;
for (std::map<std::string,int>::iterator ci = closed.begin(); ci != closed.end(); ci++)
{
- src->WriteServ("NOTICE %s :*** Closed %d unknown connection%s from [%s]",src->nick.c_str(),(*ci).second,((*ci).second>1)?"s":"",(*ci).first.c_str());
- total += (*ci).second;
+ src->WriteNotice("*** Closed " + ConvToStr(ci->second) + " unknown " + (ci->second == 1 ? "connection" : "connections") +
+ " from [" + ci->first + "]");
+ total += ci->second;
}
if (total)
- src->WriteServ("NOTICE %s :*** %i unknown connection%s closed",src->nick.c_str(),total,(total>1)?"s":"");
+ src->WriteNotice("*** " + ConvToStr(total) + " unknown " + (total == 1 ? "connection" : "connections") + " closed");
else
- src->WriteServ("NOTICE %s :*** No unknown connections found",src->nick.c_str());
+ src->WriteNotice("*** No unknown connections found");
return CMD_SUCCESS;
}
@@ -71,16 +74,7 @@ class ModuleClose : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleClose()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides /CLOSE functionality", VF_VENDOR);
}
diff --git a/src/modules/m_commonchans.cpp b/src/modules/m_commonchans.cpp
index afa17add4..4cef93029 100644
--- a/src/modules/m_commonchans.cpp
+++ b/src/modules/m_commonchans.cpp
@@ -19,59 +19,33 @@
#include "inspircd.h"
-/* $ModDesc: Adds user mode +c, which if set, users must be on a common channel with you to private message you */
-
-/** Handles user mode +c
- */
-class PrivacyMode : public SimpleUserModeHandler
-{
- public:
- PrivacyMode(Module* Creator) : SimpleUserModeHandler(Creator, "deaf_commonchan", 'c') { }
-};
-
class ModulePrivacyMode : public Module
{
- PrivacyMode pm;
+ SimpleUserModeHandler pm;
public:
- ModulePrivacyMode() : pm(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(pm);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModulePrivacyMode()
+ ModulePrivacyMode()
+ : pm(this, "deaf_commonchan", 'c')
{
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Adds user mode +c, which if set, users must be on a common channel with you to private message you", VF_VENDOR);
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (target_type == TYPE_USER)
+ if (target.type == MessageTarget::TYPE_USER)
{
- User* t = (User*)dest;
- if (!IS_OPER(user) && (t->IsModeSet('c')) && (!ServerInstance->ULine(user->server)) && !user->SharesChannelWith(t))
+ User* t = target.Get<User>();
+ if (!user->IsOper() && (t->IsModeSet(pm)) && (!user->server->IsULine()) && !user->SharesChannelWith(t))
{
- user->WriteNumeric(ERR_CANTSENDTOUSER, "%s %s :You are not permitted to send private messages to this user (+c set)", user->nick.c_str(), t->nick.c_str());
+ user->WriteNumeric(ERR_CANTSENDTOUSER, t->nick, "You are not permitted to send private messages to this user (+c set)");
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
-
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
- }
};
-
MODULE_INIT(ModulePrivacyMode)
diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp
index 6b13ab1aa..7a06aedd3 100644
--- a/src/modules/m_conn_join.cpp
+++ b/src/modules/m_conn_join.cpp
@@ -22,45 +22,94 @@
#include "inspircd.h"
-/* $ModDesc: Forces users to join the specified channel(s) on connect */
+static void JoinChannels(LocalUser* u, const std::string& chanlist)
+{
+ irc::commasepstream chans(chanlist);
+ std::string chan;
+
+ while (chans.GetToken(chan))
+ {
+ if (ServerInstance->IsChannel(chan))
+ Channel::JoinUser(u, chan);
+ }
+}
+
+class JoinTimer : public Timer
+{
+ private:
+ LocalUser* const user;
+ const std::string channels;
+ SimpleExtItem<JoinTimer>& ext;
+
+ public:
+ JoinTimer(LocalUser* u, SimpleExtItem<JoinTimer>& ex, const std::string& chans, unsigned int delay)
+ : Timer(delay, false)
+ , user(u), channels(chans), ext(ex)
+ {
+ ServerInstance->Timers.AddTimer(this);
+ }
+
+ bool Tick(time_t time) CXX11_OVERRIDE
+ {
+ if (user->chans.empty())
+ JoinChannels(user, channels);
+
+ ext.unset(user);
+ return false;
+ }
+};
class ModuleConnJoin : public Module
{
- public:
- void init()
- {
- Implementation eventlist[] = { I_OnPostConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
+ SimpleExtItem<JoinTimer> ext;
+ std::string defchans;
+ unsigned int defdelay;
- void Prioritize()
- {
- ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_LAST);
- }
+ public:
+ ModuleConnJoin()
+ : ext("join_timer", ExtensionItem::EXT_USER, this)
+ {
+ }
- Version GetVersion()
- {
- return Version("Forces users to join the specified channel(s) on connect", VF_VENDOR);
- }
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("autojoin");
+ defchans = tag->getString("channel");
+ defdelay = tag->getDuration("delay", 0, 0, 60);
+ }
- void OnPostConnect(User* user)
- {
- if (!IS_LOCAL(user))
- return;
+ void Prioritize() CXX11_OVERRIDE
+ {
+ ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_LAST);
+ }
- std::string chanlist = ServerInstance->Config->ConfValue("autojoin")->getString("channel");
- chanlist = user->GetClass()->config->getString("autojoin", chanlist);
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Forces users to join the specified channel(s) on connect", VF_VENDOR);
+ }
- irc::commasepstream chans(chanlist);
- std::string chan;
+ void OnPostConnect(User* user) CXX11_OVERRIDE
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if (!localuser)
+ return;
- while (chans.GetToken(chan))
- {
- if (ServerInstance->IsChannel(chan.c_str(), ServerInstance->Config->Limits.ChanMax))
- Channel::JoinUser(user, chan.c_str(), false, "", false, ServerInstance->Time());
- }
+ std::string chanlist = localuser->GetClass()->config->getString("autojoin");
+ unsigned int chandelay = localuser->GetClass()->config->getDuration("autojoindelay", 0, 0, 60);
+
+ if (chanlist.empty())
+ {
+ if (defchans.empty())
+ return;
+ chanlist = defchans;
+ chandelay = defdelay;
}
-};
+ if (!chandelay)
+ JoinChannels(localuser, chanlist);
+ else
+ ext.set(localuser, new JoinTimer(localuser, ext, chanlist, chandelay));
+ }
+};
MODULE_INIT(ModuleConnJoin)
diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp
index a21462ddf..1f2fe7455 100644
--- a/src/modules/m_conn_umodes.cpp
+++ b/src/modules/m_conn_umodes.cpp
@@ -22,69 +22,44 @@
#include "inspircd.h"
-/* $ModDesc: Sets (and unsets) modes on users when they connect */
-
class ModuleModesOnConnect : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnUserConnect, this);
- }
-
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
// for things like +x on connect, important, otherwise we have to resort to config order (bleh) -- w00t
ServerInstance->Modules->SetPriority(this, I_OnUserConnect, PRIORITY_FIRST);
}
- virtual ~ModuleModesOnConnect()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Sets (and unsets) modes on users when they connect", VF_VENDOR);
}
- virtual void OnUserConnect(LocalUser* user)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
// Backup and zero out the disabled usermodes, so that we can override them here.
- char save[64];
- memcpy(save, ServerInstance->Config->DisabledUModes,
- sizeof(ServerInstance->Config->DisabledUModes));
- memset(ServerInstance->Config->DisabledUModes, 0, 64);
+ const std::bitset<64> save = ServerInstance->Config->DisabledUModes;
+ ServerInstance->Config->DisabledUModes.reset();
ConfigTag* tag = user->MyClass->config;
std::string ThisModes = tag->getString("modes");
if (!ThisModes.empty())
{
std::string buf;
- std::stringstream ss(ThisModes);
-
- std::vector<std::string> tokens;
+ irc::spacesepstream ss(ThisModes);
- // split ThisUserModes into modes and mode params
- while (ss >> buf)
- tokens.push_back(buf);
-
- std::vector<std::string> modes;
+ CommandBase::Params modes;
modes.push_back(user->nick);
- modes.push_back(tokens[0]);
- if (tokens.size() > 1)
- {
- // process mode params
- for (unsigned int k = 1; k < tokens.size(); k++)
- {
- modes.push_back(tokens[k]);
- }
- }
+ // split ThisUserModes into modes and mode params
+ while (ss.GetToken(buf))
+ modes.push_back(buf);
- ServerInstance->Parser->CallHandler("MODE", modes, user);
+ ServerInstance->Parser.CallHandler("MODE", modes, user);
}
- memcpy(ServerInstance->Config->DisabledUModes, save, 64);
+ ServerInstance->Config->DisabledUModes = save;
}
};
diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp
index 1d48220a6..f2e9590c8 100644
--- a/src/modules/m_conn_waitpong.cpp
+++ b/src/modules/m_conn_waitpong.cpp
@@ -24,8 +24,6 @@
#include "inspircd.h"
-/* $ModDesc: Forces connecting clients to send a PONG message back to the server before they can complete their connection */
-
class ModuleWaitPong : public Module
{
bool sendsnotice;
@@ -34,39 +32,33 @@ class ModuleWaitPong : public Module
public:
ModuleWaitPong()
- : ext("waitpong_pingstr", this)
- {
- }
-
- void init()
+ : ext("waitpong_pingstr", ExtensionItem::EXT_USER, this)
{
- ServerInstance->Modules->AddService(ext);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserRegister, I_OnCheckReady, I_OnPreCommand, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("waitpong");
sendsnotice = tag->getBool("sendsnotice", true);
killonbadreply = tag->getBool("killonbadreply", true);
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
std::string pingrpl = ServerInstance->GenRandomStr(10);
-
- user->Write("PING :%s", pingrpl.c_str());
+ {
+ ClientProtocol::Messages::Ping pingmsg(pingrpl);
+ user->Send(ServerInstance->GetRFCEvents().ping, pingmsg);
+ }
if(sendsnotice)
- user->WriteServ("NOTICE %s :*** If you are having problems connecting due to ping timeouts, please type /quote PONG %s or /raw PONG %s now.", user->nick.c_str(), pingrpl.c_str(), pingrpl.c_str());
+ user->WriteNotice("*** If you are having problems connecting due to ping timeouts, please type /quote PONG " + pingrpl + " or /raw PONG " + pingrpl + " now.");
ext.set(user, pingrpl);
return MOD_RES_PASSTHRU;
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser* user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
if (command == "PONG")
{
@@ -90,20 +82,15 @@ class ModuleWaitPong : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
return ext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
- ~ModuleWaitPong()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Require pong prior to registration", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleWaitPong)
diff --git a/src/modules/m_connectban.cpp b/src/modules/m_connectban.cpp
index 26120add9..d57ffca02 100644
--- a/src/modules/m_connectban.cpp
+++ b/src/modules/m_connectban.cpp
@@ -20,63 +20,41 @@
#include "inspircd.h"
#include "xline.h"
-/* $ModDesc: Throttles the connections of IP ranges who try to connect flood. */
-
class ModuleConnectBan : public Module
{
- private:
- clonemap connects;
+ typedef std::map<irc::sockets::cidr_mask, unsigned int> ConnectMap;
+ ConnectMap connects;
unsigned int threshold;
unsigned int banduration;
unsigned int ipv4_cidr;
unsigned int ipv6_cidr;
- public:
- void init()
- {
- Implementation eventlist[] = { I_OnSetUserIP, I_OnGarbageCollect, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
+ std::string banmessage;
- virtual ~ModuleConnectBan()
- {
- }
-
- virtual Version GetVersion()
+ public:
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Throttles the connections of IP ranges who try to connect flood.", VF_VENDOR);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("connectban");
- ipv4_cidr = tag->getInt("ipv4cidr", 32);
- if (ipv4_cidr == 0)
- ipv4_cidr = 32;
-
- ipv6_cidr = tag->getInt("ipv6cidr", 128);
- if (ipv6_cidr == 0)
- ipv6_cidr = 128;
-
- threshold = tag->getInt("threshold", 10);
- if (threshold == 0)
- threshold = 10;
-
- banduration = ServerInstance->Duration(tag->getString("duration", "10m"));
- if (banduration == 0)
- banduration = 10*60;
+ ipv4_cidr = tag->getUInt("ipv4cidr", 32, 1, 32);
+ ipv6_cidr = tag->getUInt("ipv6cidr", 128, 1, 128);
+ threshold = tag->getUInt("threshold", 10, 1);
+ banduration = tag->getDuration("duration", 10*60, 1);
+ banmessage = tag->getString("banmessage", "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.");
}
- virtual void OnSetUserIP(LocalUser* u)
+ void OnSetUserIP(LocalUser* u) CXX11_OVERRIDE
{
if (u->exempt)
return;
- int range = 32;
- clonemap::iterator i;
+ unsigned char range = 32;
- switch (u->client_sa.sa.sa_family)
+ switch (u->client_sa.family())
{
case AF_INET6:
range = ipv6_cidr;
@@ -87,7 +65,7 @@ class ModuleConnectBan : public Module
}
irc::sockets::cidr_mask mask(u->client_sa, range);
- i = connects.find(mask);
+ ConnectMap::iterator i = connects.find(mask);
if (i != connects.end())
{
@@ -96,7 +74,7 @@ class ModuleConnectBan : public Module
if (i->second >= threshold)
{
// Create zline for set duration.
- ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.", mask.str());
+ ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, banmessage, mask.str());
if (!ServerInstance->XLines->AddLine(zl, NULL))
{
delete zl;
@@ -104,7 +82,7 @@ class ModuleConnectBan : public Module
}
ServerInstance->XLines->ApplyLines();
std::string maskstr = mask.str();
- std::string timestr = ServerInstance->TimeString(zl->expiry);
+ std::string timestr = InspIRCd::TimeString(zl->expiry);
ServerInstance->SNO->WriteGlobalSno('x',"Module m_connectban added Z:line on *@%s to expire on %s: Connect flooding",
maskstr.c_str(), timestr.c_str());
ServerInstance->SNO->WriteGlobalSno('a', "Connect flooding from IP range %s (%d)", maskstr.c_str(), threshold);
@@ -117,9 +95,9 @@ class ModuleConnectBan : public Module
}
}
- virtual void OnGarbageCollect()
+ void OnGarbageCollect() CXX11_OVERRIDE
{
- ServerInstance->Logs->Log("m_connectban",DEBUG, "Clearing map.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Clearing map.");
connects.clear();
}
};
diff --git a/src/modules/m_connflood.cpp b/src/modules/m_connflood.cpp
index f77691e32..5070dd3a7 100644
--- a/src/modules/m_connflood.cpp
+++ b/src/modules/m_connflood.cpp
@@ -21,12 +21,11 @@
#include "inspircd.h"
-/* $ModDesc: Connection throttle */
-
class ModuleConnFlood : public Module
{
-private:
- int seconds, timeout, boot_wait;
+ unsigned int seconds;
+ unsigned int timeout;
+ unsigned int boot_wait;
unsigned int conns;
unsigned int maxconns;
bool throttled;
@@ -39,35 +38,28 @@ public:
{
}
- void init()
- {
- InitConf();
- Implementation eventlist[] = { I_OnRehash, I_OnUserRegister };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Connection throttle", VF_VENDOR);
}
- void InitConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
/* read configuration variables */
ConfigTag* tag = ServerInstance->Config->ConfValue("connflood");
/* throttle configuration */
- seconds = tag->getInt("seconds");
- maxconns = tag->getInt("maxconns");
- timeout = tag->getInt("timeout");
+ seconds = tag->getDuration("period", tag->getDuration("seconds", 30));
+ maxconns = tag->getUInt("maxconns", 3);
+ timeout = tag->getDuration("timeout", 30);
quitmsg = tag->getString("quitmsg");
/* seconds to wait when the server just booted */
- boot_wait = tag->getInt("bootwait");
+ boot_wait = tag->getDuration("bootwait", 10);
first = ServerInstance->Time();
}
- virtual ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
if (user->exempt)
return MOD_RES_PASSTHRU;
@@ -114,12 +106,6 @@ public:
}
return MOD_RES_PASSTHRU;
}
-
- virtual void OnRehash(User* user)
- {
- InitConf();
- }
-
};
MODULE_INIT(ModuleConnFlood)
diff --git a/src/modules/m_customprefix.cpp b/src/modules/m_customprefix.cpp
index dfc60e082..befc9750d 100644
--- a/src/modules/m_customprefix.cpp
+++ b/src/modules/m_customprefix.cpp
@@ -19,77 +19,23 @@
#include "inspircd.h"
-/* $ModDesc: Allows custom prefix modes to be created. */
-
-class CustomPrefixMode : public ModeHandler
+class CustomPrefixMode : public PrefixMode
{
public:
reference<ConfigTag> tag;
- int rank;
- bool depriv;
- CustomPrefixMode(Module* parent, ConfigTag* Tag)
- : ModeHandler(parent, Tag->getString("name"), 0, PARAM_ALWAYS, MODETYPE_CHANNEL), tag(Tag)
- {
- list = true;
- m_paramtype = TR_NICK;
- std::string v = tag->getString("prefix");
- prefix = v.c_str()[0];
- v = tag->getString("letter");
- mode = v.c_str()[0];
- rank = tag->getInt("rank");
- levelrequired = tag->getInt("ranktoset", rank);
- depriv = tag->getBool("depriv", true);
- }
-
- unsigned int GetPrefixRank()
- {
- return rank;
- }
-
- ModResult AccessCheck(User* src, Channel*, std::string& value, bool adding)
- {
- if (!adding && src->nick == value && depriv)
- return MOD_RES_ALLOW;
- return MOD_RES_PASSTHRU;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- const UserMembList* cl = channel->GetUsers();
- std::vector<std::string> mode_junk;
- mode_junk.push_back(channel->name);
- irc::modestacker modestack(false);
- std::deque<std::string> stackresult;
-
- for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
- {
- if (i->second->hasMode(mode))
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- modestack.Push(this->GetModeChar(), i->first->nick);
- }
- }
-
- if (stack)
- return;
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
+ CustomPrefixMode(Module* parent, const std::string& Name, char Letter, char Prefix, ConfigTag* Tag)
+ : PrefixMode(parent, Name, Letter, 0, Prefix)
+ , tag(Tag)
{
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- return MODEACTION_ALLOW;
+ unsigned long rank = tag->getUInt("rank", 0, 0, UINT_MAX);
+ unsigned long setrank = tag->getUInt("ranktoset", prefixrank, rank, UINT_MAX);
+ unsigned long unsetrank = tag->getUInt("ranktounset", setrank, setrank, UINT_MAX);
+ bool depriv = tag->getBool("depriv", true);
+ this->Update(rank, setrank, unsetrank, depriv);
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Created the %s prefix: letter=%c prefix=%c rank=%u ranktoset=%u ranktounset=%i depriv=%d",
+ name.c_str(), GetModeChar(), GetPrefix(), GetPrefixRank(), GetLevelRequired(true), GetLevelRequired(false), CanSelfRemove());
}
};
@@ -97,41 +43,65 @@ class ModuleCustomPrefix : public Module
{
std::vector<CustomPrefixMode*> modes;
public:
- ModuleCustomPrefix()
- {
- }
-
- void init()
+ void init() CXX11_OVERRIDE
{
ConfigTagList tags = ServerInstance->Config->ConfTags("customprefix");
- while (tags.first != tags.second)
+ for (ConfigIter iter = tags.first; iter != tags.second; ++iter)
{
- ConfigTag* tag = tags.first->second;
- tags.first++;
- CustomPrefixMode* mh = new CustomPrefixMode(this, tag);
- modes.push_back(mh);
- if (mh->rank <= 0)
- throw ModuleException("Rank must be specified for prefix at " + tag->getTagLocation());
- if (!isalpha(mh->GetModeChar()))
- throw ModuleException("Mode must be a letter for prefix at " + tag->getTagLocation());
+ ConfigTag* tag = iter->second;
+
+ const std::string name = tag->getString("name");
+ if (name.empty())
+ throw ModuleException("<customprefix:name> must be specified at " + tag->getTagLocation());
+
+ if (tag->getBool("change"))
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+ if (!mh)
+ throw ModuleException("<customprefix:change> specified for a non-existent mode at " + tag->getTagLocation());
+
+ PrefixMode* pm = mh->IsPrefixMode();
+ if (!pm)
+ throw ModuleException("<customprefix:change> specified for a non-prefix mode at " + tag->getTagLocation());
+
+ unsigned long rank = tag->getUInt("rank", pm->GetPrefixRank(), 0, UINT_MAX);
+ unsigned long setrank = tag->getUInt("ranktoset", pm->GetLevelRequired(true), rank, UINT_MAX);
+ unsigned long unsetrank = tag->getUInt("ranktounset", pm->GetLevelRequired(false), setrank, UINT_MAX);
+ bool depriv = tag->getBool("depriv", pm->CanSelfRemove());
+ pm->Update(rank, setrank, unsetrank, depriv);
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changed the %s prefix: depriv=%u rank=%u ranktoset=%u ranktounset=%u",
+ pm->name.c_str(), pm->CanSelfRemove(), pm->GetPrefixRank(), pm->GetLevelRequired(true), pm->GetLevelRequired(false));
+ continue;
+ }
+
+ const std::string letter = tag->getString("letter");
+ if (letter.length() != 1)
+ throw ModuleException("<customprefix:letter> must be set to a mode character at " + tag->getTagLocation());
+
+ const std::string prefix = tag->getString("prefix");
+ if (prefix.length() != 1)
+ throw ModuleException("<customprefix:prefix> must be set to a mode prefix at " + tag->getTagLocation());
+
try
{
+ CustomPrefixMode* mh = new CustomPrefixMode(this, name, letter[0], prefix[0], tag);
+ modes.push_back(mh);
ServerInstance->Modules->AddService(*mh);
}
catch (ModuleException& e)
{
- throw ModuleException(e.err + " (while creating mode from " + tag->getTagLocation() + ")");
+ throw ModuleException(e.GetReason() + " (while creating mode from " + tag->getTagLocation() + ")");
}
}
}
~ModuleCustomPrefix()
{
- for (std::vector<CustomPrefixMode*>::iterator i = modes.begin(); i != modes.end(); i++)
- delete *i;
+ stdalgo::delete_all(modes);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides custom prefix channel modes", VF_VENDOR);
}
diff --git a/src/modules/m_customtitle.cpp b/src/modules/m_customtitle.cpp
index c65645bc9..c16b1eda2 100644
--- a/src/modules/m_customtitle.cpp
+++ b/src/modules/m_customtitle.cpp
@@ -20,8 +20,13 @@
#include "inspircd.h"
+#include "modules/whois.h"
-/* $ModDesc: Provides the TITLE command which allows setting of CUSTOM WHOIS TITLE line */
+enum
+{
+ // From UnrealIRCd.
+ RPL_WHOISSPECIAL = 320
+};
/** Handle /TITLE
*/
@@ -30,32 +35,15 @@ class CommandTitle : public Command
public:
StringExtItem ctitle;
CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2),
- ctitle("ctitle", Creator)
+ ctitle("ctitle", ExtensionItem::EXT_USER, Creator)
{
syntax = "<user> <password>";
}
- bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
- {
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
- }
-
- CmdResult Handle(const std::vector<std::string> &parameters, User* user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- char TheHost[MAXBUF];
- char TheIP[MAXBUF];
-
- snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(), user->host.c_str());
- snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(), user->GetIPString());
+ const std::string userHost = user->ident + "@" + user->GetRealHost();
+ const std::string userIP = user->ident + "@" + user->GetIPString();
ConfigTagList tags = ServerInstance->Config->ConfTags("title");
for (ConfigIter i = tags.first; i != tags.second; ++i)
@@ -67,65 +55,57 @@ class CommandTitle : public Command
std::string title = i->second->getString("title");
std::string vhost = i->second->getString("vhost");
- if (Name == parameters[0] && !ServerInstance->PassCompare(user, pass, parameters[1], hash) && OneOfMatches(TheHost,TheIP,host.c_str()) && !title.empty())
+ if (Name == parameters[0] && ServerInstance->PassCompare(user, pass, parameters[1], hash) &&
+ InspIRCd::MatchMask(host, userHost, userIP) && !title.empty())
{
ctitle.set(user, title);
ServerInstance->PI->SendMetaData(user, "ctitle", title);
if (!vhost.empty())
- user->ChangeDisplayedHost(vhost.c_str());
+ user->ChangeDisplayedHost(vhost);
- user->WriteServ("NOTICE %s :Custom title set to '%s'",user->nick.c_str(), title.c_str());
+ user->WriteNotice("Custom title set to '" + title + "'");
return CMD_SUCCESS;
}
}
- user->WriteServ("NOTICE %s :Invalid title credentials",user->nick.c_str());
+ user->WriteNotice("Invalid title credentials");
return CMD_SUCCESS;
}
};
-class ModuleCustomTitle : public Module
+class ModuleCustomTitle : public Module, public Whois::LineEventListener
{
CommandTitle cmd;
public:
- ModuleCustomTitle() : cmd(this)
- {
- }
-
- void init()
+ ModuleCustomTitle()
+ : Whois::LineEventListener(this)
+ , cmd(this)
{
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.ctitle);
- ServerInstance->Modules->Attach(I_OnWhoisLine, this);
}
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* We use this and not OnWhois because this triggers for remote, too */
- if (numeric == 312)
+ if (numeric.GetNumeric() == 312)
{
/* Insert our numeric before 312 */
- const std::string* ctitle = cmd.ctitle.get(dest);
+ const std::string* ctitle = cmd.ctitle.get(whois.GetTarget());
if (ctitle)
{
- ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), ctitle->c_str());
+ whois.SendLine(RPL_WHOISSPECIAL, ctitle);
}
}
/* Don't block anything */
return MOD_RES_PASSTHRU;
}
- ~ModuleCustomTitle()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Custom Title for users", VF_OPTCOMMON | VF_VENDOR);
}
diff --git a/src/modules/m_cycle.cpp b/src/modules/m_cycle.cpp
index 383e7b5a2..3ead72a45 100644
--- a/src/modules/m_cycle.cpp
+++ b/src/modules/m_cycle.cpp
@@ -20,23 +20,21 @@
#include "inspircd.h"
-/* $ModDesc: Provides command CYCLE, acts as a server-side HOP command to part and rejoin a channel. */
-
/** Handle /CYCLE
*/
-class CommandCycle : public Command
+class CommandCycle : public SplitCommand
{
public:
- CommandCycle(Module* Creator) : Command(Creator,"CYCLE", 1)
+ CommandCycle(Module* Creator)
+ : SplitCommand(Creator, "CYCLE", 1)
{
Penalty = 3; syntax = "<channel> :[reason]";
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
Channel* channel = ServerInstance->FindChan(parameters[0]);
- std::string reason = ConvToStr("Cycling");
+ std::string reason = "Cycling";
if (parameters.size() > 1)
{
@@ -46,34 +44,27 @@ class CommandCycle : public Command
if (!channel)
{
- user->WriteNumeric(403, "%s %s :No such channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
return CMD_FAILURE;
}
if (channel->HasUser(user))
{
- /*
- * technically, this is only ever sent locally, but pays to be safe ;p
- */
- if (IS_LOCAL(user))
+ if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user))
{
- if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user))
- {
- /* banned, boned. drop the message. */
- user->WriteServ("NOTICE "+user->nick+" :*** You may not cycle, as you are banned on channel " + channel->name);
- return CMD_FAILURE;
- }
-
- channel->PartUser(user, reason);
-
- Channel::JoinUser(user, parameters[0].c_str(), true, "", false, ServerInstance->Time());
+ // User is banned, send an error and don't cycle them
+ user->WriteNotice("*** You may not cycle, as you are banned on channel " + channel->name);
+ return CMD_FAILURE;
}
+ channel->PartUser(user, reason);
+ Channel::JoinUser(user, parameters[0], true);
+
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(442, "%s %s :You're not on that channel", user->nick.c_str(), channel->name.c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, channel->name, "You're not on that channel");
}
return CMD_FAILURE;
@@ -84,26 +75,17 @@ class CommandCycle : public Command
class ModuleCycle : public Module
{
CommandCycle cmd;
+
public:
ModuleCycle()
: cmd(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleCycle()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command CYCLE, acts as a server-side HOP command to part and rejoin a channel.", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleCycle)
diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp
index 05fff8937..f2d6bf105 100644
--- a/src/modules/m_dccallow.cpp
+++ b/src/modules/m_dccallow.cpp
@@ -25,7 +25,46 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the /DCCALLOW command */
+enum
+{
+ // From ircd-ratbox.
+ RPL_HELPSTART = 704,
+ RPL_HELPTXT = 705,
+ RPL_ENDOFHELP = 706,
+
+ // InspIRCd-specific?
+ RPL_DCCALLOWSTART = 990,
+ RPL_DCCALLOWLIST = 991,
+ RPL_DCCALLOWEND = 992,
+ RPL_DCCALLOWTIMED = 993,
+ RPL_DCCALLOWPERMANENT = 994,
+ RPL_DCCALLOWREMOVED = 995,
+ ERR_DCCALLOWINVALID = 996,
+ RPL_DCCALLOWEXPIRED = 997,
+ ERR_UNKNOWNDCCALLOWCMD = 998
+};
+
+static const char* const helptext[] =
+{
+ "You may allow DCCs from specific users by specifying a",
+ "DCC allow for the user you want to receive DCCs from.",
+ "For example, to allow the user Brain to send you inspircd.exe",
+ "you would type:",
+ "/DCCALLOW +Brain",
+ "Brain would then be able to send you files. They would have to",
+ "resend the file again if the server gave them an error message",
+ "before you added them to your DCCALLOW list.",
+ "DCCALLOW entries will be temporary by default, if you want to add",
+ "them to your DCCALLOW list until you leave IRC, type:",
+ "/DCCALLOW +Brain 0",
+ "To remove the user from your DCCALLOW list, type:",
+ "/DCCALLOW -Brain",
+ "To see the users in your DCCALLOW list, type:",
+ "/DCCALLOW LIST",
+ "NOTE: If the user leaves IRC or changes their nickname",
+ " they will be removed from your DCCALLOW list.",
+ " your DCCALLOW list will be deleted when you leave IRC."
+};
class BannedFileList
{
@@ -40,11 +79,17 @@ class DCCAllow
std::string nickname;
std::string hostmask;
time_t set_on;
- long length;
+ unsigned long length;
DCCAllow() { }
- DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { }
+ DCCAllow(const std::string& nick, const std::string& hm, time_t so, unsigned long ln)
+ : nickname(nick)
+ , hostmask(hm)
+ , set_on(so)
+ , length(ln)
+ {
+ }
};
typedef std::vector<User *> userlist;
@@ -53,19 +98,23 @@ typedef std::vector<DCCAllow> dccallowlist;
dccallowlist* dl;
typedef std::vector<BannedFileList> bannedfilelist;
bannedfilelist bfl;
-SimpleExtItem<dccallowlist>* ext;
+typedef SimpleExtItem<dccallowlist> DCCAllowExt;
class CommandDccallow : public Command
{
+ DCCAllowExt& ext;
+
public:
unsigned int maxentries;
- CommandDccallow(Module* parent) : Command(parent, "DCCALLOW", 0)
+ CommandDccallow(Module* parent, DCCAllowExt& Ext)
+ : Command(parent, "DCCALLOW", 0)
+ , ext(Ext)
{
syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
/* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
/* syntax: DCCALLOW [+|-]<nick> (<time>) */
if (!parameters.size())
@@ -95,21 +144,21 @@ class CommandDccallow : public Command
}
else
{
- user->WriteNumeric(998, "%s :DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP", user->nick.c_str());
+ user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
return CMD_FAILURE;
}
}
- std::string nick = parameters[0].substr(1);
+ std::string nick(parameters[0], 1);
User *target = ServerInstance->FindNickOnly(nick);
- if ((target) && (!IS_SERVER(target)) && (!target->quitting) && (target->registered == REG_ALL))
+ if ((target) && (!target->quitting) && (target->registered == REG_ALL))
{
if (action == '-')
{
// check if it contains any entries
- dl = ext->get(user);
+ dl = ext.get(user);
if (dl)
{
for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
@@ -118,7 +167,7 @@ class CommandDccallow : public Command
if (i->nickname == target->nick)
{
dl->erase(i);
- user->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
break;
}
}
@@ -128,22 +177,22 @@ class CommandDccallow : public Command
{
if (target == user)
{
- user->WriteNumeric(996, "%s %s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str(), user->nick.c_str());
+ user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
return CMD_FAILURE;
}
- dl = ext->get(user);
+ dl = ext.get(user);
if (!dl)
{
dl = new dccallowlist;
- ext->set(user, dl);
+ ext.set(user, dl);
// add this user to the userlist
ul.push_back(user);
}
if (dl->size() >= maxentries)
{
- user->WriteNumeric(996, "%s %s :Too many nicks on DCCALLOW list", user->nick.c_str(), user->nick.c_str());
+ user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
return CMD_FAILURE;
}
@@ -151,18 +200,18 @@ class CommandDccallow : public Command
{
if (k->nickname == target->nick)
{
- user->WriteNumeric(996, "%s %s :%s is already on your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
return CMD_FAILURE;
}
}
- std::string mask = target->nick+"!"+target->ident+"@"+target->dhost;
+ std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
- long length;
+ unsigned long length;
if (parameters.size() < 2)
{
- length = ServerInstance->Duration(default_length);
+ length = InspIRCd::Duration(default_length);
}
else if (!atoi(parameters[1].c_str()))
{
@@ -170,10 +219,10 @@ class CommandDccallow : public Command
}
else
{
- length = ServerInstance->Duration(parameters[1]);
+ length = InspIRCd::Duration(parameters[1]);
}
- if (!ServerInstance->IsValidMask(mask))
+ if (!InspIRCd::IsValidMask(mask))
{
return CMD_FAILURE;
}
@@ -182,11 +231,11 @@ class CommandDccallow : public Command
if (length > 0)
{
- user->WriteNumeric(993, "%s %s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), user->nick.c_str(), target->nick.c_str(), length);
+ user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %ld seconds", target->nick.c_str(), length));
}
else
{
- user->WriteNumeric(994, "%s %s :Added %s to DCCALLOW list for this session", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
}
/* route it. */
@@ -196,40 +245,24 @@ class CommandDccallow : public Command
else
{
// nick doesn't exist
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), nick.c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(nick));
return CMD_FAILURE;
}
}
return CMD_FAILURE;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
void DisplayHelp(User* user)
{
- user->WriteNumeric(998, "%s :DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]", user->nick.c_str());
- user->WriteNumeric(998, "%s :You may allow DCCs from specific users by specifying a", user->nick.c_str());
- user->WriteNumeric(998, "%s :DCC allow for the user you want to receive DCCs from.", user->nick.c_str());
- user->WriteNumeric(998, "%s :For example, to allow the user Brain to send you inspircd.exe", user->nick.c_str());
- user->WriteNumeric(998, "%s :you would type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW +Brain", user->nick.c_str());
- user->WriteNumeric(998, "%s :Brain would then be able to send you files. They would have to", user->nick.c_str());
- user->WriteNumeric(998, "%s :resend the file again if the server gave them an error message", user->nick.c_str());
- user->WriteNumeric(998, "%s :before you added them to your DCCALLOW list.", user->nick.c_str());
- user->WriteNumeric(998, "%s :DCCALLOW entries will be temporary by default, if you want to add", user->nick.c_str());
- user->WriteNumeric(998, "%s :them to your DCCALLOW list until you leave IRC, type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW +Brain 0", user->nick.c_str());
- user->WriteNumeric(998, "%s :To remove the user from your DCCALLOW list, type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW -Brain", user->nick.c_str());
- user->WriteNumeric(998, "%s :To see the users in your DCCALLOW list, type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW LIST", user->nick.c_str());
- user->WriteNumeric(998, "%s :NOTE: If the user leaves IRC or changes their nickname", user->nick.c_str());
- user->WriteNumeric(998, "%s : they will be removed from your DCCALLOW list.", user->nick.c_str());
- user->WriteNumeric(998, "%s : your DCCALLOW list will be deleted when you leave IRC.", user->nick.c_str());
- user->WriteNumeric(999, "%s :End of DCCALLOW HELP", user->nick.c_str());
+ user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
+ for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
+ user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
+ user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
LocalUser* localuser = IS_LOCAL(user);
if (localuser)
@@ -239,100 +272,75 @@ class CommandDccallow : public Command
void DisplayDCCAllowList(User* user)
{
// display current DCCALLOW list
- user->WriteNumeric(990, "%s :Users on your DCCALLOW list:", user->nick.c_str());
+ user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
- dl = ext->get(user);
+ dl = ext.get(user);
if (dl)
{
for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
{
- user->WriteNumeric(991, "%s %s :%s (%s)", user->nick.c_str(), user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
+ user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
}
}
- user->WriteNumeric(992, "%s :End of DCCALLOW list", user->nick.c_str());
+ user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
}
};
class ModuleDCCAllow : public Module
{
+ DCCAllowExt ext;
CommandDccallow cmd;
- public:
+ public:
ModuleDCCAllow()
- : cmd(this)
+ : ext("dccallow", ExtensionItem::EXT_USER, this)
+ , cmd(this, ext)
{
- ext = NULL;
}
- void init()
+ void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
{
- ext = new SimpleExtItem<dccallowlist>("dccallow", this);
- ServerInstance->Modules->AddService(*ext);
- ServerInstance->Modules->AddService(cmd);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserQuit, I_OnUserPostNick, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
- {
- ReadFileConf();
- ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
- cmd.maxentries = tag->getInt("maxentries", 20);
- }
-
- virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
- {
- dccallowlist* udl = ext->get(user);
+ dccallowlist* udl = ext.get(user);
// remove their DCCALLOW list if they have one
if (udl)
- {
- userlist::iterator it = std::find(ul.begin(), ul.end(), user);
- if (it != ul.end())
- ul.erase(it);
- }
+ stdalgo::erase(ul, user);
// remove them from any DCCALLOW lists
// they are currently on
RemoveNick(user);
}
- virtual void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
RemoveNick(user);
}
- virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
- }
-
- virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- if (target_type == TYPE_USER)
+ if (target.type == MessageTarget::TYPE_USER)
{
- User* u = (User*)dest;
+ User* u = target.Get<User>();
/* Always allow a user to dcc themselves (although... why?) */
if (user == u)
return MOD_RES_PASSTHRU;
- if ((text.length()) && (text[0] == '\1'))
+ if ((details.text.length()) && (details.text[0] == '\1'))
{
Expire();
// :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
// :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
- if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
+ if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
{
- dl = ext->get(u);
+ dl = ext.get(u);
if (dl && dl->size())
{
for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
@@ -340,17 +348,17 @@ class ModuleDCCAllow : public Module
return MOD_RES_PASSTHRU;
}
- std::string buf = text.substr(5);
+ std::string buf = details.text.substr(5);
size_t s = buf.find(' ');
if (s == std::string::npos)
return MOD_RES_PASSTHRU;
- irc::string type = assign(buf.substr(0, s));
+ const std::string type = buf.substr(0, s);
ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
bool blockchat = conftag->getBool("blockchat");
- if (type == "SEND")
+ if (stdalgo::string::equalsci(type, "SEND"))
{
size_t first;
@@ -384,7 +392,7 @@ class ModuleDCCAllow : public Module
if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
{
/* We have a matching badfile entry, override whatever the default action is */
- if (bfl[i].action == "allow")
+ if (stdalgo::string::equalsci(bfl[i].action, "allow"))
return MOD_RES_PASSTHRU;
else
{
@@ -398,16 +406,16 @@ class ModuleDCCAllow : public Module
if ((!found) && (defaultaction == "allow"))
return MOD_RES_PASSTHRU;
- user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick.c_str(), u->nick.c_str(), filename.c_str());
- u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), filename.c_str());
- u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
+ user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
+ u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
+ u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
return MOD_RES_DENY;
}
- else if ((type == "CHAT") && (blockchat))
+ else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
{
- user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick.c_str(), u->nick.c_str());
- u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str());
- u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
+ user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
+ u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
+ u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
return MOD_RES_DENY;
}
}
@@ -421,7 +429,7 @@ class ModuleDCCAllow : public Module
for (userlist::iterator iter = ul.begin(); iter != ul.end();)
{
User* u = (User*)(*iter);
- dl = ext->get(u);
+ dl = ext.get(u);
if (dl)
{
if (dl->size())
@@ -429,9 +437,10 @@ class ModuleDCCAllow : public Module
dccallowlist::iterator iter2 = dl->begin();
while (iter2 != dl->end())
{
- if (iter2->length != 0 && (iter2->set_on + iter2->length) <= ServerInstance->Time())
+ time_t expires = iter2->set_on + iter2->length;
+ if (iter2->length != 0 && expires <= ServerInstance->Time())
{
- u->WriteNumeric(997, "%s %s :DCCALLOW entry for %s has expired", u->nick.c_str(), u->nick.c_str(), iter2->nickname.c_str());
+ u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
iter2 = dl->erase(iter2);
}
else
@@ -455,7 +464,7 @@ class ModuleDCCAllow : public Module
for (userlist::iterator iter = ul.begin(); iter != ul.end();)
{
User *u = (User*)(*iter);
- dl = ext->get(u);
+ dl = ext.get(u);
if (dl)
{
if (dl->size())
@@ -465,8 +474,8 @@ class ModuleDCCAllow : public Module
if (i->nickname == user->nick)
{
- u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
- u->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", u->nick.c_str(), u->nick.c_str(), i->nickname.c_str());
+ u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
+ u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
dl->erase(i);
break;
}
@@ -495,8 +504,11 @@ class ModuleDCCAllow : public Module
}
}
- void ReadFileConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
+ cmd.maxentries = tag->getUInt("maxentries", 20);
+
bfl.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
for (ConfigIter i = tags.first; i != tags.second; ++i)
@@ -508,12 +520,7 @@ class ModuleDCCAllow : public Module
}
}
- virtual ~ModuleDCCAllow()
- {
- delete ext;
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
}
diff --git a/src/modules/m_deaf.cpp b/src/modules/m_deaf.cpp
index 43b24cfae..e58d130a5 100644
--- a/src/modules/m_deaf.cpp
+++ b/src/modules/m_deaf.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides usermode +d to block channel messages and channel notices */
-
/** User mode +d - filter out channel messages and channel notices
*/
class User_d : public ModeHandler
@@ -30,33 +28,22 @@ class User_d : public ModeHandler
public:
User_d(Module* Creator) : ModeHandler(Creator, "deaf", 'd', PARAM_NONE, MODETYPE_USER) { }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
+ if (adding == dest->IsModeSet(this))
+ return MODEACTION_DENY;
+
if (adding)
- {
- if (!dest->IsModeSet('d'))
- {
- dest->WriteServ("NOTICE %s :*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode %s -d.", dest->nick.c_str(), dest->nick.c_str());
- dest->SetMode('d',true);
- return MODEACTION_ALLOW;
- }
- }
- else
- {
- if (dest->IsModeSet('d'))
- {
- dest->SetMode('d',false);
- return MODEACTION_ALLOW;
- }
- }
- return MODEACTION_DENY;
+ dest->WriteNotice("*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode " + dest->nick + " -d.");
+
+ dest->SetMode(this, adding);
+ return MODEACTION_ALLOW;
}
};
class ModuleDeaf : public Module
{
User_d m1;
-
std::string deaf_bypasschars;
std::string deaf_bypasschars_uline;
@@ -66,84 +53,40 @@ class ModuleDeaf : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(m1);
-
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("deaf");
deaf_bypasschars = tag->getString("bypasschars");
deaf_bypasschars_uline = tag->getString("bypasscharsuline");
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (target_type == TYPE_CHANNEL)
- {
- Channel* chan = (Channel*)dest;
- if (chan)
- this->BuildDeafList(MSG_NOTICE, chan, user, status, text, exempt_list);
- }
+ if (target.type != MessageTarget::TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
- return MOD_RES_PASSTHRU;
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- {
- Channel* chan = (Channel*)dest;
- if (chan)
- this->BuildDeafList(MSG_PRIVMSG, chan, user, status, text, exempt_list);
- }
-
- return MOD_RES_PASSTHRU;
- }
-
- virtual void BuildDeafList(MessageType message_type, Channel* chan, User* sender, char status, const std::string &text, CUList &exempt_list)
- {
- const UserMembList *ulist = chan->GetUsers();
- bool is_a_uline;
- bool is_bypasschar, is_bypasschar_avail;
- bool is_bypasschar_uline, is_bypasschar_uline_avail;
-
- is_bypasschar = is_bypasschar_avail = is_bypasschar_uline = is_bypasschar_uline_avail = 0;
- if (!deaf_bypasschars.empty())
- {
- is_bypasschar_avail = 1;
- if (deaf_bypasschars.find(text[0], 0) != std::string::npos)
- is_bypasschar = 1;
- }
- if (!deaf_bypasschars_uline.empty())
- {
- is_bypasschar_uline_avail = 1;
- if (deaf_bypasschars_uline.find(text[0], 0) != std::string::npos)
- is_bypasschar_uline = 1;
- }
+ Channel* chan = target.Get<Channel>();
+ bool is_bypasschar = (deaf_bypasschars.find(details.text[0]) != std::string::npos);
+ bool is_bypasschar_uline = (deaf_bypasschars_uline.find(details.text[0]) != std::string::npos);
/*
* If we have no bypasschars_uline in config, and this is a bypasschar (regular)
* Than it is obviously going to get through +d, no build required
*/
- if (!is_bypasschar_uline_avail && is_bypasschar)
- return;
+ if (deaf_bypasschars_uline.empty() && is_bypasschar)
+ return MOD_RES_PASSTHRU;
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
/* not +d ? */
- if (!i->first->IsModeSet('d'))
+ if (!i->first->IsModeSet(m1))
continue; /* deliver message */
/* matched both U-line only and regular bypasses */
if (is_bypasschar && is_bypasschar_uline)
continue; /* deliver message */
- is_a_uline = ServerInstance->ULine(i->first->server);
+ bool is_a_uline = i->first->server->IsULine();
/* matched a U-line only bypass */
if (is_bypasschar_uline && is_a_uline)
continue; /* deliver message */
@@ -151,23 +94,17 @@ class ModuleDeaf : public Module
if (is_bypasschar && !is_a_uline)
continue; /* deliver message */
- if (status && !strchr(chan->GetAllPrefixChars(i->first), status))
- continue;
-
/* don't deliver message! */
- exempt_list.insert(i->first);
+ details.exemptions.insert(i->first);
}
- }
- virtual ~ModuleDeaf()
- {
+ return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides usermode +d to block channel messages and channel notices", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleDeaf)
diff --git a/src/modules/m_delayjoin.cpp b/src/modules/m_delayjoin.cpp
index 20d4c8e8f..d157b97bc 100644
--- a/src/modules/m_delayjoin.cpp
+++ b/src/modules/m_delayjoin.cpp
@@ -20,56 +20,91 @@
*/
-/* $ModDesc: Allows for delay-join channels (+D) where users don't appear to join until they speak */
-
#include "inspircd.h"
-#include <stdarg.h>
class DelayJoinMode : public ModeHandler
{
- private:
- CUList empty;
public:
DelayJoinMode(Module* Parent) : ModeHandler(Parent, "delayjoin", 'D', PARAM_NONE, MODETYPE_CHANNEL)
{
- levelrequired = OP_VALUE;
+ ranktoset = ranktounset = OP_VALUE;
+ }
+
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
+};
+
+
+namespace
+{
+
+/** Hook handler for join client protocol events.
+ * This allows us to block join protocol events completely, including all associated messages (e.g. MODE, away-notify AWAY).
+ * This is not the same as OnUserJoin() because that runs only when a real join happens but this runs also when a module
+ * such as hostcycle generates a join.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+ const LocalIntExt& unjoined;
+
+ public:
+ JoinHook(Module* mod, const LocalIntExt& unjoinedref)
+ : ClientProtocol::EventHook(mod, "JOIN", 10)
+ , unjoined(unjoinedref)
+ {
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+ {
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ const User* const u = join.GetMember()->user;
+ if ((unjoined.get(u)) && (u != user))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
+ }
};
+}
+
class ModuleDelayJoin : public Module
{
DelayJoinMode djm;
+ void RevealUser(User* user, Channel* chan);
public:
LocalIntExt unjoined;
- ModuleDelayJoin() : djm(this), unjoined("delayjoin", this)
- {
- }
+ JoinHook joinhook;
- void init()
+ ModuleDelayJoin()
+ : djm(this)
+ , unjoined("delayjoin", ExtensionItem::EXT_MEMBERSHIP, this)
+ , joinhook(this, unjoined)
{
- ServerInstance->Modules->AddService(djm);
- ServerInstance->Modules->AddService(unjoined);
- Implementation eventlist[] = { I_OnUserJoin, I_OnUserPart, I_OnUserKick, I_OnBuildNeighborList, I_OnNamesListItem, I_OnText, I_OnRawMode };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ~ModuleDelayJoin();
- Version GetVersion();
- void OnNamesListItem(User* issuer, Membership*, std::string &prefixes, std::string &nick);
- void OnUserJoin(Membership*, bool, bool, CUList&);
+
+ Version GetVersion() CXX11_OVERRIDE;
+ ModResult OnNamesListItem(User* issuer, Membership*, std::string& prefixes, std::string& nick) CXX11_OVERRIDE;
+ void OnUserJoin(Membership*, bool, bool, CUList&) CXX11_OVERRIDE;
void CleanUser(User* user);
- void OnUserPart(Membership*, std::string &partmessage, CUList&);
- void OnUserKick(User* source, Membership*, const std::string &reason, CUList&);
- void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception);
- void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
- ModResult OnRawMode(User* user, Channel* channel, const char mode, const std::string &param, bool adding, int pcnt);
+ void OnUserPart(Membership*, std::string &partmessage, CUList&) CXX11_OVERRIDE;
+ void OnUserKick(User* source, Membership*, const std::string &reason, CUList&) CXX11_OVERRIDE;
+ void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE;
+ void OnUserMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE;
+ ModResult OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE;
+};
+
+// TODO: make RevealUser accessible from DelayJoinMode and get rid of this.
+class DummyMessageDetails : public MessageDetails
+{
+public:
+ DummyMessageDetails() : MessageDetails(MSG_PRIVMSG, "", ClientProtocol::TagMap()) { }
+ bool IsCTCP(std::string& name, std::string& body) const CXX11_OVERRIDE { return false; }
+ bool IsCTCP(std::string&) const CXX11_OVERRIDE { return false; }
+ bool IsCTCP() const CXX11_OVERRIDE { return false; }
};
ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
{
/* no change */
- if (channel->IsModeSet('D') == adding)
+ if (channel->IsModeSet(this) == adding)
return MODEACTION_DENY;
if (!adding)
@@ -78,38 +113,40 @@ ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channe
* Make all users visible, as +D is being removed. If we don't do this,
* they remain permanently invisible on this channel!
*/
- const UserMembList* names = channel->GetUsers();
- for (UserMembCIter n = names->begin(); n != names->end(); ++n)
- creator->OnText(n->first, channel, TYPE_CHANNEL, "", 0, empty);
+ MessageTarget msgtarget(channel, 0);
+ DummyMessageDetails msgdetails;
+ const Channel::MemberMap& users = channel->GetUsers();
+ for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
+ {
+ creator->OnUserMessage(n->first, msgtarget, msgdetails);
+ }
}
- channel->SetMode('D', adding);
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
-ModuleDelayJoin::~ModuleDelayJoin()
-{
-}
-
Version ModuleDelayJoin::GetVersion()
{
return Version("Allows for delay-join channels (+D) where users don't appear to join until they speak", VF_VENDOR);
}
-void ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ModResult ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick)
{
/* don't prevent the user from seeing themself */
if (issuer == memb->user)
- return;
+ return MOD_RES_PASSTHRU;
/* If the user is hidden by delayed join, hide them from the NAMES list */
if (unjoined.get(memb))
- nick.clear();
+ return MOD_RES_DENY;
+
+ return MOD_RES_PASSTHRU;
}
static void populate(CUList& except, Membership* memb)
{
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
if (i->first == memb->user || !IS_LOCAL(i->first))
continue;
@@ -119,11 +156,8 @@ static void populate(CUList& except, Membership* memb)
void ModuleDelayJoin::OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
{
- if (memb->chan->IsModeSet('D'))
- {
+ if (memb->chan->IsModeSet(djm))
unjoined.set(memb, 1);
- populate(except, memb);
- }
}
void ModuleDelayJoin::OnUserPart(Membership* memb, std::string &partmessage, CUList& except)
@@ -138,53 +172,48 @@ void ModuleDelayJoin::OnUserKick(User* source, Membership* memb, const std::stri
populate(except, memb);
}
-void ModuleDelayJoin::OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
+void ModuleDelayJoin::OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception)
{
- UCListIter i = include.begin();
- while (i != include.end())
+ for (IncludeChanList::iterator i = include.begin(); i != include.end(); )
{
- Channel* c = *i++;
- Membership* memb = c->GetUser(source);
- if (memb && unjoined.get(memb))
- include.erase(c);
+ Membership* memb = *i;
+ if (unjoined.get(memb))
+ i = include.erase(i);
+ else
+ ++i;
}
}
-void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list)
+void ModuleDelayJoin::OnUserMessage(User* user, const MessageTarget& target, const MessageDetails& details)
{
- /* Server origin */
- if (!user)
- return;
-
- if (target_type != TYPE_CHANNEL)
+ if (target.type != MessageTarget::TYPE_CHANNEL)
return;
- Channel* channel = static_cast<Channel*>(dest);
+ Channel* channel = target.Get<Channel>();
+ RevealUser(user, channel);
+}
- Membership* memb = channel->GetUser(user);
+void ModuleDelayJoin::RevealUser(User* user, Channel* chan)
+{
+ Membership* memb = chan->GetUser(user);
if (!memb || !unjoined.set(memb, 0))
return;
/* Display the join to everyone else (the user who joined got it earlier) */
- channel->WriteAllExceptSender(user, false, 0, "JOIN %s", channel->name.c_str());
-
- std::string ms = memb->modes;
- for(unsigned int i=0; i < memb->modes.length(); i++)
- ms.append(" ").append(user->nick);
-
- if (ms.length() > 0)
- channel->WriteAllExceptSender(user, false, 0, "MODE %s +%s", channel->name.c_str(), ms.c_str());
+ CUList except_list;
+ except_list.insert(user);
+ ClientProtocol::Events::Join joinevent(memb);
+ chan->Write(joinevent, 0, except_list);
}
/* make the user visible if he receives any mode change */
-ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, const char mode, const std::string &param, bool adding, int pcnt)
+ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding)
{
- if (!user || !channel || param.empty())
+ if (!channel || param.empty())
return MOD_RES_PASSTHRU;
- ModeHandler* mh = ServerInstance->Modes->FindMode(mode, MODETYPE_CHANNEL);
// If not a prefix mode then we got nothing to do here
- if (!mh || !mh->GetPrefixRank())
+ if (!mh->IsPrefixMode())
return MOD_RES_PASSTHRU;
User* dest;
@@ -196,9 +225,7 @@ ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, const char mo
if (!dest)
return MOD_RES_PASSTHRU;
- Membership* memb = channel->GetUser(dest);
- if (memb && unjoined.set(memb, 0))
- channel->WriteAllExceptSender(dest, false, 0, "JOIN %s", channel->name.c_str());
+ RevealUser(dest, channel);
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_delaymsg.cpp b/src/modules/m_delaymsg.cpp
index 978ab55d2..b39fb1d0a 100644
--- a/src/modules/m_delaymsg.cpp
+++ b/src/modules/m_delaymsg.cpp
@@ -19,82 +19,67 @@
#include "inspircd.h"
-/* $ModDesc: Provides channelmode +d <int>, to deny messages to a channel until <int> seconds. */
-
-class DelayMsgMode : public ModeHandler
+class DelayMsgMode : public ParamMode<DelayMsgMode, LocalIntExt>
{
public:
LocalIntExt jointime;
- DelayMsgMode(Module* Parent) : ModeHandler(Parent, "delaymsg", 'd', PARAM_SETONLY, MODETYPE_CHANNEL)
- , jointime("delaymsg", Parent)
+ DelayMsgMode(Module* Parent)
+ : ParamMode<DelayMsgMode, LocalIntExt>(Parent, "delaymsg", 'd')
+ , jointime("delaymsg", ExtensionItem::EXT_MEMBERSHIP, Parent)
{
- levelrequired = OP_VALUE;
+ ranktoset = ranktounset = OP_VALUE;
}
- bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel*)
+ bool ResolveModeConflict(std::string& their_param, const std::string& our_param, Channel*) CXX11_OVERRIDE
{
return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+ ModeAction OnSet(User* source, Channel* chan, std::string& parameter) CXX11_OVERRIDE;
+ void OnUnset(User* source, Channel* chan);
+
+ void SerializeParam(Channel* chan, int n, std::string& out)
+ {
+ out += ConvToStr(n);
+ }
};
class ModuleDelayMsg : public Module
{
- private:
DelayMsgMode djm;
+ bool allownotice;
public:
ModuleDelayMsg() : djm(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(djm);
- ServerInstance->Modules->AddService(djm.jointime);
- Implementation eventlist[] = { I_OnUserJoin, I_OnUserPreMessage, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
- Version GetVersion();
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList&);
- ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list);
- ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list);
- void OnRehash(User* user);
+ Version GetVersion() CXX11_OVERRIDE;
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList&) CXX11_OVERRIDE;
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE;
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
};
-ModeAction DelayMsgMode::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ModeAction DelayMsgMode::OnSet(User* source, Channel* chan, std::string& parameter)
{
- if (adding)
- {
- if ((channel->IsModeSet('d')) && (channel->GetModeParameter('d') == parameter))
- return MODEACTION_DENY;
-
- /* Setting a new limit, sanity check */
- long limit = atoi(parameter.c_str());
-
- /* Wrap low values at 32768 */
- if (limit < 0)
- limit = 0x7FFF;
+ // Setting a new limit, sanity check
+ unsigned int limit = ConvToInt(parameter);
+ if (limit == 0)
+ limit = 1;
- parameter = ConvToStr(limit);
- }
- else
- {
- if (!channel->IsModeSet('d'))
- return MODEACTION_DENY;
-
- /*
- * Clean up metadata
- */
- const UserMembList* names = channel->GetUsers();
- for (UserMembCIter n = names->begin(); n != names->end(); ++n)
- jointime.set(n->second, 0);
- }
- channel->SetModeParam('d', adding ? parameter : "");
+ ext.set(chan, limit);
return MODEACTION_ALLOW;
}
+void DelayMsgMode::OnUnset(User* source, Channel* chan)
+{
+ /*
+ * Clean up metadata
+ */
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
+ jointime.set(n->second, 0);
+}
+
Version ModuleDelayMsg::GetVersion()
{
return Version("Provides channelmode +d <int>, to deny messages to a channel until <int> seconds.", VF_VENDOR);
@@ -102,22 +87,21 @@ Version ModuleDelayMsg::GetVersion()
void ModuleDelayMsg::OnUserJoin(Membership* memb, bool sync, bool created, CUList&)
{
- if ((IS_LOCAL(memb->user)) && (memb->chan->IsModeSet('d')))
+ if ((IS_LOCAL(memb->user)) && (memb->chan->IsModeSet(djm)))
{
djm.jointime.set(memb, ServerInstance->Time());
}
}
-ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ModResult ModuleDelayMsg::OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details)
{
- /* Server origin */
- if ((!user) || (!IS_LOCAL(user)))
+ if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- if (target_type != TYPE_CHANNEL)
+ if ((target.type != MessageTarget::TYPE_CHANNEL) || ((!allownotice) && (details.type == MSG_NOTICE)))
return MOD_RES_PASSTHRU;
- Channel* channel = (Channel*) dest;
+ Channel* channel = target.Get<Channel>();
Membership* memb = channel->GetUser(user);
if (!memb)
@@ -128,14 +112,13 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty
if (ts == 0)
return MOD_RES_PASSTHRU;
- std::string len = channel->GetModeParameter('d');
+ int len = djm.ext.get(channel);
- if (ts + atoi(len.c_str()) > ServerInstance->Time())
+ if ((ts + len) > ServerInstance->Time())
{
if (channel->GetPrefixValue(user) < VOICE_VALUE)
{
- user->WriteNumeric(404, "%s %s :You must wait %s seconds after joining to send to channel (+d)",
- user->nick.c_str(), channel->name.c_str(), len.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, channel->name, InspIRCd::Format("You must wait %d seconds after joining to send to channel (+d)", len));
return MOD_RES_DENY;
}
}
@@ -147,19 +130,10 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty
return MOD_RES_PASSTHRU;
}
-ModResult ModuleDelayMsg::OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list)
-{
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
-}
-
-void ModuleDelayMsg::OnRehash(User* user)
+void ModuleDelayMsg::ReadConfig(ConfigStatus& status)
{
ConfigTag* tag = ServerInstance->Config->ConfValue("delaymsg");
- if (tag->getBool("allownotice", true))
- ServerInstance->Modules->Detach(I_OnUserPreNotice, this);
- else
- ServerInstance->Modules->Attach(I_OnUserPreNotice, this);
+ allownotice = tag->getBool("allownotice", true);
}
MODULE_INIT(ModuleDelayMsg)
-
diff --git a/src/modules/m_denychans.cpp b/src/modules/m_denychans.cpp
index 39d9e0d34..cc4172529 100644
--- a/src/modules/m_denychans.cpp
+++ b/src/modules/m_denychans.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ *. Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
* Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
@@ -22,112 +23,160 @@
#include "inspircd.h"
-/* $ModDesc: Implements config tags which allow blocking of joins to channels */
+enum
+{
+ // InspIRCd-specific.
+ ERR_BADCHANNEL = 926
+};
+
+struct BadChannel
+{
+ bool allowopers;
+ std::string name;
+ std::string reason;
+ std::string redirect;
+
+ BadChannel(const std::string& Name, const std::string& Redirect, const std::string& Reason, bool AllowOpers)
+ : allowopers(AllowOpers)
+ , name(Name)
+ , reason(Reason)
+ , redirect(Redirect)
+ {
+ }
+};
+
+typedef std::vector<BadChannel> BadChannels;
+typedef std::vector<std::string> GoodChannels;
class ModuleDenyChannels : public Module
{
+ private:
+ BadChannels badchannels;
+ GoodChannels goodchannels;
+ UserModeReference antiredirectmode;
+ ChanModeReference redirectmode;
+
public:
- void init()
+ ModuleDenyChannels()
+ : antiredirectmode(this, "antiredirect")
+ , redirectmode(this, "redirect")
{
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- /* check for redirect validity and loops/chains */
- ConfigTagList tags = ServerInstance->Config->ConfTags("badchan");
+ GoodChannels goodchans;
+ ConfigTagList tags = ServerInstance->Config->ConfTags("goodchan");
+ for (ConfigIter iter = tags.first; iter != tags.second; ++iter)
+ {
+ ConfigTag* tag = iter->second;
+
+ // Ensure that we have the <goodchan:name> parameter.
+ const std::string name = tag->getString("name");
+ if (name.empty())
+ throw ModuleException("<goodchan:name> is a mandatory field, at " + tag->getTagLocation());
+
+ goodchans.push_back(name);
+ }
+
+ BadChannels badchans;
+ tags = ServerInstance->Config->ConfTags("badchan");
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- std::string name = i->second->getString("name");
- std::string redirect = i->second->getString("redirect");
+ ConfigTag* tag = i->second;
+
+ // Ensure that we have the <badchan:name> parameter.
+ const std::string name = tag->getString("name");
+ if (name.empty())
+ throw ModuleException("<badchan:name> is a mandatory field, at " + tag->getTagLocation());
+
+ // Ensure that we have the <badchan:reason> parameter.
+ const std::string reason = tag->getString("reason");
+ if (reason.empty())
+ throw ModuleException("<badchan:reason> is a mandatory field, at " + tag->getTagLocation());
+ const std::string redirect = tag->getString("redirect");
if (!redirect.empty())
{
+ // Ensure that <badchan:redirect> contains a channel name.
+ if (!ServerInstance->IsChannel(redirect))
+ throw ModuleException("<badchan:redirect> is not a valid channel name, at " + tag->getTagLocation());
- if (!ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- if (user)
- user->WriteServ("NOTICE %s :Invalid badchan redirect '%s'", user->nick.c_str(), redirect.c_str());
- throw ModuleException("Invalid badchan redirect, not a channel");
- }
-
- for (ConfigIter j = tags.first; j != tags.second; ++j)
- {
- if (InspIRCd::Match(redirect, j->second->getString("name")))
- {
- bool goodchan = false;
- ConfigTagList goodchans = ServerInstance->Config->ConfTags("goodchan");
- for (ConfigIter k = goodchans.first; k != goodchans.second; ++k)
- {
- if (InspIRCd::Match(redirect, k->second->getString("name")))
- goodchan = true;
- }
-
- if (!goodchan)
- {
- /* <badchan:redirect> is a badchan */
- if (user)
- user->WriteServ("NOTICE %s :Badchan %s redirects to badchan %s", user->nick.c_str(), name.c_str(), redirect.c_str());
- throw ModuleException("Badchan redirect loop");
- }
- }
- }
+ // We defer the rest of the validation of the redirect channel until we have
+ // finished parsing all of the badchans.
}
+
+ badchans.push_back(BadChannel(name, redirect, reason, tag->getBool("allowopers")));
}
- }
- virtual ~ModuleDenyChannels()
- {
+ // Now we have all of the badchan information recorded we can check that all redirect
+ // channels can actually be redirected to.
+ for (BadChannels::const_iterator i = badchans.begin(); i != badchans.end(); ++i)
+ {
+ const BadChannel& badchan = *i;
+
+ // If there is no redirect channel we have nothing to do.
+ if (badchan.redirect.empty())
+ continue;
+
+ // If the redirect channel is whitelisted then it is okay.
+ for (GoodChannels::const_iterator j = goodchans.begin(); j != goodchans.end(); ++j)
+ if (InspIRCd::Match(badchan.redirect, *j))
+ continue;
+
+ // If the redirect channel is not blacklisted then it is okay.
+ for (BadChannels::const_iterator j = badchans.begin(); j != badchans.end(); ++j)
+ if (InspIRCd::Match(badchan.redirect, j->name))
+ throw ModuleException("<badchan:redirect> cannot be a blacklisted channel name");
+ }
+
+ // The config file contained no errors so we can apply the new configuration.
+ badchannels.swap(badchans);
+ goodchannels.swap(goodchans);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements config tags which allow blocking of joins to channels", VF_VENDOR);
}
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- ConfigTagList tags = ServerInstance->Config->ConfTags("badchan");
- for (ConfigIter j = tags.first; j != tags.second; ++j)
+ for (BadChannels::const_iterator j = badchannels.begin(); j != badchannels.end(); ++j)
{
- if (InspIRCd::Match(cname, j->second->getString("name")))
- {
- if (IS_OPER(user) && j->second->getBool("allowopers"))
- {
+ const BadChannel& badchan = *j;
+
+ // If the channel does not match the current entry we have nothing else to do.
+ if (!InspIRCd::Match(cname, badchan.name))
+ continue;
+
+ // If the user is an oper and opers are allowed to enter this blacklisted channel
+ // then allow the join.
+ if (user->IsOper() && badchan.allowopers)
+ return MOD_RES_PASSTHRU;
+
+ // If the channel matches a whitelist then allow the join.
+ for (GoodChannels::const_iterator i = goodchannels.begin(); i != goodchannels.end(); ++i)
+ if (InspIRCd::Match(cname, *i))
return MOD_RES_PASSTHRU;
- }
- else
- {
- std::string reason = j->second->getString("reason");
- std::string redirect = j->second->getString("redirect");
-
- ConfigTagList goodchans = ServerInstance->Config->ConfTags("goodchan");
- for (ConfigIter i = goodchans.first; i != goodchans.second; ++i)
- {
- if (InspIRCd::Match(cname, i->second->getString("name")))
- {
- return MOD_RES_PASSTHRU;
- }
- }
-
- if (ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- /* simple way to avoid potential loops: don't redirect to +L channels */
- Channel *newchan = ServerInstance->FindChan(redirect);
- if ((!newchan) || (!(newchan->IsModeSet('L'))))
- {
- user->WriteNumeric(926, "%s %s :Channel %s is forbidden, redirecting to %s: %s",user->nick.c_str(),cname,cname,redirect.c_str(), reason.c_str());
- Channel::JoinUser(user,redirect.c_str(),false,"",false,ServerInstance->Time());
- return MOD_RES_DENY;
- }
- }
-
- user->WriteNumeric(926, "%s %s :Channel %s is forbidden: %s",user->nick.c_str(),cname,cname,reason.c_str());
- return MOD_RES_DENY;
- }
+
+ // If there is no redirect chan, the user has enabled the antiredirect mode, or
+ // the target channel redirects elsewhere we just tell the user and deny the join.
+ Channel* target = NULL;
+ if (badchan.redirect.empty() || user->IsModeSet(antiredirectmode)
+ || ((target = ServerInstance->FindChan(badchan.redirect)) && target->IsModeSet(redirectmode)))
+ {
+ user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is forbidden: %s",
+ cname.c_str(), badchan.reason.c_str()));
+ return MOD_RES_DENY;
}
+
+ // Redirect the user to the target channel.
+ user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is forbidden, redirecting to %s: %s",
+ cname.c_str(), badchan.redirect.c_str(), badchan.reason.c_str()));
+ Channel::JoinUser(user, badchan.redirect);
+ return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp
deleted file mode 100644
index 2b5de2bd6..000000000
--- a/src/modules/m_devoice.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2005, 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/*
- * DEVOICE module for InspIRCd
- * Syntax: /DEVOICE <#chan>
- */
-
-/* $ModDesc: Provides voiced users with the ability to devoice themselves. */
-
-#include "inspircd.h"
-
-/** Handle /DEVOICE
- */
-class CommandDevoice : public Command
-{
- public:
- CommandDevoice(Module* Creator) : Command(Creator,"DEVOICE", 1)
- {
- syntax = "<channel>";
- TRANSLATE2(TR_TEXT, TR_END);
- }
-
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
- {
- Channel* c = ServerInstance->FindChan(parameters[0]);
- if (c && c->HasUser(user))
- {
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back("-v");
- modes.push_back(user->nick);
-
- ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
- return CMD_SUCCESS;
- }
-
- return CMD_FAILURE;
- }
-};
-
-class ModuleDeVoice : public Module
-{
- CommandDevoice cmd;
- public:
- ModuleDeVoice() : cmd(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleDeVoice()
- {
- }
-
- virtual Version GetVersion()
- {
- return Version("Provides voiced users with the ability to devoice themselves.", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleDeVoice)
diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp
index 3dea080ce..89fecf696 100644
--- a/src/modules/m_dnsbl.cpp
+++ b/src/modules/m_dnsbl.cpp
@@ -23,8 +23,8 @@
#include "inspircd.h"
#include "xline.h"
-
-/* $ModDesc: Provides handling of DNS blacklists */
+#include "modules/dns.h"
+#include "modules/stats.h"
/* Class holding data for a single entry */
class DNSBLConfEntry : public refcountbase
@@ -35,18 +35,17 @@ class DNSBLConfEntry : public refcountbase
std::string name, ident, host, domain, reason;
EnumBanaction banaction;
EnumType type;
- long duration;
- int bitmask;
+ unsigned long duration;
+ unsigned int bitmask;
unsigned char records[256];
unsigned long stats_hits, stats_misses;
DNSBLConfEntry(): type(A_BITMASK),duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
- ~DNSBLConfEntry() { }
};
-/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
+/** Resolver for CGI:IRC hostnames encoded in ident/real name
*/
-class DNSBLResolver : public Resolver
+class DNSBLResolver : public DNS::Request
{
std::string theiruid;
LocalStringExt& nameExt;
@@ -55,175 +54,177 @@ class DNSBLResolver : public Resolver
public:
- DNSBLResolver(Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf, bool &cached)
- : Resolver(hostname, DNS_QUERY_A, cached, me), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
+ DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf)
+ : DNS::Request(mgr, me, hostname, DNS::QUERY_A, true), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
{
}
/* Note: This may be called multiple times for multiple A record results */
- virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE
{
/* Check the user still exists */
LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
- if (them)
+ if (!them)
+ return;
+
+ const DNS::ResourceRecord* const ans_record = r->FindAnswerOfType(DNS::QUERY_A);
+ if (!ans_record)
+ return;
+
+ // All replies should be in 127.0.0.0/8
+ if (ans_record->rdata.compare(0, 4, "127.") != 0)
+ {
+ ServerInstance->SNO->WriteGlobalSno('d', "DNSBL: %s returned address outside of acceptable subnet 127.0.0.0/8: %s", ConfEntry->domain.c_str(), ans_record->rdata.c_str());
+ ConfEntry->stats_misses++;
+ return;
+ }
+
+ int i = countExt.get(them);
+ if (i)
+ countExt.set(them, i - 1);
+
+ // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
+
+ unsigned int bitmask = 0, record = 0;
+ bool match = false;
+ in_addr resultip;
+
+ inet_pton(AF_INET, ans_record->rdata.c_str(), &resultip);
+
+ switch (ConfEntry->type)
+ {
+ case DNSBLConfEntry::A_BITMASK:
+ bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
+ bitmask &= ConfEntry->bitmask;
+ match = (bitmask != 0);
+ break;
+ case DNSBLConfEntry::A_RECORD:
+ record = resultip.s_addr >> 24; /* Last octet */
+ match = (ConfEntry->records[record] == 1);
+ break;
+ }
+
+ if (match)
{
- int i = countExt.get(them);
- if (i)
- countExt.set(them, i - 1);
- // All replies should be in 127.0.0.0/8
- if (result.compare(0, 4, "127.") == 0)
+ std::string reason = ConfEntry->reason;
+ std::string::size_type x = reason.find("%ip%");
+ while (x != std::string::npos)
{
- unsigned int bitmask = 0, record = 0;
- bool match = false;
- in_addr resultip;
+ reason.erase(x, 4);
+ reason.insert(x, them->GetIPString());
+ x = reason.find("%ip%");
+ }
- inet_aton(result.c_str(), &resultip);
+ ConfEntry->stats_hits++;
- switch (ConfEntry->type)
+ switch (ConfEntry->banaction)
+ {
+ case DNSBLConfEntry::I_KILL:
{
- case DNSBLConfEntry::A_BITMASK:
- // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
- bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
- bitmask &= ConfEntry->bitmask;
- match = (bitmask != 0);
- break;
- case DNSBLConfEntry::A_RECORD:
- record = resultip.s_addr >> 24; /* Last octet */
- match = (ConfEntry->records[record] == 1);
+ ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
break;
}
-
- if (match)
+ case DNSBLConfEntry::I_MARK:
{
- std::string reason = ConfEntry->reason;
- std::string::size_type x = reason.find("%ip%");
- while (x != std::string::npos)
+ if (!ConfEntry->ident.empty())
{
- reason.erase(x, 4);
- reason.insert(x, them->GetIPString());
- x = reason.find("%ip%");
+ them->WriteNotice("Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
+ them->ChangeIdent(ConfEntry->ident);
}
- ConfEntry->stats_hits++;
-
- switch (ConfEntry->banaction)
+ if (!ConfEntry->host.empty())
{
- case DNSBLConfEntry::I_KILL:
- {
- ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
- break;
- }
- case DNSBLConfEntry::I_MARK:
- {
- if (!ConfEntry->ident.empty())
- {
- them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
- them->ChangeIdent(ConfEntry->ident.c_str());
- }
-
- if (!ConfEntry->host.empty())
- {
- them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
- them->ChangeDisplayedHost(ConfEntry->host.c_str());
- }
-
- nameExt.set(them, ConfEntry->name);
- break;
- }
- case DNSBLConfEntry::I_KLINE:
- {
- KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- "*", them->GetIPString());
- if (ServerInstance->XLines->AddLine(kl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(kl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete kl;
- return;
- }
- break;
- }
- case DNSBLConfEntry::I_GLINE:
- {
- GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- "*", them->GetIPString());
- if (ServerInstance->XLines->AddLine(gl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(gl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete gl;
- return;
- }
- break;
- }
- case DNSBLConfEntry::I_ZLINE:
- {
- ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- them->GetIPString());
- if (ServerInstance->XLines->AddLine(zl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(zl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete zl;
- return;
- }
- break;
- }
- case DNSBLConfEntry::I_UNKNOWN:
- {
- break;
- }
- break;
+ them->WriteNotice("Your host has been set to " + ConfEntry->host + " because you matched " + reason);
+ them->ChangeDisplayedHost(ConfEntry->host);
}
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
+ nameExt.set(them, ConfEntry->name);
+ break;
}
- else
- ConfEntry->stats_misses++;
- }
- else
- {
- if (!result.empty())
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL: %s returned address outside of acceptable subnet 127.0.0.0/8: %s", ConfEntry->domain.c_str(), result.c_str());
- ConfEntry->stats_misses++;
+ case DNSBLConfEntry::I_KLINE:
+ {
+ KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ "*", them->GetIPString());
+ if (ServerInstance->XLines->AddLine(kl,NULL))
+ {
+ std::string timestr = InspIRCd::TimeString(kl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete kl;
+ return;
+ }
+ break;
+ }
+ case DNSBLConfEntry::I_GLINE:
+ {
+ GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ "*", them->GetIPString());
+ if (ServerInstance->XLines->AddLine(gl,NULL))
+ {
+ std::string timestr = InspIRCd::TimeString(gl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete gl;
+ return;
+ }
+ break;
+ }
+ case DNSBLConfEntry::I_ZLINE:
+ {
+ ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ them->GetIPString());
+ if (ServerInstance->XLines->AddLine(zl,NULL))
+ {
+ std::string timestr = InspIRCd::TimeString(zl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on %s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete zl;
+ return;
+ }
+ break;
+ }
+ case DNSBLConfEntry::I_UNKNOWN:
+ default:
+ break;
}
+
+ ServerInstance->SNO->WriteGlobalSno('d', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
}
+ else
+ ConfEntry->stats_misses++;
}
- virtual void OnError(ResolverError e, const std::string &errormessage)
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE
{
LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
- if (them)
- {
- int i = countExt.get(them);
- if (i)
- countExt.set(them, i - 1);
- }
- }
+ if (!them)
+ return;
- virtual ~DNSBLResolver()
- {
+ int i = countExt.get(them);
+ if (i)
+ countExt.set(them, i - 1);
+
+ if (q->error == DNS::ERROR_NO_RECORDS || q->error == DNS::ERROR_DOMAIN_NOT_FOUND)
+ ConfEntry->stats_misses++;
}
};
-class ModuleDNSBL : public Module
+class ModuleDNSBL : public Module, public Stats::EventListener
{
std::vector<reference<DNSBLConfEntry> > DNSBLConfEntries;
+ dynamic_reference<DNS::Manager> DNS;
LocalStringExt nameExt;
LocalIntExt countExt;
@@ -246,25 +247,27 @@ class ModuleDNSBL : public Module
return DNSBLConfEntry::I_UNKNOWN;
}
public:
- ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
+ ModuleDNSBL()
+ : Stats::EventListener(this)
+ , DNS(this, "DNS")
+ , nameExt("dnsbl_match", ExtensionItem::EXT_USER, this)
+ , countExt("dnsbl_pending", ExtensionItem::EXT_USER, this)
+ {
+ }
- void init()
+ void init() CXX11_OVERRIDE
{
- ReadConf();
- ServerInstance->Modules->AddService(nameExt);
- ServerInstance->Modules->AddService(countExt);
- Implementation eventlist[] = { I_OnRehash, I_OnSetUserIP, I_OnStats, I_OnSetConnectClass, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ServerInstance->SNO->EnableSnomask('d', "DNSBL");
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides handling of DNS blacklists", VF_VENDOR);
}
/** Fill our conf vector with data
*/
- void ReadConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
DNSBLConfEntries.clear();
@@ -280,10 +283,10 @@ class ModuleDNSBL : public Module
e->reason = tag->getString("reason");
e->domain = tag->getString("domain");
- if (tag->getString("type") == "bitmask")
+ if (stdalgo::string::equalsci(tag->getString("type"), "bitmask"))
{
e->type = DNSBLConfEntry::A_BITMASK;
- e->bitmask = tag->getInt("bitmask");
+ e->bitmask = tag->getUInt("bitmask", 0, 0, UINT_MAX);
}
else
{
@@ -296,7 +299,7 @@ class ModuleDNSBL : public Module
}
e->banaction = str2banaction(tag->getString("action"));
- e->duration = ServerInstance->Duration(tag->getString("duration", "60"));
+ e->duration = tag->getDuration("duration", 60, 1);
/* Use portparser for record replies */
@@ -304,34 +307,29 @@ class ModuleDNSBL : public Module
if ((e->bitmask <= 0) && (DNSBLConfEntry::A_BITMASK == e->type))
{
std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): invalid bitmask", location.c_str());
+ ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): invalid bitmask", location.c_str());
}
else if (e->name.empty())
{
std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid name", location.c_str());
+ ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): Invalid name", location.c_str());
}
else if (e->domain.empty())
{
std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid domain", location.c_str());
+ ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): Invalid domain", location.c_str());
}
else if (e->banaction == DNSBLConfEntry::I_UNKNOWN)
{
std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid banaction", location.c_str());
- }
- else if (e->duration <= 0)
- {
- std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid duration", location.c_str());
+ ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): Invalid banaction", location.c_str());
}
else
{
if (e->reason.empty())
{
std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): empty reason, using defaults", location.c_str());
+ ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): empty reason, using defaults", location.c_str());
e->reason = "Your IP has been blacklisted.";
}
@@ -341,14 +339,13 @@ class ModuleDNSBL : public Module
}
}
- void OnRehash(User* user)
+ void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
{
- ReadConf();
- }
+ if ((user->exempt) || !DNS)
+ return;
- void OnSetUserIP(LocalUser* user)
- {
- if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET))
+ // Clients can't be in a DNSBL if they aren't connected via IPv4 or IPv6.
+ if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
return;
if (user->MyClass)
@@ -357,40 +354,61 @@ class ModuleDNSBL : public Module
return;
}
else
- ServerInstance->Logs->Log("m_dnsbl", DEBUG, "User has no connect class in OnSetUserIP");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User has no connect class in OnSetUserIP");
- unsigned char a, b, c, d;
- char reversedipbuf[128];
std::string reversedip;
+ if (user->client_sa.family() == AF_INET)
+ {
+ unsigned int a, b, c, d;
+ d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
+ c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
+ b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
+ a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
- d = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
- c = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
- b = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
- a = (unsigned char) user->client_sa.in4.sin_addr.s_addr & 0xFF;
+ reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
+ }
+ else if (user->client_sa.family() == AF_INET6)
+ {
+ const unsigned char* ip = user->client_sa.in6.sin6_addr.s6_addr;
- snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
- reversedip = std::string(reversedipbuf);
+ std::string buf = BinToHex(ip, 16);
+ for (std::string::const_reverse_iterator it = buf.rbegin(); it != buf.rend(); ++it)
+ {
+ reversedip.push_back(*it);
+ reversedip.push_back('.');
+ }
+ }
+ else
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Reversed IP %s -> %s", user->GetIPString().c_str(), reversedip.c_str());
countExt.set(user, DNSBLConfEntries.size());
// For each DNSBL, we will run through this lookup
- unsigned int i = 0;
- while (i < DNSBLConfEntries.size())
+ for (unsigned i = 0; i < DNSBLConfEntries.size(); ++i)
{
// Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
std::string hostname = reversedip + "." + DNSBLConfEntries[i]->domain;
/* now we'd need to fire off lookups for `hostname'. */
- bool cached;
- DNSBLResolver *r = new DNSBLResolver(this, nameExt, countExt, hostname, user, DNSBLConfEntries[i], cached);
- ServerInstance->AddResolver(r, cached);
+ DNSBLResolver *r = new DNSBLResolver(*this->DNS, this, nameExt, countExt, hostname, user, DNSBLConfEntries[i]);
+ try
+ {
+ this->DNS->Process(r);
+ }
+ catch (DNS::Exception &ex)
+ {
+ delete r;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
+ }
+
if (user->quitting)
break;
- i++;
}
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
std::string dnsbl;
if (!myclass->config->readString("dnsbl", dnsbl))
@@ -401,17 +419,17 @@ class ModuleDNSBL : public Module
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
-
- ModResult OnCheckReady(LocalUser *user)
+
+ ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
{
if (countExt.get(user))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'd')
+ if (stats.GetSymbol() != 'd')
return MOD_RES_PASSTHRU;
unsigned long total_hits = 0, total_misses = 0;
@@ -421,12 +439,12 @@ class ModuleDNSBL : public Module
total_hits += (*i)->stats_hits;
total_misses += (*i)->stats_misses;
- results.push_back(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
+ stats.AddRow(304, "DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
}
- results.push_back(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
- results.push_back(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
+ stats.AddRow(304, "DNSBLSTATS Total hits: " + ConvToStr(total_hits));
+ stats.AddRow(304, "DNSBLSTATS Total misses: " + ConvToStr(total_misses));
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_exemptchanops.cpp b/src/modules/m_exemptchanops.cpp
index 8d6d65af2..b1d7b35df 100644
--- a/src/modules/m_exemptchanops.cpp
+++ b/src/modules/m_exemptchanops.cpp
@@ -18,9 +18,8 @@
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides the ability to allow channel operators to be exempt from certain modes. */
+#include "listmode.h"
+#include "modules/exemption.h"
/** Handles channel mode +X
*/
@@ -29,123 +28,112 @@ class ExemptChanOps : public ListModeBase
public:
ExemptChanOps(Module* Creator) : ListModeBase(Creator, "exemptchanops", 'X', "End of channel exemptchanops list", 954, 953, false, "exemptchanops") { }
- bool ValidateParam(User* user, Channel* chan, std::string &word)
- {
- // TODO actually make sure there's a prop for this
- if ((word.length() > 35) || (word.empty()))
+ bool ValidateParam(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE {
+ std::string::size_type p = word.find(':');
+ if (p == std::string::npos)
+ {
+ user->WriteNumeric(Numerics::InvalidModeParameter(chan, this, word, "Invalid exemptchanops entry, format is <restriction>:<prefix>"));
+ return false;
+ }
+
+ std::string restriction(word, 0, p);
+ // If there is a '-' in the restriction string ignore it and everything after it
+ // to support "auditorium-vis" and "auditorium-see" in m_auditorium
+ p = restriction.find('-');
+ if (p != std::string::npos)
+ restriction.erase(p);
+
+ if (!ServerInstance->Modes->FindMode(restriction, MODETYPE_CHANNEL))
{
- user->WriteNumeric(955, "%s %s %s :word is too %s for exemptchanops list",user->nick.c_str(), chan->name.c_str(), word.c_str(), (word.empty() ? "short" : "long"));
+ user->WriteNumeric(Numerics::InvalidModeParameter(chan, this, word, "Unknown restriction"));
return false;
}
return true;
}
- bool TellListTooLong(User* user, Channel* chan, std::string &word)
+ void TellListTooLong(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- user->WriteNumeric(959, "%s %s %s :Channel exemptchanops list is full", user->nick.c_str(), chan->name.c_str(), word.c_str());
- return true;
+ user->WriteNumeric(959, chan->name, word, "Channel exemptchanops list is full");
}
- void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
+ void TellAlreadyOnList(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- user->WriteNumeric(957, "%s %s :The word %s is already on the exemptchanops list",user->nick.c_str(), chan->name.c_str(), word.c_str());
+ user->WriteNumeric(957, chan->name, InspIRCd::Format("The word %s is already on the exemptchanops list", word.c_str()));
}
- void TellNotSet(User* user, Channel* chan, std::string &word)
+ void TellNotSet(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
{
- user->WriteNumeric(958, "%s %s :No such exemptchanops word is set",user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(958, chan->name, "No such exemptchanops word is set");
}
};
-class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std::string&>
+class ExemptHandler : public CheckExemption::EventListener
{
public:
ExemptChanOps ec;
- ExemptHandler(Module* me) : ec(me) {}
-
- ModeHandler* FindMode(const std::string& mid)
+ ExemptHandler(Module* me)
+ : CheckExemption::EventListener(me)
+ , ec(me)
+ {
+ }
+
+ PrefixMode* FindMode(const std::string& mid)
{
if (mid.length() == 1)
- return ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL);
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->name == mid)
- return mh;
- }
- return NULL;
+ return ServerInstance->Modes->FindPrefixMode(mid[0]);
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mid, MODETYPE_CHANNEL);
+ return mh ? mh->IsPrefixMode() : NULL;
}
- ModResult Call(User* user, Channel* chan, const std::string& restriction)
+ ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) CXX11_OVERRIDE
{
unsigned int mypfx = chan->GetPrefixValue(user);
std::string minmode;
- modelist* list = ec.extItem.get(chan);
+ ListModeBase::ModeList* list = ec.GetList(chan);
if (list)
{
- for (modelist::iterator i = list->begin(); i != list->end(); ++i)
+ for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); ++i)
{
std::string::size_type pos = (*i).mask.find(':');
if (pos == std::string::npos)
continue;
- if ((*i).mask.substr(0,pos) == restriction)
- minmode = (*i).mask.substr(pos + 1);
+ if (!i->mask.compare(0, pos, restriction))
+ minmode.assign(i->mask, pos + 1, std::string::npos);
}
}
- ModeHandler* mh = FindMode(minmode);
+ PrefixMode* mh = FindMode(minmode);
if (mh && mypfx >= mh->GetPrefixRank())
return MOD_RES_ALLOW;
if (mh || minmode == "*")
return MOD_RES_DENY;
- return ServerInstance->HandleOnCheckExemption.Call(user, chan, restriction);
+ return MOD_RES_PASSTHRU;
}
};
class ModuleExemptChanOps : public Module
{
- std::string defaults;
ExemptHandler eh;
public:
-
ModuleExemptChanOps() : eh(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(eh.ec);
- Implementation eventlist[] = { I_OnRehash, I_OnSyncChannel };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- ServerInstance->OnCheckExemption = &eh;
-
- OnRehash(NULL);
- }
-
- ~ModuleExemptChanOps()
- {
- ServerInstance->OnCheckExemption = &ServerInstance->HandleOnCheckExemption;
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ability to allow channel operators to be exempt from certain modes.",VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
eh.ec.DoRehash();
}
-
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- eh.ec.DoSyncChannel(chan, proto, opaque);
- }
};
MODULE_INIT(ModuleExemptChanOps)
diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp
index 4090f5600..2d0657c99 100644
--- a/src/modules/m_filter.cpp
+++ b/src/modules/m_filter.cpp
@@ -22,11 +22,10 @@
#include "inspircd.h"
#include "xline.h"
-#include "m_regex.h"
-
-/* $ModDesc: Text (spam) filtering */
-
-class ModuleFilter;
+#include "modules/regex.h"
+#include "modules/server.h"
+#include "modules/shun.h"
+#include "modules/stats.h"
enum FilterFlags
{
@@ -42,16 +41,18 @@ enum FilterAction
FA_BLOCK,
FA_SILENT,
FA_KILL,
+ FA_SHUN,
FA_NONE
};
class FilterResult
{
public:
+ Regex* regex;
std::string freeform;
std::string reason;
FilterAction action;
- long gline_time;
+ long duration;
bool flag_no_opers;
bool flag_part_message;
@@ -60,9 +61,15 @@ class FilterResult
bool flag_notice;
bool flag_strip_color;
- FilterResult(const std::string& free, const std::string& rea, FilterAction act, long gt, const std::string& fla) :
- freeform(free), reason(rea), action(act), gline_time(gt)
+ FilterResult(dynamic_reference<RegexFactory>& RegexEngine, const std::string& free, const std::string& rea, FilterAction act, long gt, const std::string& fla)
+ : freeform(free)
+ , reason(rea)
+ , action(act)
+ , duration(gt)
{
+ if (!RegexEngine)
+ throw ModuleException("Regex module implementing '"+RegexEngine.GetProvider()+"' is not loaded!");
+ regex = RegexEngine->Create(free);
this->FillFlags(fla);
}
@@ -144,28 +151,22 @@ class CommandFilter : public Command
: Command(f, "FILTER", 1, 5)
{
flags_needed = 'o';
- this->syntax = "<filter-definition> <action> <flags> [<gline-duration>] :<reason>";
+ this->syntax = "<filter-definition> <action> <flags> [<duration>] :<reason>";
}
- CmdResult Handle(const std::vector<std::string>&, User*);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
};
-class ImplFilter : public FilterResult
+class ModuleFilter : public Module, public ServerEventListener, public Stats::EventListener
{
- public:
- Regex* regex;
-
- ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs);
-};
+ typedef insp::flat_set<std::string, irc::insensitive_swo> ExemptTargetSet;
-
-class ModuleFilter : public Module
-{
bool initing;
+ bool notifyuser;
RegexFactory* factory;
void FreeFilters();
@@ -173,35 +174,38 @@ class ModuleFilter : public Module
CommandFilter filtcommand;
dynamic_reference<RegexFactory> RegexEngine;
- std::vector<ImplFilter> filters;
+ std::vector<FilterResult> filters;
int flags;
- std::set<std::string> exemptfromfilter; // List of channel names excluded from filtering.
+ // List of channel names excluded from filtering.
+ ExemptTargetSet exemptedchans;
+
+ // List of target nicknames excluded from filtering.
+ ExemptTargetSet exemptednicks;
ModuleFilter();
- void init();
- CullResult cull();
- ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
+ void init() CXX11_OVERRIDE;
+ CullResult cull() CXX11_OVERRIDE;
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE;
FilterResult* FilterMatch(User* user, const std::string &text, int flags);
bool DeleteFilter(const std::string &freeform);
std::pair<bool, std::string> AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flags);
- ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
- void OnRehash(User* user);
- Version GetVersion();
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+ Version GetVersion() CXX11_OVERRIDE;
std::string EncodeFilter(FilterResult* filter);
FilterResult DecodeFilter(const std::string &data);
- void OnSyncNetwork(Module* proto, void* opaque);
- void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata);
- ModResult OnStats(char symbol, User* user, string_list &results);
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line);
- void OnUnloadModule(Module* mod);
+ void OnSyncNetwork(ProtocolInterface::Server& server) CXX11_OVERRIDE;
+ void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata) CXX11_OVERRIDE;
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE;
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
bool AppliesToMe(User* user, FilterResult* filter, int flags);
void ReadFilters();
static bool StringToFilterAction(const std::string& str, FilterAction& fa);
static std::string FilterActionToString(FilterAction fa);
};
-CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User *user)
+CmdResult CommandFilter::Handle(User* user, const Params& parameters)
{
if (parameters.size() == 1)
{
@@ -209,13 +213,13 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
Module *me = creator;
if (static_cast<ModuleFilter *>(me)->DeleteFilter(parameters[0]))
{
- user->WriteServ("NOTICE %s :*** Removed filter '%s'", user->nick.c_str(), parameters[0].c_str());
- ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" removed filter '"+parameters[0]+"'");
+ user->WriteNotice("*** Removed filter '" + parameters[0] + "'");
+ ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'f' : 'F', "FILTER: "+user->nick+" removed filter '"+parameters[0]+"'");
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Filter '%s' not found in list, try /stats s.", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** Filter '" + parameters[0] + "' not found in list, try /stats s.");
return CMD_FAILURE;
}
}
@@ -232,20 +236,23 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
if (!ModuleFilter::StringToFilterAction(parameters[1], type))
{
- user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick.c_str(), parameters[1].c_str());
+ if (ServerInstance->XLines->GetFactory("SHUN"))
+ user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'none', 'block', 'silent', 'kill', and 'shun'.");
+ else
+ user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'none', 'block', 'silent', and 'kill'.");
return CMD_FAILURE;
}
- if (type == FA_GLINE)
+ if (type == FA_GLINE || type == FA_SHUN)
{
if (parameters.size() >= 5)
{
- duration = ServerInstance->Duration(parameters[3]);
+ duration = InspIRCd::Duration(parameters[3]);
reasonindex = 4;
}
else
{
- user->WriteServ("NOTICE %s :*** Not enough parameters: When setting a gline type filter, a gline duration must be specified as the third parameter.", user->nick.c_str());
+ user->WriteNotice("*** Not enough parameters: When setting a gline or shun type filter, a duration must be specified as the third parameter.");
return CMD_FAILURE;
}
}
@@ -258,23 +265,23 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
std::pair<bool, std::string> result = static_cast<ModuleFilter *>(me)->AddFilter(freeform, type, parameters[reasonindex], duration, flags);
if (result.first)
{
- user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick.c_str(), freeform.c_str(),
- parameters[1].c_str(), (duration ? ", duration " : ""), (duration ? parameters[3].c_str() : ""),
- flags.c_str(), parameters[reasonindex].c_str());
+ user->WriteNotice("*** Added filter '" + freeform + "', type '" + parameters[1] + "'" +
+ (duration ? ", duration " + parameters[3] : "") + ", flags '" + flags + "', reason: '" +
+ parameters[reasonindex] + "'");
- ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" added filter '"+freeform+"', type '"+parameters[1]+"', "+(duration ? "duration "+parameters[3]+", " : "")+"flags '"+flags+"', reason: "+parameters[reasonindex]);
+ ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'f' : 'F', "FILTER: "+user->nick+" added filter '"+freeform+"', type '"+parameters[1]+"', "+(duration ? "duration "+parameters[3]+", " : "")+"flags '"+flags+"', reason: "+parameters[reasonindex]);
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick.c_str(), freeform.c_str(), result.second.c_str());
+ user->WriteNotice("*** Filter '" + freeform + "' could not be added: " + result.second);
return CMD_FAILURE;
}
}
else
{
- user->WriteServ("NOTICE %s :*** Not enough parameters.", user->nick.c_str());
+ user->WriteNotice("*** Not enough parameters.");
return CMD_FAILURE;
}
@@ -283,7 +290,7 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
{
- if ((filter->flag_no_opers) && IS_OPER(user))
+ if ((filter->flag_no_opers) && user->IsOper())
return false;
if ((iflags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
return false;
@@ -297,16 +304,17 @@ bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
}
ModuleFilter::ModuleFilter()
- : initing(true), filtcommand(this), RegexEngine(this, "regex")
+ : ServerEventListener(this)
+ , Stats::EventListener(this)
+ , initing(true)
+ , filtcommand(this)
+ , RegexEngine(this, "regex")
{
}
void ModuleFilter::init()
{
- ServerInstance->Modules->AddService(filtcommand);
- Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncNetwork, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+ ServerInstance->SNO->EnableSnomask('f', "FILTER");
}
CullResult ModuleFilter::cull()
@@ -317,69 +325,90 @@ CullResult ModuleFilter::cull()
void ModuleFilter::FreeFilters()
{
- for (std::vector<ImplFilter>::const_iterator i = filters.begin(); i != filters.end(); ++i)
+ for (std::vector<FilterResult>::const_iterator i = filters.begin(); i != filters.end(); ++i)
delete i->regex;
filters.clear();
}
-ModResult ModuleFilter::OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ModResult ModuleFilter::OnUserPreMessage(User* user, const MessageTarget& msgtarget, MessageDetails& details)
{
+ // Leave remote users and servers alone
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- flags = FLAG_PRIVMSG;
- return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
-}
-
-ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-{
- /* Leave ulines alone */
- if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
- return MOD_RES_PASSTHRU;
-
- if (!flags)
- flags = FLAG_NOTICE;
+ flags = (details.type == MSG_PRIVMSG) ? FLAG_PRIVMSG : FLAG_NOTICE;
- FilterResult* f = this->FilterMatch(user, text, flags);
+ FilterResult* f = this->FilterMatch(user, details.text, flags);
if (f)
{
std::string target;
- if (target_type == TYPE_USER)
+ if (msgtarget.type == MessageTarget::TYPE_USER)
{
- User* t = (User*)dest;
+ User* t = msgtarget.Get<User>();
+ // Check if the target nick is exempted, if yes, ignore this message
+ if (exemptednicks.count(t->nick))
+ return MOD_RES_PASSTHRU;
+
target = t->nick;
}
- else if (target_type == TYPE_CHANNEL)
+ else if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
{
- Channel* t = (Channel*)dest;
- if (exemptfromfilter.find(t->name) != exemptfromfilter.end())
+ Channel* t = msgtarget.Get<Channel>();
+ if (exemptedchans.count(t->name))
return MOD_RES_PASSTHRU;
target = t->name;
}
if (f->action == FA_BLOCK)
{
- ServerInstance->SNO->WriteGlobalSno('a', "FILTER: "+user->nick+" had their message filtered, target was "+target+": "+f->reason);
- if (target_type == TYPE_CHANNEL)
- user->WriteNumeric(404, "%s %s :Message to channel blocked and opers notified (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s had their message to %s filtered as it matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (notifyuser)
+ {
+ if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked and opers notified (%s)", f->reason.c_str()));
+ else
+ user->WriteNotice("Your message to "+target+" was blocked and opers notified: "+f->reason);
+ }
else
- user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked and opers notified: "+f->reason);
+ details.echo_original = true;
}
else if (f->action == FA_SILENT)
{
- if (target_type == TYPE_CHANNEL)
- user->WriteNumeric(404, "%s %s :Message to channel blocked (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+ if (notifyuser)
+ {
+ if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked (%s)", f->reason.c_str()));
+ else
+ user->WriteNotice("Your message to "+target+" was blocked: "+f->reason);
+ }
else
- user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked: "+f->reason);
+ details.echo_original = true;
}
else if (f->action == FA_KILL)
{
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was killed because their message to %s matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
}
+ else if (f->action == FA_SHUN && (ServerInstance->XLines->GetFactory("SHUN")))
+ {
+ Shun* sh = new Shun(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was shunned because their message to %s matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (ServerInstance->XLines->AddLine(sh, NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete sh;
+ }
else if (f->action == FA_GLINE)
{
- GLine* gl = new GLine(ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ GLine* gl = new GLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was glined because their message to %s matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
if (ServerInstance->XLines->AddLine(gl,NULL))
{
ServerInstance->XLines->ApplyLines();
@@ -388,15 +417,15 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s
delete gl;
}
- ServerInstance->Logs->Log("FILTER",DEFAULT,"FILTER: "+ user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
-ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ModResult ModuleFilter::OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated)
{
- if (validated && IS_LOCAL(user))
+ if (validated)
{
flags = 0;
bool parting;
@@ -416,7 +445,7 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
if (parameters.size() < 2)
return MOD_RES_PASSTHRU;
- if (exemptfromfilter.find(parameters[0]) != exemptfromfilter.end())
+ if (exemptedchans.count(parameters[0]))
return MOD_RES_PASSTHRU;
parting = true;
@@ -446,13 +475,16 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
/* Are they parting, if so, kill is applicable */
if ((parting) && (f->action == FA_KILL))
{
- user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick.c_str(), f->reason.c_str());
+ user->WriteNotice("*** Your PART message was filtered: " + f->reason);
ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
}
if (f->action == FA_GLINE)
{
/* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */
- GLine* gl = new GLine(ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ GLine* gl = new GLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was glined because their %s message matched %s (%s)",
+ user->nick.c_str(), command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
if (ServerInstance->XLines->AddLine(gl,NULL))
{
ServerInstance->XLines->ApplyLines();
@@ -460,24 +492,49 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
else
delete gl;
}
+ else if (f->action == FA_SHUN && (ServerInstance->XLines->GetFactory("SHUN")))
+ {
+ /* Note: We shun *!*@IP so that if their host doesnt resolve the shun still applies. */
+ Shun* sh = new Shun(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was shunned because their %s message matched %s (%s)",
+ user->nick.c_str(), command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (ServerInstance->XLines->AddLine(sh, NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete sh;
+ }
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
-void ModuleFilter::OnRehash(User* user)
+void ModuleFilter::ReadConfig(ConfigStatus& status)
{
ConfigTagList tags = ServerInstance->Config->ConfTags("exemptfromfilter");
- exemptfromfilter.clear();
+ exemptedchans.clear();
+ exemptednicks.clear();
+
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- std::string chan = i->second->getString("channel");
- if (!chan.empty())
- exemptfromfilter.insert(chan);
+ ConfigTag* tag = i->second;
+
+ // If "target" is not found, try the old "channel" key to keep compatibility with 2.0 configs
+ const std::string target = tag->getString("target", tag->getString("channel"));
+ if (!target.empty())
+ {
+ if (target[0] == '#')
+ exemptedchans.insert(target);
+ else
+ exemptednicks.insert(target);
+ }
}
- std::string newrxengine = ServerInstance->Config->ConfValue("filteropts")->getString("engine");
+ ConfigTag* tag = ServerInstance->Config->ConfValue("filteropts");
+ std::string newrxengine = tag->getString("engine");
+ notifyuser = tag->getBool("notifyuser", true);
factory = RegexEngine ? (RegexEngine.operator->()) : NULL;
@@ -489,9 +546,9 @@ void ModuleFilter::OnRehash(User* user)
if (!RegexEngine)
{
if (newrxengine.empty())
- ServerInstance->SNO->WriteGlobalSno('a', "WARNING: No regex engine loaded - Filter functionality disabled until this is corrected.");
+ ServerInstance->SNO->WriteGlobalSno('f', "WARNING: No regex engine loaded - Filter functionality disabled until this is corrected.");
else
- ServerInstance->SNO->WriteGlobalSno('a', "WARNING: Regex engine '%s' is not loaded - Filter functionality disabled until this is corrected.", newrxengine.c_str());
+ ServerInstance->SNO->WriteGlobalSno('f', "WARNING: Regex engine '%s' is not loaded - Filter functionality disabled until this is corrected.", newrxengine.c_str());
initing = false;
FreeFilters();
@@ -500,7 +557,7 @@ void ModuleFilter::OnRehash(User* user)
if ((!initing) && (RegexEngine.operator->() != factory))
{
- ServerInstance->SNO->WriteGlobalSno('a', "Dumping all filters due to regex engine change");
+ ServerInstance->SNO->WriteGlobalSno('f', "Dumping all filters due to regex engine change");
FreeFilters();
}
@@ -523,7 +580,7 @@ std::string ModuleFilter::EncodeFilter(FilterResult* filter)
if (*n == ' ')
*n = '\7';
- stream << x << " " << FilterActionToString(filter->action) << " " << filter->GetFlags() << " " << filter->gline_time << " :" << filter->reason;
+ stream << x << " " << FilterActionToString(filter->action) << " " << filter->GetFlags() << " " << filter->duration << " :" << filter->reason;
return stream.str();
}
@@ -532,19 +589,22 @@ FilterResult ModuleFilter::DecodeFilter(const std::string &data)
std::string filteraction;
FilterResult res;
irc::tokenstream tokens(data);
- tokens.GetToken(res.freeform);
- tokens.GetToken(filteraction);
+ tokens.GetMiddle(res.freeform);
+ tokens.GetMiddle(filteraction);
if (!StringToFilterAction(filteraction, res.action))
throw ModuleException("Invalid action: " + filteraction);
std::string filterflags;
- tokens.GetToken(filterflags);
+ tokens.GetMiddle(filterflags);
char c = res.FillFlags(filterflags);
if (c != 0)
throw ModuleException("Invalid flag: '" + std::string(1, c) + "'");
- tokens.GetToken(res.gline_time);
- tokens.GetToken(res.reason);
+ std::string duration;
+ tokens.GetMiddle(duration);
+ res.duration = ConvToInt(duration);
+
+ tokens.GetTrailing(res.reason);
/* Hax to allow spaces in the freeform without changing the design of the irc protocol */
for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
@@ -554,11 +614,11 @@ FilterResult ModuleFilter::DecodeFilter(const std::string &data)
return res;
}
-void ModuleFilter::OnSyncNetwork(Module* proto, void* opaque)
+void ModuleFilter::OnSyncNetwork(ProtocolInterface::Server& server)
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); ++i)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
{
- proto->ProtoSendMetaData(opaque, NULL, "filter", EncodeFilter(&(*i)));
+ server.SendMetaData("filter", EncodeFilter(&(*i)));
}
}
@@ -569,31 +629,23 @@ void ModuleFilter::OnDecodeMetaData(Extensible* target, const std::string &extna
try
{
FilterResult data = DecodeFilter(extdata);
- this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.GetFlags());
+ this->AddFilter(data.freeform, data.action, data.reason, data.duration, data.GetFlags());
}
catch (ModuleException& e)
{
- ServerInstance->Logs->Log("m_filter", DEBUG, "Error when unserializing filter: " + std::string(e.GetReason()));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error when unserializing filter: " + e.GetReason());
}
}
}
-ImplFilter::ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs)
- : FilterResult(pat, rea, act, glinetime, flgs)
-{
- if (!mymodule->RegexEngine)
- throw ModuleException("Regex module implementing '"+mymodule->RegexEngine.GetProvider()+"' is not loaded!");
- regex = mymodule->RegexEngine->Create(pat);
-}
-
FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int flgs)
{
static std::string stripped_text;
stripped_text.clear();
- for (std::vector<ImplFilter>::iterator index = filters.begin(); index != filters.end(); index++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
{
- FilterResult* filter = dynamic_cast<FilterResult*>(&(*index));
+ FilterResult* filter = &*i;
/* Skip ones that dont apply to us */
if (!AppliesToMe(user, filter, flgs))
@@ -605,20 +657,15 @@ FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int
InspIRCd::StripColor(stripped_text);
}
- //ServerInstance->Logs->Log("m_filter", DEBUG, "Match '%s' against '%s'", text.c_str(), index->freeform.c_str());
- if (index->regex->Matches(filter->flag_strip_color ? stripped_text : text))
- {
- //ServerInstance->Logs->Log("m_filter", DEBUG, "MATCH");
- return &*index;
- }
- //ServerInstance->Logs->Log("m_filter", DEBUG, "NO MATCH");
+ if (filter->regex->Matches(filter->flag_strip_color ? stripped_text : text))
+ return filter;
}
return NULL;
}
bool ModuleFilter::DeleteFilter(const std::string &freeform)
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
{
if (i->freeform == freeform)
{
@@ -632,7 +679,7 @@ bool ModuleFilter::DeleteFilter(const std::string &freeform)
std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flgs)
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
{
if (i->freeform == freeform)
{
@@ -642,11 +689,11 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform
try
{
- filters.push_back(ImplFilter(this, reason, type, duration, freeform, flgs));
+ filters.push_back(FilterResult(RegexEngine, freeform, reason, type, duration, flgs));
}
catch (ModuleException &e)
{
- ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason().c_str());
return std::make_pair(false, e.GetReason());
}
return std::make_pair(true, "");
@@ -654,17 +701,17 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform
bool ModuleFilter::StringToFilterAction(const std::string& str, FilterAction& fa)
{
- irc::string s(str.c_str());
-
- if (s == "gline")
+ if (stdalgo::string::equalsci(str, "gline"))
fa = FA_GLINE;
- else if (s == "block")
+ else if (stdalgo::string::equalsci(str, "block"))
fa = FA_BLOCK;
- else if (s == "silent")
+ else if (stdalgo::string::equalsci(str, "silent"))
fa = FA_SILENT;
- else if (s == "kill")
+ else if (stdalgo::string::equalsci(str, "kill"))
fa = FA_KILL;
- else if (s == "none")
+ else if (stdalgo::string::equalsci(str, "shun") && (ServerInstance->XLines->GetFactory("SHUN")))
+ fa = FA_SHUN;
+ else if (stdalgo::string::equalsci(str, "none"))
fa = FA_NONE;
else
return false;
@@ -680,6 +727,7 @@ std::string ModuleFilter::FilterActionToString(FilterAction fa)
case FA_BLOCK: return "block";
case FA_SILENT: return "silent";
case FA_KILL: return "kill";
+ case FA_SHUN: return "shun";
default: return "none";
}
}
@@ -695,7 +743,7 @@ void ModuleFilter::ReadFilters()
std::string reason = i->second->getString("reason");
std::string action = i->second->getString("action");
std::string flgs = i->second->getString("flags");
- long gline_time = ServerInstance->Duration(i->second->getString("duration"));
+ unsigned long duration = i->second->getDuration("duration", 10*60, 1);
if (flgs.empty())
flgs = "*";
@@ -705,27 +753,31 @@ void ModuleFilter::ReadFilters()
try
{
- filters.push_back(ImplFilter(this, reason, fa, gline_time, pattern, flgs));
- ServerInstance->Logs->Log("m_filter", DEFAULT, "Regular expression %s loaded.", pattern.c_str());
+ filters.push_back(FilterResult(RegexEngine, pattern, reason, fa, duration, flgs));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Regular expression %s loaded.", pattern.c_str());
}
catch (ModuleException &e)
{
- ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason().c_str());
}
}
}
-ModResult ModuleFilter::OnStats(char symbol, User* user, string_list &results)
+ModResult ModuleFilter::OnStats(Stats::Context& stats)
{
- if (symbol == 's')
+ if (stats.GetSymbol() == 's')
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ stats.AddRow(223, RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->duration)+" :"+i->reason);
+ }
+ for (ExemptTargetSet::const_iterator i = exemptedchans.begin(); i != exemptedchans.end(); ++i)
{
- results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+ stats.AddRow(223, "EXEMPT "+(*i));
}
- for (std::set<std::string>::iterator i = exemptfromfilter.begin(); i != exemptfromfilter.end(); ++i)
+ for (ExemptTargetSet::const_iterator i = exemptednicks.begin(); i != exemptednicks.end(); ++i)
{
- results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :EXEMPT "+(*i));
+ stats.AddRow(223, "EXEMPT "+(*i));
}
}
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_flashpolicyd.cpp b/src/modules/m_flashpolicyd.cpp
new file mode 100644
index 000000000..d7f9a793b
--- /dev/null
+++ b/src/modules/m_flashpolicyd.cpp
@@ -0,0 +1,165 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class FlashPDSocket;
+
+namespace
+{
+ insp::intrusive_list<FlashPDSocket> sockets;
+ std::string policy_reply;
+ const std::string expected_request("<policy-file-request/>\0", 23);
+}
+
+class FlashPDSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<FlashPDSocket>
+{
+ /** True if this object is in the cull list
+ */
+ bool waitingcull;
+
+ bool Tick(time_t currtime) CXX11_OVERRIDE
+ {
+ AddToCull();
+ return false;
+ }
+
+ public:
+ FlashPDSocket(int newfd, unsigned int timeoutsec)
+ : BufferedSocket(newfd)
+ , Timer(timeoutsec)
+ , waitingcull(false)
+ {
+ ServerInstance->Timers.AddTimer(this);
+ }
+
+ ~FlashPDSocket()
+ {
+ sockets.erase(this);
+ }
+
+ void OnError(BufferedSocketError) CXX11_OVERRIDE
+ {
+ AddToCull();
+ }
+
+ void OnDataReady() CXX11_OVERRIDE
+ {
+ if (recvq == expected_request)
+ WriteData(policy_reply);
+ AddToCull();
+ }
+
+ void AddToCull()
+ {
+ if (waitingcull)
+ return;
+
+ waitingcull = true;
+ Close();
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+};
+
+class ModuleFlashPD : public Module
+{
+ unsigned int timeout;
+
+ public:
+ ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "flashpolicyd"))
+ return MOD_RES_PASSTHRU;
+
+ if (policy_reply.empty())
+ return MOD_RES_DENY;
+
+ sockets.push_front(new FlashPDSocket(nfd, timeout));
+ return MOD_RES_ALLOW;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("flashpolicyd");
+ timeout = tag->getDuration("timeout", 5, 1);
+ std::string file = tag->getString("file");
+
+ if (!file.empty())
+ {
+ try
+ {
+ FileReader reader(file);
+ policy_reply = reader.GetString();
+ }
+ catch (CoreException&)
+ {
+ const std::string error_message = "A file was specified for FlashPD, but it could not be loaded.";
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, error_message);
+ ServerInstance->SNO->WriteGlobalSno('a', error_message);
+ policy_reply.clear();
+ }
+ return;
+ }
+
+ // A file was not specified. Set the default setting.
+ // We allow access to all client ports by default
+ std::string to_ports;
+ for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+ {
+ ListenSocket* ls = *i;
+ if (!stdalgo::string::equalsci(ls->bind_tag->getString("type", "clients"), "clients") || !ls->bind_tag->getString("ssl").empty())
+ continue;
+
+ to_ports.append(ConvToStr(ls->bind_sa.port())).push_back(',');
+ }
+
+ if (to_ports.empty())
+ {
+ policy_reply.clear();
+ return;
+ }
+
+ to_ports.erase(to_ports.size() - 1);
+
+ policy_reply =
+"<?xml version=\"1.0\"?>\
+<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\
+<cross-domain-policy>\
+<site-control permitted-cross-domain-policies=\"master-only\"/>\
+<allow-access-from domain=\"*\" to-ports=\"" + to_ports + "\" />\
+</cross-domain-policy>";
+ }
+
+ CullResult cull() CXX11_OVERRIDE
+ {
+ for (insp::intrusive_list<FlashPDSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+ {
+ FlashPDSocket* sock = *i;
+ sock->AddToCull();
+ }
+ return Module::cull();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Flash Policy Daemon. Allows Flash IRC clients to connect", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleFlashPD)
diff --git a/src/modules/m_gecosban.cpp b/src/modules/m_gecosban.cpp
index 1497c1b87..ae399a81f 100644
--- a/src/modules/m_gecosban.cpp
+++ b/src/modules/m_gecosban.cpp
@@ -19,39 +19,27 @@
#include "inspircd.h"
-/* $ModDesc: Implements extban +b r: - realname (gecos) bans */
-
class ModuleGecosBan : public Module
{
public:
- void init()
- {
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleGecosBan()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Extban 'r' - realname (gecos) ban", VF_OPTCOMMON|VF_VENDOR);
+ return Version("Extban 'r' - real name ban", VF_OPTCOMMON|VF_VENDOR);
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'r') && (mask[1] == ':'))
{
- if (InspIRCd::Match(user->fullname, mask.substr(2)))
+ if (InspIRCd::Match(user->GetRealName(), mask.substr(2)))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('r');
+ tokens["EXTBAN"].push_back('r');
}
};
diff --git a/src/modules/m_globalload.cpp b/src/modules/m_globalload.cpp
index aed65045f..c0ce025bd 100644
--- a/src/modules/m_globalload.cpp
+++ b/src/modules/m_globalload.cpp
@@ -22,8 +22,6 @@
*/
-/* $ModDesc: Allows global loading of a module. */
-
#include "inspircd.h"
/** Handle /GLOADMODULE
@@ -35,10 +33,9 @@ class CommandGloadmodule : public Command
{
flags_needed = 'o';
syntax = "<modulename> [servermask]";
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
std::string servername = parameters.size() > 1 ? parameters[1] : "*";
@@ -47,11 +44,11 @@ class CommandGloadmodule : public Command
if (ServerInstance->Modules->Load(parameters[0].c_str()))
{
ServerInstance->SNO->WriteToSnoMask('a', "NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
- user->WriteNumeric(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Module successfully loaded.");
}
else
{
- user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
}
}
else
@@ -60,7 +57,7 @@ class CommandGloadmodule : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
@@ -77,8 +74,15 @@ class CommandGunloadmodule : public Command
syntax = "<modulename> [servermask]";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
+ if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
+ InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload core commands!");
+ return CMD_FAILURE;
+ }
+
std::string servername = parameters.size() > 1 ? parameters[1] : "*";
if (InspIRCd::Match(ServerInstance->Config->ServerName.c_str(), servername))
@@ -89,16 +93,15 @@ class CommandGunloadmodule : public Command
if (ServerInstance->Modules->Unload(m))
{
ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY UNLOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
- user->SendText(":%s 973 %s %s :Module successfully unloaded.",
- ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
+ user->WriteRemoteNumeric(RPL_UNLOADEDMODULE, parameters[0], "Module successfully unloaded.");
}
else
{
- user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
}
}
else
- user->SendText(":%s 972 %s %s :No such module", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
+ user->WriteRemoteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "No such module");
}
else
ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBAL UNLOAD BY '%s' (not unloaded here)",parameters[0].c_str(), user->nick.c_str());
@@ -106,31 +109,12 @@ class CommandGunloadmodule : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
};
-class GReloadModuleWorker : public HandlerBase1<void, bool>
-{
- public:
- const std::string nick;
- const std::string name;
- const std::string uid;
- GReloadModuleWorker(const std::string& usernick, const std::string& uuid, const std::string& modn)
- : nick(usernick), name(modn), uid(uuid) {}
- void Call(bool result)
- {
- ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
- User* user = ServerInstance->FindNick(uid);
- if (user)
- user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
- user->nick.c_str(), name.c_str(), result ? "" : "un");
- ServerInstance->GlobalCulls.AddItem(this);
- }
-};
-
/** Handle /GRELOADMODULE
*/
class CommandGreloadmodule : public Command
@@ -141,7 +125,7 @@ class CommandGreloadmodule : public Command
flags_needed = 'o'; syntax = "<modulename> [servermask]";
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
std::string servername = parameters.size() > 1 ? parameters[1] : "*";
@@ -150,14 +134,12 @@ class CommandGreloadmodule : public Command
Module* m = ServerInstance->Modules->Find(parameters[0]);
if (m)
{
- GReloadModuleWorker* worker = NULL;
- if ((m != creator) && (!creator->dying))
- worker = new GReloadModuleWorker(user->nick, user->uuid, parameters[0]);
- ServerInstance->Modules->Reload(m, worker);
+ ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'", parameters[0].c_str(), user->nick.c_str());
+ ServerInstance->Parser.CallHandler("RELOADMODULE", parameters, user);
}
else
{
- user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
return CMD_FAILURE;
}
}
@@ -167,7 +149,7 @@ class CommandGreloadmodule : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
@@ -185,22 +167,10 @@ class ModuleGlobalLoad : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd1);
- ServerInstance->Modules->AddService(cmd2);
- ServerInstance->Modules->AddService(cmd3);
- }
-
- ~ModuleGlobalLoad()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows global loading of a module.", VF_COMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleGlobalLoad)
-
diff --git a/src/modules/m_globops.cpp b/src/modules/m_globops.cpp
index 85d84252b..b98adce35 100644
--- a/src/modules/m_globops.cpp
+++ b/src/modules/m_globops.cpp
@@ -23,8 +23,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for GLOBOPS and snomask +g */
-
/** Handle /GLOBOPS
*/
class CommandGlobops : public Command
@@ -33,10 +31,9 @@ class CommandGlobops : public Command
CommandGlobops(Module* Creator) : Command(Creator,"GLOBOPS", 1,1)
{
flags_needed = 'o'; syntax = "<any-text>";
- TRANSLATE2(TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
ServerInstance->SNO->WriteGlobalSno('g', "From " + user->nick + ": " + parameters[0]);
return CMD_SUCCESS;
@@ -49,17 +46,15 @@ class ModuleGlobops : public Module
public:
ModuleGlobops() : cmd(this) {}
- void init()
+ void init() CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(cmd);
ServerInstance->SNO->EnableSnomask('g',"GLOBOPS");
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for GLOBOPS and snomask +g", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleGlobops)
diff --git a/src/modules/m_halfop.cpp b/src/modules/m_halfop.cpp
deleted file mode 100644
index 3194fcde8..000000000
--- a/src/modules/m_halfop.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $ModDesc: Channel half-operator mode provider */
-
-#include "inspircd.h"
-
-class ModeChannelHalfOp : public ModeHandler
-{
- public:
- ModeChannelHalfOp(Module* parent);
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
- unsigned int GetPrefixRank();
- void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
- void RemoveMode(User* user, irc::modestacker* stack = NULL);
-
- ModResult AccessCheck(User* src, Channel*, std::string& value, bool adding)
- {
- if (!adding && src->nick == value)
- return MOD_RES_ALLOW;
- return MOD_RES_PASSTHRU;
- }
-};
-
-ModeChannelHalfOp::ModeChannelHalfOp(Module* parent) : ModeHandler(parent, "halfop", 'h', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
- prefix = '%';
- levelrequired = OP_VALUE;
- m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelHalfOp::GetPrefixRank()
-{
- return HALFOP_VALUE;
-}
-
-void ModeChannelHalfOp::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- const UserMembList* clist = channel->GetUsers();
-
- for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
- {
- if (stack)
- {
- stack->Push(this->GetModeChar(), i->first->nick);
- }
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-h");
- parameters.push_back(i->first->nick);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-
-}
-
-void ModeChannelHalfOp::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelHalfOp::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
- return MODEACTION_ALLOW;
-}
-
-class ModuleHalfop : public Module
-{
- ModeChannelHalfOp mh;
- public:
- ModuleHalfop() : mh(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(mh);
- }
-
- Version GetVersion()
- {
- return Version("Channel half-operator mode provider", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleHalfop)
diff --git a/src/modules/m_haproxy.cpp b/src/modules/m_haproxy.cpp
new file mode 100644
index 000000000..8272c2980
--- /dev/null
+++ b/src/modules/m_haproxy.cpp
@@ -0,0 +1,326 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "iohook.h"
+
+enum
+{
+ // The family for TCP over IPv4.
+ PP2_FAMILY_IPV4 = 0x11,
+
+ // The length of the PP2_FAMILY_IPV4 endpoints.
+ PP2_FAMILY_IPV4_LENGTH = 12,
+
+ // The family for TCP over IPv6.
+ PP2_FAMILY_IPV6 = 0x21,
+
+ // The length of the PP2_FAMILY_IPV6 endpoints.
+ PP2_FAMILY_IPV6_LENGTH = 36,
+
+ // The family for UNIX sockets.
+ PP2_FAMILY_UNIX = 0x31,
+
+ // The length of the PP2_FAMILY_UNIX endpoints.
+ PP2_FAMILY_UNIX_LENGTH = 216,
+
+ // The bitmask we apply to extract the command.
+ PP2_COMMAND_MASK = 0x0F,
+
+ // The length of the PROXY protocol header.
+ PP2_HEADER_LENGTH = 16,
+
+ // The length of the PROXY protocol signature.
+ PP2_SIGNATURE_LENGTH = 12,
+
+ // The PROXY protocol version we support.
+ PP2_VERSION = 0x20,
+
+ // The bitmask we apply to extract the protocol version.
+ PP2_VERSION_MASK = 0xF0
+};
+
+enum HAProxyState
+{
+ // We are waiting for the PROXY header section.
+ HPS_WAITING_FOR_HEADER,
+
+ // We are waiting for the PROXY address section.
+ HPS_WAITING_FOR_ADDRESS,
+
+ // The client is fully connected.
+ HPS_CONNECTED
+};
+
+enum HAProxyCommand
+{
+ // LOCAL command.
+ HPC_LOCAL = 0x00,
+
+ // PROXY command.
+ HPC_PROXY = 0x01
+};
+
+struct HAProxyHeader
+{
+ // The signature used to identify the HAProxy protocol.
+ uint8_t signature[PP2_SIGNATURE_LENGTH];
+
+ // The version of the PROXY protocol and command being sent.
+ uint8_t version_command;
+
+ // The family for the address.
+ uint8_t family;
+
+ // The length of the address section.
+ uint16_t length;
+};
+
+class HAProxyHookProvider : public IOHookProvider
+{
+ public:
+ HAProxyHookProvider(Module* mod)
+ : IOHookProvider(mod, "haproxy", IOHookProvider::IOH_UNKNOWN, true)
+ {
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ // We don't need to implement this.
+ }
+};
+
+// The signature for a HAProxy PROXY protocol header.
+static const char proxy_signature[13] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
+
+class HAProxyHook : public IOHookMiddle
+{
+ private:
+ // The length of the address section.
+ uint16_t address_length;
+
+ // The endpoint the client is connecting from.
+ irc::sockets::sockaddrs client;
+
+ // The command sent by the proxy server.
+ HAProxyCommand command;
+
+ // The endpoint the client is connected to.
+ irc::sockets::sockaddrs server;
+
+ // The current state of the PROXY parser.
+ HAProxyState state;
+
+ int ReadProxyAddress(StreamSocket* sock)
+ {
+ // Block until we have the entire address.
+ std::string& recvq = GetRecvQ();
+ if (recvq.length() < address_length)
+ return 0;
+
+ switch (command)
+ {
+ case HPC_LOCAL:
+ // Skip the address completely.
+ recvq.erase(0, address_length);
+ break;
+
+ case HPC_PROXY:
+ // Store the endpoint information.
+ switch (client.family())
+ {
+ case AF_INET:
+ memcpy(&client.in4.sin_addr.s_addr, &recvq[0], 4);
+ memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 8);
+ memcpy(&client.in4.sin_port, &recvq[8], 2);
+ memcpy(&server.in4.sin_port, &recvq[10], 2);
+ break;
+
+ case AF_INET6:
+ memcpy(client.in6.sin6_addr.s6_addr, &recvq[0], 16);
+ memcpy(server.in6.sin6_addr.s6_addr, &recvq[16], 16);
+ memcpy(&client.in6.sin6_port, &recvq[32], 2);
+ memcpy(&server.in6.sin6_port, &recvq[34], 2);
+ break;
+
+ case AF_UNIX:
+ memcpy(client.un.sun_path, &recvq[0], 108);
+ memcpy(client.un.sun_path, &recvq[108], 108);
+ break;
+ }
+
+ sock->OnSetEndPoint(server, client);
+
+ // XXX: HAProxy's PROXY v2 specification defines Type-Length-Values that
+ // could appear here but as of 2018-07-25 it does not send anything. We
+ // should revisit this in the future to see if they actually send them.
+ recvq.erase(0, address_length);
+ }
+
+ // We're done!
+ state = HPS_CONNECTED;
+ return 1;
+ }
+
+ int ReadProxyHeader(StreamSocket* sock)
+ {
+ // Block until we have a header.
+ std::string& recvq = GetRecvQ();
+ if (recvq.length() < PP2_HEADER_LENGTH)
+ return 0;
+
+ // Read the header.
+ HAProxyHeader header;
+ memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
+ recvq.erase(0, PP2_HEADER_LENGTH);
+
+ // Check we are actually parsing a HAProxy header.
+ if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
+ {
+ // If we've reached this point the proxy server did not send a proxy information.
+ sock->SetError("Invalid HAProxy PROXY signature");
+ return -1;
+ }
+
+ // We only support this version of the protocol.
+ const uint8_t version = (header.version_command & PP2_VERSION_MASK);
+ if (version != PP2_VERSION)
+ {
+ sock->SetError("Unsupported HAProxy PROXY protocol version");
+ return -1;
+ }
+
+ // We only support the LOCAL and PROXY commands.
+ command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
+ switch (command)
+ {
+ case HPC_LOCAL:
+ // Intentionally left blank.
+ break;
+
+ case HPC_PROXY:
+ // Check the protocol support and initialise the sockaddrs.
+ uint16_t shortest_length;
+ switch (header.family)
+ {
+ case PP2_FAMILY_IPV4: // TCP over IPv4.
+ client.sa.sa_family = server.sa.sa_family = AF_INET;
+ shortest_length = PP2_FAMILY_IPV4_LENGTH;
+ break;
+
+ case PP2_FAMILY_IPV6: // TCP over IPv6.
+ client.sa.sa_family = server.sa.sa_family = AF_INET6;
+ shortest_length = PP2_FAMILY_IPV6_LENGTH;
+ break;
+
+ case PP2_FAMILY_UNIX: // UNIX stream.
+ client.sa.sa_family = server.sa.sa_family = AF_UNIX;
+ shortest_length = PP2_FAMILY_UNIX_LENGTH;
+ break;
+
+ default: // Unknown protocol.
+ sock->SetError("Invalid HAProxy PROXY protocol type");
+ return -1;
+ }
+
+ // Check that the length can actually contain the addresses.
+ address_length = ntohs(header.length);
+ if (address_length < shortest_length)
+ {
+ sock->SetError("Truncated HAProxy PROXY address section");
+ return -1;
+ }
+ break;
+
+ default:
+ sock->SetError("Unsupported HAProxy PROXY command");
+ return -1;
+ }
+
+ state = HPS_WAITING_FOR_ADDRESS;
+ return ReadProxyAddress(sock);
+ }
+
+ public:
+ HAProxyHook(IOHookProvider* Prov, StreamSocket* sock)
+ : IOHookMiddle(Prov)
+ , state(HPS_WAITING_FOR_HEADER)
+ {
+ sock->AddIOHook(this);
+ }
+
+ int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
+ {
+ // We don't need to implement this.
+ GetSendQ().moveall(uppersendq);
+ return 1;
+ }
+
+ int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
+ {
+ switch (state)
+ {
+ case HPS_WAITING_FOR_HEADER:
+ return ReadProxyHeader(sock);
+
+ case HPS_WAITING_FOR_ADDRESS:
+ return ReadProxyAddress(sock);
+
+ case HPS_CONNECTED:
+ std::string& recvq = GetRecvQ();
+ destrecvq.append(recvq);
+ recvq.clear();
+ return 1;
+ }
+
+ // We should never reach this point.
+ return -1;
+ }
+
+ void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ // We don't need to implement this.
+ }
+};
+
+void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+ new HAProxyHook(this, sock);
+}
+
+class ModuleHAProxy : public Module
+{
+ private:
+ reference<HAProxyHookProvider> hookprov;
+
+ public:
+ ModuleHAProxy()
+ : hookprov(new HAProxyHookProvider(this))
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHAProxy)
diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp
index 4bbe8785e..f88298576 100644
--- a/src/modules/m_helpop.cpp
+++ b/src/modules/m_helpop.cpp
@@ -21,11 +21,17 @@
*/
-/* $ModDesc: Provides the /HELPOP command for useful information */
-
#include "inspircd.h"
+#include "modules/whois.h"
+
+enum
+{
+ // From UnrealIRCd.
+ RPL_WHOISHELPOP = 310
+};
-static std::map<irc::string, std::string> helpop_map;
+typedef std::map<std::string, std::string, irc::insensitive_swo> HelpopMap;
+static HelpopMap helpop_map;
/** Handles user mode +h
*/
@@ -42,41 +48,40 @@ class Helpop : public SimpleUserModeHandler
*/
class CommandHelpop : public Command
{
+ const std::string startkey;
public:
- CommandHelpop(Module* Creator) : Command(Creator, "HELPOP", 0)
+ CommandHelpop(Module* Creator)
+ : Command(Creator, "HELPOP", 0)
+ , startkey("start")
{
syntax = "<any-text>";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- irc::string parameter("start");
- if (parameters.size() > 0)
- parameter = parameters[0].c_str();
+ const std::string& parameter = (!parameters.empty() ? parameters[0] : startkey);
if (parameter == "index")
{
/* iterate over all helpop items */
- user->WriteServ("290 %s :HELPOP topic index", user->nick.c_str());
- for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
- {
- user->WriteServ("292 %s : %s", user->nick.c_str(), iter->first.c_str());
- }
- user->WriteServ("292 %s :*** End of HELPOP topic index", user->nick.c_str());
+ user->WriteNumeric(290, "HELPOP topic index");
+ for (HelpopMap::const_iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
+ user->WriteNumeric(292, InspIRCd::Format(" %s", iter->first.c_str()));
+ user->WriteNumeric(292, "*** End of HELPOP topic index");
}
else
{
- user->WriteServ("290 %s :*** HELPOP for %s", user->nick.c_str(), parameter.c_str());
- user->WriteServ("292 %s : -", user->nick.c_str());
+ user->WriteNumeric(290, InspIRCd::Format("*** HELPOP for %s", parameter.c_str()));
+ user->WriteNumeric(292, " -");
- std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter);
+ HelpopMap::const_iterator iter = helpop_map.find(parameter);
if (iter == helpop_map.end())
{
iter = helpop_map.find("nohelp");
}
- std::string value = iter->second;
+ const std::string& value = iter->second;
irc::sepstream stream(value, '\n');
std::string token = "*";
@@ -84,48 +89,40 @@ class CommandHelpop : public Command
{
// Writing a blank line will not work with some clients
if (token.empty())
- user->WriteServ("292 %s : ", user->nick.c_str());
+ user->WriteNumeric(292, ' ');
else
- user->WriteServ("292 %s :%s", user->nick.c_str(), token.c_str());
+ user->WriteNumeric(292, token);
}
- user->WriteServ("292 %s : -", user->nick.c_str());
- user->WriteServ("292 %s :*** End of HELPOP", user->nick.c_str());
+ user->WriteNumeric(292, " -");
+ user->WriteNumeric(292, "*** End of HELPOP");
}
return CMD_SUCCESS;
}
};
-class ModuleHelpop : public Module
+class ModuleHelpop : public Module, public Whois::EventListener
{
- private:
CommandHelpop cmd;
Helpop ho;
public:
ModuleHelpop()
- : cmd(this), ho(this)
+ : Whois::EventListener(this)
+ , cmd(this)
+ , ho(this)
{
}
- void init()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- ReadConfig();
- ServerInstance->Modules->AddService(ho);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnRehash, I_OnWhois };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void ReadConfig()
- {
- std::map<irc::string, std::string> help;
+ HelpopMap help;
ConfigTagList tags = ServerInstance->Config->ConfTags("helpop");
for(ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- irc::string key = assign(tag->getString("key"));
+ std::string key = tag->getString("key");
std::string value;
tag->readString("value", value, true); /* Linefeeds allowed */
@@ -151,20 +148,15 @@ class ModuleHelpop : public Module
helpop_map.swap(help);
}
- void OnRehash(User* user)
- {
- ReadConfig();
- }
-
- void OnWhois(User* src, User* dst)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (dst->IsModeSet('h'))
+ if (whois.GetTarget()->IsModeSet(ho))
{
- ServerInstance->SendWhoisLine(src, dst, 310, src->nick+" "+dst->nick+" :is available for help.");
+ whois.SendLine(RPL_WHOISHELPOP, "is available for help.");
}
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the /HELPOP command for useful information", VF_VENDOR);
}
diff --git a/src/modules/m_hidechans.cpp b/src/modules/m_hidechans.cpp
index 008c62208..1664582e0 100644
--- a/src/modules/m_hidechans.cpp
+++ b/src/modules/m_hidechans.cpp
@@ -19,8 +19,7 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides support for hiding channels with user mode +I */
+#include "modules/whois.h"
/** Handles user mode +I
*/
@@ -30,49 +29,39 @@ class HideChans : public SimpleUserModeHandler
HideChans(Module* Creator) : SimpleUserModeHandler(Creator, "hidechans", 'I') { }
};
-class ModuleHideChans : public Module
+class ModuleHideChans : public Module, public Whois::LineEventListener
{
bool AffectsOpers;
HideChans hm;
public:
- ModuleHideChans() : hm(this)
- {
- }
-
- void init()
+ ModuleHideChans()
+ : Whois::LineEventListener(this)
+ , hm(this)
{
- ServerInstance->Modules->AddService(hm);
- Implementation eventlist[] = { I_OnWhoisLine, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- virtual ~ModuleHideChans()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for hiding channels with user mode +I", VF_VENDOR);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
AffectsOpers = ServerInstance->Config->ConfValue("hidechans")->getBool("affectsopers");
}
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* always show to self */
- if (user == dest)
+ if (whois.IsSelfWhois())
return MOD_RES_PASSTHRU;
/* don't touch anything except 319 */
- if (numeric != 319)
+ if (numeric.GetNumeric() != 319)
return MOD_RES_PASSTHRU;
/* don't touch if -I */
- if (!dest->IsModeSet('I'))
+ if (!whois.GetTarget()->IsModeSet(hm))
return MOD_RES_PASSTHRU;
/* if it affects opers, we don't care if they are opered */
@@ -80,7 +69,7 @@ class ModuleHideChans : public Module
return MOD_RES_DENY;
/* doesn't affect opers, sender is opered */
- if (user->HasPrivPermission("users/auspex"))
+ if (whois.GetSource()->HasPrivPermission("users/auspex"))
return MOD_RES_PASSTHRU;
/* user must be opered, boned. */
@@ -88,5 +77,4 @@ class ModuleHideChans : public Module
}
};
-
MODULE_INIT(ModuleHideChans)
diff --git a/src/modules/m_hidelist.cpp b/src/modules/m_hidelist.cpp
new file mode 100644
index 000000000..2d3f0be7c
--- /dev/null
+++ b/src/modules/m_hidelist.cpp
@@ -0,0 +1,87 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ListWatcher : public ModeWatcher
+{
+ // Minimum rank required to view the list
+ const unsigned int minrank;
+
+ public:
+ ListWatcher(Module* mod, const std::string& modename, unsigned int rank)
+ : ModeWatcher(mod, modename, MODETYPE_CHANNEL)
+ , minrank(rank)
+ {
+ }
+
+ bool BeforeMode(User* user, User* destuser, Channel* chan, std::string& param, bool adding) CXX11_OVERRIDE
+ {
+ // Only handle listmode list requests
+ if (!param.empty())
+ return true;
+
+ // If the user requesting the list is a member of the channel see if they have the
+ // rank required to view the list
+ Membership* memb = chan->GetUser(user);
+ if ((memb) && (memb->getRank() >= minrank))
+ return true;
+
+ if (user->HasPrivPermission("channels/auspex"))
+ return true;
+
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You do not have access to view the %s list", GetModeName().c_str()));
+ return false;
+ }
+};
+
+class ModuleHideList : public Module
+{
+ std::vector<ListWatcher*> watchers;
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ stdalgo::delete_all(watchers);
+ watchers.clear();
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("hidelist");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string modename = tag->getString("mode");
+ // If rank is set to 0 everyone inside the channel can view the list,
+ // but non-members may not
+ unsigned int rank = tag->getUInt("rank", HALFOP_VALUE);
+ watchers.push_back(new ListWatcher(this, modename, rank));
+ }
+ }
+
+ ~ModuleHideList()
+ {
+ stdalgo::delete_all(watchers);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for hiding the list of listmodes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHideList)
diff --git a/src/modules/m_hidemode.cpp b/src/modules/m_hidemode.cpp
new file mode 100644
index 000000000..d6ae05801
--- /dev/null
+++ b/src/modules/m_hidemode.cpp
@@ -0,0 +1,198 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+namespace
+{
+class Settings
+{
+ typedef insp::flat_map<std::string, unsigned int> RanksToSeeMap;
+ RanksToSeeMap rankstosee;
+
+ public:
+ unsigned int GetRequiredRank(const ModeHandler& mh) const
+ {
+ RanksToSeeMap::const_iterator it = rankstosee.find(mh.name);
+ if (it != rankstosee.end())
+ return it->second;
+ return 0;
+ }
+
+ void Load()
+ {
+ rankstosee.clear();
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("hidemode");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string modename = tag->getString("mode");
+ unsigned int rank = tag->getInt("rank", 0, 0);
+ if (!modename.empty() && rank)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank);
+ rankstosee.insert(std::make_pair(modename, rank));
+ }
+ }
+ }
+};
+
+class ModeHook : public ClientProtocol::EventHook
+{
+ typedef insp::flat_map<unsigned int, const ClientProtocol::MessageList*> FilteredModeMap;
+
+ std::vector<Modes::ChangeList> modechangelists;
+ std::list<ClientProtocol::Messages::Mode> filteredmodelist;
+ std::list<ClientProtocol::MessageList> filteredmsgplists;
+ FilteredModeMap cache;
+
+ static ModResult HandleResult(const ClientProtocol::MessageList* filteredmessagelist, ClientProtocol::MessageList& messagelist)
+ {
+ // Deny if member isn't allowed to see even a single mode change from this mode event
+ if (!filteredmessagelist)
+ return MOD_RES_DENY;
+
+ // Member is allowed to see at least one mode change, replace list
+ if (filteredmessagelist != &messagelist)
+ messagelist = *filteredmessagelist;
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ Modes::ChangeList* FilterModeChangeList(const ClientProtocol::Events::Mode& mode, unsigned int rank)
+ {
+ Modes::ChangeList* modechangelist = NULL;
+ for (Modes::ChangeList::List::const_iterator i = mode.GetChangeList().getlist().begin(); i != mode.GetChangeList().getlist().end(); ++i)
+ {
+ const Modes::Change& curr = *i;
+ if (settings.GetRequiredRank(*curr.mh) <= rank)
+ {
+ // No restriction on who can see this mode or there is one but the member's rank is sufficient
+ if (modechangelist)
+ modechangelist->push(curr);
+
+ continue;
+ }
+
+ // Member cannot see the current mode change
+
+ if (!modechangelist)
+ {
+ // Create new mode change list or reuse the last one if it's empty
+ if ((modechangelists.empty()) || (!modechangelists.back().empty()))
+ modechangelists.push_back(Modes::ChangeList());
+
+ // Add all modes to it which we've accepted so far
+ modechangelists.back().push(mode.GetChangeList().getlist().begin(), i);
+ modechangelist = &modechangelists.back();
+ }
+ }
+ return modechangelist;
+ }
+
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
+ {
+ cache.clear();
+ filteredmsgplists.clear();
+ filteredmodelist.clear();
+ modechangelists.clear();
+
+ // Ensure no reallocations will happen
+ const size_t numprefixmodes = ServerInstance->Modes.GetPrefixModes().size();
+ modechangelists.reserve(numprefixmodes);
+ }
+
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+ {
+ const ClientProtocol::Events::Mode& mode = static_cast<const ClientProtocol::Events::Mode&>(ev);
+ Channel* const chan = mode.GetMessages().front().GetChanTarget();
+ if (!chan)
+ return MOD_RES_PASSTHRU;
+
+ Membership* const memb = chan->GetUser(user);
+ if (!memb)
+ return MOD_RES_PASSTHRU;
+
+ // Check cache first
+ const FilteredModeMap::const_iterator it = cache.find(memb->getRank());
+ if (it != cache.end())
+ return HandleResult(it->second, messagelist);
+
+ // Message for this rank isn't cached, generate it now
+ const Modes::ChangeList* const filteredchangelist = FilterModeChangeList(mode, memb->getRank());
+
+ // If no new change list was generated (above method returned NULL) it means the member and everyone else
+ // with the same rank can see everything in the original change list.
+ ClientProtocol::MessageList* finalmsgplist = &messagelist;
+ if (filteredchangelist)
+ {
+ if (filteredchangelist->empty())
+ {
+ // This rank cannot see any mode changes in the original change list
+ finalmsgplist = NULL;
+ }
+ else
+ {
+ // This rank can see some of the mode changes in the filtered mode change list.
+ // Create and store a new protocol message from it.
+ filteredmsgplists.push_back(ClientProtocol::MessageList());
+ ClientProtocol::Events::Mode::BuildMessages(mode.GetMessages().front().GetSourceUser(), chan, NULL, *filteredchangelist, filteredmodelist, filteredmsgplists.back());
+ finalmsgplist = &filteredmsgplists.back();
+ }
+ }
+
+ // Cache the result in all cases so it can be reused for further members with the same rank
+ cache.insert(std::make_pair(memb->getRank(), finalmsgplist));
+ return HandleResult(finalmsgplist, messagelist);
+ }
+
+ public:
+ Settings settings;
+
+ ModeHook(Module* mod)
+ : ClientProtocol::EventHook(mod, "MODE", 10)
+ {
+ }
+};
+}
+
+class ModuleHideMode : public Module
+{
+ private:
+ ModeHook modehook;
+
+ public:
+ ModuleHideMode()
+ : modehook(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ modehook.settings.Load();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for hiding mode changes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHideMode)
diff --git a/src/modules/m_hideoper.cpp b/src/modules/m_hideoper.cpp
index 32999d9f0..2bca0a156 100644
--- a/src/modules/m_hideoper.cpp
+++ b/src/modules/m_hideoper.cpp
@@ -20,8 +20,8 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides support for hiding oper status with user mode +H */
+#include "modules/stats.h"
+#include "modules/whois.h"
/** Handles user mode +H
*/
@@ -36,7 +36,7 @@ class HideOper : public SimpleUserModeHandler
oper = true;
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
if (SimpleUserModeHandler::OnModeChange(source, dest, channel, parameter, adding) == MODEACTION_DENY)
return MODEACTION_DENY;
@@ -50,43 +50,33 @@ class HideOper : public SimpleUserModeHandler
}
};
-class ModuleHideOper : public Module
+class ModuleHideOper : public Module, public Stats::EventListener, public Whois::LineEventListener
{
HideOper hm;
bool active;
public:
ModuleHideOper()
- : hm(this)
+ : Stats::EventListener(this)
+ , Whois::LineEventListener(this)
+ , hm(this)
, active(false)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(hm);
- Implementation eventlist[] = { I_OnWhoisLine, I_OnSendWhoLine, I_OnStats, I_OnNumeric, I_OnUserQuit };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual ~ModuleHideOper()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for hiding oper status with user mode +H", VF_VENDOR);
}
- void OnUserQuit(User* user, const std::string&, const std::string&)
+ void OnUserQuit(User* user, const std::string&, const std::string&) CXX11_OVERRIDE
{
- if (user->IsModeSet('H'))
+ if (user->IsModeSet(hm))
hm.opercount--;
}
- ModResult OnNumeric(User* user, unsigned int numeric, const std::string& text)
+ ModResult OnNumeric(User* user, const Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- if (numeric != 252 || active || user->HasPrivPermission("users/auspex"))
+ if (numeric.GetNumeric() != RPL_LUSEROP || active || user->HasPrivPermission("users/auspex"))
return MOD_RES_PASSTHRU;
// If there are no visible operators then we shouldn't send the numeric.
@@ -94,68 +84,72 @@ class ModuleHideOper : public Module
if (opercount)
{
active = true;
- user->WriteNumeric(252, "%s %lu :operator(s) online", user->nick.c_str(), static_cast<unsigned long>(opercount));
+ user->WriteNumeric(RPL_LUSEROP, opercount, "operator(s) online");
active = false;
}
return MOD_RES_DENY;
}
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
* person doing the WHOIS is not an oper
*/
- if (numeric != 313)
+ if (numeric.GetNumeric() != 313)
return MOD_RES_PASSTHRU;
- if (!dest->IsModeSet('H'))
+ if (!whois.GetTarget()->IsModeSet(hm))
return MOD_RES_PASSTHRU;
- if (!user->HasPrivPermission("users/auspex"))
+ if (!whois.GetSource()->HasPrivPermission("users/auspex"))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+ ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- if (user->IsModeSet('H') && !source->HasPrivPermission("users/auspex"))
+ if (user->IsModeSet(hm) && !source->HasPrivPermission("users/auspex"))
{
+ // Hide the line completely if doing a "/who * o" query
+ if ((params.size() > 1) && (params[1].find('o') != std::string::npos))
+ return MOD_RES_DENY;
+
// hide the "*" that marks the user as an oper from the /WHO line
- std::string::size_type spcolon = line.find(" :");
- if (spcolon == std::string::npos)
- return; // Another module hid the user completely
- std::string::size_type sp = line.rfind(' ', spcolon-1);
- std::string::size_type pos = line.find('*', sp);
+ // #chan ident localhost insp22.test nick H@ :0 Attila
+ if (numeric.GetParams().size() < 6)
+ return MOD_RES_PASSTHRU;
+
+ std::string& param = numeric.GetParams()[5];
+ const std::string::size_type pos = param.find('*');
if (pos != std::string::npos)
- line.erase(pos, 1);
- // hide the line completely if doing a "/who * o" query
- if (params.size() > 1 && params[1].find('o') != std::string::npos)
- line.clear();
+ param.erase(pos, 1);
}
+ return MOD_RES_PASSTHRU;
}
- ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'P')
+ if (stats.GetSymbol() != 'P')
return MOD_RES_PASSTHRU;
unsigned int count = 0;
- for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
{
User* oper = *i;
- if (!ServerInstance->ULine(oper->server) && (IS_OPER(user) || !oper->IsModeSet('H')))
+ if (!oper->server->IsULine() && (stats.GetSource()->IsOper() || !oper->IsModeSet(hm)))
{
- results.push_back(ServerInstance->Config->ServerName+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
- (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
+ LocalUser* lu = IS_LOCAL(oper);
+ stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->GetDisplayedHost() + ") Idle: " +
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
count++;
}
}
- results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
+ stats.AddRow(249, ConvToStr(count)+" OPER(s)");
return MOD_RES_DENY;
}
};
-
MODULE_INIT(ModuleHideOper)
diff --git a/src/modules/m_hostchange.cpp b/src/modules/m_hostchange.cpp
index 7433fccd3..201c4b59b 100644
--- a/src/modules/m_hostchange.cpp
+++ b/src/modules/m_hostchange.cpp
@@ -20,151 +20,216 @@
#include "inspircd.h"
+#include "modules/account.h"
-/* $ModDesc: Provides masking of user hostnames in a different way to m_cloaking */
-
-/** Holds information on a host set by m_hostchange
- */
-class Host
+// Holds information about a <hostchange> rule.
+class HostRule
{
public:
enum HostChangeAction
{
- HCA_SET,
- HCA_SUFFIX,
- HCA_ADDNICK
+ // Add the user's account name to their hostname.
+ HCA_ADDACCOUNT,
+
+ // Add the user's nickname to their hostname.
+ HCA_ADDNICK,
+
+ // Set the user's hostname to the specific value.
+ HCA_SET
};
+ private:
HostChangeAction action;
- std::string newhost;
- std::string ports;
+ std::string host;
+ std::string mask;
+ insp::flat_set<int> ports;
+ std::string prefix;
+ std::string suffix;
+
+ public:
+ HostRule(const std::string& Host, const std::string& Mask, const insp::flat_set<int>& Ports)
+ : action(HCA_SET)
+ , host(Host)
+ , mask(Mask)
+ , ports(Ports)
+ {
+ }
+
+ HostRule(HostChangeAction Action, const std::string& Mask, const insp::flat_set<int>& Ports, const std::string& Prefix, const std::string& Suffix)
+ : action(Action)
+ , mask(Mask)
+ , ports(Ports)
+ , prefix(Prefix)
+ , suffix(Suffix)
+ {
+ }
+
+ HostChangeAction GetAction() const
+ {
+ return action;
+ }
- Host(HostChangeAction Action, const std::string& Newhost, const std::string& Ports) :
- action(Action), newhost(Newhost), ports(Ports) {}
+ const std::string& GetHost() const
+ {
+ return host;
+ }
+
+ bool Matches(LocalUser* user) const
+ {
+ if (!ports.empty() && !ports.count(user->GetServerPort()))
+ return false;
+
+ if (InspIRCd::MatchCIDR(user->MakeHost(), mask))
+ return true;
+
+ return InspIRCd::MatchCIDR(user->MakeHostIP(), mask);
+ }
+
+ void Wrap(const std::string& value, std::string& out) const
+ {
+ if (!prefix.empty())
+ out.append(prefix);
+
+ out.append(value);
+
+ if (!suffix.empty())
+ out.append(suffix);
+ }
};
-typedef std::vector<std::pair<std::string, Host> > hostchanges_t;
+typedef std::vector<HostRule> HostRules;
class ModuleHostChange : public Module
{
- private:
- hostchanges_t hostchanges;
- std::string MySuffix;
- std::string MyPrefix;
- std::string MySeparator;
+private:
+ std::bitset<UCHAR_MAX> hostmap;
+ HostRules hostrules;
- public:
- void init()
+ std::string CleanName(const std::string& name)
{
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ std::string buffer;
+ buffer.reserve(name.length());
+ for (std::string::const_iterator iter = name.begin(); iter != name.end(); ++iter)
+ {
+ if (hostmap.test(static_cast<unsigned char>(*iter)))
+ {
+ buffer.push_back(*iter);
+ }
+ }
+ return buffer;
}
- virtual void OnRehash(User* user)
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- ConfigTag* host = ServerInstance->Config->ConfValue("host");
- MySuffix = host->getString("suffix");
- MyPrefix = host->getString("prefix");
- MySeparator = host->getString("separator", ".");
- hostchanges.clear();
+ HostRules rules;
- std::set<std::string> dupecheck;
ConfigTagList tags = ServerInstance->Config->ConfTags("hostchange");
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- std::string mask = tag->getString("mask");
- if (!dupecheck.insert(mask).second)
- throw ModuleException("Duplicate hostchange entry: " + mask);
- Host::HostChangeAction act;
- std::string newhost;
- std::string action = tag->getString("action");
- if (!strcasecmp(action.c_str(), "set"))
+ // Ensure that we have the <hostchange:mask> parameter.
+ const std::string mask = tag->getString("mask");
+ if (mask.empty())
+ throw ModuleException("<hostchange:mask> is a mandatory field, at " + tag->getTagLocation());
+
+ insp::flat_set<int> ports;
+ const std::string portlist = tag->getString("ports");
+ if (!ports.empty())
{
- act = Host::HCA_SET;
- newhost = tag->getString("value");
+ irc::portparser portrange(portlist, false);
+ while (int port = portrange.GetToken())
+ ports.insert(port);
}
- else if (!strcasecmp(action.c_str(), "suffix"))
- act = Host::HCA_SUFFIX;
- else if (!strcasecmp(action.c_str(), "addnick"))
- act = Host::HCA_ADDNICK;
- else
- throw ModuleException("Invalid hostchange action: " + action);
- hostchanges.push_back(std::make_pair(mask, Host(act, newhost, tag->getString("ports"))));
+ // Determine what type of host rule this is.
+ const std::string action = tag->getString("action");
+ if (stdalgo::string::equalsci(action, "addaccount"))
+ {
+ // The hostname is in the format [prefix]<account>[suffix].
+ rules.push_back(HostRule(HostRule::HCA_ADDACCOUNT, mask, ports, tag->getString("prefix"), tag->getString("suffix")));
+ }
+ else if (stdalgo::string::equalsci(action, "addnick"))
+ {
+ // The hostname is in the format [prefix]<nick>[suffix].
+ rules.push_back(HostRule(HostRule::HCA_ADDNICK, mask, ports, tag->getString("prefix"), tag->getString("suffix")));
+ }
+ else if (stdalgo::string::equalsci(action, "set"))
+ {
+ // Ensure that we have the <hostchange:value> parameter.
+ const std::string value = tag->getString("value");
+ if (value.empty())
+ throw ModuleException("<hostchange:value> is a mandatory field when using the 'set' action, at " + tag->getTagLocation());
+
+ // The hostname is in the format <value>.
+ rules.push_back(HostRule(mask, value, ports));
+ continue;
+ }
+ else
+ {
+ throw ModuleException(action + " is an invalid <hostchange:action> type, at " + tag->getTagLocation());
+ }
}
+
+ const std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
+ hostmap.reset();
+ for (std::string::const_iterator iter = hmap.begin(); iter != hmap.end(); ++iter)
+ hostmap.set(static_cast<unsigned char>(*iter));
+ hostrules.swap(rules);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- // returns the version number of the module to be
- // listed in /MODULES
- return Version("Provides masking of user hostnames in a different way to m_cloaking", VF_VENDOR);
+ return Version("Provides rule-based masking of user hostnames", VF_VENDOR);
}
- virtual void OnUserConnect(LocalUser* user)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
+ for (HostRules::const_iterator iter = hostrules.begin(); iter != hostrules.end(); ++iter)
{
- if (((InspIRCd::MatchCIDR(user->MakeHost(), i->first)) || (InspIRCd::MatchCIDR(user->MakeHostIP(), i->first))))
+ const HostRule& rule = *iter;
+ if (!rule.Matches(user))
+ continue;
+
+ std::string newhost;
+ if (rule.GetAction() == HostRule::HCA_ADDACCOUNT)
+ {
+ // Retrieve the account name.
+ const AccountExtItem* accountext = GetAccountExtItem();
+ const std::string* accountptr = accountext ? accountext->get(user) : NULL;
+ if (!accountptr)
+ continue;
+
+ // Remove invalid hostname characters.
+ std::string accountname = CleanName(*accountptr);
+ if (accountname.empty())
+ continue;
+
+ // Create the hostname.
+ rule.Wrap(accountname, newhost);
+ }
+ else if (rule.GetAction() == HostRule::HCA_ADDNICK)
+ {
+ // Remove invalid hostname characters.
+ const std::string nickname = CleanName(user->nick);
+ if (nickname.empty())
+ continue;
+
+ // Create the hostname.
+ rule.Wrap(nickname, newhost);
+ }
+ else if (rule.GetAction() == HostRule::HCA_SET)
+ {
+ newhost.assign(rule.GetHost());
+ }
+
+ if (!newhost.empty())
{
- const Host& h = i->second;
-
- if (!h.ports.empty())
- {
- irc::portparser portrange(h.ports, false);
- long portno = -1;
- bool foundany = false;
-
- while ((portno = portrange.GetToken()))
- if (portno == user->GetServerPort())
- foundany = true;
-
- if (!foundany)
- continue;
- }
-
- // host of new user matches a hostchange tag's mask
- std::string newhost;
- if (h.action == Host::HCA_SET)
- {
- newhost = h.newhost;
- }
- else if (h.action == Host::HCA_SUFFIX)
- {
- newhost = MySuffix;
- }
- else if (h.action == Host::HCA_ADDNICK)
- {
- // first take their nick and strip out non-dns, leaving just [A-Z0-9\-]
- std::string complete;
- for (std::string::const_iterator j = user->nick.begin(); j != user->nick.end(); ++j)
- {
- if (((*j >= 'A') && (*j <= 'Z')) ||
- ((*j >= 'a') && (*j <= 'z')) ||
- ((*j >= '0') && (*j <= '9')) ||
- (*j == '-'))
- {
- complete = complete + *j;
- }
- }
- if (complete.empty())
- complete = "i-have-a-lame-nick";
-
- if (!MyPrefix.empty())
- newhost = MyPrefix + MySeparator + complete;
- else
- newhost = complete + MySeparator + MySuffix;
- }
- if (!newhost.empty())
- {
- user->WriteServ("NOTICE "+user->nick+" :Setting your virtual host: " + newhost);
- if (!user->ChangeDisplayedHost(newhost.c_str()))
- user->WriteServ("NOTICE "+user->nick+" :Could not set your virtual host: " + newhost);
- return;
- }
+ user->WriteNotice("Setting your virtual host: " + newhost);
+ if (!user->ChangeDisplayedHost(newhost))
+ user->WriteNotice("Could not set your virtual host: " + newhost);
+ return;
}
}
}
diff --git a/src/modules/m_hostcycle.cpp b/src/modules/m_hostcycle.cpp
new file mode 100644
index 000000000..a3c81df6e
--- /dev/null
+++ b/src/modules/m_hostcycle.cpp
@@ -0,0 +1,120 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+class ModuleHostCycle : public Module
+{
+ Cap::Reference chghostcap;
+ const std::string quitmsghost;
+ const std::string quitmsgident;
+
+ /** Send fake quit/join/mode messages for host or ident cycle.
+ */
+ void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const std::string& reason)
+ {
+ // The user has the original ident/host at the time this function is called
+ ClientProtocol::Messages::Quit quitmsg(user, reason);
+ ClientProtocol::Event quitevent(ServerInstance->GetRFCEvents().quit, quitmsg);
+
+ already_sent_t silent_id = ServerInstance->Users.NextAlreadySentId();
+ already_sent_t seen_id = ServerInstance->Users.NextAlreadySentId();
+
+ IncludeChanList include_chans(user->chans.begin(), user->chans.end());
+ std::map<User*,bool> exceptions;
+
+ FOREACH_MOD(OnBuildNeighborList, (user, include_chans, exceptions));
+
+ // Users shouldn't see themselves quitting when host cycling
+ exceptions.erase(user);
+ for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+ {
+ LocalUser* u = IS_LOCAL(i->first);
+ if ((u) && (!u->quitting) && (!chghostcap.get(u)))
+ {
+ if (i->second)
+ {
+ u->already_sent = seen_id;
+ u->Send(quitevent);
+ }
+ else
+ {
+ u->already_sent = silent_id;
+ }
+ }
+ }
+
+ std::string newfullhost = user->nick + "!" + newident + "@" + newhost;
+
+ for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
+ {
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+
+ ClientProtocol::Events::Join joinevent(memb, newfullhost);
+
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator j = ulist.begin(); j != ulist.end(); ++j)
+ {
+ LocalUser* u = IS_LOCAL(j->first);
+ if (u == NULL || u == user)
+ continue;
+ if (u->already_sent == silent_id)
+ continue;
+ if (chghostcap.get(u))
+ continue;
+
+ if (u->already_sent != seen_id)
+ {
+ u->Send(quitevent);
+ u->already_sent = seen_id;
+ }
+
+ u->Send(joinevent);
+ }
+ }
+ }
+
+ public:
+ ModuleHostCycle()
+ : chghostcap(this, "chghost")
+ , quitmsghost("Changing host")
+ , quitmsgident("Changing ident")
+ {
+ }
+
+ void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+ {
+ DoHostCycle(user, newident, user->GetDisplayedHost(), quitmsgident);
+ }
+
+ void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+ {
+ DoHostCycle(user, user->ident, newhost, quitmsghost);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Cycles users in all their channels when their host or ident changes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHostCycle)
diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp
index cb17a0383..28177a7a2 100644
--- a/src/modules/m_httpd.cpp
+++ b/src/modules/m_httpd.cpp
@@ -23,16 +23,15 @@
#include "inspircd.h"
-#include "httpd.h"
-
-/* $ModDesc: Provides HTTP serving facilities to modules */
-/* $ModDep: httpd.h */
+#include "iohook.h"
+#include "modules/httpd.h"
class ModuleHttpServer;
static ModuleHttpServer* HttpModule;
-static bool claimed;
-static std::set<HttpServerSocket*> sockets;
+static insp::intrusive_list<HttpServerSocket> sockets;
+static Events::ModuleEventProvider* aclevprov;
+static Events::ModuleEventProvider* reqevprov;
/** HTTP socket states
*/
@@ -45,7 +44,7 @@ enum HttpState
/** A socket used for HTTP transport
*/
-class HttpServerSocket : public BufferedSocket
+class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
{
HttpState InternalState;
std::string ip;
@@ -58,18 +57,37 @@ class HttpServerSocket : public BufferedSocket
std::string uri;
std::string http_version;
- public:
- const time_t createtime;
+ /** True if this object is in the cull list
+ */
+ bool waitingcull;
- HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- : BufferedSocket(newfd), ip(IP), postsize(0)
- , createtime(ServerInstance->Time())
+ bool Tick(time_t currtime) CXX11_OVERRIDE
{
- InternalState = HTTP_SERVE_WAIT_REQUEST;
+ AddToCull();
+ return false;
+ }
- FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
- if (GetIOHook())
- GetIOHook()->OnStreamSocketAccept(this, client, server);
+ public:
+ HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
+ : BufferedSocket(newfd)
+ , Timer(timeoutsec)
+ , InternalState(HTTP_SERVE_WAIT_REQUEST)
+ , ip(IP)
+ , postsize(0)
+ , waitingcull(false)
+ {
+ if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
+ {
+ via->iohookprovs.back()->OnAccept(this, client, server);
+ // IOHook may have errored
+ if (!getError().empty())
+ {
+ AddToCull();
+ return;
+ }
+ }
+
+ ServerInstance->Timers.AddTimer(this);
}
~HttpServerSocket()
@@ -77,12 +95,12 @@ class HttpServerSocket : public BufferedSocket
sockets.erase(this);
}
- virtual void OnError(BufferedSocketError)
+ void OnError(BufferedSocketError) CXX11_OVERRIDE
{
- ServerInstance->GlobalCulls.AddItem(this);
+ AddToCull();
}
- std::string Response(int response)
+ std::string Response(unsigned int response)
{
switch (response)
{
@@ -173,7 +191,7 @@ class HttpServerSocket : public BufferedSocket
}
}
- void SendHTTPError(int response)
+ void SendHTTPError(unsigned int response)
{
HTTPHeaders empty;
std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
@@ -183,18 +201,13 @@ class HttpServerSocket : public BufferedSocket
WriteData(data);
}
- void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
+ void SendHeaders(unsigned long size, unsigned int response, HTTPHeaders &rheaders)
{
WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
- time_t local = ServerInstance->Time();
- struct tm *timeinfo = gmtime(&local);
- char *date = asctime(timeinfo);
- date[strlen(date) - 1] = '\0';
- rheaders.CreateHeader("Date", date);
-
- rheaders.CreateHeader("Server", BRANCH);
+ rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
+ rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
rheaders.SetHeader("Content-Length", ConvToStr(size));
if (size)
@@ -211,7 +224,7 @@ class HttpServerSocket : public BufferedSocket
WriteData("\r\n");
}
- void OnDataReady()
+ void OnDataReady() CXX11_OVERRIDE
{
if (InternalState == HTTP_SERVE_RECV_POSTDATA)
{
@@ -225,7 +238,7 @@ class HttpServerSocket : public BufferedSocket
if (reqbuffer.length() >= 8192)
{
- ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
reqbuffer.clear();
SetError("Buffer");
}
@@ -265,7 +278,7 @@ class HttpServerSocket : public BufferedSocket
continue;
}
- std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
+ std::string cheader(reqbuffer, hbegin, hend - hbegin);
std::string::size_type fieldsep = cheader.find(':');
if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
@@ -296,7 +309,7 @@ class HttpServerSocket : public BufferedSocket
if (reqbuffer.length() >= postsize)
{
- postdata = reqbuffer.substr(0, postsize);
+ postdata.assign(reqbuffer, 0, postsize);
reqbuffer.erase(0, postsize);
}
else if (!reqbuffer.empty())
@@ -318,93 +331,96 @@ class HttpServerSocket : public BufferedSocket
{
InternalState = HTTP_SERVE_SEND_DATA;
- claimed = false;
- HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
- acl.Send();
- if (!claimed)
+ ModResult MOD_RESULT;
+ HTTPRequest acl(request_type, uri, &headers, this, ip, postdata);
+ FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
+ if (MOD_RESULT != MOD_RES_DENY)
{
- HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
- url.Send();
- if (!claimed)
+ HTTPRequest url(request_type, uri, &headers, this, ip, postdata);
+ FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
{
SendHTTPError(404);
}
}
}
- void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
+ void Page(std::stringstream* n, unsigned int response, HTTPHeaders *hheaders)
{
SendHeaders(n->str().length(), response, *hheaders);
WriteData(n->str());
Close();
}
+
+ void AddToCull()
+ {
+ if (waitingcull)
+ return;
+
+ waitingcull = true;
+ Close();
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
};
-class ModuleHttpServer : public Module
+class HTTPdAPIImpl : public HTTPdAPIBase
{
- unsigned int timeoutsec;
-
public:
+ HTTPdAPIImpl(Module* parent)
+ : HTTPdAPIBase(parent)
+ {
+ }
- void init()
+ void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
{
- HttpModule = this;
- Implementation eventlist[] = { I_OnAcceptConnection, I_OnBackgroundTimer, I_OnRehash, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+ resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
}
+};
- void OnRehash(User* user)
+class ModuleHttpServer : public Module
+{
+ HTTPdAPIImpl APIImpl;
+ unsigned int timeoutsec;
+ Events::ModuleEventProvider acleventprov;
+ Events::ModuleEventProvider reqeventprov;
+
+ public:
+ ModuleHttpServer()
+ : APIImpl(this)
+ , acleventprov(this, "event/http-acl")
+ , reqeventprov(this, "event/http-request")
{
- ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
- timeoutsec = tag->getInt("timeout");
+ aclevprov = &acleventprov;
+ reqevprov = &reqeventprov;
}
- void OnRequest(Request& request)
+ void init() CXX11_OVERRIDE
{
- if (strcmp(request.id, "HTTP-DOC") != 0)
- return;
- HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
- claimed = true;
- resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
+ HttpModule = this;
}
- ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- if (from->bind_tag->getString("type") != "httpd")
- return MOD_RES_PASSTHRU;
- int port;
- std::string incomingip;
- irc::sockets::satoap(*client, incomingip, port);
- sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
- return MOD_RES_ALLOW;
+ ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
+ timeoutsec = tag->getDuration("timeout", 10, 1);
}
- void OnBackgroundTimer(time_t curtime)
+ ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
- if (!timeoutsec)
- return;
+ if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "httpd"))
+ return MOD_RES_PASSTHRU;
- time_t oldest_allowed = curtime - timeoutsec;
- for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
- {
- HttpServerSocket* sock = *i;
- ++i;
- if (sock->createtime < oldest_allowed)
- {
- sock->cull();
- delete sock;
- }
- }
+ sockets.push_front(new HttpServerSocket(nfd, client->addr(), from, client, server, timeoutsec));
+ return MOD_RES_ALLOW;
}
- void OnUnloadModule(Module* mod)
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
{
- for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
+ for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); )
{
HttpServerSocket* sock = *i;
++i;
- if (sock->GetIOHook() == mod)
+ if (sock->GetModHook(mod))
{
sock->cull();
delete sock;
@@ -412,20 +428,17 @@ class ModuleHttpServer : public Module
}
}
- CullResult cull()
+ CullResult cull() CXX11_OVERRIDE
{
- std::set<HttpServerSocket*> local;
- local.swap(sockets);
- for (std::set<HttpServerSocket*>::const_iterator i = local.begin(); i != local.end(); ++i)
+ for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
{
HttpServerSocket* sock = *i;
- sock->cull();
- delete sock;
+ sock->AddToCull();
}
return Module::cull();
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
}
diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp
index c25cabc0a..2dbc1be69 100644
--- a/src/modules/m_httpd_acl.cpp
+++ b/src/modules/m_httpd_acl.cpp
@@ -19,10 +19,7 @@
#include "inspircd.h"
-#include "httpd.h"
-#include "protocol.h"
-
-/* $ModDesc: Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules */
+#include "modules/httpd.h"
class HTTPACL
{
@@ -37,19 +34,22 @@ class HTTPACL
const std::string &set_whitelist, const std::string &set_blacklist)
: path(set_path), username(set_username), password(set_password), whitelist(set_whitelist),
blacklist(set_blacklist) { }
-
- ~HTTPACL() { }
};
-class ModuleHTTPAccessList : public Module
+class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
{
-
std::string stylesheet;
std::vector<HTTPACL> acl_list;
+ HTTPdAPI API;
public:
+ ModuleHTTPAccessList()
+ : HTTPACLEventListener(this)
+ , API(this)
+ {
+ }
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
acl_list.clear();
ConfigTagList acls = ServerInstance->Config->ConfTags("httpdacl");
@@ -67,16 +67,16 @@ class ModuleHTTPAccessList : public Module
while (sep.GetToken(type))
{
- if (type == "password")
+ if (stdalgo::string::equalsci(type, "password"))
{
username = c->getString("username");
password = c->getString("password");
}
- else if (type == "whitelist")
+ else if (stdalgo::string::equalsci(type, "whitelist"))
{
whitelist = c->getString("whitelist");
}
- else if (type == "blacklist")
+ else if (stdalgo::string::equalsci(type, "blacklist"))
{
blacklist = c->getString("blacklist");
}
@@ -86,38 +86,29 @@ class ModuleHTTPAccessList : public Module
}
}
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
password.c_str(), whitelist.c_str(), blacklist.c_str());
acl_list.push_back(HTTPACL(path, username, password, whitelist, blacklist));
}
}
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnEvent, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void BlockAccess(HTTPRequest* http, int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="")
+ void BlockAccess(HTTPRequest* http, unsigned int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="")
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "BlockAccess (%d)", returnval);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BlockAccess (%u)", returnval);
std::stringstream data("Access to this resource is denied by an access control list. Please contact your IRC administrator.");
HTTPDocumentResponse response(this, *http, &data, returnval);
- response.headers.SetHeader("X-Powered-By", "m_httpd_acl.so");
+ response.headers.SetHeader("X-Powered-By", MODNAME);
if (!extraheaderkey.empty())
response.headers.SetHeader(extraheaderkey, extraheaderval);
- response.Send();
+ API->SendResponse(response);
}
- void OnEvent(Event& event)
+ bool IsAccessAllowed(HTTPRequest* http)
{
- if (event.id == "httpd_acl")
{
- ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd acl event");
- HTTPRequest* http = (HTTPRequest*)&event;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd acl event");
for (std::vector<HTTPACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl)
{
@@ -133,10 +124,10 @@ class ModuleHTTPAccessList : public Module
{
if (InspIRCd::Match(http->GetIP(), entry, ascii_case_insensitive_map))
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
BlockAccess(http, 403);
- return;
+ return false;
}
}
}
@@ -155,16 +146,16 @@ class ModuleHTTPAccessList : public Module
if (!allow_access)
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
BlockAccess(http, 403);
- return;
+ return false;
}
}
if (!this_acl->password.empty() && !this_acl->username.empty())
{
/* Password auth, first look to see if we have a basic authentication header */
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
if (http->headers->IsSet("Authorization"))
@@ -183,7 +174,7 @@ class ModuleHTTPAccessList : public Module
sep.GetToken(base64);
std::string userpass = Base64ToBin(base64);
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
irc::sepstream userpasspair(userpass, ':');
if (userpasspair.GetToken(user))
@@ -193,8 +184,8 @@ class ModuleHTTPAccessList : public Module
/* Access granted if username and password are correct */
if (user == this_acl->username && pass == this_acl->password)
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: password and username match");
- return;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: password and username match");
+ return true;
}
else
/* Invalid password */
@@ -213,22 +204,27 @@ class ModuleHTTPAccessList : public Module
/* No password given at all, access denied */
BlockAccess(http, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\"");
}
+ return false;
}
/* A path may only match one ACL (the first it finds in the config file) */
- return;
+ break;
}
}
}
+ return true;
}
- virtual ~ModuleHTTPAccessList()
+ ModResult OnHTTPACLCheck(HTTPRequest& req) CXX11_OVERRIDE
{
+ if (IsAccessAllowed(&req))
+ return MOD_RES_PASSTHRU;
+ return MOD_RES_DENY;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules", VF_VENDOR);
+ return Version("Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd dependent modules", VF_VENDOR);
}
};
diff --git a/src/modules/m_httpd_config.cpp b/src/modules/m_httpd_config.cpp
index 62314cd7e..f729b2774 100644
--- a/src/modules/m_httpd_config.cpp
+++ b/src/modules/m_httpd_config.cpp
@@ -19,96 +19,58 @@
#include "inspircd.h"
-#include "httpd.h"
-#include "protocol.h"
+#include "modules/httpd.h"
-/* $ModDesc: Allows for the server configuration to be viewed over HTTP via m_httpd.so */
-
-class ModuleHttpConfig : public Module
+class ModuleHttpConfig : public Module, public HTTPRequestEventListener
{
+ HTTPdAPI API;
+
public:
- void init()
+ ModuleHttpConfig()
+ : HTTPRequestEventListener(this)
+ , API(this)
{
- Implementation eventlist[] = { I_OnEvent };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- std::string Sanitize(const std::string &str)
+ ModResult OnHTTPRequest(HTTPRequest& request) CXX11_OVERRIDE
{
- std::string ret;
+ if ((request.GetURI() != "/config") && (request.GetURI() != "/config/"))
+ return MOD_RES_PASSTHRU;
- for (std::string::const_iterator x = str.begin(); x != str.end(); ++x)
- {
- switch (*x)
- {
- case '<':
- ret += "&lt;";
- break;
- case '>':
- ret += "&gt;";
- break;
- case '&':
- ret += "&amp;";
- break;
- case '"':
- ret += "&quot;";
- break;
- default:
- if (*x < 32 || *x > 126)
- {
- int n = *x;
- ret += ("&#" + ConvToStr(n) + ";");
- }
- else
- ret += *x;
- break;
- }
- }
- return ret;
- }
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling request for the HTTP /config route");
+ std::stringstream buffer;
- void OnEvent(Event& event)
- {
- std::stringstream data("");
-
- if (event.id == "httpd_url")
+ ConfigDataHash& config = ServerInstance->Config->config_data;
+ for (ConfigDataHash::const_iterator citer = config.begin(); citer != config.end(); ++citer)
{
- ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
- HTTPRequest* http = (HTTPRequest*)&event;
+ // Show the location of the tag in a comment.
+ ConfigTag* tag = citer->second;
+ buffer << "# " << tag->getTagLocation() << std::endl
+ << '<' << tag->tag << ' ';
- if ((http->GetURI() == "/config") || (http->GetURI() == "/config/"))
+ // Print out the tag with all keys aligned vertically.
+ const std::string indent(tag->tag.length() + 2, ' ');
+ const ConfigItems& items = tag->getItems();
+ for (ConfigItems::const_iterator kiter = items.begin(); kiter != items.end(); )
{
- data << "<html><head><title>InspIRCd Configuration</title></head><body>";
- data << "<h1>InspIRCd Configuration</h1><p>";
-
- for (ConfigDataHash::iterator x = ServerInstance->Config->config_data.begin(); x != ServerInstance->Config->config_data.end(); ++x)
- {
- data << "&lt;" << x->first << " ";
- ConfigTag* tag = x->second;
- for (std::vector<KeyVal>::const_iterator j = tag->getItems().begin(); j != tag->getItems().end(); j++)
- {
- data << Sanitize(j->first) << "=&quot;" << Sanitize(j->second) << "&quot; ";
- }
- data << "&gt;<br>";
- }
-
- data << "</body></html>";
- /* Send the document back to m_httpd */
- HTTPDocumentResponse response(this, *http, &data, 200);
- response.headers.SetHeader("X-Powered-By", "m_httpd_config.so");
- response.headers.SetHeader("Content-Type", "text/html");
- response.Send();
+ ConfigItems::const_iterator curr = kiter++;
+ buffer << curr->first << "=\"" << ServerConfig::Escape(curr->second) << '"';
+ if (kiter != items.end())
+ buffer << std::endl << indent;
}
+ buffer << '>' << std::endl << std::endl;
}
- }
- virtual ~ModuleHttpConfig()
- {
+ HTTPDocumentResponse response(this, request, &buffer, 200);
+ response.headers.SetHeader("X-Powered-By", MODNAME);
+ response.headers.SetHeader("Content-Type", "text/plain");
+ API->SendResponse(response);
+ return MOD_RES_DENY;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Allows for the server configuration to be viewed over HTTP via m_httpd.so", VF_VENDOR);
+ return Version("Allows for the server configuration to be viewed over HTTP via m_httpd", VF_VENDOR);
}
};
diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp
index e17bf514f..6db292eb3 100644
--- a/src/modules/m_httpd_stats.cpp
+++ b/src/modules/m_httpd_stats.cpp
@@ -22,22 +22,19 @@
#include "inspircd.h"
-#include "httpd.h"
+#include "modules/httpd.h"
#include "xline.h"
-#include "protocol.h"
-/* $ModDesc: Provides statistics over HTTP via m_httpd.so */
-
-class ModuleHttpStats : public Module
+class ModuleHttpStats : public Module, public HTTPRequestEventListener
{
- static std::map<char, char const*> const &entities;
+ static const insp::flat_map<char, char const*>& entities;
+ HTTPdAPI API;
public:
-
- void init()
+ ModuleHttpStats()
+ : HTTPRequestEventListener(this)
+ , API(this)
{
- Implementation eventlist[] = { I_OnEvent };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
std::string Sanitize(const std::string &str)
@@ -47,7 +44,7 @@ class ModuleHttpStats : public Module
for (std::string::const_iterator x = str.begin(); x != str.end(); ++x)
{
- std::map<char, char const*>::const_iterator it = entities.find(*x);
+ insp::flat_map<char, char const*>::const_iterator it = entities.find(*x);
if (it != entities.end())
{
@@ -91,35 +88,35 @@ class ModuleHttpStats : public Module
data << "</metadata>";
}
- void OnEvent(Event& event)
+ ModResult HandleRequest(HTTPRequest* http)
{
std::stringstream data("");
- if (event.id == "httpd_url")
{
- ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
- HTTPRequest* http = (HTTPRequest*)&event;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
{
- data << "<inspircdstats><server><name>" << ServerInstance->Config->ServerName << "</name><gecos>"
- << Sanitize(ServerInstance->Config->ServerDesc) << "</gecos><version>"
+ data << "<inspircdstats><server><name>" << ServerInstance->Config->ServerName << "</name><description>"
+ << Sanitize(ServerInstance->Config->ServerDesc) << "</description><version>"
<< Sanitize(ServerInstance->GetVersionString()) << "</version></server>";
data << "<general>";
- data << "<usercount>" << ServerInstance->Users->clientlist->size() << "</usercount>";
- data << "<channelcount>" << ServerInstance->chanlist->size() << "</channelcount>";
+ data << "<usercount>" << ServerInstance->Users->GetUsers().size() << "</usercount>";
+ data << "<channelcount>" << ServerInstance->GetChans().size() << "</channelcount>";
data << "<opercount>" << ServerInstance->Users->all_opers.size() << "</opercount>";
- data << "<socketcount>" << (ServerInstance->SE->GetUsedFds()) << "</socketcount><socketmax>" << ServerInstance->SE->GetMaxFds() << "</socketmax><socketengine>" << ServerInstance->SE->GetName() << "</socketengine>";
-
- time_t current_time = 0;
- current_time = ServerInstance->Time();
- time_t server_uptime = current_time - ServerInstance->startup_time;
- struct tm* stime;
- stime = gmtime(&server_uptime);
- data << "<uptime><days>" << stime->tm_yday << "</days><hours>" << stime->tm_hour << "</hours><mins>" << stime->tm_min << "</mins><secs>" << stime->tm_sec << "</secs><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
+ data << "<socketcount>" << (SocketEngine::GetUsedFds()) << "</socketcount><socketmax>" << SocketEngine::GetMaxFds() << "</socketmax>";
+ data << "<uptime><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
- data << "<isupport>" << Sanitize(ServerInstance->Config->data005) << "</isupport></general><xlines>";
+ data << "<isupport>";
+ const std::vector<Numeric::Numeric>& isupport = ServerInstance->ISupport.GetLines();
+ for (std::vector<Numeric::Numeric>::const_iterator i = isupport.begin(); i != isupport.end(); ++i)
+ {
+ const Numeric::Numeric& num = *i;
+ for (std::vector<std::string>::const_iterator j = num.GetParams().begin(); j != num.GetParams().end()-1; ++j)
+ data << "<token>" << Sanitize(*j) << "</token>" << std::endl;
+ }
+ data << "</isupport></general><xlines>";
std::vector<std::string> xltypes = ServerInstance->XLines->GetAllTypes();
for (std::vector<std::string>::iterator it = xltypes.begin(); it != xltypes.end(); ++it)
{
@@ -138,35 +135,35 @@ class ModuleHttpStats : public Module
}
data << "</xlines><modulelist>";
- std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
- for (std::vector<std::string>::iterator i = module_names.begin(); i != module_names.end(); ++i)
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
{
- Module* m = ServerInstance->Modules->Find(i->c_str());
- Version v = m->GetVersion();
- data << "<module><name>" << *i << "</name><description>" << Sanitize(v.description) << "</description></module>";
+ Version v = i->second->GetVersion();
+ data << "<module><name>" << i->first << "</name><description>" << Sanitize(v.description) << "</description></module>";
}
data << "</modulelist><channellist>";
- for (chan_hash::const_iterator a = ServerInstance->chanlist->begin(); a != ServerInstance->chanlist->end(); ++a)
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
{
- Channel* c = a->second;
+ Channel* c = i->second;
data << "<channel>";
- data << "<usercount>" << c->GetUsers()->size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
+ data << "<usercount>" << c->GetUsers().size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
data << "<channeltopic>";
data << "<topictext>" << Sanitize(c->topic) << "</topictext>";
data << "<setby>" << Sanitize(c->setby) << "</setby>";
data << "<settime>" << c->topicset << "</settime>";
data << "</channeltopic>";
data << "<channelmodes>" << Sanitize(c->ChanModes(true)) << "</channelmodes>";
- const UserMembList* ulist = c->GetUsers();
- for (UserMembCIter x = ulist->begin(); x != ulist->end(); ++x)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator x = ulist.begin(); x != ulist.end(); ++x)
{
Membership* memb = x->second;
data << "<channelmember><uid>" << memb->user->uuid << "</uid><privs>"
- << Sanitize(c->GetAllPrefixChars(x->first)) << "</privs><modes>"
+ << Sanitize(memb->GetAllPrefixChars()) << "</privs><modes>"
<< memb->modes << "</modes>";
DumpMeta(data, memb);
data << "</channelmember>";
@@ -179,23 +176,24 @@ class ModuleHttpStats : public Module
data << "</channellist><userlist>";
- for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); ++a)
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
{
- User* u = a->second;
+ User* u = i->second;
data << "<user>";
data << "<nickname>" << u->nick << "</nickname><uuid>" << u->uuid << "</uuid><realhost>"
- << u->host << "</realhost><displayhost>" << u->dhost << "</displayhost><gecos>"
- << Sanitize(u->fullname) << "</gecos><server>" << u->server << "</server>";
- if (IS_AWAY(u))
+ << u->GetRealHost() << "</realhost><displayhost>" << u->GetDisplayedHost() << "</displayhost><realname>"
+ << Sanitize(u->GetRealName()) << "</realname><server>" << u->server->GetName() << "</server>";
+ if (u->IsAway())
data << "<away>" << Sanitize(u->awaymsg) << "</away><awaytime>" << u->awaytime << "</awaytime>";
- if (IS_OPER(u))
- data << "<opertype>" << Sanitize(u->oper->NameStr()) << "</opertype>";
- data << "<modes>" << u->FormatModes() << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
+ if (u->IsOper())
+ data << "<opertype>" << Sanitize(u->oper->name) << "</opertype>";
+ data << "<modes>" << u->GetModeLetters().substr(1) << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
LocalUser* lu = IS_LOCAL(u);
if (lu)
data << "<port>" << lu->GetServerPort() << "</port><servaddr>"
- << irc::sockets::satouser(lu->server_sa) << "</servaddr>";
+ << lu->server_sa.str() << "</servaddr>";
data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
DumpMeta(data, u);
@@ -205,15 +203,15 @@ class ModuleHttpStats : public Module
data << "</userlist><serverlist>";
- ProtoServerList sl;
+ ProtocolInterface::ServerList sl;
ServerInstance->PI->GetServerList(sl);
- for (ProtoServerList::iterator b = sl.begin(); b != sl.end(); ++b)
+ for (ProtocolInterface::ServerList::const_iterator b = sl.begin(); b != sl.end(); ++b)
{
data << "<server>";
data << "<servername>" << b->servername << "</servername>";
data << "<parentname>" << b->parentname << "</parentname>";
- data << "<gecos>" << Sanitize(b->gecos) << "</gecos>";
+ data << "<description>" << Sanitize(b->description) << "</description>";
data << "<usercount>" << b->usercount << "</usercount>";
// This is currently not implemented, so, commented out.
// data << "<opercount>" << b->opercount << "</opercount>";
@@ -221,30 +219,41 @@ class ModuleHttpStats : public Module
data << "</server>";
}
- data << "</serverlist></inspircdstats>";
+ data << "</serverlist><commandlist>";
+
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ data << "<command><name>" << i->second->name << "</name><usecount>" << i->second->use_count << "</usecount></command>";
+ }
+
+ data << "</commandlist></inspircdstats>";
/* Send the document back to m_httpd */
HTTPDocumentResponse response(this, *http, &data, 200);
- response.headers.SetHeader("X-Powered-By", "m_httpd_stats.so");
+ response.headers.SetHeader("X-Powered-By", MODNAME);
response.headers.SetHeader("Content-Type", "text/xml");
- response.Send();
+ API->SendResponse(response);
+ return MOD_RES_DENY; // Handled
}
}
+ return MOD_RES_PASSTHRU;
}
- virtual ~ModuleHttpStats()
+ ModResult OnHTTPRequest(HTTPRequest& req) CXX11_OVERRIDE
{
+ return HandleRequest(&req);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides statistics over HTTP via m_httpd.so", VF_VENDOR);
+ return Version("Provides statistics over HTTP via m_httpd", VF_VENDOR);
}
};
-static std::map<char, char const*> const &init_entities()
+static const insp::flat_map<char, char const*>& init_entities()
{
- static std::map<char, char const*> entities;
+ static insp::flat_map<char, char const*> entities;
entities['<'] = "lt";
entities['>'] = "gt";
entities['&'] = "amp";
@@ -252,6 +261,6 @@ static std::map<char, char const*> const &init_entities()
return entities;
}
-std::map<char, char const*> const &ModuleHttpStats::entities = init_entities ();
+const insp::flat_map<char, char const*>& ModuleHttpStats::entities = init_entities();
MODULE_INIT(ModuleHttpStats)
diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp
index f0ced1db7..ca12a9ba3 100644
--- a/src/modules/m_ident.cpp
+++ b/src/modules/m_ident.cpp
@@ -24,8 +24,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for RFC1413 ident lookups */
-
/* --------------------------------------------------------------
* Note that this is the third incarnation of m_ident. The first
* two attempts were pretty crashy, mainly due to the fact we tried
@@ -94,7 +92,7 @@ class IdentRequestSocket : public EventHandler
{
age = ServerInstance->Time();
- SetFd(socket(user->server_sa.sa.sa_family, SOCK_STREAM, 0));
+ SetFd(socket(user->server_sa.family(), SOCK_STREAM, 0));
if (GetFd() == -1)
throw ModuleException("Could not create socket");
@@ -107,7 +105,7 @@ class IdentRequestSocket : public EventHandler
memcpy(&bindaddr, &user->server_sa, sizeof(bindaddr));
memcpy(&connaddr, &user->client_sa, sizeof(connaddr));
- if (connaddr.sa.sa_family == AF_INET6)
+ if (connaddr.family() == AF_INET6)
{
bindaddr.in6.sin6_port = 0;
connaddr.in6.sin6_port = htons(113);
@@ -119,39 +117,38 @@ class IdentRequestSocket : public EventHandler
}
/* Attempt to bind (ident requests must come from the ip the query is referring to */
- if (ServerInstance->SE->Bind(GetFd(), bindaddr) < 0)
+ if (SocketEngine::Bind(GetFd(), bindaddr) < 0)
{
this->Close();
throw ModuleException("failed to bind()");
}
- ServerInstance->SE->NonBlocking(GetFd());
+ SocketEngine::NonBlocking(GetFd());
/* Attempt connection (nonblocking) */
- if (ServerInstance->SE->Connect(this, &connaddr.sa, connaddr.sa_size()) == -1 && errno != EINPROGRESS)
+ if (SocketEngine::Connect(this, connaddr) == -1 && errno != EINPROGRESS)
{
this->Close();
throw ModuleException("connect() failed");
}
/* Add fd to socket engine */
- if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_READ | FD_WANT_POLL_WRITE))
+ if (!SocketEngine::AddFd(this, FD_WANT_NO_READ | FD_WANT_POLL_WRITE))
{
this->Close();
throw ModuleException("out of fds");
}
}
- virtual void OnConnected()
+ void OnEventHandlerWrite() CXX11_OVERRIDE
{
- ServerInstance->Logs->Log("m_ident",DEBUG,"OnConnected()");
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
char req[32];
/* Build request in the form 'localport,remoteport\r\n' */
int req_size;
- if (user->client_sa.sa.sa_family == AF_INET6)
+ if (user->client_sa.family() == AF_INET6)
req_size = snprintf(req, sizeof(req), "%d,%d\r\n",
ntohs(user->client_sa.in6.sin6_port), ntohs(user->server_sa.in6.sin6_port));
else
@@ -161,34 +158,10 @@ class IdentRequestSocket : public EventHandler
/* Send failed if we didnt write the whole ident request --
* might as well give up if this happens!
*/
- if (ServerInstance->SE->Send(this, req, req_size, 0) < req_size)
+ if (SocketEngine::Send(this, req, req_size, 0) < req_size)
done = true;
}
- virtual void HandleEvent(EventType et, int errornum = 0)
- {
- switch (et)
- {
- case EVENT_READ:
- /* fd readable event, received ident response */
- ReadResponse();
- break;
- case EVENT_WRITE:
- /* fd writeable event, successfully connected! */
- OnConnected();
- break;
- case EVENT_ERROR:
- /* fd error event, ohshi- */
- ServerInstance->Logs->Log("m_ident",DEBUG,"EVENT_ERROR");
- /* We *must* Close() here immediately or we get a
- * huge storm of EVENT_ERROR events!
- */
- Close();
- done = true;
- break;
- }
- }
-
void Close()
{
/* Remove ident socket from engine, and close it, but dont detatch it
@@ -196,10 +169,8 @@ class IdentRequestSocket : public EventHandler
*/
if (GetFd() > -1)
{
- ServerInstance->Logs->Log("m_ident",DEBUG,"Close ident socket %d", GetFd());
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(GetFd());
- this->SetFd(-1);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Close ident socket %d", GetFd());
+ SocketEngine::Close(this);
}
}
@@ -208,13 +179,13 @@ class IdentRequestSocket : public EventHandler
return done;
}
- void ReadResponse()
+ void OnEventHandlerRead() CXX11_OVERRIDE
{
/* We don't really need to buffer for incomplete replies here, since IDENT replies are
* extremely short - there is *no* sane reason it'd be in more than one packet
*/
- char ibuf[MAXBUF];
- int recvresult = ServerInstance->SE->Recv(this, ibuf, MAXBUF-1, 0);
+ char ibuf[256];
+ int recvresult = SocketEngine::Recv(this, ibuf, sizeof(ibuf)-1, 0);
/* Close (but don't delete from memory) our socket
* and flag as done since the ident lookup has finished
@@ -228,7 +199,7 @@ class IdentRequestSocket : public EventHandler
if (recvresult < 3)
return;
- ServerInstance->Logs->Log("m_ident",DEBUG,"ReadResponse()");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "ReadResponse()");
/* Truncate at the first null character, but first make sure
* there is at least one null char (at the end of the buffer).
@@ -260,67 +231,70 @@ class IdentRequestSocket : public EventHandler
* we're done.
*/
result += *i;
- if (!ServerInstance->IsIdent(result.c_str()))
+ if (!ServerInstance->IsIdent(result))
{
result.erase(result.end()-1);
break;
}
}
}
-};
-class ModuleIdent : public Module
-{
- int RequestTimeout;
- SimpleExtItem<IdentRequestSocket> ext;
- public:
- ModuleIdent() : ext("ident_socket", this)
+ void OnEventHandlerError(int errornum) CXX11_OVERRIDE
{
+ Close();
+ done = true;
}
- void init()
+ CullResult cull() CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(ext);
- OnRehash(NULL);
- Implementation eventlist[] = {
- I_OnRehash, I_OnUserInit, I_OnCheckReady,
- I_OnUserDisconnect, I_OnSetConnectClass
- };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ Close();
+ return EventHandler::cull();
}
+};
- ~ModuleIdent()
+class ModuleIdent : public Module
+{
+ int RequestTimeout;
+ bool NoLookupPrefix;
+ SimpleExtItem<IdentRequestSocket, stdalgo::culldeleter> ext;
+ public:
+ ModuleIdent()
+ : ext("ident_socket", ExtensionItem::EXT_USER, this)
{
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for RFC1413 ident lookups", VF_VENDOR);
}
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- RequestTimeout = ServerInstance->Config->ConfValue("ident")->getInt("timeout", 5);
- if (!RequestTimeout)
- RequestTimeout = 5;
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ident");
+ RequestTimeout = tag->getDuration("timeout", 5, 1);
+ NoLookupPrefix = tag->getBool("nolookupprefix", false);
}
- void OnUserInit(LocalUser *user)
+ void OnUserInit(LocalUser *user) CXX11_OVERRIDE
{
+ // The ident protocol requires that clients are connecting over a protocol with ports.
+ if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
+ return;
+
ConfigTag* tag = user->MyClass->config;
if (!tag->getBool("useident", true))
return;
- user->WriteServ("NOTICE Auth :*** Looking up your ident...");
+ user->WriteNotice("*** Looking up your ident...");
try
{
- IdentRequestSocket *isock = new IdentRequestSocket(IS_LOCAL(user));
+ IdentRequestSocket *isock = new IdentRequestSocket(user);
ext.set(user, isock);
}
catch (ModuleException &e)
{
- ServerInstance->Logs->Log("m_ident",DEBUG,"Ident exception: %s", e.GetReason());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Ident exception: " + e.GetReason());
}
}
@@ -328,18 +302,17 @@ class ModuleIdent : public Module
* creating a Timer object and especially better than creating a
* Timer per ident lookup!
*/
- virtual ModResult OnCheckReady(LocalUser *user)
+ ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
{
/* Does user have an ident socket attached at all? */
IdentRequestSocket *isock = ext.get(user);
if (!isock)
{
- ServerInstance->Logs->Log("m_ident",DEBUG, "No ident socket :(");
+ if ((NoLookupPrefix) && (user->ident[0] != '~'))
+ user->ident.insert(user->ident.begin(), 1, '~');
return MOD_RES_PASSTHRU;
}
- ServerInstance->Logs->Log("m_ident",DEBUG, "Has ident_socket");
-
time_t compare = isock->age;
compare += RequestTimeout;
@@ -347,28 +320,24 @@ class ModuleIdent : public Module
if (ServerInstance->Time() >= compare)
{
/* Ident timeout */
- user->WriteServ("NOTICE Auth :*** Ident request timed out.");
- ServerInstance->Logs->Log("m_ident",DEBUG, "Timeout");
+ user->WriteNotice("*** Ident request timed out.");
}
else if (!isock->HasResult())
{
// time still good, no result yet... hold the registration
- ServerInstance->Logs->Log("m_ident",DEBUG, "No result yet");
return MOD_RES_DENY;
}
- ServerInstance->Logs->Log("m_ident",DEBUG, "Yay, result!");
-
/* wooo, got a result (it will be good, or bad) */
if (isock->result.empty())
{
user->ident.insert(user->ident.begin(), 1, '~');
- user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident.c_str());
+ user->WriteNotice("*** Could not find your ident, using " + user->ident + " instead.");
}
else
{
user->ident = isock->result;
- user->WriteServ("NOTICE Auth :*** Found your ident, '%s'", user->ident.c_str());
+ user->WriteNotice("*** Found your ident, '" + user->ident + "'");
}
user->InvalidateCache();
@@ -377,35 +346,12 @@ class ModuleIdent : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
if (myclass->config->getBool("requireident") && user->ident[0] == '~')
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
-
- virtual void OnCleanup(int target_type, void *item)
- {
- /* Module unloading, tidy up users */
- if (target_type == TYPE_USER)
- {
- LocalUser* user = IS_LOCAL((User*) item);
- if (user)
- OnUserDisconnect(user);
- }
- }
-
- virtual void OnUserDisconnect(LocalUser *user)
- {
- /* User disconnect (generic socket detatch event) */
- IdentRequestSocket *isock = ext.get(user);
- if (isock)
- {
- isock->Close();
- ext.unset(user);
- }
- }
};
MODULE_INIT(ModuleIdent)
-
diff --git a/src/modules/m_inviteexception.cpp b/src/modules/m_inviteexception.cpp
index 747a3b30a..1317e6e57 100644
--- a/src/modules/m_inviteexception.cpp
+++ b/src/modules/m_inviteexception.cpp
@@ -22,10 +22,7 @@
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +I channel mode */
-/* $ModDep: ../../include/u_listmode.h */
+#include "listmode.h"
/*
* Written by Om <om@inspircd.org>, April 2005.
@@ -54,27 +51,17 @@ public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(ie);
-
- OnRehash(NULL);
- ie.DoImplements(this);
- Implementation eventlist[] = { I_On005Numeric, I_OnCheckInvite, I_OnCheckKey, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" INVEX=I");
+ tokens["INVEX"] = ConvToStr(ie.GetModeChar());
}
- ModResult OnCheckInvite(User* user, Channel* chan)
+ ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
{
- modelist* list = ie.extItem.get(chan);
+ ListModeBase::ModeList* list = ie.GetList(chan);
if (list)
{
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
if (chan->CheckBan(user, it->mask))
{
@@ -86,25 +73,20 @@ public:
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckKey(User* user, Channel* chan, const std::string& key)
+ ModResult OnCheckKey(User* user, Channel* chan, const std::string& key) CXX11_OVERRIDE
{
if (invite_bypass_key)
return OnCheckInvite(user, chan);
return MOD_RES_PASSTHRU;
}
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- ie.DoSyncChannel(chan, proto, opaque);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
invite_bypass_key = ServerInstance->Config->ConfValue("inviteexception")->getBool("bypasskey", true);
ie.DoRehash();
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the +I channel mode", VF_VENDOR);
}
diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp
index b7dd0e81b..14b1cf8a1 100644
--- a/src/modules/m_ircv3.cpp
+++ b/src/modules/m_ircv3.cpp
@@ -16,227 +16,161 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* $ModDesc: Provides support for extended-join, away-notify and account-notify CAP capabilities */
-
#include "inspircd.h"
-#include "account.h"
-#include "m_cap.h"
+#include "modules/account.h"
+#include "modules/away.h"
+#include "modules/cap.h"
+#include "modules/ircv3.h"
-class ModuleIRCv3 : public Module
+class AwayMessage : public ClientProtocol::Message
{
- GenericCap cap_accountnotify;
- GenericCap cap_awaynotify;
- GenericCap cap_extendedjoin;
- bool accountnotify;
- bool awaynotify;
- bool extendedjoin;
-
- CUList last_excepts;
-
- void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
+ public:
+ AwayMessage(User* user)
+ : ClientProtocol::Message("AWAY", user)
{
- UserChanList chans(user->chans);
-
- std::map<User*, bool> exceptions;
- FOREACH_MOD(I_OnBuildNeighborList, OnBuildNeighborList(user, chans, exceptions));
-
- // Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
- for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if ((u) && (i->second) && (ext.get(u)))
- u->Write(line);
- }
-
- // Now consider sending it to all other users who has at least a common channel with the user
- std::set<User*> already_sent;
- for (UCListIter i = chans.begin(); i != chans.end(); ++i)
- {
- const UserMembList* userlist = (*i)->GetUsers();
- for (UserMembList::const_iterator m = userlist->begin(); m != userlist->end(); ++m)
- {
- /*
- * Send the line if the channel member in question meets all of the following criteria:
- * - local
- * - not the user who is doing the action (i.e. whose channels we're iterating)
- * - has the given extension
- * - not on the except list built by modules
- * - we haven't sent the line to the member yet
- *
- */
- LocalUser* member = IS_LOCAL(m->first);
- if ((member) && (member != user) && (ext.get(member)) && (exceptions.find(member) == exceptions.end()) && (already_sent.insert(member).second))
- member->Write(line);
- }
- }
+ SetParams(user, user->awaymsg);
}
- public:
- ModuleIRCv3() : cap_accountnotify(this, "account-notify"),
- cap_awaynotify(this, "away-notify"),
- cap_extendedjoin(this, "extended-join")
+ AwayMessage()
+ : ClientProtocol::Message("AWAY")
{
}
- void init()
+ void SetParams(User* user, const std::string& awaymsg)
{
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserJoin, I_OnPostJoin, I_OnSetAway, I_OnEvent, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ // Going away: 1 parameter which is the away reason
+ // Back from away: no parameter
+ if (!awaymsg.empty())
+ PushParam(awaymsg);
}
+};
+
+class JoinHook : public ClientProtocol::EventHook
+{
+ ClientProtocol::Events::Join extendedjoinmsg;
- void OnRehash(User* user)
+ public:
+ const std::string asterisk;
+ ClientProtocol::EventProvider awayprotoev;
+ AwayMessage awaymsg;
+ Cap::Capability extendedjoincap;
+ Cap::Capability awaycap;
+
+ JoinHook(Module* mod)
+ : ClientProtocol::EventHook(mod, "JOIN")
+ , asterisk(1, '*')
+ , awayprotoev(mod, "AWAY")
+ , extendedjoincap(mod, "extended-join")
+ , awaycap(mod, "away-notify")
{
- ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
- accountnotify = conf->getBool("accountnotify", conf->getBool("accoutnotify", true));
- awaynotify = conf->getBool("awaynotify", true);
- extendedjoin = conf->getBool("extendedjoin", true);
}
- void OnEvent(Event& ev)
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
{
- if (awaynotify)
- cap_awaynotify.HandleEvent(ev);
- if (extendedjoin)
- cap_extendedjoin.HandleEvent(ev);
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+
+ // An extended join has two extra parameters:
+ // First the account name of the joining user or an asterisk if the user is not logged in.
+ // The second parameter is the realname of the joining user.
+
+ Membership* const memb = join.GetMember();
+ const std::string* account = &asterisk;
+ const AccountExtItem* const accountext = GetAccountExtItem();
+ if (accountext)
+ {
+ const std::string* accountname = accountext->get(memb->user);
+ if (accountname)
+ account = accountname;
+ }
+
+ extendedjoinmsg.ClearParams();
+ extendedjoinmsg.SetSource(join);
+ extendedjoinmsg.PushParamRef(memb->chan->name);
+ extendedjoinmsg.PushParamRef(*account);
+ extendedjoinmsg.PushParamRef(memb->user->GetRealName());
- if (accountnotify)
+ awaymsg.ClearParams();
+ if ((memb->user->IsAway()) && (awaycap.IsActive()))
{
- cap_accountnotify.HandleEvent(ev);
-
- if (ev.id == "account_login")
- {
- AccountEvent* ae = static_cast<AccountEvent*>(&ev);
-
- // :nick!user@host ACCOUNT account
- // or
- // :nick!user@host ACCOUNT *
- std::string line = ":" + ae->user->GetFullHost() + " ACCOUNT ";
- if (ae->account.empty())
- line += "*";
- else
- line += std::string(ae->account);
-
- WriteNeighboursWithExt(ae->user, line, cap_accountnotify.ext);
- }
+ awaymsg.SetSource(join);
+ awaymsg.SetParams(memb->user, memb->user->awaymsg);
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
{
- // Remember who is not going to see the JOIN because of other modules
- if ((awaynotify) && (IS_AWAY(memb->user)))
- last_excepts = excepts;
+ if (extendedjoincap.get(user))
+ messagelist.front() = &extendedjoinmsg;
- if (!extendedjoin)
- return;
+ if ((!awaymsg.GetParams().empty()) && (awaycap.get(user)))
+ messagelist.push_back(&awaymsg);
- /*
- * Send extended joins to clients who have the extended-join capability.
- * An extended join looks like this:
- *
- * :nick!user@host JOIN #chan account :realname
- *
- * account is the joining user's account if he's logged in, otherwise it's an asterisk (*).
- */
+ return MOD_RES_PASSTHRU;
+ }
+};
- std::string line;
- std::string mode;
+class ModuleIRCv3
+ : public Module
+ , public AccountEventListener
+ , public Away::EventListener
+{
+ Cap::Capability cap_accountnotify;
+ JoinHook joinhook;
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
- {
- // Send the extended join line if the current member is local, has the extended-join cap and isn't excepted
- User* member = IS_LOCAL(it->first);
- if ((member) && (cap_extendedjoin.ext.get(member)) && (excepts.find(member) == excepts.end()))
- {
- // Construct the lines we're going to send if we haven't constructed them already
- if (line.empty())
- {
- bool has_account = false;
- line = ":" + memb->user->GetFullHost() + " JOIN " + memb->chan->name + " ";
- const AccountExtItem* accountext = GetAccountExtItem();
- if (accountext)
- {
- std::string* accountname;
- accountname = accountext->get(memb->user);
- if (accountname)
- {
- line += *accountname;
- has_account = true;
- }
- }
-
- if (!has_account)
- line += "*";
-
- line += " :" + memb->user->fullname;
-
- // If the joining user received privileges from another module then we must send them as well,
- // since silencing the normal join means the MODE will be silenced as well
- if (!memb->modes.empty())
- {
- const std::string& modefrom = ServerInstance->Config->CycleHostsFromUser ? memb->user->GetFullHost() : ServerInstance->Config->ServerName;
- mode = ":" + modefrom + " MODE " + memb->chan->name + " +" + memb->modes;
-
- for (unsigned int i = 0; i < memb->modes.length(); i++)
- mode += " " + memb->user->nick;
- }
- }
-
- // Write the JOIN and the MODE, if any
- member->Write(line);
- if ((!mode.empty()) && (member != memb->user))
- member->Write(mode);
-
- // Prevent the core from sending the JOIN and MODE to this user
- excepts.insert(it->first);
- }
- }
+ ClientProtocol::EventProvider accountprotoev;
+
+ public:
+ ModuleIRCv3()
+ : AccountEventListener(this)
+ , Away::EventListener(this)
+ , cap_accountnotify(this, "account-notify")
+ , joinhook(this)
+ , accountprotoev(this, "ACCOUNT")
+ {
}
- ModResult OnSetAway(User* user, const std::string &awaymsg)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- if (awaynotify)
- {
- // Going away: n!u@h AWAY :reason
- // Back from away: n!u@h AWAY
- std::string line = ":" + user->GetFullHost() + " AWAY";
- if (!awaymsg.empty())
- line += " :" + awaymsg;
+ ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
+ cap_accountnotify.SetActive(conf->getBool("accountnotify", true));
+ joinhook.awaycap.SetActive(conf->getBool("awaynotify", true));
+ joinhook.extendedjoincap.SetActive(conf->getBool("extendedjoin", true));
+ }
- WriteNeighboursWithExt(user, line, cap_awaynotify.ext);
- }
- return MOD_RES_PASSTHRU;
+ void OnAccountChange(User* user, const std::string& newaccount) CXX11_OVERRIDE
+ {
+ // Logged in: 1 parameter which is the account name
+ // Logged out: 1 parameter which is a "*"
+ ClientProtocol::Message msg("ACCOUNT", user);
+ const std::string& param = (newaccount.empty() ? joinhook.asterisk : newaccount);
+ msg.PushParamRef(param);
+ ClientProtocol::Event accountevent(accountprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, accountevent, cap_accountnotify);
}
- void OnPostJoin(Membership *memb)
+ void OnUserAway(User* user) CXX11_OVERRIDE
{
- if ((!awaynotify) || (!IS_AWAY(memb->user)))
+ if (!joinhook.awaycap.IsActive())
return;
- std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
-
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
- {
- // Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
- User* member = IS_LOCAL(it->first);
- if ((member) && (cap_awaynotify.ext.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
- {
- member->Write(line);
- }
- }
-
- last_excepts.clear();
+ // Going away: n!u@h AWAY :reason
+ AwayMessage msg(user);
+ ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
}
- void Prioritize()
+ void OnUserBack(User* user) CXX11_OVERRIDE
{
- ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
+ if (!joinhook.awaycap.IsActive())
+ return;
+
+ // Back from away: n!u@h AWAY
+ AwayMessage msg(user);
+ ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for extended-join, away-notify and account-notify CAP capabilities", VF_VENDOR);
}
diff --git a/src/modules/m_ircv3_accounttag.cpp b/src/modules/m_ircv3_accounttag.cpp
new file mode 100644
index 000000000..45fcf8022
--- /dev/null
+++ b/src/modules/m_ircv3_accounttag.cpp
@@ -0,0 +1,62 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ircv3.h"
+#include "modules/account.h"
+
+class AccountTag : public IRCv3::CapTag<AccountTag>
+{
+ public:
+ const std::string* GetValue(const ClientProtocol::Message& msg) const
+ {
+ User* const user = msg.GetSourceUser();
+ if (!user)
+ return NULL;
+
+ AccountExtItem* const accextitem = GetAccountExtItem();
+ if (!accextitem)
+ return NULL;
+
+ return accextitem->get(user);
+ }
+
+ AccountTag(Module* mod)
+ : IRCv3::CapTag<AccountTag>(mod, "account-tag", "account")
+ {
+ }
+};
+
+class ModuleIRCv3AccountTag : public Module
+{
+ AccountTag tag;
+
+ public:
+ ModuleIRCv3AccountTag()
+ : tag(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the account-tag IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3AccountTag)
diff --git a/src/modules/m_ircv3_batch.cpp b/src/modules/m_ircv3_batch.cpp
new file mode 100644
index 000000000..df2b00f49
--- /dev/null
+++ b/src/modules/m_ircv3_batch.cpp
@@ -0,0 +1,216 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/ircv3_batch.h"
+
+class BatchMessage : public ClientProtocol::Message
+{
+ public:
+ BatchMessage(const IRCv3::Batch::Batch& batch, bool start)
+ : ClientProtocol::Message("BATCH", ServerInstance->Config->ServerName)
+ {
+ char c = (start ? '+' : '-');
+ PushParam(std::string(1, c) + batch.GetRefTagStr());
+ if ((start) && (!batch.GetType().empty()))
+ PushParamRef(batch.GetType());
+ }
+};
+
+/** Extra structure allocated only for running batches, containing objects only relevant for
+ * that specific run of the batch.
+ */
+struct IRCv3::Batch::BatchInfo
+{
+ /** List of users that have received the batch start message
+ */
+ std::vector<LocalUser*> users;
+ BatchMessage startmsg;
+ ClientProtocol::Event startevent;
+
+ BatchInfo(ClientProtocol::EventProvider& protoevprov, IRCv3::Batch::Batch& b)
+ : startmsg(b, true)
+ , startevent(protoevprov, startmsg)
+ {
+ }
+};
+
+class IRCv3::Batch::ManagerImpl : public Manager
+{
+ typedef std::vector<Batch*> BatchList;
+
+ Cap::Capability cap;
+ ClientProtocol::EventProvider protoevprov;
+ LocalIntExt batchbits;
+ BatchList active_batches;
+ bool unloading;
+
+ bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
+ {
+ if (!cap.get(user))
+ return false;
+
+ Batch& batch = *static_cast<Batch*>(tagdata.provdata);
+ // Check if this is the first message the user is getting that is part of the batch
+ const intptr_t bits = batchbits.get(user);
+ if (!(bits & batch.GetBit()))
+ {
+ // Send the start batch command ("BATCH +reftag TYPE"), remember the user so we can send them a
+ // "BATCH -reftag" message later when the batch ends and set the flag we just checked so this is
+ // only done once per user per batch.
+ batchbits.set(user, (bits | batch.GetBit()));
+ batch.batchinfo->users.push_back(user);
+ user->Send(batch.batchinfo->startevent);
+ }
+
+ return true;
+ }
+
+ unsigned int NextFreeId() const
+ {
+ if (active_batches.empty())
+ return 0;
+ return active_batches.back()->GetId()+1;
+ }
+
+ public:
+ ManagerImpl(Module* mod)
+ : Manager(mod)
+ , cap(mod, "batch")
+ , protoevprov(mod, "BATCH")
+ , batchbits("batchbits", ExtensionItem::EXT_USER, mod)
+ , unloading(false)
+ {
+ }
+
+ void Init()
+ {
+ // Set batchbits to 0 for all users in case we were reloaded and the previous, now meaningless,
+ // batchbits are set on users
+ const UserManager::LocalList& users = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ LocalUser* const user = *i;
+ batchbits.set(user, 0);
+ }
+ }
+
+ void Shutdown()
+ {
+ unloading = true;
+ while (!active_batches.empty())
+ ManagerImpl::End(*active_batches.back());
+ }
+
+ void RemoveFromAll(LocalUser* user)
+ {
+ const intptr_t bits = batchbits.get(user);
+
+ // User is quitting, remove them from all lists
+ for (BatchList::iterator i = active_batches.begin(); i != active_batches.end(); ++i)
+ {
+ Batch& batch = **i;
+ // Check the bit first to avoid list scan in case they're not on the list
+ if ((bits & batch.GetBit()) != 0)
+ stdalgo::vector::swaperase(batch.batchinfo->users, user);
+ }
+ }
+
+ void Start(Batch& batch) CXX11_OVERRIDE
+ {
+ if (unloading)
+ return;
+
+ if (batch.IsRunning())
+ return; // Already started, don't start again
+
+ const size_t id = NextFreeId();
+ if (id >= MAX_BATCHES)
+ return;
+
+ batch.Setup(id);
+ // Set the manager field which Batch::IsRunning() checks and is also used by AddToBatch()
+ // to set the message tag
+ batch.manager = this;
+ batch.batchinfo = new IRCv3::Batch::BatchInfo(protoevprov, batch);
+ batch.batchstartmsg = &batch.batchinfo->startmsg;
+ active_batches.push_back(&batch);
+ }
+
+ void End(Batch& batch) CXX11_OVERRIDE
+ {
+ if (!batch.IsRunning())
+ return;
+
+ // Mark batch as stopped
+ batch.manager = NULL;
+
+ BatchInfo& batchinfo = *batch.batchinfo;
+ // Send end batch message to all users who got the batch start message and unset bit so it can be reused
+ BatchMessage endbatchmsg(batch, false);
+ ClientProtocol::Event endbatchevent(protoevprov, endbatchmsg);
+ for (std::vector<LocalUser*>::const_iterator i = batchinfo.users.begin(); i != batchinfo.users.end(); ++i)
+ {
+ LocalUser* const user = *i;
+ user->Send(endbatchevent);
+ batchbits.set(user, batchbits.get(user) & ~batch.GetBit());
+ }
+
+ // erase() not swaperase because the reftag generation logic depends on the order of the elements
+ stdalgo::erase(active_batches, &batch);
+ delete batch.batchinfo;
+ batch.batchinfo = NULL;
+ }
+};
+
+class ModuleIRCv3Batch : public Module
+{
+ IRCv3::Batch::ManagerImpl manager;
+
+ public:
+ ModuleIRCv3Batch()
+ : manager(this)
+ {
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ manager.Init();
+ }
+
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+ {
+ if (mod == this)
+ manager.Shutdown();
+ }
+
+ void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ // Remove the user from all internal lists
+ manager.RemoveFromAll(user);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the batch IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3Batch)
diff --git a/src/modules/m_ircv3_capnotify.cpp b/src/modules/m_ircv3_capnotify.cpp
new file mode 100644
index 000000000..6190f15a8
--- /dev/null
+++ b/src/modules/m_ircv3_capnotify.cpp
@@ -0,0 +1,185 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/reload.h"
+
+class CapNotify : public Cap::Capability
+{
+ bool OnRequest(LocalUser* user, bool add) CXX11_OVERRIDE
+ {
+ // Users using the negotiation protocol v3.2 or newer may not turn off cap-notify
+ if ((!add) && (GetProtocol(user) != Cap::CAP_LEGACY))
+ return false;
+ return true;
+ }
+
+ bool OnList(LocalUser* user) CXX11_OVERRIDE
+ {
+ // If the client supports 3.2 enable cap-notify for them
+ if (GetProtocol(user) != Cap::CAP_LEGACY)
+ set(user, true);
+ return true;
+ }
+
+ public:
+ CapNotify(Module* mod)
+ : Cap::Capability(mod, "cap-notify")
+ {
+ }
+};
+
+class CapNotifyMessage : public Cap::MessageBase
+{
+ public:
+ CapNotifyMessage(bool add, const std::string& capname)
+ : Cap::MessageBase((add ? "NEW" : "DEL"))
+ {
+ PushParamRef(capname);
+ }
+};
+
+class CapNotifyValueMessage : public Cap::MessageBase
+{
+ std::string s;
+ const std::string::size_type pos;
+
+ public:
+ CapNotifyValueMessage(const std::string& capname)
+ : Cap::MessageBase("NEW")
+ , s(capname)
+ , pos(s.size()+1)
+ {
+ s.push_back('=');
+ PushParamRef(s);
+ }
+
+ void SetCapValue(const std::string& capvalue)
+ {
+ s.erase(pos);
+ s.append(capvalue);
+ InvalidateCache();
+ }
+};
+
+class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public ReloadModule::EventListener
+{
+ CapNotify capnotify;
+ std::string reloadedmod;
+ std::vector<std::string> reloadedcaps;
+ ClientProtocol::EventProvider protoev;
+
+ void Send(const std::string& capname, Cap::Capability* cap, bool add)
+ {
+ CapNotifyMessage msg(add, capname);
+ CapNotifyValueMessage msgwithval(capname);
+
+ ClientProtocol::Event event(protoev, msg);
+ ClientProtocol::Event eventwithval(protoev, msgwithval);
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ if (!capnotify.get(user))
+ continue;
+
+ // If the cap is being added and the client supports cap values then show the value, if any
+ if ((add) && (capnotify.GetProtocol(user) != Cap::CAP_LEGACY))
+ {
+ const std::string* capvalue = cap->GetValue(user);
+ if ((capvalue) && (!capvalue->empty()))
+ {
+ msgwithval.SetUser(user);
+ msgwithval.SetCapValue(*capvalue);
+ user->Send(eventwithval);
+ continue;
+ }
+ }
+ msg.SetUser(user);
+ user->Send(event);
+ }
+ }
+
+ public:
+ ModuleIRCv3CapNotify()
+ : Cap::EventListener(this)
+ , ReloadModule::EventListener(this)
+ , capnotify(this)
+ , protoev(this, "CAP_NOTIFY")
+ {
+ }
+
+ void OnCapAddDel(Cap::Capability* cap, bool add) CXX11_OVERRIDE
+ {
+ if (cap->creator == this)
+ return;
+
+ if (cap->creator->ModuleSourceFile == reloadedmod)
+ {
+ if (!add)
+ reloadedcaps.push_back(cap->GetName());
+ return;
+ }
+ Send(cap->GetName(), cap, add);
+ }
+
+ void OnCapValueChange(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // The value of a cap has changed, send CAP DEL and CAP NEW with the new value
+ Send(cap->GetName(), cap, false);
+ Send(cap->GetName(), cap, true);
+ }
+
+ void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+ {
+ if (mod == this)
+ return;
+ reloadedmod = mod->ModuleSourceFile;
+ // Request callback when reload is complete
+ cd.add(this, NULL);
+ }
+
+ void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
+ {
+ // Reloading can change the set of caps provided by a module so assuming that if the reload succeded all
+ // caps that the module previously provided are available or all were lost if the reload failed is wrong.
+ // Instead, we verify the availability of each cap individually.
+ dynamic_reference_nocheck<Cap::Manager> capmanager(this, "capmanager");
+ if (capmanager)
+ {
+ for (std::vector<std::string>::const_iterator i = reloadedcaps.begin(); i != reloadedcaps.end(); ++i)
+ {
+ const std::string& capname = *i;
+ if (!capmanager->Find(capname))
+ Send(capname, NULL, false);
+ }
+ }
+ reloadedmod.clear();
+ reloadedcaps.clear();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the cap-notify IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3CapNotify)
diff --git a/src/modules/m_ircv3_chghost.cpp b/src/modules/m_ircv3_chghost.cpp
new file mode 100644
index 000000000..fd142803f
--- /dev/null
+++ b/src/modules/m_ircv3_chghost.cpp
@@ -0,0 +1,61 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/ircv3.h"
+
+class ModuleIRCv3ChgHost : public Module
+{
+ Cap::Capability cap;
+ ClientProtocol::EventProvider protoevprov;
+
+ void DoChgHost(User* user, const std::string& ident, const std::string& host)
+ {
+ ClientProtocol::Message msg("CHGHOST", user);
+ msg.PushParamRef(ident);
+ msg.PushParamRef(host);
+ ClientProtocol::Event protoev(protoevprov, msg);
+ IRCv3::WriteNeighborsWithCap(user, protoev, cap);
+ }
+
+ public:
+ ModuleIRCv3ChgHost()
+ : cap(this, "chghost")
+ , protoevprov(this, "CHGHOST")
+ {
+ }
+
+ void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+ {
+ DoChgHost(user, newident, user->GetDisplayedHost());
+ }
+
+ void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+ {
+ DoChgHost(user, user->ident, newhost);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the chghost IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3ChgHost)
diff --git a/src/modules/m_ircv3_echomessage.cpp b/src/modules/m_ircv3_echomessage.cpp
new file mode 100644
index 000000000..f6eae5a00
--- /dev/null
+++ b/src/modules/m_ircv3_echomessage.cpp
@@ -0,0 +1,79 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2013-2015 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+class ModuleIRCv3EchoMessage : public Module
+{
+ Cap::Capability cap;
+
+ public:
+ ModuleIRCv3EchoMessage()
+ : cap(this, "echo-message")
+ {
+ }
+
+ void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+ {
+ if (!cap.get(user) || !details.echo)
+ return;
+
+ // Caps are only set on local users
+ LocalUser* const localuser = static_cast<LocalUser*>(user);
+
+ const std::string& text = details.echo_original ? details.original_text : details.text;
+ if (target.type == MessageTarget::TYPE_USER)
+ {
+ User* destuser = target.Get<User>();
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, destuser, text, details.type);
+ privmsg.AddTags(details.tags_in);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+ }
+ else if (target.type == MessageTarget::TYPE_CHANNEL)
+ {
+ Channel* chan = target.Get<Channel>();
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, chan, text, details.type, target.status);
+ privmsg.AddTags(details.tags_in);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+ }
+ else
+ {
+ const std::string* servername = target.Get<std::string>();
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, *servername, text, details.type);
+ privmsg.AddTags(details.tags_in);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+ }
+ }
+
+ void OnUserMessageBlocked(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+ {
+ // Prevent spammers from knowing that their spam was blocked.
+ if (details.echo_original)
+ OnUserPostMessage(user, target, details);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the echo-message IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3EchoMessage)
diff --git a/src/modules/m_ircv3_invitenotify.cpp b/src/modules/m_ircv3_invitenotify.cpp
new file mode 100644
index 000000000..8bd4d5642
--- /dev/null
+++ b/src/modules/m_ircv3_invitenotify.cpp
@@ -0,0 +1,70 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+class ModuleIRCv3InviteNotify : public Module
+{
+ Cap::Capability cap;
+
+ public:
+ ModuleIRCv3InviteNotify()
+ : cap(this, "invite-notify")
+ {
+ }
+
+ void OnUserInvite(User* source, User* dest, Channel* chan, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE
+ {
+ ClientProtocol::Messages::Invite invitemsg(source, dest, chan);
+ ClientProtocol::Event inviteevent(ServerInstance->GetRFCEvents().invite, invitemsg);
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* user = i->first;
+ // Skip members who don't use this extension or were excluded by other modules
+ if ((!cap.get(user)) || (notifyexcepts.count(user)))
+ continue;
+
+ Membership* memb = i->second;
+ // Check whether the member has a high enough rank to see the notification
+ if (memb->getRank() < notifyrank)
+ continue;
+
+ // Caps are only set on local users
+ LocalUser* const localuser = static_cast<LocalUser*>(user);
+ // Send and add the user to the exceptions so they won't get the NOTICE invite announcement message
+ localuser->Send(inviteevent);
+ notifyexcepts.insert(user);
+ }
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ // Prioritize after all modules to see all excepted users
+ ServerInstance->Modules.SetPriority(this, I_OnUserInvite, PRIORITY_LAST);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the invite-notify IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3InviteNotify)
diff --git a/src/modules/m_ircv3_servertime.cpp b/src/modules/m_ircv3_servertime.cpp
new file mode 100644
index 000000000..1c35c422b
--- /dev/null
+++ b/src/modules/m_ircv3_servertime.cpp
@@ -0,0 +1,72 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ircv3.h"
+#include "modules/ircv3_servertime.h"
+
+class ServerTimeTag : public IRCv3::ServerTime::Manager, public IRCv3::CapTag<ServerTimeTag>
+{
+ time_t lasttime;
+ std::string lasttimestring;
+
+ void RefreshTimeString()
+ {
+ const time_t currtime = ServerInstance->Time();
+ if (currtime != lasttime)
+ {
+ lasttime = currtime;
+ // Cache the string so it's not recreated every time a message is sent
+ lasttimestring = IRCv3::ServerTime::FormatTime(currtime);
+ }
+ }
+
+ public:
+ ServerTimeTag(Module* mod)
+ : IRCv3::ServerTime::Manager(mod)
+ , IRCv3::CapTag<ServerTimeTag>(mod, "server-time", "time")
+ , lasttime(0)
+ {
+ tagprov = this;
+ }
+
+ const std::string* GetValue(const ClientProtocol::Message& msg)
+ {
+ RefreshTimeString();
+ return &lasttimestring;
+ }
+};
+
+class ModuleIRCv3ServerTime : public Module
+{
+ ServerTimeTag tag;
+
+ public:
+ ModuleIRCv3ServerTime()
+ : tag(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the server-time IRCv3 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3ServerTime)
diff --git a/src/modules/m_ircv3_sts.cpp b/src/modules/m_ircv3_sts.cpp
new file mode 100644
index 000000000..cbfdd1307
--- /dev/null
+++ b/src/modules/m_ircv3_sts.cpp
@@ -0,0 +1,181 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2017 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/ssl.h"
+
+class STSCap : public Cap::Capability
+{
+ private:
+ std::string host;
+ std::string plaintextpolicy;
+ std::string securepolicy;
+
+ bool OnList(LocalUser* user) CXX11_OVERRIDE
+ {
+ // Don't send the cap to clients that only support cap-3.1.
+ if (GetProtocol(user) == Cap::CAP_LEGACY)
+ return false;
+
+ // Plaintext listeners have their own policy.
+ SSLIOHook* sslhook = SSLIOHook::IsSSL(&user->eh);
+ if (!sslhook)
+ return true;
+
+ // If no hostname has been provided for the connection, an STS persistence policy SHOULD NOT be advertised.
+ std::string snihost;
+ if (!sslhook->GetServerName(snihost))
+ return false;
+
+ // Before advertising an STS persistence policy over a secure connection, servers SHOULD verify whether the
+ // hostname provided by clients, for example, via TLS Server Name Indication (SNI), has been whitelisted by
+ // administrators in the server configuration.
+ return InspIRCd::Match(snihost, host, ascii_case_insensitive_map);
+ }
+
+ bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
+ {
+ // Clients MUST NOT request this capability with CAP REQ. Servers MAY reply with a CAP NAK message if a
+ // client requests this capability.
+ return false;
+ }
+
+ const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
+ {
+ return SSLIOHook::IsSSL(&user->eh) ? &securepolicy : &plaintextpolicy;
+ }
+
+ public:
+ STSCap(Module* mod)
+ : Cap::Capability(mod, "sts")
+ {
+ }
+
+ ~STSCap()
+ {
+ // TODO: Send duration=0 when STS vanishes.
+ }
+
+ void SetPolicy(const std::string& newhost, unsigned long duration, unsigned int port, bool preload)
+ {
+ // To enforce an STS upgrade policy, servers MUST send this key to insecurely connected clients. Servers
+ // MAY send this key to securely connected clients, but it will be ignored.
+ std::string newplaintextpolicy("port=");
+ newplaintextpolicy.append(ConvToStr(port));
+
+ // To enforce an STS persistence policy, servers MUST send this key to securely connected clients. Servers
+ // MAY send this key to all clients, but insecurely connected clients MUST ignore it.
+ std::string newsecurepolicy("duration=");
+ newsecurepolicy.append(ConvToStr(duration));
+
+ // Servers MAY send this key to all clients, but insecurely connected clients MUST ignore it.
+ if (preload)
+ newsecurepolicy.append(",preload");
+
+ // Apply the new policy.
+ bool changed = false;
+ if (!irc::equals(host, newhost))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing STS SNI hostname from \"%s\" to \"%s\"", host.c_str(), newhost.c_str());
+ host = newhost;
+ changed = true;
+ }
+
+ if (plaintextpolicy != newplaintextpolicy)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing plaintext STS policy from \"%s\" to \"%s\"", plaintextpolicy.c_str(), newplaintextpolicy.c_str());
+ plaintextpolicy.swap(newplaintextpolicy);
+ changed = true;
+ }
+
+ if (securepolicy != newsecurepolicy)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing secure STS policy from \"%s\" to \"%s\"", securepolicy.c_str(), newsecurepolicy.c_str());
+ securepolicy.swap(newsecurepolicy);
+ changed = true;
+ }
+
+ // If the policy has changed then notify all clients via cap-notify.
+ if (changed)
+ NotifyValueChange();
+ }
+};
+
+class ModuleIRCv3STS : public Module
+{
+ private:
+ STSCap cap;
+
+ // The IRCv3 STS specification requires that the server is listening using SSL using a valid certificate.
+ bool HasValidSSLPort(unsigned int port)
+ {
+ for (std::vector<ListenSocket*>::const_iterator iter = ServerInstance->ports.begin(); iter != ServerInstance->ports.end(); ++iter)
+ {
+ ListenSocket* ls = *iter;
+
+ // Is this listener on the right port?
+ unsigned int saport = ls->bind_sa.port();
+ if (saport != port)
+ continue;
+
+ // Is this listener using SSL?
+ if (ls->bind_tag->getString("ssl").empty())
+ continue;
+
+ // TODO: Add a way to check if a listener's TLS cert is CA-verified.
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ ModuleIRCv3STS()
+ : cap(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ // TODO: Multiple SNI profiles
+ ConfigTag* tag = ServerInstance->Config->ConfValue("sts");
+ if (tag == ServerInstance->Config->EmptyTag)
+ throw ModuleException("You must define a STS policy!");
+
+ const std::string host = tag->getString("host");
+ if (host.empty())
+ throw ModuleException("<sts:host> must contain a hostname, at " + tag->getTagLocation());
+
+ unsigned int port = tag->getUInt("port", 0, 0, UINT16_MAX);
+ if (!HasValidSSLPort(port))
+ throw ModuleException("<sts:port> must be a TLS port, at " + tag->getTagLocation());
+
+ unsigned long duration = tag->getDuration("duration", 60*60*24*30*2);
+ bool preload = tag->getBool("preload");
+ cap.SetPolicy(host, duration, port, preload);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides IRCv3 Strict Transport Security policy advertisement", VF_OPTCOMMON|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3STS)
diff --git a/src/modules/m_joinflood.cpp b/src/modules/m_joinflood.cpp
index 63bcc38a4..aaf266216 100644
--- a/src/modules/m_joinflood.cpp
+++ b/src/modules/m_joinflood.cpp
@@ -23,35 +23,41 @@
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +j (join flood protection) */
+enum
+{
+ // From RFC 2182.
+ ERR_UNAVAILRESOURCE = 437
+};
+
+// The number of seconds the channel will be closed for.
+static unsigned int duration;
/** Holds settings and state associated with channel mode +j
*/
class joinfloodsettings
{
public:
- int secs;
- int joins;
+ unsigned int secs;
+ unsigned int joins;
time_t reset;
time_t unlocktime;
- int counter;
- bool locked;
+ unsigned int counter;
- joinfloodsettings(int b, int c) : secs(b), joins(c)
+ joinfloodsettings(unsigned int b, unsigned int c)
+ : secs(b), joins(c), unlocktime(0), counter(0)
{
reset = ServerInstance->Time() + secs;
- counter = 0;
- locked = false;
- };
+ }
void addjoin()
{
- counter++;
if (ServerInstance->Time() > reset)
{
- counter = 0;
+ counter = 1;
reset = ServerInstance->Time() + secs;
}
+ else
+ counter++;
}
bool shouldlock()
@@ -66,155 +72,93 @@ class joinfloodsettings
bool islocked()
{
- if (locked)
- {
- if (ServerInstance->Time() > unlocktime)
- {
- locked = false;
- return false;
- }
- else
- {
- return true;
- }
- }
- return false;
+ if (ServerInstance->Time() > unlocktime)
+ unlocktime = 0;
+
+ return (unlocktime != 0);
}
void lock()
{
- locked = true;
- unlocktime = ServerInstance->Time() + 60;
+ unlocktime = ServerInstance->Time() + duration;
}
+ bool operator==(const joinfloodsettings& other) const
+ {
+ return ((this->secs == other.secs) && (this->joins == other.joins));
+ }
};
/** Handles channel mode +j
*/
-class JoinFlood : public ModeHandler
+class JoinFlood : public ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >
{
public:
- SimpleExtItem<joinfloodsettings> ext;
- JoinFlood(Module* Creator) : ModeHandler(Creator, "joinflood", 'j', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("joinflood", Creator) { }
+ JoinFlood(Module* Creator)
+ : ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >(Creator, "joinflood", 'j')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- char ndata[MAXBUF];
- char* data = ndata;
- strlcpy(ndata,parameter.c_str(),MAXBUF);
- char* joins = data;
- char* secs = NULL;
- while (*data)
- {
- if (*data == ':')
- {
- *data = 0;
- data++;
- secs = data;
- break;
- }
- else data++;
- }
- if (secs)
-
- {
- /* Set up the flood parameters for this channel */
- int njoins = atoi(joins);
- int nsecs = atoi(secs);
- if ((njoins<1) || (nsecs<1))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
- else
- {
- joinfloodsettings* f = ext.get(channel);
- if (!f)
- {
- parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
- f = new joinfloodsettings(nsecs, njoins);
- ext.set(channel, f);
- channel->SetModeParam('j', parameter);
- return MODEACTION_ALLOW;
- }
- else
- {
- std::string cur_param = channel->GetModeParameter('j');
- parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
- if (cur_param == parameter)
- {
- // mode params match
- return MODEACTION_DENY;
- }
- else
- {
- // new mode param, replace old with new
- f = new joinfloodsettings(nsecs, njoins);
- ext.set(channel, f);
- channel->SetModeParam('j', parameter);
- return MODEACTION_ALLOW;
- }
- }
- }
- }
- else
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
}
- else
+
+ /* Set up the flood parameters for this channel */
+ unsigned int njoins = ConvToInt(parameter.substr(0, colon));
+ unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
+ if ((njoins<1) || (nsecs<1))
{
- if (channel->IsModeSet('j'))
- {
- ext.unset(channel);
- channel->SetModeParam('j', "");
- return MODEACTION_ALLOW;
- }
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
}
- return MODEACTION_DENY;
+
+ ext.set(channel, new joinfloodsettings(nsecs, njoins));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const joinfloodsettings* jfs, std::string& out)
+ {
+ out.append(ConvToStr(jfs->joins)).push_back(':');
+ out.append(ConvToStr(jfs->secs));
}
};
class ModuleJoinFlood : public Module
{
-
JoinFlood jf;
public:
-
ModuleJoinFlood()
: jf(this)
{
}
- void init()
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(jf);
- ServerInstance->Modules->AddService(jf.ext);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ConfigTag* tag = ServerInstance->Config->ConfValue("joinflood");
+ duration = tag->getDuration("duration", 60, 10, 600);
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
joinfloodsettings *f = jf.ext.get(chan);
if (f && f->islocked())
{
- user->WriteNumeric(609, "%s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick.c_str(),chan->name.c_str());
+ user->WriteNumeric(ERR_UNAVAILRESOURCE, chan->name, "This channel is temporarily unavailable (+j). Please try again later.");
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
{
/* We arent interested in JOIN events caused by a network burst */
if (sync)
@@ -230,16 +174,12 @@ class ModuleJoinFlood : public Module
{
f->clear();
f->lock();
- memb->chan->WriteChannelWithServ((char*)ServerInstance->Config->ServerName.c_str(), "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", memb->chan->name.c_str(), f->joins, f->secs);
+ memb->chan->WriteNotice(InspIRCd::Format("This channel has been closed to new users for %u seconds because there have been more than %d joins in %d seconds.", duration, f->joins, f->secs));
}
}
}
- ~ModuleJoinFlood()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +j (join flood protection)", VF_VENDOR);
}
diff --git a/src/modules/m_jumpserver.cpp b/src/modules/m_jumpserver.cpp
index 44c6fc0d9..682e353d2 100644
--- a/src/modules/m_jumpserver.cpp
+++ b/src/modules/m_jumpserver.cpp
@@ -20,8 +20,13 @@
#include "inspircd.h"
+#include "modules/ssl.h"
-/* $ModDesc: Provides support for the RPL_REDIR numeric and the /JUMPSERVER command. */
+enum
+{
+ // From ircd-ratbox.
+ RPL_REDIR = 10
+};
/** Handle /JUMPSERVER
*/
@@ -32,15 +37,18 @@ class CommandJumpserver : public Command
std::string redirect_to;
std::string reason;
int port;
+ int sslport;
CommandJumpserver(Module* Creator) : Command(Creator, "JUMPSERVER", 0, 4)
{
- flags_needed = 'o'; syntax = "[<server> <port> <+/-an> <reason>]";
+ flags_needed = 'o';
+ syntax = "[<server> <port>[:<sslport>] <+/-an> <reason>]";
port = 0;
+ sslport = 0;
redirect_new_users = false;
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
int n_done = 0;
reason = (parameters.size() < 4) ? "Please use this server/port instead" : parameters[3];
@@ -50,14 +58,15 @@ class CommandJumpserver : public Command
std::string n_done_s;
/* No parameters: jumpserver disabled */
- if (!parameters.size())
+ if (parameters.empty())
{
if (port)
- user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick.c_str(), redirect_to.c_str(), port);
+ user->WriteNotice("*** Disabled jumpserver (previously set to '" + redirect_to + ":" + ConvToStr(port) + "')");
else
- user->WriteServ("NOTICE %s :*** Jumpserver was not enabled.", user->nick.c_str());
+ user->WriteNotice("*** Jumpserver was not enabled.");
port = 0;
+ sslport = 0;
redirect_to.clear();
return CMD_SUCCESS;
}
@@ -84,27 +93,36 @@ class CommandJumpserver : public Command
redirect_new_users = direction;
break;
default:
- user->WriteServ("NOTICE %s :*** Invalid JUMPSERVER flag: %c", user->nick.c_str(), *n);
+ user->WriteNotice("*** Invalid JUMPSERVER flag: " + ConvToStr(*n));
return CMD_FAILURE;
break;
}
}
- if (!atoi(parameters[1].c_str()))
+ size_t delimpos = parameters[1].find(':');
+ port = ConvToInt(parameters[1].substr(0, delimpos ? delimpos : std::string::npos));
+ sslport = (delimpos == std::string::npos ? 0 : ConvToInt(parameters[1].substr(delimpos + 1)));
+
+ if (parameters[1].find_first_not_of("0123456789:") != std::string::npos
+ || parameters[1].rfind(':') != delimpos
+ || port > 65535 || sslport > 65535)
{
- user->WriteServ("NOTICE %s :*** Invalid port number", user->nick.c_str());
+ user->WriteNotice("*** Invalid port number");
return CMD_FAILURE;
}
if (redirect_all_immediately)
{
/* Redirect everyone but the oper sending the command */
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); )
{
- User* t = *i;
- if (!IS_OPER(t))
+ // Quitting the user removes it from the list
+ LocalUser* t = *i;
+ ++i;
+ if (!t->IsOper())
{
- t->WriteNumeric(10, "%s %s %s :Please use this Server/Port instead", t->nick.c_str(), parameters[0].c_str(), parameters[1].c_str());
+ t->WriteNumeric(RPL_REDIR, parameters[0], GetPort(t), "Please use this Server/Port instead");
ServerInstance->Users->QuitUser(t, reason);
n_done++;
}
@@ -116,24 +134,24 @@ class CommandJumpserver : public Command
}
if (redirect_new_users)
- {
redirect_to = parameters[0];
- port = atoi(parameters[1].c_str());
- }
- user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick.c_str(), parameters[0].c_str(), parameters[1].c_str(),
- redirect_all_immediately ? "a" : "",
- redirect_new_users ? "n" : "",
- n_done ? " (" : "",
- n_done ? n_done_s.c_str() : "",
- n_done ? " user(s) redirected)" : "",
- reason.c_str());
+ user->WriteNotice("*** Set jumpserver to server '" + parameters[0] + "' port '" + (port ? ConvToStr(port) : "Auto") + ", SSL " + (sslport ? ConvToStr(sslport) : "Auto") + "', flags '+" +
+ (redirect_all_immediately ? "a" : "") + (redirect_new_users ? "n'" : "'") +
+ (n_done ? " (" + n_done_s + "user(s) redirected): " : ": ") + reason);
}
return CMD_SUCCESS;
}
-};
+ int GetPort(LocalUser* user)
+ {
+ int p = (SSLIOHook::IsSSL(&user->eh) ? sslport : port);
+ if (p == 0)
+ p = user->GetServerPort();
+ return p;
+ }
+};
class ModuleJumpServer : public Module
{
@@ -143,40 +161,35 @@ class ModuleJumpServer : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(js);
- Implementation eventlist[] = { I_OnUserRegister, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleJumpServer()
+ void OnModuleRehash(User* user, const std::string& param) CXX11_OVERRIDE
{
+ if (irc::equals(param, "jumpserver") && js.redirect_new_users)
+ js.redirect_new_users = false;
}
- virtual ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
- if (js.port && js.redirect_new_users)
+ if (js.redirect_new_users)
{
- user->WriteNumeric(10, "%s %s %d :Please use this Server/Port instead",
- user->nick.c_str(), js.redirect_to.c_str(), js.port);
+ int port = js.GetPort(user);
+ user->WriteNumeric(RPL_REDIR, js.redirect_to, port, "Please use this Server/Port instead");
ServerInstance->Users->QuitUser(user, js.reason);
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
// Emergency way to unlock
- if (!user) js.redirect_new_users = false;
+ if (!status.srcuser)
+ js.redirect_new_users = false;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the RPL_REDIR numeric and the /JUMPSERVER command.", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleJumpServer)
diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp
index a914f3869..4d911e78b 100644
--- a/src/modules/m_kicknorejoin.cpp
+++ b/src/modules/m_kicknorejoin.cpp
@@ -24,136 +24,150 @@
#include "inspircd.h"
+#include "modules/invite.h"
-/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */
+enum
+{
+ // From RFC 2182.
+ ERR_UNAVAILRESOURCE = 437
+};
+
+
+class KickRejoinData
+{
+ struct KickedUser
+ {
+ std::string uuid;
+ time_t expire;
+
+ KickedUser(User* user, unsigned int Delay)
+ : uuid(user->uuid)
+ , expire(ServerInstance->Time() + Delay)
+ {
+ }
+ };
+
+ typedef std::vector<KickedUser> KickedList;
+
+ mutable KickedList kicked;
+
+ public:
+ const unsigned int delay;
-typedef std::map<std::string, time_t> delaylist;
+ KickRejoinData(unsigned int Delay) : delay(Delay) { }
+
+ bool canjoin(LocalUser* user) const
+ {
+ for (KickedList::iterator i = kicked.begin(); i != kicked.end(); )
+ {
+ KickedUser& rec = *i;
+ if (rec.expire > ServerInstance->Time())
+ {
+ if (rec.uuid == user->uuid)
+ return false;
+ ++i;
+ }
+ else
+ {
+ // Expired record, remove.
+ stdalgo::vector::swaperase(kicked, i);
+ if (kicked.empty())
+ break;
+ }
+ }
+ return true;
+ }
+
+ void add(User* user)
+ {
+ // One user can be in the list multiple times if the user gets kicked, force joins
+ // (skipping OnUserPreJoin) and gets kicked again, but that's okay because canjoin()
+ // works correctly in this case as well
+ kicked.push_back(KickedUser(user, delay));
+ }
+};
/** Handles channel mode +J
*/
-class KickRejoin : public ModeHandler
+class KickRejoin : public ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >
{
+ const unsigned int max;
public:
- unsigned int max;
- SimpleExtItem<delaylist> ext;
KickRejoin(Module* Creator)
- : ModeHandler(Creator, "kicknorejoin", 'J', PARAM_SETONLY, MODETYPE_CHANNEL)
- , ext("norejoinusers", Creator)
+ : ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >(Creator, "kicknorejoin", 'J')
+ , max(60)
{
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
{
- if (adding)
+ int v = ConvToInt(parameter);
+ if (v <= 0)
{
- int v = ConvToInt(parameter);
- if (v <= 0)
- return MODEACTION_DENY;
- if (parameter == channel->GetModeParameter(this))
- return MODEACTION_DENY;
-
- if ((IS_LOCAL(source) && ((unsigned int)v > max)))
- v = max;
-
- parameter = ConvToStr(v);
- channel->SetModeParam(this, parameter);
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
}
- else
- {
- if (!channel->IsModeSet(this))
- return MODEACTION_DENY;
- ext.unset(channel);
- channel->SetModeParam(this, "");
- }
+ if ((IS_LOCAL(source) && ((unsigned int)v > max)))
+ v = max;
+
+ ext.set(channel, new KickRejoinData(v));
return MODEACTION_ALLOW;
}
+
+ void SerializeParam(Channel* chan, const KickRejoinData* krd, std::string& out)
+ {
+ out.append(ConvToStr(krd->delay));
+ }
+
+ std::string GetModuleSettings() const
+ {
+ return ConvToStr(max);
+ }
};
class ModuleKickNoRejoin : public Module
{
KickRejoin kr;
+ Invite::API invapi;
public:
-
ModuleKickNoRejoin()
: kr(this)
+ , invapi(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(kr);
- ServerInstance->Modules->AddService(kr.ext);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserKick, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- void OnRehash(User* user)
- {
- kr.max = ServerInstance->Duration(ServerInstance->Config->ConfValue("kicknorejoin")->getString("maxtime"));
- if (!kr.max)
- kr.max = 30*60;
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
- delaylist* dl = kr.ext.get(chan);
- if (dl)
+ const KickRejoinData* data = kr.ext.get(chan);
+ if ((data) && !invapi->IsInvited(user, chan) && (!data->canjoin(user)))
{
- for (delaylist::iterator iter = dl->begin(); iter != dl->end(); )
- {
- if (iter->second > ServerInstance->Time())
- {
- if (iter->first == user->uuid)
- {
- std::string modeparam = chan->GetModeParameter(&kr);
- user->WriteNumeric(ERR_DELAYREJOIN, "%s %s :You must wait %s seconds after being kicked to rejoin (+J)",
- user->nick.c_str(), chan->name.c_str(), modeparam.c_str());
- return MOD_RES_DENY;
- }
- ++iter;
- }
- else
- {
- // Expired record, remove.
- dl->erase(iter++);
- }
- }
-
- if (dl->empty())
- kr.ext.unset(chan);
+ user->WriteNumeric(ERR_UNAVAILRESOURCE, chan, InspIRCd::Format("You must wait %u seconds after being kicked to rejoin (+J)", data->delay));
+ return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE
{
- if (memb->chan->IsModeSet(&kr) && (IS_LOCAL(memb->user)) && (source != memb->user))
+ if ((!IS_LOCAL(memb->user)) || (source == memb->user))
+ return;
+
+ KickRejoinData* data = kr.ext.get(memb->chan);
+ if (data)
{
- delaylist* dl = kr.ext.get(memb->chan);
- if (!dl)
- {
- dl = new delaylist;
- kr.ext.set(memb->chan, dl);
- }
- (*dl)[memb->user->uuid] = ServerInstance->Time() + ConvToInt(memb->chan->GetModeParameter(&kr));
+ data->add(memb->user);
}
}
- ~ModuleKickNoRejoin()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Channel mode to delay rejoin after kick", VF_VENDOR);
+ return Version("Channel mode to delay rejoin after kick", VF_VENDOR | VF_COMMON, kr.GetModuleSettings());
}
};
-
MODULE_INIT(ModuleKickNoRejoin)
diff --git a/src/modules/m_knock.cpp b/src/modules/m_knock.cpp
index 8d2aa4543..1b66e01a4 100644
--- a/src/modules/m_knock.cpp
+++ b/src/modules/m_knock.cpp
@@ -21,102 +21,105 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for /KNOCK and channel mode +K */
+enum
+{
+ // From UnrealIRCd.
+ ERR_CANNOTKNOCK = 480,
+
+ // From ircd-ratbox.
+ ERR_CHANOPEN = 713,
+ ERR_KNOCKONCHAN = 714
+};
/** Handles the /KNOCK command
*/
class CommandKnock : public Command
{
+ SimpleChannelModeHandler& noknockmode;
+ ChanModeReference inviteonlymode;
+
public:
bool sendnotice;
bool sendnumeric;
- CommandKnock(Module* Creator) : Command(Creator,"KNOCK", 2, 2)
+ CommandKnock(Module* Creator, SimpleChannelModeHandler& Noknockmode)
+ : Command(Creator,"KNOCK", 2, 2)
+ , noknockmode(Noknockmode)
+ , inviteonlymode(Creator, "inviteonly")
{
syntax = "<channel> <reason>";
Penalty = 5;
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
Channel* c = ServerInstance->FindChan(parameters[0]);
if (!c)
{
- user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
return CMD_FAILURE;
}
if (c->HasUser(user))
{
- user->WriteNumeric(480, "%s :Can't KNOCK on %s, you are already on that channel.", user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_KNOCKONCHAN, c->name, InspIRCd::Format("Can't KNOCK on %s, you are already on that channel.", c->name.c_str()));
return CMD_FAILURE;
}
- if (c->IsModeSet('K'))
+ if (c->IsModeSet(noknockmode))
{
- user->WriteNumeric(480, "%s :Can't KNOCK on %s, +K is set.",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTKNOCK, InspIRCd::Format("Can't KNOCK on %s, +K is set.", c->name.c_str()));
return CMD_FAILURE;
}
- if (!c->IsModeSet('i'))
+ if (!c->IsModeSet(inviteonlymode))
{
- user->WriteNumeric(480, "%s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CHANOPEN, c->name, InspIRCd::Format("Can't KNOCK on %s, channel is not invite only so knocking is pointless!", c->name.c_str()));
return CMD_FAILURE;
}
if (sendnotice)
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :User %s is KNOCKing on %s (%s)", c->name.c_str(), user->nick.c_str(), c->name.c_str(), parameters[1].c_str());
+ c->WriteNotice(InspIRCd::Format("User %s is KNOCKing on %s (%s)", user->nick.c_str(), c->name.c_str(), parameters[1].c_str()));
if (sendnumeric)
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "710 %s %s %s :is KNOCKing: %s", c->name.c_str(), c->name.c_str(), user->GetFullHost().c_str(), parameters[1].c_str());
+ {
+ Numeric::Numeric numeric(710);
+ numeric.push(c->name).push(user->GetFullHost()).push("is KNOCKing: " + parameters[1]);
- user->WriteServ("NOTICE %s :KNOCKing on %s", user->nick.c_str(), c->name.c_str());
+ ClientProtocol::Messages::Numeric numericmsg(numeric, c->name);
+ c->Write(ServerInstance->GetRFCEvents().numeric, numericmsg);
+ }
+
+ user->WriteNotice("KNOCKing on " + c->name);
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_OPT_BCAST;
}
};
-/** Handles channel mode +K
- */
-class Knock : public SimpleChannelModeHandler
-{
- public:
- Knock(Module* Creator) : SimpleChannelModeHandler(Creator, "noknock", 'K') { }
-};
-
class ModuleKnock : public Module
{
+ SimpleChannelModeHandler kn;
CommandKnock cmd;
- Knock kn;
- public:
- ModuleKnock() : cmd(this), kn(this)
- {
- }
- void init()
+ public:
+ ModuleKnock()
+ : kn(this, "noknock", 'K')
+ , cmd(this, kn)
{
- ServerInstance->Modules->AddService(kn);
- ServerInstance->Modules->AddService(cmd);
-
- ServerInstance->Modules->Attach(I_OnRehash, this);
- OnRehash(NULL);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string knocknotify = ServerInstance->Config->ConfValue("knock")->getString("notify");
- irc::string notify(knocknotify.c_str());
-
- if (notify == "numeric")
+ if (stdalgo::string::equalsci(knocknotify, "numeric"))
{
cmd.sendnotice = false;
cmd.sendnumeric = true;
}
- else if (notify == "both")
+ else if (stdalgo::string::equalsci(knocknotify, "both"))
{
cmd.sendnotice = true;
cmd.sendnumeric = true;
@@ -128,7 +131,7 @@ class ModuleKnock : public Module
}
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for /KNOCK and channel mode +K", VF_OPTCOMMON | VF_VENDOR);
}
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp
new file mode 100644
index 000000000..fedf02b4d
--- /dev/null
+++ b/src/modules/m_ldapauth.cpp
@@ -0,0 +1,447 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
+ * Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+namespace
+{
+ Module* me;
+ std::string killreason;
+ LocalIntExt* authed;
+ bool verbose;
+ std::string vhost;
+ LocalStringExt* vhosts;
+ std::vector<std::pair<std::string, std::string> > requiredattributes;
+}
+
+class BindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uid;
+ std::string DN;
+ bool checkingAttributes;
+ bool passed;
+ int attrCount;
+
+ static std::string SafeReplace(const std::string& text, std::map<std::string, std::string>& replacements)
+ {
+ std::string result;
+ result.reserve(text.length());
+
+ for (unsigned int i = 0; i < text.length(); ++i)
+ {
+ char c = text[i];
+ if (c == '$')
+ {
+ // find the first nonalpha
+ i++;
+ unsigned int start = i;
+
+ while (i < text.length() - 1 && isalpha(text[i + 1]))
+ ++i;
+
+ std::string key(text, start, (i - start) + 1);
+ result.append(replacements[key]);
+ }
+ else
+ result.push_back(c);
+ }
+
+ return result;
+ }
+
+ static void SetVHost(User* user, const std::string& DN)
+ {
+ if (!vhost.empty())
+ {
+ irc::commasepstream stream(DN);
+
+ // mashed map of key:value parts of the DN
+ std::map<std::string, std::string> dnParts;
+
+ std::string dnPart;
+ while (stream.GetToken(dnPart))
+ {
+ std::string::size_type pos = dnPart.find('=');
+ if (pos == std::string::npos) // malformed
+ continue;
+
+ std::string key(dnPart, 0, pos);
+ std::string value(dnPart, pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
+ dnParts[key] = value;
+ }
+
+ // change host according to config key
+ vhosts->set(user, SafeReplace(vhost, dnParts));
+ }
+ }
+
+ public:
+ BindInterface(Module* c, const std::string& p, const std::string& u, const std::string& dn)
+ : LDAPInterface(c)
+ , provider(p), uid(u), DN(dn), checkingAttributes(false), passed(false), attrCount(0)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+
+ if (!user || !LDAP)
+ {
+ if (!checkingAttributes || !--attrCount)
+ delete this;
+ return;
+ }
+
+ if (!checkingAttributes && requiredattributes.empty())
+ {
+ // We're done, there are no attributes to check
+ SetVHost(user, DN);
+ authed->set(user, 1);
+
+ delete this;
+ return;
+ }
+
+ // Already checked attributes?
+ if (checkingAttributes)
+ {
+ if (!passed)
+ {
+ // Only one has to pass
+ passed = true;
+
+ SetVHost(user, DN);
+ authed->set(user, 1);
+ }
+
+ // Delete this if this is the last ref
+ if (!--attrCount)
+ delete this;
+
+ return;
+ }
+
+ // check required attributes
+ checkingAttributes = true;
+
+ for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
+ {
+ // Note that only one of these has to match for it to be success
+ const std::string& attr = it->first;
+ const std::string& val = it->second;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
+ try
+ {
+ LDAP->Compare(this, DN, attr, val);
+ ++attrCount;
+ }
+ catch (LDAPException &ex)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Unable to compare attributes %s=%s: %s", attr.c_str(), val.c_str(), ex.GetReason().c_str());
+ }
+ }
+
+ // Nothing done
+ if (!attrCount)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (unable to validate attributes)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ }
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ if (checkingAttributes && --attrCount)
+ return;
+
+ if (passed)
+ {
+ delete this;
+ return;
+ }
+
+ User* user = ServerInstance->FindUUID(uid);
+ if (user)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), err.getError().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ }
+
+ delete this;
+ }
+};
+
+class SearchInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uid;
+
+ public:
+ SearchInterface(Module* c, const std::string& p, const std::string& u)
+ : LDAPInterface(c), provider(p), uid(u)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(uid));
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (!LDAP || r.empty() || !user)
+ {
+ if (user)
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ return;
+ }
+
+ try
+ {
+ const LDAPAttributes& a = r.get(0);
+ std::string bindDn = a.get("dn");
+ if (bindDn.empty())
+ {
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ return;
+ }
+
+ LDAP->Bind(new BindInterface(this->creator, provider, uid, bindDn), bindDn, user->password);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+ delete this;
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str());
+ User* user = ServerInstance->FindUUID(uid);
+ if (user)
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ }
+};
+
+class AdminBindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uuid;
+ const std::string base;
+ const std::string what;
+
+ public:
+ AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& b, const std::string& w)
+ : LDAPInterface(c), provider(p), uuid(u), base(b), what(w)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (LDAP)
+ {
+ try
+ {
+ LDAP->Search(new SearchInterface(this->creator, provider, uuid), base, what);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+ }
+ delete this;
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError());
+ delete this;
+ }
+};
+
+class ModuleLDAPAuth : public Module
+{
+ dynamic_reference<LDAPProvider> LDAP;
+ LocalIntExt ldapAuthed;
+ LocalStringExt ldapVhost;
+ std::string base;
+ std::string attribute;
+ std::vector<std::string> allowpatterns;
+ std::vector<std::string> whitelistedcidrs;
+ bool useusername;
+
+public:
+ ModuleLDAPAuth()
+ : LDAP(this, "LDAP")
+ , ldapAuthed("ldapauth", ExtensionItem::EXT_USER, this)
+ , ldapVhost("ldapauth_vhost", ExtensionItem::EXT_USER, this)
+ {
+ me = this;
+ authed = &ldapAuthed;
+ vhosts = &ldapVhost;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
+ whitelistedcidrs.clear();
+ requiredattributes.clear();
+
+ base = tag->getString("baserdn");
+ attribute = tag->getString("attribute");
+ killreason = tag->getString("killreason");
+ vhost = tag->getString("host");
+ // Set to true if failed connects should be reported to operators
+ verbose = tag->getBool("verbose");
+ useusername = tag->getBool("userfield");
+
+ LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+
+ ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
+
+ for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
+ {
+ std::string cidr = i->second->getString("cidr");
+ if (!cidr.empty()) {
+ whitelistedcidrs.push_back(cidr);
+ }
+ }
+
+ ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
+
+ for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
+ {
+ const std::string attr = i->second->getString("attribute");
+ const std::string val = i->second->getString("value");
+
+ if (!attr.empty() && !val.empty())
+ requiredattributes.push_back(make_pair(attr, val));
+ }
+
+ std::string allowpattern = tag->getString("allowpattern");
+ irc::spacesepstream ss(allowpattern);
+ for (std::string more; ss.GetToken(more); )
+ {
+ allowpatterns.push_back(more);
+ }
+ }
+
+ void OnUserConnect(LocalUser *user) CXX11_OVERRIDE
+ {
+ std::string* cc = ldapVhost.get(user);
+ if (cc)
+ {
+ user->ChangeDisplayedHost(*cc);
+ ldapVhost.unset(user);
+ }
+ }
+
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
+ {
+ for (std::vector<std::string>::const_iterator i = allowpatterns.begin(); i != allowpatterns.end(); ++i)
+ {
+ if (InspIRCd::Match(user->nick, *i))
+ {
+ ldapAuthed.set(user,1);
+ return MOD_RES_PASSTHRU;
+ }
+ }
+
+ for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
+ {
+ if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
+ {
+ ldapAuthed.set(user,1);
+ return MOD_RES_PASSTHRU;
+ }
+ }
+
+ if (user->password.empty())
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ return MOD_RES_DENY;
+ }
+
+ if (!LDAP)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Unable to find LDAP provider)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ return MOD_RES_DENY;
+ }
+
+ std::string what;
+ std::string::size_type pos = user->password.find(':');
+ if (pos != std::string::npos)
+ {
+ what = attribute + "=" + user->password.substr(0, pos);
+
+ // Trim the user: prefix, leaving just 'pass' for later password check
+ user->password = user->password.substr(pos + 1);
+ }
+ else
+ {
+ what = attribute + "=" + (useusername ? user->ident : user->nick);
+ }
+
+ try
+ {
+ LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, base, what));
+ }
+ catch (LDAPException &ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+ ServerInstance->Users->QuitUser(user, killreason);
+ }
+
+ return MOD_RES_DENY;
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp
new file mode 100644
index 000000000..cde5b00d7
--- /dev/null
+++ b/src/modules/m_ldapoper.cpp
@@ -0,0 +1,249 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+namespace
+{
+ Module* me;
+}
+
+class LDAPOperBase : public LDAPInterface
+{
+ protected:
+ const std::string uid;
+ const std::string opername;
+ const std::string password;
+
+ void Fallback(User* user)
+ {
+ if (!user)
+ return;
+
+ Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
+ if (!oper_command)
+ return;
+
+ CommandBase::Params params;
+ params.push_back(opername);
+ params.push_back(password);
+ ClientProtocol::TagMap tags;
+ oper_command->Handle(user, CommandBase::Params(params, tags));
+ }
+
+ void Fallback()
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ Fallback(user);
+ }
+
+ public:
+ LDAPOperBase(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass)
+ : LDAPInterface(mod)
+ , uid(uuid), opername(oper), password(pass)
+ {
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str());
+ Fallback();
+ delete this;
+ }
+};
+
+class BindInterface : public LDAPOperBase
+{
+ public:
+ BindInterface(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass)
+ : LDAPOperBase(mod, uuid, oper, pass)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->oper_blocks.find(opername);
+
+ if (!user || iter == ServerInstance->Config->oper_blocks.end())
+ {
+ Fallback();
+ delete this;
+ return;
+ }
+
+ OperInfo* ifo = iter->second;
+ user->Oper(ifo);
+ delete this;
+ }
+};
+
+class SearchInterface : public LDAPOperBase
+{
+ const std::string provider;
+
+ bool HandleResult(const LDAPResult& result)
+ {
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (!LDAP || result.empty())
+ return false;
+
+ try
+ {
+ const LDAPAttributes& attr = result.get(0);
+ std::string bindDn = attr.get("dn");
+ if (bindDn.empty())
+ return false;
+
+ LDAP->Bind(new BindInterface(this->creator, uid, opername, password), bindDn, password);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+
+ return true;
+ }
+
+ public:
+ SearchInterface(Module* mod, const std::string& prov, const std::string &uuid, const std::string& oper, const std::string& pass)
+ : LDAPOperBase(mod, uuid, oper, pass)
+ , provider(prov)
+ {
+ }
+
+ void OnResult(const LDAPResult& result) CXX11_OVERRIDE
+ {
+ if (!HandleResult(result))
+ Fallback();
+ delete this;
+ }
+};
+
+class AdminBindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string user;
+ const std::string opername;
+ const std::string password;
+ const std::string base;
+ const std::string what;
+
+ public:
+ AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& o, const std::string& pa, const std::string& b, const std::string& w)
+ : LDAPInterface(c), provider(p), user(u), opername(p), password(pa), base(b), what(w)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (LDAP)
+ {
+ try
+ {
+ LDAP->Search(new SearchInterface(this->creator, provider, user, opername, password), base, what);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+ }
+ delete this;
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError());
+ delete this;
+ }
+};
+
+class ModuleLDAPAuth : public Module
+{
+ dynamic_reference<LDAPProvider> LDAP;
+ std::string base;
+ std::string attribute;
+
+ public:
+ ModuleLDAPAuth()
+ : LDAP(this, "LDAP")
+ {
+ me = this;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
+
+ LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+ base = tag->getString("baserdn");
+ attribute = tag->getString("attribute");
+ }
+
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+ {
+ if (validated && command == "OPER" && parameters.size() >= 2)
+ {
+ const std::string& opername = parameters[0];
+ const std::string& password = parameters[1];
+
+ ServerConfig::OperIndex::const_iterator it = ServerInstance->Config->oper_blocks.find(opername);
+ if (it == ServerInstance->Config->oper_blocks.end())
+ return MOD_RES_PASSTHRU;
+
+ ConfigTag* tag = it->second->oper_block;
+ if (!tag)
+ return MOD_RES_PASSTHRU;
+
+ std::string acceptedhosts = tag->getString("host");
+ std::string hostname = user->ident + "@" + user->GetRealHost();
+ if (!InspIRCd::MatchMask(acceptedhosts, hostname, user->GetIPString()))
+ return MOD_RES_PASSTHRU;
+
+ if (!LDAP)
+ return MOD_RES_PASSTHRU;
+
+ try
+ {
+ std::string what = attribute + "=" + opername;
+ LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, opername, password, base, what));
+ return MOD_RES_DENY;
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+ }
+ }
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/m_lockserv.cpp b/src/modules/m_lockserv.cpp
index 4983ae16a..2db01b99a 100644
--- a/src/modules/m_lockserv.cpp
+++ b/src/modules/m_lockserv.cpp
@@ -20,33 +20,38 @@
#include "inspircd.h"
-/* $ModDesc: Allows locking of the server to stop all incoming connections till unlocked again */
-
/** Adds numerics
* 988 <nick> <servername> :Closed for new connections
* 989 <nick> <servername> :Open for new connections
-*/
-
+ */
+enum
+{
+ // InspIRCd-specific.
+ RPL_SERVLOCKON = 988,
+ RPL_SERVLOCKOFF = 989
+};
class CommandLockserv : public Command
{
- bool& locked;
-public:
- CommandLockserv(Module* Creator, bool& lock) : Command(Creator, "LOCKSERV", 0), locked(lock)
+ std::string& locked;
+
+ public:
+ CommandLockserv(Module* Creator, std::string& lock) : Command(Creator, "LOCKSERV", 0, 1), locked(lock)
{
+ allow_empty_last_param = false;
flags_needed = 'o';
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (locked)
+ if (!locked.empty())
{
- user->WriteServ("NOTICE %s :The server is already locked.", user->nick.c_str());
+ user->WriteNotice("The server is already locked.");
return CMD_FAILURE;
}
- locked = true;
- user->WriteNumeric(988, "%s %s :Closed for new connections", user->nick.c_str(), user->server.c_str());
+ locked = parameters.empty() ? "Server is temporarily closed. Please try again later." : parameters[0];
+ user->WriteNumeric(RPL_SERVLOCKON, user->server->GetName(), "Closed for new connections");
ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used LOCKSERV to temporarily disallow new connections", user->nick.c_str());
return CMD_SUCCESS;
}
@@ -54,25 +59,24 @@ public:
class CommandUnlockserv : public Command
{
-private:
- bool& locked;
+ std::string& locked;
-public:
- CommandUnlockserv(Module* Creator, bool &lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
+ public:
+ CommandUnlockserv(Module* Creator, std::string& lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
{
flags_needed = 'o';
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (!locked)
+ if (locked.empty())
{
- user->WriteServ("NOTICE %s :The server isn't locked.", user->nick.c_str());
+ user->WriteNotice("The server isn't locked.");
return CMD_FAILURE;
}
- locked = false;
- user->WriteNumeric(989, "%s %s :Open for new connections", user->nick.c_str(), user->server.c_str());
+ locked.clear();
+ user->WriteNumeric(RPL_SERVLOCKOFF, user->server->GetName(), "Open for new connections");
ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used UNLOCKSERV to allow new connections", user->nick.c_str());
return CMD_SUCCESS;
}
@@ -80,52 +84,44 @@ public:
class ModuleLockserv : public Module
{
-private:
- bool locked;
+ std::string locked;
CommandLockserv lockcommand;
CommandUnlockserv unlockcommand;
-public:
+ public:
ModuleLockserv() : lockcommand(this, locked), unlockcommand(this, locked)
{
}
- void init()
- {
- locked = false;
- ServerInstance->Modules->AddService(lockcommand);
- ServerInstance->Modules->AddService(unlockcommand);
- Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleLockserv()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
+ // Emergency way to unlock
+ if (!status.srcuser)
+ locked.clear();
}
-
- virtual void OnRehash(User* user)
+ void OnModuleRehash(User* user, const std::string& param) CXX11_OVERRIDE
{
- // Emergency way to unlock
- if (!user) locked = false;
+ if (irc::equals(param, "lockserv") && !locked.empty())
+ locked.clear();
}
- virtual ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
- if (locked)
+ if (!locked.empty())
{
- ServerInstance->Users->QuitUser(user, "Server is temporarily closed. Please try again later.");
+ ServerInstance->Users->QuitUser(user, locked);
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- return locked ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ return !locked.empty() ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows locking of the server to stop all incoming connections until unlocked again", VF_VENDOR);
}
diff --git a/src/modules/m_maphide.cpp b/src/modules/m_maphide.cpp
index 546e342ae..7a1ee39e1 100644
--- a/src/modules/m_maphide.cpp
+++ b/src/modules/m_maphide.cpp
@@ -19,44 +19,30 @@
#include "inspircd.h"
-/* $ModDesc: Hide /MAP and /LINKS in the same form as ircu (mostly useless) */
-
class ModuleMapHide : public Module
{
std::string url;
public:
- void init()
- {
- Implementation eventlist[] = { I_OnPreCommand, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
url = ServerInstance->Config->ConfValue("security")->getString("maphide");
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
- if (validated && !IS_OPER(user) && !url.empty() && (command == "MAP" || command == "LINKS"))
+ if (validated && !user->IsOper() && !url.empty() && (command == "MAP" || command == "LINKS"))
{
- user->WriteServ("NOTICE %s :/%s has been disabled; visit %s", user->nick.c_str(), command.c_str(), url.c_str());
+ user->WriteNotice("/" + command + " has been disabled; visit " + url);
return MOD_RES_DENY;
}
else
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleMapHide()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Hide /MAP and /LINKS in the same form as ircu (mostly useless)", VF_VENDOR);
}
};
MODULE_INIT(ModuleMapHide)
-
diff --git a/src/modules/m_md5.cpp b/src/modules/m_md5.cpp
index c902ee3cb..8de70872f 100644
--- a/src/modules/m_md5.cpp
+++ b/src/modules/m_md5.cpp
@@ -21,13 +21,8 @@
*/
-/* $ModDesc: Allows for MD5 encrypted oper passwords */
-
#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
+#include "modules/hash.h"
/* The four core functions - F1 is optimized somewhat */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
@@ -39,10 +34,6 @@
#define MD5STEP(f,w,x,y,z,in,s) \
(w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
-#ifndef HAS_STDINT
-typedef unsigned int uint32_t;
-#endif
-
typedef uint32_t word32; /* NOT unsigned long. We don't support 16 bit platforms, anyway. */
typedef unsigned char byte;
@@ -70,23 +61,13 @@ class MD5Provider : public HashProvider
} while (--words);
}
- void MD5Init(MD5Context *ctx, unsigned int* ikey = NULL)
+ void MD5Init(MD5Context *ctx)
{
/* These are the defaults for md5 */
- if (!ikey)
- {
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
- }
- else
- {
- ctx->buf[0] = ikey[0];
- ctx->buf[1] = ikey[1];
- ctx->buf[2] = ikey[2];
- ctx->buf[3] = ikey[3];
- }
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
ctx->bytes[0] = 0;
ctx->bytes[1] = 0;
@@ -245,44 +226,23 @@ class MD5Provider : public HashProvider
}
- void MyMD5(void *dest, void *orig, int len, unsigned int* ikey)
+ void MyMD5(void *dest, void *orig, int len)
{
MD5Context context;
- MD5Init(&context, ikey);
+ MD5Init(&context);
MD5Update(&context, (const unsigned char*)orig, len);
MD5Final((unsigned char*)dest, &context);
}
-
- void GenHash(const char* src, char* dest, const char* xtab, unsigned int* ikey, size_t srclen)
- {
- unsigned char bytes[16];
-
- MyMD5((char*)bytes, (void*)src, srclen, ikey);
-
- for (int i = 0; i < 16; i++)
- {
- *dest++ = xtab[bytes[i] / 16];
- *dest++ = xtab[bytes[i] % 16];
- }
- *dest++ = 0;
- }
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
{
char res[16];
- MyMD5(res, (void*)data.data(), data.length(), NULL);
+ MyMD5(res, (void*)data.data(), data.length());
return std::string(res, 16);
}
- std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
- {
- char res[33];
- GenHash(sdata.data(), res, HexMap, IV, sdata.length());
- return res;
- }
-
- MD5Provider(Module* parent) : HashProvider(parent, "hash/md5", 16, 64) {}
+ MD5Provider(Module* parent) : HashProvider(parent, "md5", 16, 64) {}
};
class ModuleMD5 : public Module
@@ -291,10 +251,9 @@ class ModuleMD5 : public Module
public:
ModuleMD5() : md5(this)
{
- ServerInstance->Modules->AddService(md5);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements MD5 hashing",VF_VENDOR);
}
diff --git a/src/modules/m_messageflood.cpp b/src/modules/m_messageflood.cpp
index 9ff17924d..0707e6ae4 100644
--- a/src/modules/m_messageflood.cpp
+++ b/src/modules/m_messageflood.cpp
@@ -24,8 +24,7 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +f (message flood protection) */
+#include "modules/exemption.h"
/** Holds flood settings and state for mode +f
*/
@@ -36,9 +35,12 @@ class floodsettings
unsigned int secs;
unsigned int lines;
time_t reset;
- std::map<User*, unsigned int> counters;
+ insp::flat_map<User*, unsigned int> counters;
- floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
+ floodsettings(bool a, unsigned int b, unsigned int c)
+ : ban(a)
+ , secs(b)
+ , lines(c)
{
reset = ServerInstance->Time() + secs;
}
@@ -56,92 +58,77 @@ class floodsettings
void clear(User* who)
{
- std::map<User*, unsigned int>::iterator iter = counters.find(who);
- if (iter != counters.end())
- {
- counters.erase(iter);
- }
+ counters.erase(who);
}
};
/** Handles channel mode +f
*/
-class MsgFlood : public ModeHandler
+class MsgFlood : public ParamMode<MsgFlood, SimpleExtItem<floodsettings> >
{
public:
- SimpleExtItem<floodsettings> ext;
- MsgFlood(Module* Creator) : ModeHandler(Creator, "flood", 'f', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("messageflood", Creator) { }
+ MsgFlood(Module* Creator)
+ : ParamMode<MsgFlood, SimpleExtItem<floodsettings> >(Creator, "flood", 'f')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- std::string::size_type colon = parameter.find(':');
- if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
-
- /* Set up the flood parameters for this channel */
- bool ban = (parameter[0] == '*');
- unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
- unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
-
- if ((nlines<2) || (nsecs<1))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
+ }
- floodsettings* f = ext.get(channel);
- if ((f) && (nlines == f->lines) && (nsecs == f->secs) && (ban == f->ban))
- // mode params match
- return MODEACTION_DENY;
+ /* Set up the flood parameters for this channel */
+ bool ban = (parameter[0] == '*');
+ unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
+ unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
- ext.set(channel, new floodsettings(ban, nsecs, nlines));
- parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" + ConvToStr(nsecs);
- channel->SetModeParam('f', parameter);
- return MODEACTION_ALLOW;
- }
- else
+ if ((nlines<2) || (nsecs<1))
{
- if (!channel->IsModeSet('f'))
- return MODEACTION_DENY;
-
- ext.unset(channel);
- channel->SetModeParam('f', "");
- return MODEACTION_ALLOW;
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
}
+
+ ext.set(channel, new floodsettings(ban, nsecs, nlines));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const floodsettings* fs, std::string& out)
+ {
+ if (fs->ban)
+ out.push_back('*');
+ out.append(ConvToStr(fs->lines)).push_back(':');
+ out.append(ConvToStr(fs->secs));
}
};
class ModuleMsgFlood : public Module
{
+ CheckExemption::EventProvider exemptionprov;
MsgFlood mf;
public:
ModuleMsgFlood()
- : mf(this)
+ : exemptionprov(this)
+ , mf(this)
{
}
- void init()
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(mf);
- ServerInstance->Modules->AddService(mf.ext);
- Implementation eventlist[] = { I_OnUserPreNotice, I_OnUserPreMessage };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
+ if (target.type != MessageTarget::TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
- ModResult ProcessMessages(User* user,Channel* dest, const std::string &text)
- {
- if ((!IS_LOCAL(user)) || !dest->IsModeSet('f'))
+ Channel* dest = target.Get<Channel>();
+ if ((!IS_LOCAL(user)) || !dest->IsModeSet(mf))
return MOD_RES_PASSTHRU;
- if (ServerInstance->OnCheckExemption(user,dest,"flood") == MOD_RES_ALLOW)
+ ModResult res = CheckExemption::Call(exemptionprov, user, dest, "flood");
+ if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
floodsettings *f = mf.ext.get(dest);
@@ -153,17 +140,15 @@ class ModuleMsgFlood : public Module
f->clear(user);
if (f->ban)
{
- std::vector<std::string> parameters;
- parameters.push_back(dest->name);
- parameters.push_back("+b");
- parameters.push_back(user->MakeWildHost());
- ServerInstance->SendGlobalMode(parameters, ServerInstance->FakeClient);
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->GetDisplayedHost());
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, dest, NULL, changelist);
}
- char kickmessage[MAXBUF];
- snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %u lines in %u secs)", f->lines, f->secs);
+ const std::string kickMessage = "Channel flood triggered (trigger is " + ConvToStr(f->lines) +
+ " lines in " + ConvToStr(f->secs) + " secs)";
- dest->KickUser(ServerInstance->FakeClient, user, kickmessage);
+ dest->KickUser(ServerInstance->FakeClient, user, kickMessage);
return MOD_RES_DENY;
}
@@ -172,30 +157,13 @@ class ModuleMsgFlood : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- return ProcessMessages(user,(Channel*)dest,text);
-
- return MOD_RES_PASSTHRU;
- }
-
- ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- return ProcessMessages(user,(Channel*)dest,text);
-
- return MOD_RES_PASSTHRU;
- }
-
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
// we want to be after all modules that might deny the message (e.g. m_muteban, m_noctcp, m_blockcolor, etc.)
ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
- ServerInstance->Modules->SetPriority(this, I_OnUserPreNotice, PRIORITY_LAST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +f (message flood protection)", VF_VENDOR);
}
diff --git a/src/modules/m_mlock.cpp b/src/modules/m_mlock.cpp
index d1df81354..85787ae96 100644
--- a/src/modules/m_mlock.cpp
+++ b/src/modules/m_mlock.cpp
@@ -17,30 +17,30 @@
*/
-/* $ModDesc: Implements the ability to have server-side MLOCK enforcement. */
-
#include "inspircd.h"
+enum
+{
+ // From Charybdis.
+ ERR_MLOCKRESTRICTED = 742
+};
+
class ModuleMLock : public Module
{
-private:
StringExtItem mlock;
-public:
- ModuleMLock() : mlock("mlock", this) {};
-
- void init()
+ public:
+ ModuleMLock()
+ : mlock("mlock", ExtensionItem::EXT_CHANNEL, this)
{
- ServerInstance->Modules->Attach(I_OnRawMode, this);
- ServerInstance->Modules->AddService(this->mlock);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements the ability to have server-side MLOCK enforcement.", VF_VENDOR);
}
- ModResult OnRawMode(User* source, Channel* channel, const char mode, const std::string& parameter, bool adding, int pcnt)
+ ModResult OnRawMode(User* source, Channel* channel, ModeHandler* mh, const std::string& parameter, bool adding) CXX11_OVERRIDE
{
if (!channel)
return MOD_RES_PASSTHRU;
@@ -52,17 +52,16 @@ public:
if (!mlock_str)
return MOD_RES_PASSTHRU;
+ const char mode = mh->GetModeChar();
std::string::size_type p = mlock_str->find(mode);
if (p != std::string::npos)
{
- source->WriteNumeric(742, "%s %c %s :MODE cannot be set due to channel having an active MLOCK restriction policy",
- channel->name.c_str(), mode, mlock_str->c_str());
+ source->WriteNumeric(ERR_MLOCKRESTRICTED, channel->name, mode, *mlock_str, "MODE cannot be set due to channel having an active MLOCK restriction policy");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
-
};
MODULE_INIT(ModuleMLock)
diff --git a/src/commands/cmd_modenotice.cpp b/src/modules/m_modenotice.cpp
index 852b72f6a..5311015b7 100644
--- a/src/commands/cmd_modenotice.cpp
+++ b/src/modules/m_modenotice.cpp
@@ -28,10 +28,12 @@ class CommandModeNotice : public Command
flags_needed = 'o';
}
- CmdResult Handle(const std::vector<std::string>& parameters, User *src)
+ CmdResult Handle(User* src, const Params& parameters) CXX11_OVERRIDE
{
+ std::string msg = "*** From " + src->nick + ": " + parameters[1];
int mlen = parameters[0].length();
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
User* user = *i;
for (int n = 0; n < mlen; n++)
@@ -39,17 +41,32 @@ class CommandModeNotice : public Command
if (!user->IsModeSet(parameters[0][n]))
goto next_user;
}
- user->Write(":%s NOTICE %s :*** From %s: %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), src->nick.c_str(), parameters[1].c_str());
+ user->WriteNotice(msg);
next_user: ;
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- return ROUTE_BROADCAST;
+ return ROUTE_OPT_BCAST;
}
};
-COMMAND_INIT(CommandModeNotice)
+class ModuleModeNotice : public Module
+{
+ CommandModeNotice cmd;
+
+ public:
+ ModuleModeNotice()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the /MODENOTICE command", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleModeNotice)
diff --git a/src/modules/m_monitor.cpp b/src/modules/m_monitor.cpp
new file mode 100644
index 000000000..fd72c7320
--- /dev/null
+++ b/src/modules/m_monitor.cpp
@@ -0,0 +1,444 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+namespace IRCv3
+{
+ namespace Monitor
+ {
+ class ExtItem;
+ struct Entry;
+ class Manager;
+ class ManagerInternal;
+
+ typedef std::vector<Entry*> WatchedList;
+ typedef std::vector<LocalUser*> WatcherList;
+ }
+}
+
+struct IRCv3::Monitor::Entry
+{
+ WatcherList watchers;
+ std::string nick;
+
+ void SetNick(const std::string& Nick)
+ {
+ nick.clear();
+ // We may show this string to other users so do not leak the casing
+ std::transform(Nick.begin(), Nick.end(), std::back_inserter(nick), ::tolower);
+ }
+
+ const std::string& GetNick() const { return nick; }
+};
+
+class IRCv3::Monitor::Manager
+{
+ struct ExtData
+ {
+ WatchedList list;
+ };
+
+ class ExtItem : public ExtensionItem
+ {
+ Manager& manager;
+
+ public:
+ ExtItem(Module* mod, const std::string& extname, Manager& managerref)
+ : ExtensionItem(extname, ExtensionItem::EXT_USER, mod)
+ , manager(managerref)
+ {
+ }
+
+ ExtData* get(Extensible* container, bool create = false)
+ {
+ ExtData* extdata = static_cast<ExtData*>(get_raw(container));
+ if ((!extdata) && (create))
+ {
+ extdata = new ExtData;
+ set_raw(container, extdata);
+ }
+ return extdata;
+ }
+
+ void unset(Extensible* container)
+ {
+ free(container, unset_raw(container));
+ }
+
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
+ {
+ std::string ret;
+ if (format == FORMAT_NETWORK)
+ return ret;
+
+ const ExtData* extdata = static_cast<ExtData*>(item);
+ for (WatchedList::const_iterator i = extdata->list.begin(); i != extdata->list.end(); ++i)
+ {
+ const Entry* entry = *i;
+ ret.append(entry->GetNick()).push_back(' ');
+ }
+ if (!ret.empty())
+ ret.erase(ret.size()-1);
+ return ret;
+ }
+
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE;
+
+ void free(Extensible* container, void* item) CXX11_OVERRIDE
+ {
+ delete static_cast<ExtData*>(item);
+ }
+ };
+
+ public:
+ Manager(Module* mod, const std::string& extname)
+ : ext(mod, extname, *this)
+ {
+ }
+
+ enum WatchResult
+ {
+ WR_OK,
+ WR_TOOMANY,
+ WR_ALREADYWATCHING,
+ WR_INVALIDNICK
+ };
+
+ WatchResult Watch(LocalUser* user, const std::string& nick, unsigned int maxwatch)
+ {
+ if (!ServerInstance->IsNick(nick))
+ return WR_INVALIDNICK;
+
+ WatchedList* watched = GetWatchedPriv(user, true);
+ if (watched->size() >= maxwatch)
+ return WR_TOOMANY;
+
+ Entry* entry = AddWatcher(nick, user);
+ if (stdalgo::isin(*watched, entry))
+ return WR_ALREADYWATCHING;
+
+ entry->watchers.push_back(user);
+ watched->push_back(entry);
+ return WR_OK;
+ }
+
+ bool Unwatch(LocalUser* user, const std::string& nick)
+ {
+ WatchedList* list = GetWatchedPriv(user);
+ if (!list)
+ return false;
+
+ bool ret = RemoveWatcher(nick, user, *list);
+ // If no longer watching any nick unset ext
+ if (list->empty())
+ ext.unset(user);
+ return ret;
+ }
+
+ const WatchedList& GetWatched(LocalUser* user)
+ {
+ WatchedList* list = GetWatchedPriv(user);
+ if (list)
+ return *list;
+ return emptywatchedlist;
+ }
+
+ void UnwatchAll(LocalUser* user)
+ {
+ WatchedList* list = GetWatchedPriv(user);
+ if (!list)
+ return;
+
+ while (!list->empty())
+ {
+ Entry* entry = list->front();
+ RemoveWatcher(entry->GetNick(), user, *list);
+ }
+ ext.unset(user);
+ }
+
+ WatcherList* GetWatcherList(const std::string& nick)
+ {
+ Entry* entry = Find(nick);
+ if (entry)
+ return &entry->watchers;
+ return NULL;
+ }
+
+ static User* FindNick(const std::string& nick)
+ {
+ User* user = ServerInstance->FindNickOnly(nick);
+ if ((user) && (user->registered == REG_ALL))
+ return user;
+ return NULL;
+ }
+
+ private:
+ typedef TR1NS::unordered_map<std::string, Entry, irc::insensitive, irc::StrHashComp> NickHash;
+
+ Entry* Find(const std::string& nick)
+ {
+ NickHash::iterator it = nicks.find(nick);
+ if (it != nicks.end())
+ return &it->second;
+ return NULL;
+ }
+
+ Entry* AddWatcher(const std::string& nick, LocalUser* user)
+ {
+ std::pair<NickHash::iterator, bool> ret = nicks.insert(std::make_pair(nick, Entry()));
+ Entry& entry = ret.first->second;
+ if (ret.second)
+ entry.SetNick(nick);
+ return &entry;
+ }
+
+ bool RemoveWatcher(const std::string& nick, LocalUser* user, WatchedList& watchedlist)
+ {
+ NickHash::iterator it = nicks.find(nick);
+ // If nobody is watching this nick the user trying to remove it isn't watching it for sure
+ if (it == nicks.end())
+ return false;
+
+ Entry& entry = it->second;
+ // Erase from the user's list of watched nicks
+ if (!stdalgo::vector::swaperase(watchedlist, &entry))
+ return false; // User is not watching this nick
+
+ // Erase from the nick's list of watching users
+ stdalgo::vector::swaperase(entry.watchers, user);
+
+ // If nobody else is watching the nick remove map entry
+ if (entry.watchers.empty())
+ nicks.erase(it);
+
+ return true;
+ }
+
+ WatchedList* GetWatchedPriv(LocalUser* user, bool create = false)
+ {
+ ExtData* extdata = ext.get(user, create);
+ if (!extdata)
+ return NULL;
+ return &extdata->list;
+ }
+
+ NickHash nicks;
+ ExtItem ext;
+ WatchedList emptywatchedlist;
+};
+
+// inline is needed in static builds to support m_watch including the Manager code from this file
+inline void IRCv3::Monitor::Manager::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format == FORMAT_NETWORK)
+ return;
+
+ irc::spacesepstream ss(value);
+ for (std::string nick; ss.GetToken(nick); )
+ manager.Watch(static_cast<LocalUser*>(container), nick, UINT_MAX);
+}
+
+#ifndef INSPIRCD_MONITOR_MANAGER_ONLY
+
+enum
+{
+ RPL_MONONLINE = 730,
+ RPL_MONOFFLINE = 731,
+ RPL_MONLIST = 732,
+ RPL_ENDOFMONLIST = 733,
+ ERR_MONLISTFULL = 734
+};
+
+class CommandMonitor : public SplitCommand
+{
+ typedef Numeric::Builder<> ReplyBuilder;
+ // Additional penalty for the /MONITOR L and /MONITOR S commands that request a list from the server
+ static const unsigned int ListPenalty = 3000;
+
+ IRCv3::Monitor::Manager& manager;
+
+ void HandlePlus(LocalUser* user, const std::string& input)
+ {
+ ReplyBuilder online(user, RPL_MONONLINE);
+ ReplyBuilder offline(user, RPL_MONOFFLINE);
+ irc::commasepstream ss(input);
+ for (std::string nick; ss.GetToken(nick); )
+ {
+ IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxmonitor);
+ if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
+ {
+ // List is full, send error which includes the remaining nicks that were not processed
+ user->WriteNumeric(ERR_MONLISTFULL, maxmonitor, InspIRCd::Format("%s%s%s", nick.c_str(), (ss.StreamEnd() ? "" : ","), ss.GetRemaining().c_str()), "Monitor list is full");
+ break;
+ }
+ else if (result != IRCv3::Monitor::Manager::WR_OK)
+ continue; // Already added or invalid nick
+
+ ReplyBuilder& out = (IRCv3::Monitor::Manager::FindNick(nick) ? online : offline);
+ out.Add(nick);
+ }
+
+ online.Flush();
+ offline.Flush();
+ }
+
+ void HandleMinus(LocalUser* user, const std::string& input)
+ {
+ irc::commasepstream ss(input);
+ for (std::string nick; ss.GetToken(nick); )
+ manager.Unwatch(user, nick);
+ }
+
+ public:
+ unsigned int maxmonitor;
+
+ CommandMonitor(Module* mod, IRCv3::Monitor::Manager& managerref)
+ : SplitCommand(mod, "MONITOR", 1)
+ , manager(managerref)
+ {
+ Penalty = 2;
+ allow_empty_last_param = false;
+ syntax = "[C|L|S|+ <nick1>[,<nick2>]|- <nick1>[,<nick2>]";
+ }
+
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ char subcmd = toupper(parameters[0][0]);
+ if (subcmd == '+')
+ {
+ if (parameters.size() > 1)
+ HandlePlus(user, parameters[1]);
+ }
+ else if (subcmd == '-')
+ {
+ if (parameters.size() > 1)
+ HandleMinus(user, parameters[1]);
+ }
+ else if (subcmd == 'C')
+ {
+ manager.UnwatchAll(user);
+ }
+ else if (subcmd == 'L')
+ {
+ user->CommandFloodPenalty += ListPenalty;
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ ReplyBuilder out(user, RPL_MONLIST);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ IRCv3::Monitor::Entry* entry = *i;
+ out.Add(entry->GetNick());
+ }
+ out.Flush();
+ user->WriteNumeric(RPL_ENDOFMONLIST, "End of MONITOR list");
+ }
+ else if (subcmd == 'S')
+ {
+ user->CommandFloodPenalty += ListPenalty;
+
+ ReplyBuilder online(user, RPL_MONONLINE);
+ ReplyBuilder offline(user, RPL_MONOFFLINE);
+
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ IRCv3::Monitor::Entry* entry = *i;
+ ReplyBuilder& out = (IRCv3::Monitor::Manager::FindNick(entry->GetNick()) ? online : offline);
+ out.Add(entry->GetNick());
+ }
+
+ online.Flush();
+ offline.Flush();
+ }
+ else
+ return CMD_FAILURE;
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleMonitor : public Module
+{
+ IRCv3::Monitor::Manager manager;
+ CommandMonitor cmd;
+
+ void SendAlert(unsigned int numeric, const std::string& nick)
+ {
+ const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
+ if (!list)
+ return;
+
+ for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ LocalUser* curr = *i;
+ curr->WriteNumeric(numeric, nick);
+ }
+ }
+
+ public:
+ ModuleMonitor()
+ : manager(this, "monitor")
+ , cmd(this, manager)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("monitor");
+ cmd.maxmonitor = tag->getUInt("maxentries", 30, 1);
+ }
+
+ void OnPostConnect(User* user) CXX11_OVERRIDE
+ {
+ SendAlert(RPL_MONONLINE, user->nick);
+ }
+
+ void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
+ {
+ // Detect and ignore nickname case change
+ if (ServerInstance->FindNickOnly(oldnick) == user)
+ return;
+
+ SendAlert(RPL_MONOFFLINE, oldnick);
+ SendAlert(RPL_MONONLINE, user->nick);
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ manager.UnwatchAll(localuser);
+ SendAlert(RPL_MONOFFLINE, user->nick);
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["MONITOR"] = ConvToStr(cmd.maxmonitor);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides MONITOR support", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleMonitor)
+
+#endif
diff --git a/src/modules/m_muteban.cpp b/src/modules/m_muteban.cpp
index 767af2901..7698835b1 100644
--- a/src/modules/m_muteban.cpp
+++ b/src/modules/m_muteban.cpp
@@ -20,53 +20,40 @@
#include "inspircd.h"
-/* $ModDesc: Implements extban +b m: - mute bans */
-
class ModuleQuietBan : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleQuietBan()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements extban +b m: - mute bans",VF_OPTCOMMON|VF_VENDOR);
}
- virtual ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user) || target_type != TYPE_CHANNEL)
+ if (!IS_LOCAL(user) || target.type != MessageTarget::TYPE_CHANNEL)
return MOD_RES_PASSTHRU;
- Channel* chan = static_cast<Channel*>(dest);
+ Channel* chan = target.Get<Channel>();
if (chan->GetExtBanStatus(user, 'm') == MOD_RES_DENY && chan->GetPrefixValue(user) < VOICE_VALUE)
{
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're muted)", user->nick.c_str(), chan->name.c_str());
+ bool notifyuser = ServerInstance->Config->ConfValue("muteban")->getBool("notifyuser", true);
+ if (!notifyuser)
+ {
+ details.echo_original = true;
+ return MOD_RES_DENY;
+ }
+
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're muted)");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
- }
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('m');
+ tokens["EXTBAN"].push_back('m');
}
};
-
MODULE_INIT(ModuleQuietBan)
-
diff --git a/src/modules/m_namedmodes.cpp b/src/modules/m_namedmodes.cpp
index 46710946b..8872ba629 100644
--- a/src/modules/m_namedmodes.cpp
+++ b/src/modules/m_namedmodes.cpp
@@ -17,56 +17,64 @@
*/
-/* $ModDesc: Provides the ability to manipulate modes via long names. */
-
#include "inspircd.h"
-static void DisplayList(User* user, Channel* channel)
+enum
{
- std::stringstream items;
- for(char letter = 'A'; letter <= 'z'; letter++)
+ // InspIRCd-specific.
+ RPL_ENDOFPROPLIST = 960,
+ RPL_PROPLIST = 961
+};
+
+static void DisplayList(LocalUser* user, Channel* channel)
+{
+ Numeric::ParamBuilder<1> numeric(user, RPL_PROPLIST);
+ numeric.AddStatic(channel->name);
+
+ const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+ for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
- if (!mh || mh->IsListMode())
+ ModeHandler* mh = i->second;
+ if (!channel->IsModeSet(mh))
continue;
- if (!channel->IsModeSet(letter))
- continue;
- items << " +" << mh->name;
- if (mh->GetNumParams(true))
+ numeric.Add("+" + mh->name);
+ if (mh->NeedsParam(true))
{
- if ((letter == 'k') && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
- items << " <key>";
+ if ((mh->name == "key") && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
+ numeric.Add("<key>");
else
- items << " " << channel->GetModeParameter(letter);
+ numeric.Add(channel->GetModeParameter(mh));
}
}
- char pfx[MAXBUF];
- snprintf(pfx, MAXBUF, ":%s 961 %s %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), channel->name.c_str());
- user->SendText(std::string(pfx), items);
- user->WriteNumeric(960, "%s %s :End of mode list", user->nick.c_str(), channel->name.c_str());
+ numeric.Flush();
+ user->WriteNumeric(RPL_ENDOFPROPLIST, channel->name, "End of mode list");
}
-class CommandProp : public Command
+class CommandProp : public SplitCommand
{
public:
- CommandProp(Module* parent) : Command(parent, "PROP", 1)
+ CommandProp(Module* parent)
+ : SplitCommand(parent, "PROP", 1)
{
syntax = "<user|channel> {[+-]<mode> [<value>]}*";
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *src)
+ CmdResult HandleLocal(LocalUser* src, const Params& parameters) CXX11_OVERRIDE
{
+ Channel* const chan = ServerInstance->FindChan(parameters[0]);
+ if (!chan)
+ {
+ src->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+ }
+
if (parameters.size() == 1)
{
- Channel* chan = ServerInstance->FindChan(parameters[0]);
- if (chan)
- DisplayList(src, chan);
+ DisplayList(src, chan);
return CMD_SUCCESS;
}
unsigned int i = 1;
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back("");
+ Modes::ChangeList modes;
while (i < parameters.size())
{
std::string prop = parameters[i++];
@@ -76,21 +84,19 @@ class CommandProp : public Command
if (prop[0] == '+' || prop[0] == '-')
prop.erase(prop.begin());
- for(char letter = 'A'; letter <= 'z'; letter++)
+ ModeHandler* mh = ServerInstance->Modes->FindMode(prop, MODETYPE_CHANNEL);
+ if (mh)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
- if (mh && mh->name == prop)
+ if (mh->NeedsParam(plus))
{
- modes[1].append((plus ? "+" : "-") + std::string(1, letter));
- if (mh->GetNumParams(plus))
- {
- if (i != parameters.size())
- modes.push_back(parameters[i++]);
- }
+ if (i != parameters.size())
+ modes.push(mh, plus, parameters[i++]);
}
+ else
+ modes.push(mh, plus);
}
}
- ServerInstance->SendGlobalMode(modes, src);
+ ServerInstance->Modes->ProcessSingle(src, chan, NULL, modes, ModeParser::MODE_CHECKACCESS);
return CMD_SUCCESS;
}
};
@@ -102,6 +108,13 @@ class DummyZ : public ModeHandler
{
list = true;
}
+
+ // Handle /MODE #chan Z
+ void DisplayList(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ if (IS_LOCAL(user))
+ ::DisplayList(static_cast<LocalUser*>(user), chan);
+ }
};
class ModuleNamedModes : public Module
@@ -113,99 +126,69 @@ class ModuleNamedModes : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(dummyZ);
-
- Implementation eventlist[] = { I_OnPreMode };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ability to manipulate modes via long names.",VF_VENDOR);
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_FIRST);
}
- ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters)
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (!channel)
return MOD_RES_PASSTHRU;
- if (parameters[1].find('Z') == std::string::npos)
- return MOD_RES_PASSTHRU;
- if (parameters.size() <= 2)
- {
- DisplayList(source, channel);
- return MOD_RES_DENY;
- }
- std::vector<std::string> newparms;
- newparms.push_back(parameters[0]);
- newparms.push_back(parameters[1]);
-
- std::string modelist = newparms[1];
- bool adding = true;
- unsigned int param_at = 2;
- for(unsigned int i = 0; i < modelist.length(); i++)
+ Modes::ChangeList::List& list = modes.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin(); i != list.end(); )
{
- unsigned char modechar = modelist[i];
- if (modechar == '+' || modechar == '-')
+ Modes::Change& curr = *i;
+ // Replace all namebase (dummyZ) modes being changed with the actual
+ // mode handler and parameter. The parameter format of the namebase mode is
+ // <modename>[=<parameter>].
+ if (curr.mh == &dummyZ)
{
- adding = (modechar == '+');
- continue;
- }
- ModeHandler *mh = ServerInstance->Modes->FindMode(modechar, MODETYPE_CHANNEL);
- if (modechar == 'Z')
- {
- modechar = 0;
- std::string name, value;
- if (param_at < parameters.size())
- name = parameters[param_at++];
+ std::string name = curr.param;
+ std::string value;
std::string::size_type eq = name.find('=');
if (eq != std::string::npos)
{
- value = name.substr(eq + 1);
- name = name.substr(0, eq);
+ value.assign(name, eq + 1, std::string::npos);
+ name.erase(eq);
+ }
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+ if (!mh)
+ {
+ // Mode handler not found
+ i = list.erase(i);
+ continue;
}
- for(char letter = 'A'; modechar == 0 && letter <= 'z'; letter++)
+
+ curr.param.clear();
+ if (mh->NeedsParam(curr.adding))
{
- mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
- if (mh && mh->name == name)
+ if (value.empty())
{
- if (mh->GetNumParams(adding))
- {
- if (!value.empty())
- {
- newparms.push_back(value);
- modechar = letter;
- break;
- }
- }
- else
- {
- modechar = letter;
- break;
- }
+ // Mode needs a parameter but there wasn't one
+ i = list.erase(i);
+ continue;
}
+
+ // Change parameter to the text after the '='
+ curr.param = value;
}
- if (modechar)
- modelist[i] = modechar;
- else
- modelist.erase(i--, 1);
- }
- else if (mh && mh->GetNumParams(adding) && param_at < parameters.size())
- {
- newparms.push_back(parameters[param_at++]);
+
+ // Put the actual ModeHandler in place of the namebase handler
+ curr.mh = mh;
}
+
+ ++i;
}
- newparms[1] = modelist;
- ServerInstance->Modes->Process(newparms, source, false);
- return MOD_RES_DENY;
+
+ return MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/m_namesx.cpp b/src/modules/m_namesx.cpp
index 82d311773..708b98e56 100644
--- a/src/modules/m_namesx.cpp
+++ b/src/modules/m_namesx.cpp
@@ -21,40 +21,27 @@
#include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the NAMESX (CAP multi-prefix) capability. */
+#include "modules/cap.h"
class ModuleNamesX : public Module
{
+ Cap::Capability cap;
public:
- GenericCap cap;
ModuleNamesX() : cap(this, "multi-prefix")
{
}
- void init()
- {
- Implementation eventlist[] = { I_OnPreCommand, I_OnNamesListItem, I_On005Numeric, I_OnEvent, I_OnSendWhoLine };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- ~ModuleNamesX()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the NAMESX (CAP multi-prefix) capability.",VF_VENDOR);
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" NAMESX");
+ tokens["NAMESX"];
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
/* We don't actually create a proper command handler class for PROTOCTL,
* because other modules might want to have PROTOCTL hooks too.
@@ -65,66 +52,37 @@ class ModuleNamesX : public Module
{
if ((parameters.size()) && (!strcasecmp(parameters[0].c_str(),"NAMESX")))
{
- cap.ext.set(user, 1);
+ cap.set(user, true);
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (!cap.ext.get(issuer))
- return;
+ if (cap.get(issuer))
+ prefixes = memb->GetAllPrefixChars();
- /* Some module hid this from being displayed, dont bother */
- if (nick.empty())
- return;
-
- prefixes = memb->chan->GetAllPrefixChars(memb->user);
+ return MOD_RES_PASSTHRU;
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+ ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- if (!cap.ext.get(source))
- return;
-
- // Channel names can contain ":", and ":" as a 'start-of-token' delimiter is
- // only ever valid after whitespace, so... find the actual delimiter first!
- // Thanks to FxChiP for pointing this out.
- std::string::size_type pos = line.find(" :");
- if (pos == std::string::npos || pos == 0)
- return;
- pos--;
- // Don't do anything if the user has no prefixes
- if ((line[pos] == 'H') || (line[pos] == 'G') || (line[pos] == '*'))
- return;
-
- // 352 21DAAAAAB #chan ident localhost insp21.test 21DAAAAAB H@ :0 a
- // a b pos
- std::string::size_type a = 4 + source->nick.length() + 1;
- std::string::size_type b = line.find(' ', a);
- if (b == std::string::npos)
- return;
-
- // Try to find this channel
- std::string channame = line.substr(a, b-a);
- Channel* chan = ServerInstance->FindChan(channame);
- if (!chan)
- return;
+ if ((!memb) || (!cap.get(source)))
+ return MOD_RES_PASSTHRU;
// Don't do anything if the user has only one prefix
- std::string prefixes = chan->GetAllPrefixChars(user);
+ std::string prefixes = memb->GetAllPrefixChars();
if (prefixes.length() <= 1)
- return;
+ return MOD_RES_PASSTHRU;
- line.erase(pos, 1);
- line.insert(pos, prefixes);
- }
+ // #chan ident localhost insp22.test nick H@ :0 Attila
+ if (numeric.GetParams().size() < 6)
+ return MOD_RES_PASSTHRU;
- void OnEvent(Event& ev)
- {
- cap.HandleEvent(ev);
+ numeric.GetParams()[5].append(prefixes, 1, std::string::npos);
+ return MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/m_nationalchars.cpp b/src/modules/m_nationalchars.cpp
index bc90c9fad..81c2d2959 100644
--- a/src/modules/m_nationalchars.cpp
+++ b/src/modules/m_nationalchars.cpp
@@ -26,17 +26,12 @@
by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru */
#include "inspircd.h"
-#include "caller.h"
#include <fstream>
-/* $ModDesc: Provides an ability to have non-RFC1459 nicks & support for national CASEMAPPING */
-
-class lwbNickHandler : public HandlerBase2<bool, const char*, size_t>
+class lwbNickHandler
{
public:
- lwbNickHandler() { }
- virtual ~lwbNickHandler() { }
- virtual bool Call(const char*, size_t);
+ static bool Call(const std::string&);
};
/*,m_reverse_additionalUp[256];*/
@@ -71,11 +66,12 @@ char utf8size(unsigned char * mb)
/* Conditions added */
-bool lwbNickHandler::Call(const char* n, size_t max)
+bool lwbNickHandler::Call(const std::string& nick)
{
- if (!n || !*n)
+ if (nick.empty())
return false;
+ const char* n = nick.c_str();
unsigned int p = 0;
for (const char* i = n; *i; i++, p++)
{
@@ -215,21 +211,28 @@ bool lwbNickHandler::Call(const char* n, size_t max)
}
/* too long? or not -- pointer arithmetic rocks */
- return (p < max);
+ return (p < ServerInstance->Config->Limits.NickMax);
}
class ModuleNationalChars : public Module
{
- private:
- lwbNickHandler myhandler;
- std::string charset, casemapping;
+ std::string charset;
unsigned char m_additional[256], m_additionalUp[256], m_lower[256], m_upper[256];
- caller2<bool, const char*, size_t> rememberer;
+ TR1NS::function<bool(const std::string&)> rememberer;
bool forcequit;
const unsigned char * lowermap_rememberer;
unsigned char prev_map[256];
+ template <typename T>
+ void RehashHashmap(T& hashmap)
+ {
+ T newhash(hashmap.bucket_count());
+ for (typename T::const_iterator i = hashmap.begin(); i != hashmap.end(); ++i)
+ newhash.insert(std::make_pair(i->first, i->second));
+ hashmap.swap(newhash);
+ }
+
void CheckRehash()
{
// See if anything changed
@@ -238,20 +241,9 @@ class ModuleNationalChars : public Module
memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
- ServerInstance->RehashUsersAndChans();
-
- // The OnGarbageCollect() method in m_watch rebuilds the hashmap used by it
- Module* mod = ServerInstance->Modules->Find("m_watch.so");
- if (mod)
- mod->OnGarbageCollect();
-
- // Send a Request to m_spanningtree asking it to rebuild its hashmaps
- mod = ServerInstance->Modules->Find("m_spanningtree.so");
- if (mod)
- {
- Request req(this, mod, "rehash");
- req.Send();
- }
+ RehashHashmap(ServerInstance->Users.clientlist);
+ RehashHashmap(ServerInstance->Users.uuidlist);
+ RehashHashmap(ServerInstance->chanlist);
}
public:
@@ -261,34 +253,24 @@ class ModuleNationalChars : public Module
memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
}
- void init()
+ void init() CXX11_OVERRIDE
{
memcpy(m_lower, rfc_case_insensitive_map, 256);
national_case_insensitive_map = m_lower;
- ServerInstance->IsNick = &myhandler;
-
- Implementation eventlist[] = { I_OnRehash, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- virtual void On005Numeric(std::string &output)
- {
- std::string tmp(casemapping);
- tmp.insert(0, "CASEMAPPING=");
- SearchAndReplace(output, std::string("CASEMAPPING=rfc1459"), tmp);
+ ServerInstance->IsNick = &lwbNickHandler::Call;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("nationalchars");
charset = tag->getString("file");
- casemapping = tag->getString("casemapping", ServerConfig::CleanFilename(charset.c_str()));
+ std::string casemapping = tag->getString("casemapping", FileSystem::GetFileName(charset));
if (casemapping.find(' ') != std::string::npos)
throw ModuleException("<nationalchars:casemapping> must not contain any spaces!");
+ ServerInstance->Config->CaseMapping = casemapping;
#if defined _WIN32
- if (!ServerInstance->Config->StartsWithWindowsDriveLetter(charset))
+ if (!FileSystem::StartsWithWindowsDriveLetter(charset))
charset.insert(0, "./locales/");
#else
if(charset[0] != '/')
@@ -307,16 +289,19 @@ class ModuleNationalChars : public Module
if (!forcequit)
return;
- for (LocalUserList::const_iterator iter = ServerInstance->Users->local_users.begin(); iter != ServerInstance->Users->local_users.end(); ++iter)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); )
{
/* Fix by Brain: Dont quit UID users */
+ // Quitting the user removes it from the list
User* n = *iter;
- if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick.c_str(), ServerInstance->Config->Limits.NickMax))
+ ++iter;
+ if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick))
ServerInstance->Users->QuitUser(n, message);
}
}
- virtual ~ModuleNationalChars()
+ ~ModuleNationalChars()
{
ServerInstance->IsNick = rememberer;
national_case_insensitive_map = lowermap_rememberer;
@@ -324,7 +309,7 @@ class ModuleNationalChars : public Module
CheckRehash();
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides an ability to have non-RFC1459 nicks & support for national CASEMAPPING", VF_VENDOR | VF_COMMON, charset);
}
@@ -340,10 +325,10 @@ class ModuleNationalChars : public Module
/*so Bynets Unreal distribution stuff*/
bool loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
{
- std::ifstream ifs(filename.c_str());
+ std::ifstream ifs(ServerInstance->Config->Paths.PrependConfig(filename).c_str());
if (ifs.fail())
{
- ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for missing file: %s", filename.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for missing file: %s", filename.c_str());
return false;
}
@@ -358,7 +343,7 @@ class ModuleNationalChars : public Module
{
if (loadtable(ifs, tables[n], 255) && (n < faillimit))
{
- ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
return false;
}
}
diff --git a/src/modules/m_nickflood.cpp b/src/modules/m_nickflood.cpp
index 04d7c8b5e..50e2b3d93 100644
--- a/src/modules/m_nickflood.cpp
+++ b/src/modules/m_nickflood.cpp
@@ -19,8 +19,10 @@
#include "inspircd.h"
+#include "modules/exemption.h"
-/* $ModDesc: Provides channel mode +F (nick flood protection) */
+// The number of seconds nickname changing will be blocked for.
+static unsigned int duration;
/** Holds settings and state associated with channel mode +F
*/
@@ -74,101 +76,85 @@ class nickfloodsettings
void lock()
{
- unlocktime = ServerInstance->Time() + 60;
+ unlocktime = ServerInstance->Time() + duration;
}
};
/** Handles channel mode +F
*/
-class NickFlood : public ModeHandler
+class NickFlood : public ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >
{
public:
- SimpleExtItem<nickfloodsettings> ext;
- NickFlood(Module* Creator) : ModeHandler(Creator, "nickflood", 'F', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("nickflood", Creator) { }
+ NickFlood(Module* Creator)
+ : ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >(Creator, "nickflood", 'F')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- std::string::size_type colon = parameter.find(':');
- if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
-
- /* Set up the flood parameters for this channel */
- unsigned int nnicks = ConvToInt(parameter.substr(0, colon));
- unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
-
- if ((nnicks<1) || (nsecs<1))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
+ }
- nickfloodsettings* f = ext.get(channel);
- if ((f) && (nnicks == f->nicks) && (nsecs == f->secs))
- // mode params match
- return MODEACTION_DENY;
+ /* Set up the flood parameters for this channel */
+ unsigned int nnicks = ConvToInt(parameter.substr(0, colon));
+ unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
- ext.set(channel, new nickfloodsettings(nsecs, nnicks));
- parameter = ConvToStr(nnicks) + ":" + ConvToStr(nsecs);
- channel->SetModeParam('F', parameter);
- return MODEACTION_ALLOW;
- }
- else
+ if ((nnicks<1) || (nsecs<1))
{
- if (!channel->IsModeSet('F'))
- return MODEACTION_DENY;
-
- ext.unset(channel);
- channel->SetModeParam('F', "");
- return MODEACTION_ALLOW;
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+ return MODEACTION_DENY;
}
+
+ ext.set(channel, new nickfloodsettings(nsecs, nnicks));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const nickfloodsettings* nfs, std::string& out)
+ {
+ out.append(ConvToStr(nfs->nicks)).push_back(':');
+ out.append(ConvToStr(nfs->secs));
}
};
class ModuleNickFlood : public Module
{
+ CheckExemption::EventProvider exemptionprov;
NickFlood nf;
public:
-
ModuleNickFlood()
- : nf(this)
+ : exemptionprov(this)
+ , nf(this)
{
}
- void init()
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(nf);
- ServerInstance->Modules->AddService(nf.ext);
- Implementation eventlist[] = { I_OnUserPreNick, I_OnUserPostNick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ConfigTag* tag = ServerInstance->Config->ConfValue("nickflood");
+ duration = tag->getDuration("duration", 60, 10, 600);
}
- ModResult OnUserPreNick(User* user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (ServerInstance->NICKForced.get(user)) /* Allow forced nick changes */
- return MOD_RES_PASSTHRU;
-
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
- Channel *channel = *i;
+ Channel* channel = (*i)->chan;
ModResult res;
nickfloodsettings *f = nf.ext.get(channel);
if (f)
{
- res = ServerInstance->OnCheckExemption(user,channel,"nickflood");
+ res = CheckExemption::Call(exemptionprov, user, channel, "nickflood");
if (res == MOD_RES_ALLOW)
continue;
if (f->islocked())
{
- user->WriteNumeric(447, "%s :%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", user->nick.c_str(), channel->name.c_str(), f->nicks, f->secs);
+ user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("%s has been locked for nickchanges for %u seconds because there have been more than %u nick changes in %u seconds", channel->name.c_str(), duration, f->nicks, f->secs));
return MOD_RES_DENY;
}
@@ -176,7 +162,7 @@ class ModuleNickFlood : public Module
{
f->clear();
f->lock();
- channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName.c_str(), "NOTICE %s :No nick changes are allowed for 60 seconds because there have been more than %u nick changes in %u seconds.", channel->name.c_str(), f->nicks, f->secs);
+ channel->WriteNotice(InspIRCd::Format("No nick changes are allowed for %u seconds because there have been more than %u nick changes in %u seconds.", duration, f->nicks, f->secs));
return MOD_RES_DENY;
}
}
@@ -188,20 +174,20 @@ class ModuleNickFlood : public Module
/*
* XXX: HACK: We do the increment on the *POST* event here (instead of all together) because we have no way of knowing whether other modules would block a nickchange.
*/
- void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
if (isdigit(user->nick[0])) /* allow switches to UID */
return;
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); ++i)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
{
- Channel *channel = *i;
+ Channel* channel = (*i)->chan;
ModResult res;
nickfloodsettings *f = nf.ext.get(channel);
if (f)
{
- res = ServerInstance->OnCheckExemption(user,channel,"nickflood");
+ res = CheckExemption::Call(exemptionprov, user, channel, "nickflood");
if (res == MOD_RES_ALLOW)
return;
@@ -214,11 +200,7 @@ class ModuleNickFlood : public Module
}
}
- ~ModuleNickFlood()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Channel mode F - nick flood protection", VF_VENDOR);
}
diff --git a/src/modules/m_nicklock.cpp b/src/modules/m_nicklock.cpp
index 4f5e5941c..964ed9780 100644
--- a/src/modules/m_nicklock.cpp
+++ b/src/modules/m_nicklock.cpp
@@ -22,7 +22,13 @@
#include "inspircd.h"
-/* $ModDesc: Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit */
+enum
+{
+ // InspIRCd-specific.
+ ERR_NICKNOTLOCKED = 946,
+ RPL_NICKLOCKON = 947,
+ RPL_NICKLOCKOFF = 945
+};
/** Handle /NICKLOCK
*/
@@ -35,29 +41,29 @@ class CommandNicklock : public Command
{
flags_needed = 'o';
syntax = "<oldnick> <newnick>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* target = ServerInstance->FindNick(parameters[0]);
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
return CMD_FAILURE;
}
/* Do local sanity checks and bails */
if (IS_LOCAL(user))
{
- if (!ServerInstance->IsNick(parameters[1].c_str(), ServerInstance->Config->Limits.NickMax))
+ if (!ServerInstance->IsNick(parameters[1]))
{
- user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNotice("*** Invalid nickname '" + parameters[1] + "'");
return CMD_FAILURE;
}
- user->WriteServ("947 %s %s :Nickname now locked.", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNumeric(RPL_NICKLOCKON, parameters[1], "Nickname now locked.");
}
/* If we made it this far, extend the user */
@@ -66,7 +72,7 @@ class CommandNicklock : public Command
locked.set(target, 1);
std::string oldnick = target->nick;
- if (target->ForceNickChange(parameters[1].c_str()))
+ if (target->ChangeNick(parameters[1]))
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKLOCK to change and hold "+oldnick+" to "+parameters[1]);
else
{
@@ -78,12 +84,9 @@ class CommandNicklock : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -98,16 +101,16 @@ class CommandNickunlock : public Command
{
flags_needed = 'o';
syntax = "<locked-nick>";
- TRANSLATE2(TR_NICK, TR_END);
+ TRANSLATE1(TR_NICK);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* target = ServerInstance->FindNick(parameters[0]);
if (!target)
{
- user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
return CMD_FAILURE;
}
@@ -116,13 +119,11 @@ class CommandNickunlock : public Command
if (locked.set(target, 0))
{
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKUNLOCK on "+target->nick);
- user->SendText(":%s 945 %s %s :Nickname now unlocked.",
- ServerInstance->Config->ServerName.c_str(),user->nick.c_str(),target->nick.c_str());
+ user->WriteRemoteNumeric(RPL_NICKLOCKOFF, target->nick, "Nickname now unlocked.");
}
else
{
- user->SendText(":%s 946 %s %s :This user's nickname is not locked.",
- ServerInstance->Config->ServerName.c_str(),user->nick.c_str(),target->nick.c_str());
+ user->WriteRemoteNumeric(ERR_NICKNOTLOCKED, target->nick, "This user's nickname is not locked.");
return CMD_FAILURE;
}
}
@@ -130,16 +131,12 @@ class CommandNickunlock : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
-
class ModuleNickLock : public Module
{
LocalIntExt locked;
@@ -147,47 +144,31 @@ class ModuleNickLock : public Module
CommandNickunlock cmd2;
public:
ModuleNickLock()
- : locked("nick_locked", this), cmd1(this, locked), cmd2(this, locked)
- {
- }
-
- void init()
+ : locked("nick_locked", ExtensionItem::EXT_USER, this)
+ , cmd1(this, locked)
+ , cmd2(this, locked)
{
- ServerInstance->Modules->AddService(cmd1);
- ServerInstance->Modules->AddService(cmd2);
- ServerInstance->Modules->AddService(locked);
- ServerInstance->Modules->Attach(I_OnUserPreNick, this);
}
- ~ModuleNickLock()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit", VF_OPTCOMMON | VF_VENDOR);
}
- ModResult OnUserPreNick(User* user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
- if (ServerInstance->NICKForced.get(user)) /* Allow forced nick changes */
- return MOD_RES_PASSTHRU;
-
if (locked.get(user))
{
- user->WriteNumeric(447, "%s :You cannot change your nickname (your nick is locked)",user->nick.c_str());
+ user->WriteNumeric(ERR_CANTCHANGENICK, "You cannot change your nickname (your nick is locked)");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
Module *nflood = ServerInstance->Modules->Find("m_nickflood.so");
- ServerInstance->Modules->SetPriority(this, I_OnUserPreNick, PRIORITY_BEFORE, &nflood);
+ ServerInstance->Modules->SetPriority(this, I_OnUserPreNick, PRIORITY_BEFORE, nflood);
}
};
diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp
index c934a05c6..af14c73ec 100644
--- a/src/modules/m_noctcp.cpp
+++ b/src/modules/m_noctcp.cpp
@@ -20,72 +20,75 @@
#include "inspircd.h"
+#include "modules/exemption.h"
-/* $ModDesc: Provides channel mode +C to block CTCPs */
-
-class NoCTCP : public SimpleChannelModeHandler
+class NoCTCPUser : public SimpleUserModeHandler
{
- public:
- NoCTCP(Module* Creator) : SimpleChannelModeHandler(Creator, "noctcp", 'C') { }
+public:
+ NoCTCPUser(Module* Creator)
+ : SimpleUserModeHandler(Creator, "u_noctcp", 'T')
+ {
+ if (!ServerInstance->Config->ConfValue("noctcp")->getBool("enableumode"))
+ DisableAutoRegister();
+ }
};
class ModuleNoCTCP : public Module
{
-
- NoCTCP nc;
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler nc;
+ NoCTCPUser ncu;
public:
-
ModuleNoCTCP()
- : nc(this)
+ : exemptionprov(this)
+ , nc(this, "noctcp", 'C')
+ , ncu(this)
{
}
- void init()
+ Version GetVersion() CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(nc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ return Version("Provides user mode +T and channel mode +C to block CTCPs", VF_VENDOR);
}
- virtual ~ModuleNoCTCP()
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- }
-
- virtual Version GetVersion()
- {
- return Version("Provides channel mode +C to block CTCPs", VF_VENDOR);
- }
+ if (!IS_LOCAL(user))
+ return MOD_RES_PASSTHRU;
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
- }
+ std::string ctcpname;
+ if (!details.IsCTCP(ctcpname) || irc::equals(ctcpname, "ACTION"))
+ return MOD_RES_PASSTHRU;
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ if (target.type == MessageTarget::TYPE_CHANNEL)
{
- Channel* c = (Channel*)dest;
- if ((text.empty()) || (text[0] != '\001') || (!strncmp(text.c_str(),"\1ACTION ", 8)) || (text == "\1ACTION\1") || (text == "\1ACTION"))
- return MOD_RES_PASSTHRU;
-
- ModResult res = ServerInstance->OnCheckExemption(user,c,"noctcp");
+ Channel* c = target.Get<Channel>();
+ ModResult res = CheckExemption::Call(exemptionprov, user, c, "noctcp");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet('C')))
+ if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet(nc)))
{
- user->WriteNumeric(ERR_NOCTCPALLOWED, "%s %s :Can't send CTCP to channel (+C set)",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send CTCP to channel (+C set)");
return MOD_RES_DENY;
}
}
+ else if (target.type == MessageTarget::TYPE_USER)
+ {
+ User* u = target.Get<User>();
+ if (u->IsModeSet(ncu))
+ {
+ user->WriteNumeric(ERR_CANTSENDTOUSER, u->nick, "Can't send CTCP to user (+T set)");
+ return MOD_RES_PASSTHRU;
+ }
+ }
return MOD_RES_PASSTHRU;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('C');
+ tokens["EXTBAN"].push_back('C');
}
};
diff --git a/src/modules/m_nokicks.cpp b/src/modules/m_nokicks.cpp
index 1f58a2e08..3155dcb6c 100644
--- a/src/modules/m_nokicks.cpp
+++ b/src/modules/m_nokicks.cpp
@@ -22,56 +22,36 @@
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +Q to prevent kicks on the channel. */
-
-class NoKicks : public SimpleChannelModeHandler
-{
- public:
- NoKicks(Module* Creator) : SimpleChannelModeHandler(Creator, "nokick", 'Q') { }
-};
-
class ModuleNoKicks : public Module
{
- NoKicks nk;
+ SimpleChannelModeHandler nk;
public:
ModuleNoKicks()
- : nk(this)
- {
- }
-
- void init()
+ : nk(this, "nokick", 'Q')
{
- ServerInstance->Modules->AddService(nk);
- Implementation eventlist[] = { I_OnUserPreKick, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('Q');
+ tokens["EXTBAN"].push_back('Q');
}
- ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
- if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet('Q')))
+ if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet(nk)))
{
// Can't kick with Q in place, not even opers with override, and founders
- source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Can't kick user %s from channel (+Q set)",source->nick.c_str(), memb->chan->name.c_str(), memb->user->nick.c_str());
+ source->WriteNumeric(ERR_CHANOPRIVSNEEDED, memb->chan->name, InspIRCd::Format("Can't kick user %s from channel (+Q set)", memb->user->nick.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ~ModuleNoKicks()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +Q to prevent kicks on the channel.", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleNoKicks)
diff --git a/src/modules/m_nonicks.cpp b/src/modules/m_nonicks.cpp
index 672a48f8d..998662c3c 100644
--- a/src/modules/m_nonicks.cpp
+++ b/src/modules/m_nonicks.cpp
@@ -20,83 +20,53 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides support for channel mode +N & extban +b N: which prevents nick changes on channel */
-
-class NoNicks : public SimpleChannelModeHandler
-{
- public:
- NoNicks(Module* Creator) : SimpleChannelModeHandler(Creator, "nonick", 'N') { }
-};
+#include "modules/exemption.h"
class ModuleNoNickChange : public Module
{
- NoNicks nn;
- bool override;
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler nn;
public:
- ModuleNoNickChange() : nn(this)
+ ModuleNoNickChange()
+ : exemptionprov(this)
+ , nn(this, "nonick", 'N')
{
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(nn);
- Implementation eventlist[] = { I_OnUserPreNick, I_On005Numeric, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleNoNickChange()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +N & extban +b N: which prevents nick changes on channel", VF_VENDOR);
}
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('N');
+ tokens["EXTBAN"].push_back('N');
}
- virtual ModResult OnUserPreNick(User* user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
- // Allow forced nick changes.
- if (ServerInstance->NICKForced.get(user))
- return MOD_RES_PASSTHRU;
-
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
- Channel* curr = *i;
+ Channel* curr = (*i)->chan;
- ModResult res = ServerInstance->OnCheckExemption(user,curr,"nonick");
+ ModResult res = CheckExemption::Call(exemptionprov, user, curr, "nonick");
if (res == MOD_RES_ALLOW)
continue;
- if (override && IS_OPER(user))
+ if (user->HasPrivPermission("channels/ignore-nonicks"))
continue;
- if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet('N')))
+ if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet(nn)))
{
- user->WriteNumeric(ERR_CANTCHANGENICK, "%s :Can't change nickname while on %s (+N is set)",
- user->nick.c_str(), curr->name.c_str());
+ user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Cannot change nickname while on %s (+N is set)",
+ curr->name.c_str()));
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
-
- virtual void OnRehash(User* user)
- {
- override = ServerInstance->Config->ConfValue("nonicks")->getBool("operoverride", false);
- }
};
MODULE_INIT(ModuleNoNickChange)
diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp
index c5b9f3a1c..77b0c9aa3 100644
--- a/src/modules/m_nonotice.cpp
+++ b/src/modules/m_nonotice.cpp
@@ -20,51 +20,39 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +T to block notices to the channel */
-
-class NoNotice : public SimpleChannelModeHandler
-{
- public:
- NoNotice(Module* Creator) : SimpleChannelModeHandler(Creator, "nonotice", 'T') { }
-};
+#include "modules/exemption.h"
class ModuleNoNotice : public Module
{
- NoNotice nt;
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler nt;
public:
ModuleNoNotice()
- : nt(this)
+ : exemptionprov(this)
+ , nt(this, "nonotice", 'T')
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(nt);
- Implementation eventlist[] = { I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["EXTBAN"].push_back('T');
}
- virtual void On005Numeric(std::string &output)
- {
- ServerInstance->AddExtBanChar('T');
- }
-
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
ModResult res;
- if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ if ((details.type == MSG_NOTICE) && (target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user)))
{
- Channel* c = (Channel*)dest;
- if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet('T')))
+ Channel* c = target.Get<Channel>();
+ if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet(nt)))
{
- res = ServerInstance->OnCheckExemption(user,c,"nonotice");
+ res = CheckExemption::Call(exemptionprov, user, c, "nonotice");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
else
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Can't send NOTICE to channel (+T set)",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send NOTICE to channel (+T set)");
return MOD_RES_DENY;
}
}
@@ -72,11 +60,7 @@ class ModuleNoNotice : public Module
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleNoNotice()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +T to block notices to the channel", VF_VENDOR);
}
diff --git a/src/modules/m_nopartmsg.cpp b/src/modules/m_nopartmsg.cpp
index ad3413101..7aeb66920 100644
--- a/src/modules/m_nopartmsg.cpp
+++ b/src/modules/m_nopartmsg.cpp
@@ -19,45 +19,27 @@
#include "inspircd.h"
-/* $ModDesc: Implements extban +b p: - part message bans */
-
class ModulePartMsgBan : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnUserPart, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModulePartMsgBan()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements extban +b p: - part message bans", VF_OPTCOMMON|VF_VENDOR);
}
-
- virtual void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+ void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
{
if (!IS_LOCAL(memb->user))
return;
if (memb->chan->GetExtBanStatus(memb->user, 'p') == MOD_RES_DENY)
partmessage.clear();
-
- return;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('p');
+ tokens["EXTBAN"].push_back('p');
}
};
-
MODULE_INIT(ModulePartMsgBan)
-
diff --git a/src/modules/m_ojoin.cpp b/src/modules/m_ojoin.cpp
index 026d98f86..67b79e07c 100644
--- a/src/modules/m_ojoin.cpp
+++ b/src/modules/m_ojoin.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2009 Taros <taros34@hotmail.com>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
@@ -16,57 +17,39 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/*
- * Written for InspIRCd-1.2 by Taros on the Tel'Laerad M&D Team
- * <http://tellaerad.net>
- */
-
#include "inspircd.h"
-/* $ModConfig: <ojoin prefix="!" notice="yes" op="yes">
- * Specify the prefix that +Y will grant here, it should be unused.
- * Leave prefix empty if you do not wish +Y to grant a prefix
- * If notice is set to on, upon ojoin, the server will notice
- * the channel saying that the oper is joining on network business
- * If op is set to on, it will give them +o along with +Y */
-/* $ModDesc: Provides the /ojoin command, which joins a user to a channel on network business, and gives them +Y, which makes them immune to kick / deop and so on. */
-/* $ModAuthor: Taros */
-/* $ModAuthorMail: taros34@hotmail.com */
-
-/* A note: This will not protect against kicks from services,
- * ulines, or operoverride. */
-
#define NETWORK_VALUE 9000000
-char NPrefix;
-bool notice;
-bool op;
-
/** Handle /OJOIN
*/
-class CommandOjoin : public Command
+class CommandOjoin : public SplitCommand
{
public:
bool active;
- CommandOjoin(Module* parent) : Command(parent,"OJOIN", 1)
- {
- flags_needed = 'o'; Penalty = 0; syntax = "<channel>";
+ bool notice;
+ bool op;
+ ModeHandler* npmh;
+ CommandOjoin(Module* parent, ModeHandler& mode)
+ : SplitCommand(parent, "OJOIN", 1)
+ , npmh(&mode)
+ {
+ flags_needed = 'o'; syntax = "<channel>";
active = false;
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
// Make sure the channel name is allowable.
- if (!ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(parameters[0]))
{
- user->WriteServ("NOTICE "+user->nick+" :*** Invalid characters in channel name or name too long");
+ user->WriteNotice("*** Invalid characters in channel name or name too long");
return CMD_FAILURE;
}
active = true;
- Channel* channel = Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
+ // override is false because we want OnUserPreJoin to run
+ Channel* channel = Channel::JoinUser(user, parameters[0], false);
active = false;
if (channel)
@@ -75,22 +58,24 @@ class CommandOjoin : public Command
if (notice)
{
- channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s joined on official network business.",
- parameters[0].c_str(), user->nick.c_str());
- ServerInstance->PI->SendChannelNotice(channel, 0, user->nick + " joined on official network business.");
+ const std::string msg = user->nick + " joined on official network business.";
+ channel->WriteNotice(msg);
+ ServerInstance->PI->SendChannelNotice(channel, 0, msg);
}
}
else
{
+ channel = ServerInstance->FindChan(parameters[0]);
+ if (!channel)
+ return CMD_FAILURE;
+
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used OJOIN in "+parameters[0]);
// they're already in the channel
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back(op ? "+Yo" : "+Y");
- modes.push_back(user->nick);
+ Modes::ChangeList changelist;
+ changelist.push_add(npmh, user->nick);
if (op)
- modes.push_back(user->nick);
- ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
+ changelist.push_add(ServerInstance->Modes->FindMode('o', MODETYPE_CHANNEL), user->nick);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, channel, NULL, changelist);
}
return CMD_SUCCESS;
}
@@ -98,57 +83,16 @@ class CommandOjoin : public Command
/** channel mode +Y
*/
-class NetworkPrefix : public ModeHandler
+class NetworkPrefix : public PrefixMode
{
public:
- NetworkPrefix(Module* parent) : ModeHandler(parent, "official-join", 'Y', PARAM_ALWAYS, MODETYPE_CHANNEL)
- {
- list = true;
- prefix = NPrefix;
- levelrequired = INT_MAX;
- m_paramtype = TR_NICK;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- const UserMembList* cl = channel->GetUsers();
- std::vector<std::string> mode_junk;
- mode_junk.push_back(channel->name);
- irc::modestacker modestack(false);
- std::deque<std::string> stackresult;
-
- for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
- {
- if (i->second->hasMode('Y'))
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- modestack.Push(this->GetModeChar(), i->first->nick);
- }
- }
-
- if (stack)
- return;
-
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- unsigned int GetPrefixRank()
+ NetworkPrefix(Module* parent, char NPrefix)
+ : PrefixMode(parent, "official-join", 'Y', NETWORK_VALUE, NPrefix)
{
- return NETWORK_VALUE;
+ ranktoset = ranktounset = UINT_MAX;
}
- void RemoveMode(User* user, irc::modestacker* stack)
- {
- }
-
- ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
+ ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE
{
User* theuser = ServerInstance->FindNick(parameter);
// remove own privs?
@@ -157,47 +101,27 @@ class NetworkPrefix : public ModeHandler
return MOD_RES_PASSTHRU;
}
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
-
};
class ModuleOjoin : public Module
{
- NetworkPrefix* np;
+ NetworkPrefix np;
CommandOjoin mycommand;
public:
ModuleOjoin()
- : np(NULL), mycommand(this)
+ : np(this, ServerInstance->Config->ConfValue("ojoin")->getString("prefix").c_str()[0])
+ , mycommand(this, np)
{
}
- void init()
- {
- /* Load config stuff */
- OnRehash(NULL);
-
- /* Initialise module variables */
- np = new NetworkPrefix(this);
-
- ServerInstance->Modules->AddService(*np);
- ServerInstance->Modules->AddService(mycommand);
-
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserPreKick, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (mycommand.active)
{
- privs += 'Y';
- if (op)
+ privs += np.GetModeChar();
+ if (mycommand.op)
privs += 'o';
return MOD_RES_ALLOW;
}
@@ -205,53 +129,36 @@ class ModuleOjoin : public Module
return MOD_RES_PASSTHRU;
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* Conf = ServerInstance->Config->ConfValue("ojoin");
-
- if (!np)
- {
- // This is done on module load only
- std::string npre = Conf->getString("prefix");
- NPrefix = npre.empty() ? 0 : npre[0];
-
- if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix))
- throw ModuleException("Looks like the +Y prefix you picked for m_ojoin is already in use. Pick another.");
- }
-
- notice = Conf->getBool("notice", true);
- op = Conf->getBool("op", true);
+ mycommand.notice = Conf->getBool("notice", true);
+ mycommand.op = Conf->getBool("op", true);
}
- ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
// Don't do anything if they're not +Y
- if (!memb->hasMode('Y'))
+ if (!memb->HasMode(&np))
return MOD_RES_PASSTHRU;
// Let them do whatever they want to themselves.
if (source == memb->user)
return MOD_RES_PASSTHRU;
- source->WriteNumeric(484, source->nick+" "+memb->chan->name+" :Can't kick "+memb->user->nick+" as they're on official network business.");
+ source->WriteNumeric(ERR_RESTRICTED, memb->chan->name, "Can't kick "+memb->user->nick+" as they're on official network business.");
return MOD_RES_DENY;
}
- ~ModuleOjoin()
- {
- delete np;
- }
-
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Network Business Join", VF_VENDOR);
}
};
MODULE_INIT(ModuleOjoin)
-
diff --git a/src/modules/m_operchans.cpp b/src/modules/m_operchans.cpp
index ca948d95b..f0812ce27 100644
--- a/src/modules/m_operchans.cpp
+++ b/src/modules/m_operchans.cpp
@@ -22,7 +22,11 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for oper-only chans via the +O channel mode */
+enum
+{
+ // From UnrealIRCd.
+ ERR_CANTJOINOPERSONLY = 520
+};
class OperChans : public SimpleChannelModeHandler
{
@@ -42,44 +46,32 @@ class ModuleOperChans : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(oc);
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric, I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- if (chan && chan->IsModeSet('O') && !IS_OPER(user))
+ if (chan && chan->IsModeSet(oc) && !user->IsOper())
{
- user->WriteNumeric(ERR_CANTJOINOPERSONLY, "%s %s :Only IRC operators may join %s (+O is set)",
- user->nick.c_str(), chan->name.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_CANTJOINOPERSONLY, chan->name, InspIRCd::Format("Only IRC operators may join %s (+O is set)", chan->name.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'O') && (mask[1] == ':'))
{
- if (IS_OPER(user) && InspIRCd::Match(user->oper->name, mask.substr(2)))
+ if (user->IsOper() && InspIRCd::Match(user->oper->name, mask.substr(2)))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
- {
- ServerInstance->AddExtBanChar('O');
- }
-
- ~ModuleOperChans()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
+ tokens["EXTBAN"].push_back('O');
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for oper-only chans via the +O channel mode and 'O' extban", VF_VENDOR);
}
diff --git a/src/modules/m_operjoin.cpp b/src/modules/m_operjoin.cpp
index bd77384a6..dd80d99ba 100644
--- a/src/modules/m_operjoin.cpp
+++ b/src/modules/m_operjoin.cpp
@@ -24,84 +24,44 @@
#include "inspircd.h"
-/* $ModDesc: Forces opers to join the specified channel(s) on oper-up */
-
class ModuleOperjoin : public Module
{
- private:
- std::string operChan;
std::vector<std::string> operChans;
bool override;
- int tokenize(const std::string &str, std::vector<std::string> &tokens)
- {
- // skip delimiters at beginning.
- std::string::size_type lastPos = str.find_first_not_of(",", 0);
- // find first "non-delimiter".
- std::string::size_type pos = str.find_first_of(",", lastPos);
-
- while (std::string::npos != pos || std::string::npos != lastPos)
- {
- // found a token, add it to the vector.
- tokens.push_back(str.substr(lastPos, pos - lastPos));
- // skip delimiters. Note the "not_of"
- lastPos = str.find_first_not_of(",", pos);
- // find next "non-delimiter"
- pos = str.find_first_of(",", lastPos);
- }
- return tokens.size();
- }
-
public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnPostOper, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("operjoin");
- operChan = tag->getString("channel");
override = tag->getBool("override", false);
+ irc::commasepstream ss(tag->getString("channel"));
operChans.clear();
- if (!operChan.empty())
- tokenize(operChan,operChans);
- }
- virtual ~ModuleOperjoin()
- {
+ for (std::string channame; ss.GetToken(channame); )
+ operChans.push_back(channame);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Forces opers to join the specified channel(s) on oper-up", VF_VENDOR);
}
- virtual void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+ void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
+ LocalUser* localuser = IS_LOCAL(user);
+ if (!localuser)
return;
- for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++)
- if (ServerInstance->IsChannel(it->c_str(), ServerInstance->Config->Limits.ChanMax))
- Channel::JoinUser(user, it->c_str(), override, "", false, ServerInstance->Time());
+ for (std::vector<std::string>::const_iterator i = operChans.begin(); i != operChans.end(); ++i)
+ if (ServerInstance->IsChannel(*i))
+ Channel::JoinUser(localuser, *i, override);
- std::string chanList = IS_OPER(user)->getConfig("autojoin");
- if (!chanList.empty())
+ irc::commasepstream ss(localuser->oper->getConfig("autojoin"));
+ for (std::string channame; ss.GetToken(channame); )
{
- std::vector<std::string> typechans;
- tokenize(chanList, typechans);
- for (std::vector<std::string>::const_iterator it = typechans.begin(); it != typechans.end(); ++it)
- {
- if (ServerInstance->IsChannel(it->c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- Channel::JoinUser(user, it->c_str(), override, "", false, ServerInstance->Time());
- }
- }
+ if (ServerInstance->IsChannel(channame))
+ Channel::JoinUser(localuser, channame, override);
}
}
};
diff --git a/src/modules/m_operlevels.cpp b/src/modules/m_operlevels.cpp
index 569defd49..2e02ffddf 100644
--- a/src/modules/m_operlevels.cpp
+++ b/src/modules/m_operlevels.cpp
@@ -20,38 +20,33 @@
*/
-/* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */
-
#include "inspircd.h"
class ModuleOperLevels : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnKill, this);
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Gives each oper type a 'level', cannot kill opers 'above' your level.", VF_VENDOR);
}
- virtual ModResult OnKill(User* source, User* dest, const std::string &reason)
+ ModResult OnKill(User* source, User* dest, const std::string &reason) CXX11_OVERRIDE
{
// oper killing an oper?
- if (IS_OPER(dest) && IS_OPER(source))
+ if (dest->IsOper() && source->IsOper())
{
- std::string level = dest->oper->getConfig("level");
- long dest_level = atol(level.c_str());
- level = source->oper->getConfig("level");
- long source_level = atol(level.c_str());
+ unsigned long dest_level = ConvToNum<unsigned long>(dest->oper->getConfig("level"));
+ unsigned long source_level = ConvToNum<unsigned long>(source->oper->getConfig("level"));
if (dest_level > source_level)
{
- if (IS_LOCAL(source)) ServerInstance->SNO->WriteGlobalSno('a', "Oper %s (level %ld) attempted to /kill a higher oper: %s (level %ld): Reason: %s",source->nick.c_str(),source_level,dest->nick.c_str(),dest_level,reason.c_str());
- dest->WriteServ("NOTICE %s :*** Oper %s attempted to /kill you!",dest->nick.c_str(),source->nick.c_str());
- source->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper %s is a higher level than you",source->nick.c_str(),dest->nick.c_str());
+ if (IS_LOCAL(source))
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "Oper %s (level %lu) attempted to /kill a higher oper: %s (level %lu): Reason: %s",
+ source->nick.c_str(), source_level, dest->nick.c_str(), dest_level, reason.c_str());
+ }
+ dest->WriteNotice("*** Oper " + source->nick + " attempted to /kill you!");
+ source->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper %s is a higher level than you", dest->nick.c_str()));
return MOD_RES_DENY;
}
}
@@ -60,4 +55,3 @@ class ModuleOperLevels : public Module
};
MODULE_INIT(ModuleOperLevels)
-
diff --git a/src/modules/m_operlog.cpp b/src/modules/m_operlog.cpp
index edb9109e8..b64e18870 100644
--- a/src/modules/m_operlog.cpp
+++ b/src/modules/m_operlog.cpp
@@ -21,51 +21,39 @@
#include "inspircd.h"
-/* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */
-
class ModuleOperLog : public Module
{
bool tosnomask;
public:
- void init()
+ void init() CXX11_OVERRIDE
{
- Implementation eventlist[] = { I_OnPreCommand, I_On005Numeric, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
ServerInstance->SNO->EnableSnomask('r', "OPERLOG");
- OnRehash(NULL);
}
- virtual ~ModuleOperLog()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("A module which logs all oper commands to the ircd log at default loglevel.", VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
tosnomask = ServerInstance->Config->ConfValue("operlog")->getBool("tosnomask", false);
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return MOD_RES_PASSTHRU;
- if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command)))
+ if ((user->IsOper()) && (user->HasPermission(command)))
{
- Command* thiscommand = ServerInstance->Parser->GetHandler(command);
+ Command* thiscommand = ServerInstance->Parser.GetHandler(command);
if ((thiscommand) && (thiscommand->flags_needed == 'o'))
{
- std::string line;
- if (!parameters.empty())
- line = irc::stringjoiner(" ", parameters, 0, parameters.size() - 1).GetJoined();
- std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + line;
- ServerInstance->Logs->Log("m_operlog", DEFAULT, "OPERLOG: " + msg);
+ std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + stdalgo::string::join(parameters);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OPERLOG: " + msg);
if (tosnomask)
ServerInstance->SNO->WriteGlobalSno('r', msg);
}
@@ -74,12 +62,11 @@ class ModuleOperLog : public Module
return MOD_RES_PASSTHRU;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" OPERLOG");
+ tokens["OPERLOG"];
}
};
-
MODULE_INIT(ModuleOperLog)
diff --git a/src/modules/m_opermodes.cpp b/src/modules/m_opermodes.cpp
index 8b49f685e..475d561ba 100644
--- a/src/modules/m_opermodes.cpp
+++ b/src/modules/m_opermodes.cpp
@@ -22,26 +22,15 @@
#include "inspircd.h"
-/* $ModDesc: Sets (and unsets) modes on opers when they oper up */
-
class ModuleModesOnOper : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnPostOper, this);
- }
-
- virtual ~ModuleModesOnOper()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Sets (and unsets) modes on opers when they oper up", VF_VENDOR);
}
- virtual void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+ void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return;
@@ -63,15 +52,15 @@ class ModuleModesOnOper : public Module
smodes = "+" + smodes;
std::string buf;
- std::stringstream ss(smodes);
- std::vector<std::string> modes;
+ irc::spacesepstream ss(smodes);
+ CommandBase::Params modes;
modes.push_back(u->nick);
// split into modes and mode params
- while (ss >> buf)
+ while (ss.GetToken(buf))
modes.push_back(buf);
- ServerInstance->SendMode(modes, u);
+ ServerInstance->Parser.CallHandler("MODE", modes, u);
}
};
diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp
index 989f97689..49409a1c0 100644
--- a/src/modules/m_opermotd.cpp
+++ b/src/modules/m_opermotd.cpp
@@ -22,8 +22,6 @@
#include "inspircd.h"
-/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */
-
/** Handle /OPERMOTD
*/
class CommandOpermotd : public Command
@@ -36,37 +34,36 @@ class CommandOpermotd : public Command
flags_needed = 'o'; syntax = "[<servername>]";
}
- CmdResult Handle (const std::vector<std::string>& parameters, User* user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
if ((parameters.empty()) || (parameters[0] == ServerInstance->Config->ServerName))
ShowOperMOTD(user);
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (!parameters.empty())
+ if ((!parameters.empty()) && (parameters[0].find('.') != std::string::npos))
return ROUTE_OPT_UCAST(parameters[0]);
return ROUTE_LOCALONLY;
}
void ShowOperMOTD(User* user)
{
- const std::string& servername = ServerInstance->Config->ServerName;
if (opermotd.empty())
{
- user->SendText(":%s 455 %s :OPERMOTD file is missing", servername.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(455, "OPERMOTD file is missing");
return;
}
- user->SendText(":%s 375 %s :- IRC Operators Message of the Day", servername.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(RPL_MOTDSTART, "- IRC Operators Message of the Day");
for (file_cache::const_iterator i = opermotd.begin(); i != opermotd.end(); ++i)
{
- user->SendText(":%s 372 %s :- %s", servername.c_str(), user->nick.c_str(), i->c_str());
+ user->WriteRemoteNumeric(RPL_MOTD, InspIRCd::Format("- %s", i->c_str()));
}
- user->SendText(":%s 376 %s :- End of OPERMOTD", servername.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(RPL_ENDOFMOTD, "- End of OPERMOTD");
}
};
@@ -82,37 +79,33 @@ class ModuleOpermotd : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnOper };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Shows a message to opers after oper-up, adds /opermotd", VF_VENDOR | VF_OPTCOMMON);
}
- virtual void OnOper(User* user, const std::string &opertype)
+ void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE
{
if (onoper && IS_LOCAL(user))
cmd.ShowOperMOTD(user);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
cmd.opermotd.clear();
ConfigTag* conf = ServerInstance->Config->ConfValue("opermotd");
onoper = conf->getBool("onoper", true);
- FileReader f(conf->getString("file", "opermotd"));
- for (int i=0, filesize = f.FileSize(); i < filesize; i++)
- cmd.opermotd.push_back(f.GetLine(i));
-
- if (conf->getBool("processcolors"))
+ try
+ {
+ FileReader reader(conf->getString("file", "opermotd"));
+ cmd.opermotd = reader.GetVector();
InspIRCd::ProcessColors(cmd.opermotd);
+ }
+ catch (CoreException&)
+ {
+ // Nothing happens here as we do the error handling in ShowOperMOTD.
+ }
}
};
diff --git a/src/modules/m_operprefix.cpp b/src/modules/m_operprefix.cpp
index 7d5e6d118..a8afd7c96 100644
--- a/src/modules/m_operprefix.cpp
+++ b/src/modules/m_operprefix.cpp
@@ -22,74 +22,18 @@
* Originally by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru
*/
-/* $ModDesc: Gives opers cmode +y which provides a staff prefix. */
-
#include "inspircd.h"
#define OPERPREFIX_VALUE 1000000
-class OperPrefixMode : public ModeHandler
+class OperPrefixMode : public PrefixMode
{
public:
- OperPrefixMode(Module* Creator) : ModeHandler(Creator, "operprefix", 'y', PARAM_ALWAYS, MODETYPE_CHANNEL)
- {
- std::string pfx = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!");
- list = true;
- prefix = pfx.empty() ? '!' : pfx[0];
- levelrequired = OPERPREFIX_VALUE;
- m_paramtype = TR_NICK;
- }
-
- unsigned int GetPrefixRank()
- {
- return OPERPREFIX_VALUE;
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- if (IS_SERVER(source) || ServerInstance->ULine(source->server))
- return MODEACTION_ALLOW;
- else
- {
- if (channel)
- source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only servers are permitted to change channel mode '%c'", source->nick.c_str(), channel->name.c_str(), 'y');
- return MODEACTION_DENY;
- }
- }
-
- bool NeedsOper() { return true; }
-
- void RemoveMode(Channel* chan, irc::modestacker* stack)
- {
- irc::modestacker modestack(false);
- const UserMembList* users = chan->GetUsers();
- for (UserMembCIter i = users->begin(); i != users->end(); ++i)
- {
- if (i->second->hasMode(mode))
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- modestack.Push(this->GetModeChar(), i->first->nick);
- }
- }
-
- if (stack)
- return;
-
- std::deque<std::string> stackresult;
- std::vector<std::string> mode_junk;
- mode_junk.push_back(chan->name);
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
+ OperPrefixMode(Module* Creator)
+ : PrefixMode(Creator, "operprefix", 'y', OPERPREFIX_VALUE)
{
+ prefix = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!", 1, 1)[0];
+ ranktoset = ranktounset = UINT_MAX;
}
};
@@ -97,111 +41,70 @@ class ModuleOperPrefixMode;
class HideOperWatcher : public ModeWatcher
{
ModuleOperPrefixMode* parentmod;
+
public:
- HideOperWatcher(ModuleOperPrefixMode* parent) : ModeWatcher((Module*) parent, 'H', MODETYPE_USER), parentmod(parent) {}
- void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding, ModeType type);
+ HideOperWatcher(ModuleOperPrefixMode* parent);
+ void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding) CXX11_OVERRIDE;
};
class ModuleOperPrefixMode : public Module
{
- private:
OperPrefixMode opm;
- bool mw_added;
HideOperWatcher hideoperwatcher;
+ UserModeReference hideopermode;
+
public:
ModuleOperPrefixMode()
- : opm(this), mw_added(false), hideoperwatcher(this)
- {
- }
-
- void init()
+ : opm(this), hideoperwatcher(this)
+ , hideopermode(this, "hideoper")
{
- ServerInstance->Modules->AddService(opm);
-
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnPostOper, I_OnLoadModule, I_OnUnloadModule, I_OnPostJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
/* To give clients a chance to learn about the new prefix we don't give +y to opers
* right now. That means if the module was loaded after opers have joined channels
* they need to rejoin them in order to get the oper prefix.
*/
-
- if (ServerInstance->Modules->Find("m_hideoper.so"))
- mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string& privs, const std::string& keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- /* The user may have the +H umode on himself, but +H does not necessarily correspond
- * to the +H of m_hideoper.
- * However we only add the modewatcher when m_hideoper is loaded, so these
- * conditions (mw_added and the user being +H) together mean the user is a hidden oper.
- */
-
- if (IS_OPER(user) && (!mw_added || !user->IsModeSet('H')))
+ if ((user->IsOper()) && (!user->IsModeSet(hideopermode)))
privs.push_back('y');
return MOD_RES_PASSTHRU;
}
- void OnPostJoin(Membership* memb)
+ void OnPostJoin(Membership* memb) CXX11_OVERRIDE
{
- if ((!IS_LOCAL(memb->user)) || (!IS_OPER(memb->user)) || (((mw_added) && (memb->user->IsModeSet('H')))))
+ if ((!IS_LOCAL(memb->user)) || (!memb->user->IsOper()) || (memb->user->IsModeSet(hideopermode)))
return;
- if (memb->hasMode(opm.GetModeChar()))
+ if (memb->HasMode(&opm))
return;
// The user was force joined and OnUserPreJoin() did not run. Set the operprefix now.
- std::vector<std::string> modechange;
- modechange.push_back(memb->chan->name);
- modechange.push_back("+y");
- modechange.push_back(memb->user->nick);
- ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
+ Modes::ChangeList changelist;
+ changelist.push_add(&opm, memb->user->nick);
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
}
void SetOperPrefix(User* user, bool add)
{
- std::vector<std::string> modechange;
- modechange.push_back("");
- modechange.push_back(add ? "+y" : "-y");
- modechange.push_back(user->nick);
- for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
- {
- modechange[0] = (*v)->name;
- ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
- }
+ Modes::ChangeList changelist;
+ changelist.push(&opm, add, user->nick);
+ for (User::ChanList::iterator v = user->chans.begin(); v != user->chans.end(); v++)
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, (*v)->chan, NULL, changelist);
}
- void OnPostOper(User* user, const std::string& opername, const std::string& opertype)
+ void OnPostOper(User* user, const std::string& opername, const std::string& opertype) CXX11_OVERRIDE
{
- if (IS_LOCAL(user) && (!mw_added || !user->IsModeSet('H')))
+ if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode)))
SetOperPrefix(user, true);
}
- void OnLoadModule(Module* mod)
- {
- if ((!mw_added) && (mod->ModuleSourceFile == "m_hideoper.so"))
- mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
- }
-
- void OnUnloadModule(Module* mod)
- {
- if ((mw_added) && (mod->ModuleSourceFile == "m_hideoper.so") && (ServerInstance->Modes->DelModeWatcher(&hideoperwatcher)))
- mw_added = false;
- }
-
- ~ModuleOperPrefixMode()
- {
- if (mw_added)
- ServerInstance->Modes->DelModeWatcher(&hideoperwatcher);
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Gives opers cmode +y which provides a staff prefix.", VF_VENDOR);
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
// m_opermodes may set +H on the oper to hide him, we don't want to set the oper prefix in that case
Module* opermodes = ServerInstance->Modules->Find("m_opermodes.so");
@@ -209,10 +112,16 @@ class ModuleOperPrefixMode : public Module
}
};
-void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
+HideOperWatcher::HideOperWatcher(ModuleOperPrefixMode* parent)
+ : ModeWatcher(parent, "hideoper", MODETYPE_USER)
+ , parentmod(parent)
+{
+}
+
+void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
{
// If hideoper is being unset because the user is deopering, don't set +y
- if (IS_LOCAL(dest) && IS_OPER(dest))
+ if (IS_LOCAL(dest) && dest->IsOper())
parentmod->SetOperPrefix(dest, !adding);
}
diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp
index 3266d3eb0..7155e7e76 100644
--- a/src/modules/m_override.cpp
+++ b/src/modules/m_override.cpp
@@ -25,66 +25,105 @@
#include "inspircd.h"
+#include "modules/invite.h"
-/* $ModDesc: Provides support for allowing opers to override certain things. */
+class Override : public SimpleUserModeHandler
+{
+ public:
+ Override(Module* Creator) : SimpleUserModeHandler(Creator, "override", 'O')
+ {
+ oper = true;
+ if (!ServerInstance->Config->ConfValue("override")->getBool("enableumode"))
+ DisableAutoRegister();
+ }
+};
class ModuleOverride : public Module
{
bool RequireKey;
bool NoisyOverride;
-
- static bool IsOverride(unsigned int userlevel, const std::string& modeline)
+ bool UmodeEnabled;
+ Override ou;
+ ChanModeReference topiclock;
+ ChanModeReference inviteonly;
+ ChanModeReference key;
+ ChanModeReference limit;
+ Invite::API invapi;
+
+ static bool IsOverride(unsigned int userlevel, const Modes::ChangeList::List& list)
{
- for (std::string::const_iterator i = modeline.begin(); i != modeline.end(); ++i)
+ for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
- if (!mh)
- continue;
-
- if (mh->GetLevelRequired() > userlevel)
+ ModeHandler* mh = i->mh;
+ if (mh->GetLevelRequired(i->adding) > userlevel)
return true;
}
return false;
}
+ ModResult HandleJoinOverride(LocalUser* user, Channel* chan, const std::string& keygiven, const char* bypasswhat, const char* mode)
+ {
+ if (RequireKey && keygiven != "override")
+ {
+ // Can't join normally -- must use a special key to bypass restrictions
+ user->WriteNotice("*** You may not join normally. You must join with a key of 'override' to oper override.");
+ return MOD_RES_PASSTHRU;
+ }
+
+ if (NoisyOverride)
+ chan->WriteNotice(InspIRCd::Format("%s used oper override to bypass %s", user->nick.c_str(), bypasswhat));
+ ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass " + mode + " on " + chan->name);
+ return MOD_RES_ALLOW;
+ }
+
public:
+ ModuleOverride()
+ : UmodeEnabled(false)
+ , ou(this)
+ , topiclock(this, "topiclock")
+ , inviteonly(this, "inviteonly")
+ , key(this, "key")
+ , limit(this, "limit")
+ , invapi(this)
+ {
+ }
- void init()
+ void init() CXX11_OVERRIDE
{
- // read our config options (main config file)
- OnRehash(NULL);
ServerInstance->SNO->EnableSnomask('v', "OVERRIDE");
- Implementation eventlist[] = { I_OnRehash, I_OnPreMode, I_On005Numeric, I_OnUserPreJoin, I_OnUserPreKick, I_OnPreTopicChange };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ UmodeEnabled = ServerInstance->Config->ConfValue("override")->getBool("enableumode");
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- // re-read our config options on a rehash
+ // re-read our config options
ConfigTag* tag = ServerInstance->Config->ConfValue("override");
NoisyOverride = tag->getBool("noisy");
RequireKey = tag->getBool("requirekey");
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" OVERRIDE");
+ tokens["OVERRIDE"];
}
bool CanOverride(User* source, const char* token)
{
- std::string tokenlist = source->oper->getConfig("override");
+ // If we require oper override umode (+O) but it is not set
+ if (UmodeEnabled && !source->IsModeSet(ou))
+ return false;
+ std::string tokenlist = source->oper->getConfig("override");
// its defined or * is set, return its value as a boolean for if the token is set
return ((tokenlist.find(token, 0) != std::string::npos) || (tokenlist.find("*", 0) != std::string::npos));
}
- ModResult OnPreTopicChange(User *source, Channel *channel, const std::string &topic)
+ ModResult OnPreTopicChange(User *source, Channel *channel, const std::string &topic) CXX11_OVERRIDE
{
- if (IS_LOCAL(source) && IS_OPER(source) && CanOverride(source, "TOPIC"))
+ if (IS_LOCAL(source) && source->IsOper() && CanOverride(source, "TOPIC"))
{
- if (!channel->HasUser(source) || (channel->IsModeSet('t') && channel->GetPrefixValue(source) < HALFOP_VALUE))
+ if (!channel->HasUser(source) || (channel->IsModeSet(topiclock) && channel->GetPrefixValue(source) < HALFOP_VALUE))
{
ServerInstance->SNO->WriteGlobalSno('v',source->nick+" used oper override to change a topic on "+channel->name);
}
@@ -96,9 +135,9 @@ class ModuleOverride : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
- if (IS_OPER(source) && CanOverride(source,"KICK"))
+ if (source->IsOper() && CanOverride(source,"KICK"))
{
// If the kicker's status is less than the target's, or the kicker's status is less than or equal to voice
if ((memb->chan->GetPrefixValue(source) < memb->getRank()) || (memb->chan->GetPrefixValue(source) <= VOICE_VALUE) ||
@@ -111,104 +150,75 @@ class ModuleOverride : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
- if (!source || !channel)
+ if (!channel)
return MOD_RES_PASSTHRU;
- if (!IS_OPER(source) || !IS_LOCAL(source))
+ if (!source->IsOper() || !IS_LOCAL(source))
return MOD_RES_PASSTHRU;
+ const Modes::ChangeList::List& list = modes.getlist();
unsigned int mode = channel->GetPrefixValue(source);
- if (!IsOverride(mode, parameters[1]))
+ if (!IsOverride(mode, list))
return MOD_RES_PASSTHRU;
if (CanOverride(source, "MODE"))
{
- std::string msg = source->nick+" overriding modes:";
- for(unsigned int i=0; i < parameters.size(); i++)
- msg += " " + parameters[i];
+ std::string msg = source->nick + " overriding modes: ";
+
+ // Construct a MODE string in the old format for sending it as a snotice
+ std::string params;
+ char pm = 0;
+ for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ if (!item.param.empty())
+ params.append(1, ' ').append(item.param);
+
+ char wanted_pm = (item.adding ? '+' : '-');
+ if (wanted_pm != pm)
+ {
+ pm = wanted_pm;
+ msg += pm;
+ }
+
+ msg += item.mh->GetModeChar();
+ }
+ msg += params;
ServerInstance->SNO->WriteGlobalSno('v',msg);
return MOD_RES_ALLOW;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- if (IS_LOCAL(user) && IS_OPER(user))
+ if (user->IsOper())
{
if (chan)
{
- if (chan->IsModeSet('i') && (CanOverride(user,"INVITE")))
+ if (chan->IsModeSet(inviteonly) && (CanOverride(user,"INVITE")))
{
- irc::string x(chan->name.c_str());
- if (!IS_LOCAL(user)->IsInvited(x))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass invite-only", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +i on "+std::string(cname));
- }
+ if (!invapi->IsInvited(user, chan))
+ return HandleJoinOverride(user, chan, keygiven, "invite-only", "+i");
return MOD_RES_ALLOW;
}
- if (chan->IsModeSet('k') && (CanOverride(user,"KEY")) && keygiven != chan->GetModeParameter('k'))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass the channel key", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +k on "+std::string(cname));
- return MOD_RES_ALLOW;
- }
+ if (chan->IsModeSet(key) && (CanOverride(user,"KEY")) && keygiven != chan->GetModeParameter(key))
+ return HandleJoinOverride(user, chan, keygiven, "the channel key", "+k");
- if (chan->IsModeSet('l') && (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter('l'))) && (CanOverride(user,"LIMIT")))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass the channel limit", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +l on "+std::string(cname));
- return MOD_RES_ALLOW;
- }
+ if (chan->IsModeSet(limit) && (chan->GetUserCounter() >= ConvToNum<size_t>(chan->GetModeParameter(limit))) && (CanOverride(user,"LIMIT")))
+ return HandleJoinOverride(user, chan, keygiven, "the channel limit", "+l");
if (chan->IsBanned(user) && CanOverride(user,"BANWALK"))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass channel ban", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v',"%s used oper override to bypass channel ban on %s", user->nick.c_str(), cname);
- return MOD_RES_ALLOW;
- }
+ return HandleJoinOverride(user, chan, keygiven, "channel ban", "channel ban");
}
}
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for allowing opers to override certain things",VF_VENDOR);
}
diff --git a/src/modules/m_passforward.cpp b/src/modules/m_passforward.cpp
index c04b306b1..08d3533cd 100644
--- a/src/modules/m_passforward.cpp
+++ b/src/modules/m_passforward.cpp
@@ -17,29 +17,19 @@
*/
-/* $ModDesc: Forwards a password users can send on connect (for example for NickServ identification). */
-
#include "inspircd.h"
class ModulePassForward : public Module
{
- private:
std::string nickrequired, forwardmsg, forwardcmd;
public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnPostConnect, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Sends server password to NickServ", VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("passforward");
nickrequired = tag->getString("nick", "NickServ");
@@ -54,22 +44,22 @@ class ModulePassForward : public Module
char c = format[i];
if (c == '$')
{
- if (format.substr(i, 13) == "$nickrequired")
+ if (!format.compare(i, 13, "$nickrequired", 13))
{
result.append(nickrequired);
i += 12;
}
- else if (format.substr(i, 5) == "$nick")
+ else if (!format.compare(i, 5, "$nick", 5))
{
result.append(user->nick);
i += 4;
}
- else if (format.substr(i, 5) == "$user")
+ else if (!format.compare(i, 5, "$user", 5))
{
result.append(user->ident);
i += 4;
}
- else if (format.substr(i,5) == "$pass")
+ else if (!format.compare(i, 5, "$pass", 5))
{
result.append(user->password);
i += 4;
@@ -82,27 +72,31 @@ class ModulePassForward : public Module
}
}
- virtual void OnPostConnect(User* ruser)
+ void OnPostConnect(User* ruser) CXX11_OVERRIDE
{
LocalUser* user = IS_LOCAL(ruser);
if (!user || user->password.empty())
return;
+ // If the connect class requires a password, don't forward it
+ if (!user->MyClass->config->getString("password").empty())
+ return;
+
if (!nickrequired.empty())
{
/* Check if nick exists and its server is ulined */
User* u = ServerInstance->FindNick(nickrequired);
- if (!u || !ServerInstance->ULine(u->server))
+ if (!u || !u->server->IsULine())
return;
}
std::string tmp;
- FormatStr(tmp,forwardmsg, user);
- user->WriteServ(tmp);
+ FormatStr(tmp, forwardmsg, user);
+ ServerInstance->Parser.ProcessBuffer(user, tmp);
tmp.clear();
FormatStr(tmp,forwardcmd, user);
- ServerInstance->Parser->ProcessBuffer(tmp,user);
+ ServerInstance->Parser.ProcessBuffer(user, tmp);
}
};
diff --git a/src/modules/m_password_hash.cpp b/src/modules/m_password_hash.cpp
index 98462780b..137ddb96c 100644
--- a/src/modules/m_password_hash.cpp
+++ b/src/modules/m_password_hash.cpp
@@ -18,10 +18,8 @@
*/
-/* $ModDesc: Allows for hashed oper passwords */
-
#include "inspircd.h"
-#include "hash.h"
+#include "modules/hash.h"
/* Handle /MKPASSWD
*/
@@ -34,74 +32,71 @@ class CommandMkpasswd : public Command
Penalty = 5;
}
- void MakeHash(User* user, const std::string& algo, const std::string& stuff)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (algo.substr(0,5) == "hmac-")
+ if (!parameters[0].compare(0, 5, "hmac-", 5))
{
- std::string type = algo.substr(5);
+ std::string type(parameters[0], 5);
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
if (!hp)
{
- user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
- return;
+ user->WriteNotice("Unknown hash type");
+ return CMD_FAILURE;
+ }
+
+ if (hp->IsKDF())
+ {
+ user->WriteNotice(type + " does not support HMAC");
+ return CMD_FAILURE;
}
- std::string salt = ServerInstance->GenRandomStr(6, false);
- std::string target = hp->hmac(salt, stuff);
+
+ std::string salt = ServerInstance->GenRandomStr(hp->out_size, false);
+ std::string target = hp->hmac(salt, parameters[1]);
std::string str = BinToBase64(salt) + "$" + BinToBase64(target, NULL, 0);
- user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
- user->nick.c_str(), algo.c_str(), stuff.c_str(), str.c_str());
- return;
- }
- HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + algo);
- if (hp)
- {
- /* Now attempt to generate a hash */
- std::string hexsum = hp->hexsum(stuff);
- user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
- user->nick.c_str(), algo.c_str(), stuff.c_str(), hexsum.c_str());
+ user->WriteNotice(parameters[0] + " hashed password for " + parameters[1] + " is " + str);
+ return CMD_SUCCESS;
}
- else
+
+ HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + parameters[0]);
+ if (!hp)
{
- user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
+ user->WriteNotice("Unknown hash type");
+ return CMD_FAILURE;
}
- }
-
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
- {
- MakeHash(user, parameters[0], parameters[1]);
+ std::string hexsum = hp->Generate(parameters[1]);
+ user->WriteNotice(parameters[0] + " hashed password for " + parameters[1] + " is " + hexsum);
return CMD_SUCCESS;
}
};
-class ModuleOperHash : public Module
+class ModulePasswordHash : public Module
{
+ private:
CommandMkpasswd cmd;
- public:
-
- ModuleOperHash() : cmd(this)
- {
- }
- void init()
+ public:
+ ModulePasswordHash()
+ : cmd(this)
{
- /* Read the config file first */
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnPassCompare };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
+ ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) CXX11_OVERRIDE
{
- if (hashtype.substr(0,5) == "hmac-")
+ if (!hashtype.compare(0, 5, "hmac-", 5))
{
- std::string type = hashtype.substr(5);
+ std::string type(hashtype, 5);
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
if (!hp)
return MOD_RES_PASSTHRU;
+
+ if (hp->IsKDF())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Tried to use HMAC with %s, which does not support HMAC", type.c_str());
+ return MOD_RES_DENY;
+ }
+
// this is a valid hash, from here on we either accept or deny
std::string::size_type sep = data.find('$');
if (sep == std::string::npos)
@@ -120,22 +115,21 @@ class ModuleOperHash : public Module
/* Is this a valid hash name? */
if (hp)
{
- /* Compare the hash in the config to the generated hash */
- if (data == hp->hexsum(input))
+ if (hp->Compare(input, data))
return MOD_RES_ALLOW;
else
/* No match, and must be hashed, forbid */
return MOD_RES_DENY;
}
- /* Not a hash, fall through to strcmp in core */
+ // We don't handle this type, let other mods or the core decide
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Allows for hashed oper passwords",VF_VENDOR);
+ return Version("Provides the ability to hash passwords to other modules", VF_VENDOR);
}
};
-MODULE_INIT(ModuleOperHash)
+MODULE_INIT(ModulePasswordHash)
diff --git a/src/modules/m_pbkdf2.cpp b/src/modules/m_pbkdf2.cpp
new file mode 100644
index 000000000..89bc39ef4
--- /dev/null
+++ b/src/modules/m_pbkdf2.cpp
@@ -0,0 +1,226 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+// Format:
+// Iterations:B64(Hash):B64(Salt)
+// E.g.
+// 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+class PBKDF2Hash
+{
+ public:
+ unsigned int iterations;
+ unsigned int length;
+ std::string salt;
+ std::string hash;
+
+ PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
+ : iterations(itr), length(dkl), salt(slt), hash(hsh)
+ {
+ }
+
+ PBKDF2Hash(const std::string& data)
+ {
+ irc::sepstream ss(data, ':');
+ std::string tok;
+
+ ss.GetToken(tok);
+ this->iterations = ConvToInt(tok);
+
+ ss.GetToken(tok);
+ this->hash = Base64ToBin(tok);
+
+ ss.GetToken(tok);
+ this->salt = Base64ToBin(tok);
+
+ this->length = this->hash.length();
+ }
+
+ std::string ToString()
+ {
+ if (!IsValid())
+ return "";
+ return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
+ }
+
+ bool IsValid()
+ {
+ if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
+ return false;
+ return true;
+ }
+};
+
+class PBKDF2Provider : public HashProvider
+{
+ public:
+ HashProvider* provider;
+ unsigned int iterations;
+ unsigned int dkey_length;
+
+ std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
+ {
+ size_t blocks = std::ceil((double)dkl / provider->out_size);
+
+ std::string output;
+ std::string tmphash;
+ std::string salt_block = salt;
+ for (size_t block = 1; block <= blocks; block++)
+ {
+ char salt_data[4];
+ for (size_t i = 0; i < sizeof(salt_data); i++)
+ salt_data[i] = block >> (24 - i * 8) & 0x0F;
+
+ salt_block.erase(salt.length());
+ salt_block.append(salt_data, sizeof(salt_data));
+
+ std::string blockdata = provider->hmac(pass, salt_block);
+ std::string lasthash = blockdata;
+ for (size_t iter = 1; iter < itr; iter++)
+ {
+ tmphash = provider->hmac(pass, lasthash);
+ for (size_t i = 0; i < provider->out_size; i++)
+ blockdata[i] ^= tmphash[i];
+
+ lasthash.swap(tmphash);
+ }
+ output += blockdata;
+ }
+
+ output.erase(dkl);
+ return output;
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
+ hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
+ return hs.ToString();
+ }
+
+ bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+ {
+ PBKDF2Hash hs(hash);
+ if (!hs.IsValid())
+ return false;
+
+ std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
+ return (cmp == hs.hash);
+ }
+
+ std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+ {
+ return raw;
+ }
+
+ PBKDF2Provider(Module* mod, HashProvider* hp)
+ : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
+ , provider(hp)
+ {
+ DisableAutoRegister();
+ }
+};
+
+class ModulePBKDF2 : public Module
+{
+ std::vector<PBKDF2Provider*> providers;
+
+ void GetConfig()
+ {
+ // First set the common values
+ ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
+ unsigned int global_iterations = tag->getUInt("iterations", 12288, 1);
+ unsigned int global_dkey_length = tag->getUInt("length", 32, 1, 1024);
+ for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+ {
+ PBKDF2Provider* pi = *i;
+ pi->iterations = global_iterations;
+ pi->dkey_length = global_dkey_length;
+ }
+
+ // Then the specific values
+ ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ tag = i->second;
+ std::string hash_name = "hash/" + tag->getString("hash");
+ for (std::vector<PBKDF2Provider*>::iterator j = providers.begin(); j != providers.end(); ++j)
+ {
+ PBKDF2Provider* pi = *j;
+ if (pi->provider->name != hash_name)
+ continue;
+
+ pi->iterations = tag->getUInt("iterations", global_iterations, 1);
+ pi->dkey_length = tag->getUInt("length", global_dkey_length, 1, 1024);
+ }
+ }
+ }
+
+ public:
+ ~ModulePBKDF2()
+ {
+ stdalgo::delete_all(providers);
+ }
+
+ void OnServiceAdd(ServiceProvider& provider) CXX11_OVERRIDE
+ {
+ // Check if it's a hash provider
+ if (provider.name.compare(0, 5, "hash/"))
+ return;
+
+ HashProvider* hp = static_cast<HashProvider*>(&provider);
+ if (hp->IsKDF())
+ return;
+
+ PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
+ providers.push_back(prov);
+ ServerInstance->Modules.AddService(*prov);
+
+ GetConfig();
+ }
+
+ void OnServiceDel(ServiceProvider& prov) CXX11_OVERRIDE
+ {
+ for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+ {
+ PBKDF2Provider* item = *i;
+ if (item->provider != &prov)
+ continue;
+
+ ServerInstance->Modules->DelService(*item);
+ delete item;
+ providers.erase(i);
+ break;
+ }
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ GetConfig();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Implements PBKDF2 hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModulePBKDF2)
diff --git a/src/modules/m_permchannels.cpp b/src/modules/m_permchannels.cpp
index 74a798356..a7be6df08 100644
--- a/src/modules/m_permchannels.cpp
+++ b/src/modules/m_permchannels.cpp
@@ -19,192 +19,144 @@
#include "inspircd.h"
+#include "listmode.h"
+#include <fstream>
-/* $ModDesc: Provides support for channel mode +P to provide permanent channels */
-struct ListModeData
+/** Handles the +P channel mode
+ */
+class PermChannel : public ModeHandler
{
- std::string modes;
- std::string params;
+ public:
+ PermChannel(Module* Creator)
+ : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL)
+ {
+ oper = true;
+ }
+
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
+ {
+ if (adding == channel->IsModeSet(this))
+ return MODEACTION_DENY;
+
+ channel->SetMode(this, adding);
+ if (!adding)
+ channel->CheckDestroy();
+
+ return MODEACTION_ALLOW;
+ }
};
// Not in a class due to circular dependancy hell.
static std::string permchannelsconf;
-static bool WriteDatabase(Module* mod, bool save_listmodes)
+static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_listmodes)
{
- FILE *f;
+ ChanModeReference ban(mod, "ban");
+ /*
+ * We need to perform an atomic write so as not to fuck things up.
+ * So, let's write to a temporary file, flush it, then rename the file..
+ * -- w00t
+ */
+ // If the user has not specified a configuration file then we don't write one.
if (permchannelsconf.empty())
- {
- // Fake success.
return true;
- }
- std::string tempname = permchannelsconf + ".tmp";
-
- /*
- * We need to perform an atomic write so as not to fuck things up.
- * So, let's write to a temporary file, flush and sync the FD, then rename the file..
- * -- w00t
- */
- f = fopen(tempname.c_str(), "w");
- if (!f)
+ std::string permchannelsnewconf = permchannelsconf + ".tmp";
+ std::ofstream stream(permchannelsnewconf.c_str());
+ if (!stream.is_open())
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot create database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database \"%s\"! %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new permchan db \"%s\": %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
- fputs("# Permchannels DB\n# This file is autogenerated; any changes will be overwritten!\n<config format=\"compat\">\n", f);
- // Now, let's write.
- std::string line;
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+ stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
+ << "<config format=\"xml\">" << std::endl;
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
{
Channel* chan = i->second;
- if (!chan->IsModeSet('P'))
+ if (!chan->IsModeSet(permchanmode))
continue;
std::string chanmodes = chan->ChanModes(true);
if (save_listmodes)
{
- ListModeData lm;
+ std::string modes;
+ std::string params;
- // Bans are managed by the core, so we have to process them separately
- lm.modes = std::string(chan->bans.size(), 'b');
- for (BanList::const_iterator j = chan->bans.begin(); j != chan->bans.end(); ++j)
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator j = listmodes.begin(); j != listmodes.end(); ++j)
{
- lm.params += j->data;
- lm.params += ' ';
- }
+ ListModeBase* lm = *j;
+ ListModeBase::ModeList* list = lm->GetList(chan);
+ if (!list || list->empty())
+ continue;
+
+ size_t n = 0;
+ // Append the parameters
+ for (ListModeBase::ModeList::const_iterator k = list->begin(); k != list->end(); ++k, n++)
+ {
+ params += k->mask;
+ params += ' ';
+ }
- // All other listmodes are managed by modules, so we need to ask them (call their
- // OnSyncChannel() handler) to give our ProtoSendMode() a list of modes that are
- // set on the channel. The ListModeData struct is passed as an opaque pointer
- // that will be passed back to us by the module handling the mode.
- FOREACH_MOD(I_OnSyncChannel, OnSyncChannel(chan, mod, &lm));
+ // Append the mode letters (for example "IIII", "gg")
+ modes.append(n, lm->GetModeChar());
+ }
- if (!lm.modes.empty())
+ if (!params.empty())
{
// Remove the last space
- lm.params.erase(lm.params.end()-1);
+ params.erase(params.end()-1);
// If there is at least a space in chanmodes (that is, a non-listmode has a parameter)
// insert the listmode mode letters before the space. Otherwise just append them.
std::string::size_type p = chanmodes.find(' ');
if (p == std::string::npos)
- chanmodes += lm.modes;
+ chanmodes += modes;
else
- chanmodes.insert(p, lm.modes);
+ chanmodes.insert(p, modes);
// Append the listmode parameters (the masks themselves)
chanmodes += ' ';
- chanmodes += lm.params;
+ chanmodes += params;
}
}
- std::string chants = ConvToStr(chan->age);
- std::string topicts = ConvToStr(chan->topicset);
- const char* items[] =
- {
- "<permchannels channel=",
- chan->name.c_str(),
- " ts=",
- chants.c_str(),
- " topic=",
- chan->topic.c_str(),
- " topicts=",
- topicts.c_str(),
- " topicsetby=",
- chan->setby.c_str(),
- " modes=",
- chanmodes.c_str(),
- ">\n"
- };
-
- line.clear();
- int item = 0, ipos = 0;
- while (item < 13)
- {
- char c = items[item][ipos++];
- if (c == 0)
- {
- // end of this string; hop to next string, insert a quote
- item++;
- ipos = 0;
- c = '"';
- }
- else if (c == '\\' || c == '"')
- {
- line += '\\';
- }
- line += c;
- }
-
- // Erase last '"'
- line.erase(line.end()-1);
- fputs(line.c_str(), f);
+ stream << "<permchannels channel=\"" << ServerConfig::Escape(chan->name)
+ << "\" ts=\"" << chan->age
+ << "\" topic=\"" << ServerConfig::Escape(chan->topic)
+ << "\" topicts=\"" << chan->topicset
+ << "\" topicsetby=\"" << ServerConfig::Escape(chan->setby)
+ << "\" modes=\"" << ServerConfig::Escape(chanmodes)
+ << "\">" << std::endl;
}
- int write_error = 0;
- write_error = ferror(f);
- write_error |= fclose(f);
- if (write_error)
+ if (stream.fail())
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot write to new database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot write to new database \"%s\"! %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new permchan db \"%s\": %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
+ stream.close();
#ifdef _WIN32
remove(permchannelsconf.c_str());
#endif
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
- if (rename(tempname.c_str(), permchannelsconf.c_str()) < 0)
+ if (rename(permchannelsnewconf.c_str(), permchannelsconf.c_str()) < 0)
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot move new to old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot replace old database \"%s\" with new database \"%s\"! %s (%d)", permchannelsconf.c_str(), permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old permchan db \"%s\" with new db \"%s\": %s (%d)", permchannelsconf.c_str(), permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
return true;
}
-
-
-/** Handles the +P channel mode
- */
-class PermChannel : public ModeHandler
-{
- public:
- PermChannel(Module* Creator) : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL) { oper = true; }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
- {
- if (adding)
- {
- if (!channel->IsModeSet('P'))
- {
- channel->SetMode('P',true);
- return MODEACTION_ALLOW;
- }
- }
- else
- {
- if (channel->IsModeSet('P'))
- {
- channel->SetMode(this,false);
- if (channel->GetUserCounter() == 0)
- {
- channel->DelUser(ServerInstance->FakeClient);
- }
- return MODEACTION_ALLOW;
- }
- }
-
- return MODEACTION_DENY;
- }
-};
-
class ModulePermanentChannels : public Module
{
PermChannel p;
@@ -218,46 +170,14 @@ public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(p);
- Implementation eventlist[] = { I_OnChannelPreDelete, I_OnPostTopicChange, I_OnRawMode, I_OnRehash, I_OnBackgroundTimer };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
- }
-
- CullResult cull()
- {
- /*
- * DelMode can't remove the +P mode on empty channels, or it will break
- * merging modes with remote servers. Remove the empty channels now as
- * we know this is not the case.
- */
- chan_hash::iterator iter = ServerInstance->chanlist->begin();
- while (iter != ServerInstance->chanlist->end())
- {
- Channel* c = iter->second;
- if (c->GetUserCounter() == 0)
- {
- chan_hash::iterator at = iter;
- iter++;
- FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(c));
- ServerInstance->chanlist->erase(at);
- ServerInstance->GlobalCulls.AddItem(c);
- }
- else
- iter++;
- }
- ServerInstance->Modes->DelMode(&p);
- return Module::cull();
- }
-
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("permchanneldb");
permchannelsconf = tag->getString("filename");
save_listmodes = tag->getBool("listmodes");
+
+ if (!permchannelsconf.empty())
+ permchannelsconf = ServerInstance->Config->Paths.PrependConfig(permchannelsconf);
}
void LoadDatabase()
@@ -271,12 +191,11 @@ public:
{
ConfigTag* tag = i->second;
std::string channel = tag->getString("channel");
- std::string topic = tag->getString("topic");
std::string modes = tag->getString("modes");
if ((channel.empty()) || (channel.length() > ServerInstance->Config->Limits.ChanMax))
{
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
continue;
}
@@ -284,26 +203,24 @@ public:
if (!c)
{
- time_t TS = tag->getInt("ts");
- c = new Channel(channel, ((TS > 0) ? TS : ServerInstance->Time()));
+ time_t TS = tag->getInt("ts", ServerInstance->Time(), 1);
+ c = new Channel(channel, TS);
- c->SetTopic(NULL, topic, true);
- c->setby = tag->getString("topicsetby");
- if (c->setby.empty())
- c->setby = ServerInstance->Config->ServerName;
- unsigned int topicset = tag->getInt("topicts");
- // SetTopic() sets the topic TS to now, if there was no topicts saved then don't overwrite that with a 0
- if (topicset > 0)
- c->topicset = topicset;
+ time_t topicset = tag->getInt("topicts", 0);
+ std::string topic = tag->getString("topic");
- ServerInstance->Logs->Log("m_permchannels", DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
-
- if (modes.find('P') == std::string::npos)
+ if ((topicset != 0) || (!topic.empty()))
{
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "%s (%s) does not have +P set in <permchannels:modes>; it will be deleted when empty!",
- c->name.c_str(), tag->getTagLocation().c_str());
+ if (topicset == 0)
+ topicset = ServerInstance->Time();
+ std::string topicsetby = tag->getString("topicsetby");
+ if (topicsetby.empty())
+ topicsetby = ServerInstance->Config->ServerName;
+ c->SetTopic(ServerInstance->FakeClient, topic, topicset, &topicsetby);
}
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str());
+
if (modes.empty())
continue;
@@ -319,7 +236,7 @@ public:
ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
if (mode)
{
- if (mode->GetNumParams(true))
+ if (mode->NeedsParam(true))
list.GetToken(par);
else
par.clear();
@@ -327,32 +244,36 @@ public:
mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, c, par, true);
}
}
+
+ // We always apply the permchannels mode to permanent channels.
+ par.clear();
+ p.OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, c, par, true);
}
}
}
- virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt)
+ ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
{
- if (chan && (chan->IsModeSet('P') || mode == 'P'))
+ if (chan && (chan->IsModeSet(p) || mh == &p))
dirty = true;
return MOD_RES_PASSTHRU;
}
- virtual void OnPostTopicChange(User*, Channel *c, const std::string&)
+ void OnPostTopicChange(User*, Channel *c, const std::string&) CXX11_OVERRIDE
{
- if (c->IsModeSet('P'))
+ if (c->IsModeSet(p))
dirty = true;
}
- void OnBackgroundTimer(time_t)
+ void OnBackgroundTimer(time_t) CXX11_OVERRIDE
{
if (dirty)
- WriteDatabase(this, save_listmodes);
+ WriteDatabase(p, this, save_listmodes);
dirty = false;
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
// XXX: Load the DB here because the order in which modules are init()ed at boot is
// alphabetical, this means we must wait until all modules have done their init()
@@ -367,7 +288,7 @@ public:
// Load only when there are no linked servers - we set the TS of the channels we
// create to the current time, this can lead to desync because spanningtree has
// no way of knowing what we do
- ProtoServerList serverlist;
+ ProtocolInterface::ServerList serverlist;
ServerInstance->PI->GetServerList(serverlist);
if (serverlist.size() < 2)
{
@@ -377,38 +298,19 @@ public:
}
catch (CoreException& e)
{
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
}
}
}
- void ProtoSendMode(void* opaque, TargetTypeFlags type, void* target, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate)
- {
- // We never pass an empty modelist but better be sure
- if (modes.empty())
- return;
-
- ListModeData* lm = static_cast<ListModeData*>(opaque);
-
- // Append the mode letters without the trailing '+' (for example "IIII", "gg")
- lm->modes.append(modes[0].begin()+1, modes[0].end());
-
- // Append the parameters
- for (std::vector<std::string>::const_iterator i = modes.begin()+1; i != modes.end(); ++i)
- {
- lm->params += *i;
- lm->params += ' ';
- }
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +P to provide permanent channels",VF_VENDOR);
}
- virtual ModResult OnChannelPreDelete(Channel *c)
+ ModResult OnChannelPreDelete(Channel *c) CXX11_OVERRIDE
{
- if (c->IsModeSet('P'))
+ if (c->IsModeSet(p))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_randquote.cpp b/src/modules/m_randquote.cpp
index 668eea0e5..1bb28583e 100644
--- a/src/modules/m_randquote.cpp
+++ b/src/modules/m_randquote.cpp
@@ -21,80 +21,37 @@
*/
-/* $ModDesc: Provides random quotes on connect. */
-
#include "inspircd.h"
-static FileReader *quotes = NULL;
-
-std::string prefix;
-std::string suffix;
-
-/** Handle /RANDQUOTE
- */
-class CommandRandquote : public Command
-{
- public:
- CommandRandquote(Module* Creator) : Command(Creator,"RANDQUOTE", 0)
- {
- }
-
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
- {
- int fsize = quotes->FileSize();
- if (fsize)
- {
- std::string str = quotes->GetLine(ServerInstance->GenRandomInt(fsize));
- if (!str.empty())
- user->WriteServ("NOTICE %s :%s%s%s",user->nick.c_str(),prefix.c_str(),str.c_str(),suffix.c_str());
- }
-
- return CMD_SUCCESS;
- }
-};
-
class ModuleRandQuote : public Module
{
private:
- CommandRandquote cmd;
- public:
- ModuleRandQuote()
- : cmd(this)
- {
- }
+ std::string prefix;
+ std::string suffix;
+ std::vector<std::string> quotes;
- void init()
+ public:
+ void init() CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("randquote");
-
- std::string q_file = conf->getString("file","quotes");
prefix = conf->getString("prefix");
suffix = conf->getString("suffix");
-
- quotes = new FileReader(q_file);
- if (!quotes->Exists())
- {
- throw ModuleException("m_randquote: QuoteFile not Found!! Please check your config - module will not function.");
- }
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ FileReader reader(conf->getString("file", "quotes"));
+ quotes = reader.GetVector();
}
-
- virtual ~ModuleRandQuote()
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- delete quotes;
- }
-
- virtual Version GetVersion()
- {
- return Version("Provides random quotes on connect.",VF_VENDOR);
+ if (!quotes.empty())
+ {
+ unsigned long random = ServerInstance->GenRandomInt(quotes.size());
+ user->WriteNotice(prefix + quotes[random] + suffix);
+ }
}
- virtual void OnUserConnect(LocalUser* user)
+ Version GetVersion() CXX11_OVERRIDE
{
- cmd.Handle(std::vector<std::string>(), user);
+ return Version("Provides random quotes on connect.", VF_VENDOR);
}
};
diff --git a/src/modules/m_redirect.cpp b/src/modules/m_redirect.cpp
index 26d6b162b..b71a2f3db 100644
--- a/src/modules/m_redirect.cpp
+++ b/src/modules/m_redirect.cpp
@@ -24,143 +24,95 @@
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +L (limit redirection) and usermode +L (no forced redirection) */
-
/** Handle channel mode +L
*/
-class Redirect : public ModeHandler
+class Redirect : public ParamMode<Redirect, LocalStringExt>
{
public:
- Redirect(Module* Creator) : ModeHandler(Creator, "redirect", 'L', PARAM_SETONLY, MODETYPE_CHANNEL) { }
+ Redirect(Module* Creator)
+ : ParamMode<Redirect, LocalStringExt>(Creator, "redirect", 'L') { }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
{
- if (adding)
+ if (IS_LOCAL(source))
{
- if (IS_LOCAL(source))
+ if (!ServerInstance->IsChannel(parameter))
{
- if (!ServerInstance->IsChannel(parameter.c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- source->WriteNumeric(403, "%s %s :Invalid channel name", source->nick.c_str(), parameter.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(Numerics::NoSuchChannel(parameter));
+ return MODEACTION_DENY;
}
+ }
- if (IS_LOCAL(source) && !IS_OPER(source))
+ if (IS_LOCAL(source) && !source->IsOper())
+ {
+ Channel* c = ServerInstance->FindChan(parameter);
+ if (!c)
{
- Channel* c = ServerInstance->FindChan(parameter);
- if (!c)
- {
- source->WriteNumeric(690, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),parameter.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
- else if (c->GetPrefixValue(source) < OP_VALUE)
- {
- source->WriteNumeric(690, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(),parameter.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
- }
-
- if (channel->GetModeParameter('L') == parameter)
+ source->WriteNumeric(690, InspIRCd::Format("Target channel %s must exist to be set as a redirect.", parameter.c_str()));
return MODEACTION_DENY;
- /*
- * We used to do some checking for circular +L here, but there is no real need for this any more especially as we
- * now catch +L looping in PreJoin. Remove it, since O(n) logic makes me sad, and we catch it anyway. :) -- w00t
- */
- channel->SetModeParam('L', parameter);
- return MODEACTION_ALLOW;
- }
- else
- {
- if (channel->IsModeSet('L'))
+ }
+ else if (c->GetPrefixValue(source) < OP_VALUE)
{
- channel->SetModeParam('L', "");
- return MODEACTION_ALLOW;
+ source->WriteNumeric(690, InspIRCd::Format("You must be opped on %s to set it as a redirect.", parameter.c_str()));
+ return MODEACTION_DENY;
}
}
- return MODEACTION_DENY;
-
+ /*
+ * We used to do some checking for circular +L here, but there is no real need for this any more especially as we
+ * now catch +L looping in PreJoin. Remove it, since O(n) logic makes me sad, and we catch it anyway. :) -- w00t
+ */
+ ext.set(channel, parameter);
+ return MODEACTION_ALLOW;
}
-};
-/** Handles usermode +L to stop forced redirection and print an error.
-*/
-class AntiRedirect : public SimpleUserModeHandler
-{
- public:
- AntiRedirect(Module* Creator) : SimpleUserModeHandler(Creator, "antiredirect", 'L') {}
+ void SerializeParam(Channel* chan, const std::string* str, std::string& out)
+ {
+ out += *str;
+ }
};
class ModuleRedirect : public Module
{
-
Redirect re;
- AntiRedirect re_u;
- bool UseUsermode;
+ SimpleUserModeHandler antiredirectmode;
+ ChanModeReference limitmode;
public:
-
ModuleRedirect()
- : re(this), re_u(this)
+ : re(this)
+ , antiredirectmode(this, "antiredirect", 'L')
+ , limitmode(this, "limit")
{
}
- void init()
- {
- /* Setting this here so it isn't changable by rehasing the config later. */
- UseUsermode = ServerInstance->Config->ConfValue("redirect")->getBool("antiredirect");
-
- /* Channel mode */
- ServerInstance->Modules->AddService(re);
-
- /* Check to see if the usermode is enabled in the config */
- if (UseUsermode)
- {
- /* Log noting that this breaks compatability. */
- ServerInstance->Logs->Log("m_redirect", DEFAULT, "REDIRECT: Enabled usermode +L. This breaks linking with servers that do not have this enabled. This is disabled by default in the 2.0 branch but will be enabled in the next version.");
-
- /* Try to add the usermode */
- ServerInstance->Modules->AddService(re_u);
- }
-
- Implementation eventlist[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
- if (chan->IsModeSet('L') && chan->IsModeSet('l'))
+ if (chan->IsModeSet(re) && chan->IsModeSet(limitmode))
{
- if (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter('l')))
+ if (chan->GetUserCounter() >= ConvToNum<size_t>(chan->GetModeParameter(limitmode)))
{
- std::string channel = chan->GetModeParameter('L');
+ const std::string& channel = *re.ext.get(chan);
/* sometimes broken ulines can make circular or chained +L, avoid this */
- Channel* destchan = NULL;
- destchan = ServerInstance->FindChan(channel);
- if (destchan && destchan->IsModeSet('L'))
+ Channel* destchan = ServerInstance->FindChan(channel);
+ if (destchan && destchan->IsModeSet(re))
{
- user->WriteNumeric(470, "%s %s * :You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.", user->nick.c_str(), cname);
+ user->WriteNumeric(470, cname, '*', "You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.");
return MOD_RES_DENY;
}
- /* We check the bool value here to make sure we have it enabled, if we don't then
- usermode +L might be assigned to something else. */
- if (UseUsermode && user->IsModeSet('L'))
+
+ if (user->IsModeSet(antiredirectmode))
{
- user->WriteNumeric(470, "%s %s %s :Force redirection stopped.",
- user->nick.c_str(), cname, channel.c_str());
+ user->WriteNumeric(470, cname, channel, "Force redirection stopped.");
return MOD_RES_DENY;
}
else
{
- user->WriteNumeric(470, "%s %s %s :You may not join this channel, so you are automatically being transferred to the redirect channel.", user->nick.c_str(), cname, channel.c_str());
- Channel::JoinUser(user, channel.c_str(), false, "", false, ServerInstance->Time());
+ user->WriteNumeric(470, cname, channel, "You may not join this channel, so you are automatically being transferred to the redirect channel.");
+ Channel::JoinUser(user, channel);
return MOD_RES_DENY;
}
}
@@ -169,11 +121,7 @@ class ModuleRedirect : public Module
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleRedirect()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +L (limit redirection) and user mode +L (no forced redirection)", VF_VENDOR);
}
diff --git a/src/modules/m_regex.h b/src/modules/m_regex.h
deleted file mode 100644
index 0233f938a..000000000
--- a/src/modules/m_regex.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef M_REGEX_H
-#define M_REGEX_H
-
-#include "inspircd.h"
-
-class Regex : public classbase
-{
-protected:
- std::string regex_string; // The raw uncompiled regex string.
-
- // Constructor may as well be protected, as this class is abstract.
- Regex(const std::string& rx) : regex_string(rx)
- {
- }
-
-public:
-
- virtual ~Regex()
- {
- }
-
- virtual bool Matches(const std::string& text) = 0;
-
- const std::string& GetRegexString() const
- {
- return regex_string;
- }
-};
-
-class RegexFactory : public DataProvider
-{
- public:
- RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
-
- virtual Regex* Create(const std::string& expr) = 0;
-};
-
-#endif
diff --git a/src/modules/m_regex_glob.cpp b/src/modules/m_regex_glob.cpp
index 44d1a5898..9c3162885 100644
--- a/src/modules/m_regex_glob.cpp
+++ b/src/modules/m_regex_glob.cpp
@@ -18,11 +18,9 @@
*/
-#include "m_regex.h"
+#include "modules/regex.h"
#include "inspircd.h"
-/* $ModDesc: Regex module using plain wildcard matching. */
-
class GlobRegex : public Regex
{
public:
@@ -30,11 +28,7 @@ public:
{
}
- virtual ~GlobRegex()
- {
- }
-
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
return InspIRCd::Match(text, this->regex_string);
}
@@ -43,7 +37,7 @@ public:
class GlobFactory : public RegexFactory
{
public:
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new GlobRegex(expr);
}
@@ -55,11 +49,12 @@ class ModuleRegexGlob : public Module
{
GlobFactory gf;
public:
- ModuleRegexGlob() : gf(this) {
- ServerInstance->Modules->AddService(gf);
+ ModuleRegexGlob()
+ : gf(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex module using plain wildcard matching.", VF_VENDOR);
}
diff --git a/src/modules/m_regonlycreate.cpp b/src/modules/m_regonlycreate.cpp
index 61f94c0bd..78b20ef6b 100644
--- a/src/modules/m_regonlycreate.cpp
+++ b/src/modules/m_regonlycreate.cpp
@@ -21,28 +21,27 @@
#include "inspircd.h"
-#include "account.h"
-
-/* $ModDesc: Prevents users whose nicks are not registered from creating new channels */
+#include "modules/account.h"
class ModuleRegOnlyCreate : public Module
{
+ UserModeReference regusermode;
+
public:
- void init()
+ ModuleRegOnlyCreate()
+ : regusermode(this, "u_registered")
{
- Implementation eventlist[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
return MOD_RES_PASSTHRU;
- if (IS_OPER(user))
+ if (user->IsOper())
return MOD_RES_PASSTHRU;
- if (user->IsModeSet('r'))
+ if (user->IsModeSet(regusermode))
return MOD_RES_PASSTHRU;
const AccountExtItem* ext = GetAccountExtItem();
@@ -50,15 +49,11 @@ class ModuleRegOnlyCreate : public Module
return MOD_RES_PASSTHRU;
// XXX. there may be a better numeric for this..
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have a registered nickname to create a new channel", user->nick.c_str(), cname);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, cname, "You must have a registered nickname to create a new channel");
return MOD_RES_DENY;
}
- ~ModuleRegOnlyCreate()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Prevents users whose nicks are not registered from creating new channels", VF_VENDOR);
}
diff --git a/src/modules/m_remove.cpp b/src/modules/m_remove.cpp
index cf139f4a3..8d60ff8c6 100644
--- a/src/modules/m_remove.cpp
+++ b/src/modules/m_remove.cpp
@@ -24,8 +24,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
-
/*
* This module supports the use of the +q and +a usermodes, but should work without them too.
* Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
@@ -36,23 +34,27 @@
*/
class RemoveBase : public Command
{
- private:
bool& supportnokicks;
+ ChanModeReference& nokicksmode;
public:
- RemoveBase(Module* Creator, bool& snk, const char* cmdn)
- : Command(Creator, cmdn, 2, 3), supportnokicks(snk)
+ unsigned int protectedrank;
+
+ RemoveBase(Module* Creator, bool& snk, ChanModeReference& nkm, const char* cmdn)
+ : Command(Creator, cmdn, 2, 3)
+ , supportnokicks(snk)
+ , nokicksmode(nkm)
{
}
- CmdResult HandleRMB(const std::vector<std::string>& parameters, User *user, bool neworder)
+ CmdResult HandleRMB(User* user, const CommandBase::Params& parameters, bool fpart)
{
User* target;
Channel* channel;
std::string reason;
- std::string protectkey;
- std::string founderkey;
- bool hasnokicks;
+
+ // If the command is a /REMOVE then detect the parameter order
+ bool neworder = ((fpart) || (parameters[0][0] == '#'));
/* Set these to the parameters needed, the new version of this module switches it's parameters around
* supplying a new command with the new order while keeping the old /remove with the older order.
@@ -72,42 +74,55 @@ class RemoveBase : public Command
channel = ServerInstance->FindChan(channame);
/* Fix by brain - someone needs to learn to validate their input! */
- if ((!target) || (target->registered != REG_ALL) || (!channel))
+ if (!channel)
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(channame));
+ return CMD_FAILURE;
+ }
+ if ((!target) || (target->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), !channel ? channame.c_str() : username.c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(username));
return CMD_FAILURE;
}
if (!channel->HasUser(target))
{
- user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
+ user->WriteNotice(InspIRCd::Format("*** The user %s is not on channel %s", target->nick.c_str(), channel->name.c_str()));
return CMD_FAILURE;
}
- int ulevel = channel->GetPrefixValue(user);
- int tlevel = channel->GetPrefixValue(target);
-
- hasnokicks = (ServerInstance->Modules->Find("m_nokicks.so") && channel->IsModeSet('Q'));
-
- if (ServerInstance->ULine(target->server))
+ if (target->server->IsULine())
{
- user->WriteNumeric(482, "%s %s :Only a u-line may remove a u-line from a channel.", user->nick.c_str(), channame.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channame, "Only a u-line may remove a u-line from a channel.");
return CMD_FAILURE;
}
/* We support the +Q channel mode via. the m_nokicks module, if the module is loaded and the mode is set then disallow the /remove */
- if ((!IS_LOCAL(user)) || (!supportnokicks || !hasnokicks))
+ if ((!IS_LOCAL(user)) || (!supportnokicks) || (!channel->IsModeSet(nokicksmode)))
{
/* We'll let everyone remove their level and below, eg:
* ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
- * a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
- * Nobody may remove a founder.
+ a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
+ * Nobody may remove people with >= protectedrank rank.
*/
- if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && (tlevel != 50000)))
+ unsigned int ulevel = channel->GetPrefixValue(user);
+ unsigned int tlevel = channel->GetPrefixValue(target);
+ if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && ((protectedrank == 0) || (tlevel < protectedrank))))
{
- // REMOVE/FPART will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
+ // REMOVE will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
if (!IS_LOCAL(target))
+ {
+ // Send an ENCAP REMOVE with parameters being in the old <user> <chan> order which is
+ // compatible with both 2.0 and 3.0. This also turns FPART into REMOVE.
+ CommandBase::Params p;
+ p.push_back(target->uuid);
+ p.push_back(channel->name);
+ if (parameters.size() > 2)
+ p.push_back(":" + parameters[2]);
+ ServerInstance->PI->SendEncapsulatedData(target->server->GetName(), "REMOVE", p, user);
+
return CMD_SUCCESS;
+ }
std::string reasonparam;
@@ -120,27 +135,26 @@ class RemoveBase : public Command
/* Build up the part reason string. */
reason = "Removed by " + user->nick + ": " + reasonparam;
- channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name.c_str(), user->nick.c_str(), target->nick.c_str());
- target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick.c_str(), user->nick.c_str(), channel->name.c_str(), reasonparam.c_str());
+ channel->WriteNotice(InspIRCd::Format("%s removed %s from the channel", user->nick.c_str(), target->nick.c_str()));
+ target->WriteNotice("*** " + user->nick + " removed you from " + channel->name + " with the message: " + reasonparam);
channel->PartUser(target, reason);
}
else
{
- user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
+ user->WriteNotice(InspIRCd::Format("*** You do not have access to /remove %s from %s", target->nick.c_str(), channel->name.c_str()));
return CMD_FAILURE;
}
}
else
{
/* m_nokicks.so was loaded and +Q was set, block! */
- user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick.c_str(), channel->name.c_str(), target->nick.c_str());
+ user->WriteNumeric(ERR_RESTRICTED, channel->name, InspIRCd::Format("Can't remove user %s from channel (nokicks mode is set)", target->nick.c_str()));
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
- virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) = 0;
};
/** Handle /REMOVE
@@ -148,24 +162,16 @@ class RemoveBase : public Command
class CommandRemove : public RemoveBase
{
public:
- CommandRemove(Module* Creator, bool& snk)
- : RemoveBase(Creator, snk, "REMOVE")
- {
- syntax = "<nick> <channel> [<reason>]";
- TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
- }
-
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CommandRemove(Module* Creator, bool& snk, ChanModeReference& nkm)
+ : RemoveBase(Creator, snk, nkm, "REMOVE")
{
- return HandleRMB(parameters, user, false);
+ syntax = "<channel> <nick> [<reason>]";
+ TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return HandleRMB(user, parameters, false);
}
};
@@ -174,67 +180,50 @@ class CommandRemove : public RemoveBase
class CommandFpart : public RemoveBase
{
public:
- CommandFpart(Module* Creator, bool& snk)
- : RemoveBase(Creator, snk, "FPART")
+ CommandFpart(Module* Creator, bool& snk, ChanModeReference& nkm)
+ : RemoveBase(Creator, snk, nkm, "FPART")
{
syntax = "<channel> <nick> [<reason>]";
- TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
- }
-
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
- {
- return HandleRMB(parameters, user, true);
+ TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[1]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return HandleRMB(user, parameters, true);
}
};
class ModuleRemove : public Module
{
+ ChanModeReference nokicksmode;
CommandRemove cmd1;
CommandFpart cmd2;
bool supportnokicks;
-
public:
- ModuleRemove() : cmd1(this, supportnokicks), cmd2(this, supportnokicks)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(cmd1);
- ServerInstance->Modules->AddService(cmd2);
- OnRehash(NULL);
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void On005Numeric(std::string &output)
+ ModuleRemove()
+ : nokicksmode(this, "nokick")
+ , cmd1(this, supportnokicks, nokicksmode)
+ , cmd2(this, supportnokicks, nokicksmode)
{
- output.append(" REMOVE");
}
- virtual void OnRehash(User* user)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- supportnokicks = ServerInstance->Config->ConfValue("remove")->getBool("supportnokicks");
+ tokens["REMOVE"];
}
- virtual ~ModuleRemove()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("remove");
+ supportnokicks = tag->getBool("supportnokicks");
+ cmd1.protectedrank = cmd2.protectedrank = tag->getUInt("protectedrank", 50000);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleRemove)
diff --git a/src/modules/m_repeat.cpp b/src/modules/m_repeat.cpp
new file mode 100644
index 000000000..ef62e9ab1
--- /dev/null
+++ b/src/modules/m_repeat.cpp
@@ -0,0 +1,416 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/exemption.h"
+
+class ChannelSettings
+{
+ public:
+ enum RepeatAction
+ {
+ ACT_KICK,
+ ACT_BLOCK,
+ ACT_BAN
+ };
+
+ RepeatAction Action;
+ unsigned int Backlog;
+ unsigned int Lines;
+ unsigned int Diff;
+ unsigned int Seconds;
+
+ void serialize(std::string& out) const
+ {
+ if (Action == ACT_BAN)
+ out.push_back('*');
+ else if (Action == ACT_BLOCK)
+ out.push_back('~');
+
+ out.append(ConvToStr(Lines)).push_back(':');
+ out.append(ConvToStr(Seconds));
+ if (Diff)
+ {
+ out.push_back(':');
+ out.append(ConvToStr(Diff));
+ if (Backlog)
+ {
+ out.push_back(':');
+ out.append(ConvToStr(Backlog));
+ }
+ }
+ }
+};
+
+class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
+{
+ private:
+ struct RepeatItem
+ {
+ time_t ts;
+ std::string line;
+ RepeatItem(time_t TS, const std::string& Line) : ts(TS), line(Line) { }
+ };
+
+ typedef std::deque<RepeatItem> RepeatItemList;
+
+ struct MemberInfo
+ {
+ RepeatItemList ItemList;
+ unsigned int Counter;
+ MemberInfo() : Counter(0) {}
+ };
+
+ struct ModuleSettings
+ {
+ unsigned int MaxLines;
+ unsigned int MaxSecs;
+ unsigned int MaxBacklog;
+ unsigned int MaxDiff;
+ unsigned int MaxMessageSize;
+ ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { }
+ };
+
+ std::vector<unsigned int> mx[2];
+ ModuleSettings ms;
+
+ bool CompareLines(const std::string& message, const std::string& historyline, unsigned int trigger)
+ {
+ if (message == historyline)
+ return true;
+ else if (trigger)
+ return (Levenshtein(message, historyline) <= trigger);
+
+ return false;
+ }
+
+ unsigned int Levenshtein(const std::string& s1, const std::string& s2)
+ {
+ unsigned int l1 = s1.size();
+ unsigned int l2 = s2.size();
+
+ for (unsigned int i = 0; i < l2; i++)
+ mx[0][i] = i;
+ for (unsigned int i = 0; i < l1; i++)
+ {
+ mx[1][0] = i + 1;
+ for (unsigned int j = 0; j < l2; j++)
+ mx[1][j + 1] = std::min(std::min(mx[1][j] + 1, mx[0][j + 1] + 1), mx[0][j] + ((s1[i] == s2[j]) ? 0 : 1));
+
+ mx[0].swap(mx[1]);
+ }
+ return mx[0][l2];
+ }
+
+ public:
+ SimpleExtItem<MemberInfo> MemberInfoExt;
+
+ RepeatMode(Module* Creator)
+ : ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
+ , MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator)
+ {
+ }
+
+ void OnUnset(User* source, Channel* chan)
+ {
+ // Unset the per-membership extension when the mode is removed
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ MemberInfoExt.unset(i->second);
+ }
+
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
+ {
+ ChannelSettings settings;
+ if (!ParseSettings(source, parameter, settings))
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+ "Invalid repeat syntax. Syntax is {[~*]}[lines]:[time]{:[difference]}{:[backlog]}."));
+ return MODEACTION_DENY;
+ }
+
+ if ((settings.Backlog > 0) && (settings.Lines > settings.Backlog))
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+ "Invalid repeat syntax. You can't set needed lines higher than backlog."));
+ return MODEACTION_DENY;
+ }
+
+ LocalUser* localsource = IS_LOCAL(source);
+ if ((localsource) && (!ValidateSettings(localsource, channel, parameter, settings)))
+ return MODEACTION_DENY;
+
+ ext.set(channel, settings);
+
+ return MODEACTION_ALLOW;
+ }
+
+ bool MatchLine(Membership* memb, ChannelSettings* rs, std::string message)
+ {
+ // If the message is larger than whatever size it's set to,
+ // let's pretend it isn't. If the first 512 (def. setting) match, it's probably spam.
+ if (message.size() > ms.MaxMessageSize)
+ message.erase(ms.MaxMessageSize);
+
+ MemberInfo* rp = MemberInfoExt.get(memb);
+ if (!rp)
+ {
+ rp = new MemberInfo;
+ MemberInfoExt.set(memb, rp);
+ }
+
+ unsigned int matches = 0;
+ if (!rs->Backlog)
+ matches = rp->Counter;
+
+ RepeatItemList& items = rp->ItemList;
+ const unsigned int trigger = (message.size() * rs->Diff / 100);
+ const time_t now = ServerInstance->Time();
+
+ std::transform(message.begin(), message.end(), message.begin(), ::tolower);
+
+ for (std::deque<RepeatItem>::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ if (it->ts < now)
+ {
+ items.erase(it, items.end());
+ matches = 0;
+ break;
+ }
+
+ if (CompareLines(message, it->line, trigger))
+ {
+ if (++matches >= rs->Lines)
+ {
+ if (rs->Action != ChannelSettings::ACT_BLOCK)
+ rp->Counter = 0;
+ return true;
+ }
+ }
+ else if ((ms.MaxBacklog == 0) || (rs->Backlog == 0))
+ {
+ matches = 0;
+ items.clear();
+ break;
+ }
+ }
+
+ unsigned int max_items = (rs->Backlog ? rs->Backlog : 1);
+ if (items.size() >= max_items)
+ items.pop_back();
+
+ items.push_front(RepeatItem(now + rs->Seconds, message));
+ rp->Counter = matches;
+ return false;
+ }
+
+ void Resize(size_t size)
+ {
+ size_t newsize = size+1;
+ if (newsize <= mx[0].size())
+ return;
+ ms.MaxMessageSize = size;
+ mx[0].resize(newsize);
+ mx[1].resize(newsize);
+ }
+
+ void ReadConfig()
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("repeat");
+ ms.MaxLines = conf->getUInt("maxlines", 20);
+ ms.MaxBacklog = conf->getUInt("maxbacklog", 20);
+ ms.MaxSecs = conf->getDuration("maxtime", conf->getDuration("maxsecs", 0));
+
+ ms.MaxDiff = conf->getUInt("maxdistance", 50);
+ if (ms.MaxDiff > 100)
+ ms.MaxDiff = 100;
+
+ unsigned int newsize = conf->getUInt("size", 512);
+ if (newsize > ServerInstance->Config->Limits.MaxLine)
+ newsize = ServerInstance->Config->Limits.MaxLine;
+ Resize(newsize);
+ }
+
+ std::string GetModuleSettings() const
+ {
+ return ConvToStr(ms.MaxLines) + ":" + ConvToStr(ms.MaxSecs) + ":" + ConvToStr(ms.MaxDiff) + ":" + ConvToStr(ms.MaxBacklog);
+ }
+
+ void SerializeParam(Channel* chan, const ChannelSettings* chset, std::string& out)
+ {
+ chset->serialize(out);
+ }
+
+ private:
+ bool ParseSettings(User* source, std::string& parameter, ChannelSettings& settings)
+ {
+ irc::sepstream stream(parameter, ':');
+ std::string item;
+ if (!stream.GetToken(item))
+ // Required parameter missing
+ return false;
+
+ if ((item[0] == '*') || (item[0] == '~'))
+ {
+ settings.Action = ((item[0] == '*') ? ChannelSettings::ACT_BAN : ChannelSettings::ACT_BLOCK);
+ item.erase(item.begin());
+ }
+ else
+ settings.Action = ChannelSettings::ACT_KICK;
+
+ if ((settings.Lines = ConvToInt(item)) == 0)
+ return false;
+
+ if ((!stream.GetToken(item)) || ((settings.Seconds = InspIRCd::Duration(item)) == 0))
+ // Required parameter missing
+ return false;
+
+ // The diff and backlog parameters are optional
+ settings.Diff = settings.Backlog = 0;
+ if (stream.GetToken(item))
+ {
+ // There is a diff parameter, see if it's valid (> 0)
+ if ((settings.Diff = ConvToInt(item)) == 0)
+ return false;
+
+ if (stream.GetToken(item))
+ {
+ // There is a backlog parameter, see if it's valid
+ if ((settings.Backlog = ConvToInt(item)) == 0)
+ return false;
+
+ // If there are still tokens, then it's invalid because we allow only 4
+ if (stream.GetToken(item))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool ValidateSettings(LocalUser* source, Channel* channel, const std::string& parameter, const ChannelSettings& settings)
+ {
+ if (ms.MaxLines && settings.Lines > ms.MaxLines)
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+ "Invalid repeat parameter. The line number you specified is too great. Maximum allowed is %u.", ms.MaxLines)));
+ return false;
+ }
+
+ if (ms.MaxSecs && settings.Seconds > ms.MaxSecs)
+ {
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+ "Invalid repeat parameter. The seconds you specified is too great. Maximum allowed is %u.", ms.MaxSecs)));
+ return false;
+ }
+
+ if (settings.Diff && settings.Diff > ms.MaxDiff)
+ {
+ if (ms.MaxDiff == 0)
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+ "Invalid repeat parameter. The server administrator has disabled matching on edit distance."));
+ else
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+ "Invalid repeat parameter. The distance you specified is too great. Maximum allowed is %u.", ms.MaxDiff)));
+ return false;
+ }
+
+ if (settings.Backlog && settings.Backlog > ms.MaxBacklog)
+ {
+ if (ms.MaxBacklog == 0)
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+ "Invalid repeat parameter. The server administrator has disabled backlog matching."));
+ else
+ source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+ "Invalid repeat paramter. The backlog you specified is too great. Maximum allowed is %u.", ms.MaxBacklog)));
+ return false;
+ }
+
+ return true;
+ }
+};
+
+class RepeatModule : public Module
+{
+ CheckExemption::EventProvider exemptionprov;
+ RepeatMode rm;
+
+ public:
+ RepeatModule()
+ : exemptionprov(this)
+ , rm(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ rm.ReadConfig();
+ }
+
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+ {
+ if (target.type != MessageTarget::TYPE_CHANNEL || !IS_LOCAL(user))
+ return MOD_RES_PASSTHRU;
+
+ Channel* chan = target.Get<Channel>();
+ ChannelSettings* settings = rm.ext.get(chan);
+ if (!settings)
+ return MOD_RES_PASSTHRU;
+
+ Membership* memb = chan->GetUser(user);
+ if (!memb)
+ return MOD_RES_PASSTHRU;
+
+ ModResult res = CheckExemption::Call(exemptionprov, user, chan, "repeat");
+ if (res == MOD_RES_ALLOW)
+ return MOD_RES_PASSTHRU;
+
+ if (rm.MatchLine(memb, settings, details.text))
+ {
+ if (settings->Action == ChannelSettings::ACT_BLOCK)
+ {
+ user->WriteNotice("*** This line is too similar to one of your last lines.");
+ return MOD_RES_DENY;
+ }
+
+ if (settings->Action == ChannelSettings::ACT_BAN)
+ {
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->GetDisplayedHost());
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist);
+ }
+
+ memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the +E channel mode - for blocking of similar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+ }
+};
+
+MODULE_INIT(RepeatModule)
diff --git a/src/modules/m_restrictchans.cpp b/src/modules/m_restrictchans.cpp
index c76b0e79f..9c7ed1213 100644
--- a/src/modules/m_restrictchans.cpp
+++ b/src/modules/m_restrictchans.cpp
@@ -22,13 +22,12 @@
#include "inspircd.h"
-/* $ModDesc: Only opers may create new channels if this module is loaded */
-
class ModuleRestrictChans : public Module
{
- std::set<irc::string> allowchans;
+ insp::flat_set<std::string, irc::insensitive_swo> allowchans;
- void ReadConfig()
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
allowchans.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("allowchannel");
@@ -36,48 +35,26 @@ class ModuleRestrictChans : public Module
{
ConfigTag* tag = i->second;
std::string txt = tag->getString("name");
- allowchans.insert(txt.c_str());
+ allowchans.insert(txt);
}
}
- public:
- void init()
- {
- ReadConfig();
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
- {
- ReadConfig();
- }
-
-
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- irc::string x = cname;
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
// channel does not yet exist (record is null, about to be created IF we were to allow it)
if (!chan)
{
// user is not an oper and its not in the allow list
- if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end()))
+ if ((!user->IsOper()) && (allowchans.find(cname) == allowchans.end()))
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Only IRC operators may create new channels",user->nick.c_str(),cname);
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, cname, "Only IRC operators may create new channels");
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleRestrictChans()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Only opers may create new channels if this module is loaded",VF_VENDOR);
}
diff --git a/src/modules/m_restrictmsg.cpp b/src/modules/m_restrictmsg.cpp
index 2a9f1dc93..16a9bae86 100644
--- a/src/modules/m_restrictmsg.cpp
+++ b/src/modules/m_restrictmsg.cpp
@@ -21,44 +21,25 @@
#include "inspircd.h"
-/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */
-
-
class ModuleRestrictMsg : public Module
{
- private:
- bool uline;
-
public:
-
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User*)
- {
- uline = ServerInstance->Config->ConfValue("restrictmsg")->getBool("uline", false);
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
+ if ((target.type == MessageTarget::TYPE_USER) && (IS_LOCAL(user)))
{
- User* u = (User*)dest;
+ User* u = target.Get<User>();
// message allowed if:
// (1) the sender is opered
// (2) the recipient is opered
// (3) the recipient is on a ulined server
// anything else, blocked.
- if (IS_OPER(u) || IS_OPER(user) || (uline && ServerInstance->ULine(u->server)))
+ if (u->IsOper() || user->IsOper() || u->server->IsULine())
{
return MOD_RES_PASSTHRU;
}
- user->WriteNumeric(ERR_CANTSENDTOUSER, "%s %s :You are not permitted to send private messages to this user",user->nick.c_str(),u->nick.c_str());
+ user->WriteNumeric(ERR_CANTSENDTOUSER, u->nick, "You are not permitted to send private messages to this user");
return MOD_RES_DENY;
}
@@ -66,16 +47,7 @@ class ModuleRestrictMsg : public Module
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return this->OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual ~ModuleRestrictMsg()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Forbids users from messaging each other. Users may still message opers and opers may message other opers.",VF_VENDOR);
}
diff --git a/src/modules/m_ripemd160.cpp b/src/modules/m_ripemd160.cpp
deleted file mode 100644
index 04c27e83d..000000000
--- a/src/modules/m_ripemd160.cpp
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/*
- *
- * AUTHOR: Antoon Bosselaers, ESAT-COSIC
- * DATE: 1 March 1996
- * VERSION: 1.0
- *
- * Copyright (c) Katholieke Universiteit Leuven
- * 1996, All Rights Reserved
- *
- * Conditions for use of the RIPEMD-160 Software
- *
- * The RIPEMD-160 software is freely available for use under the terms and
- * conditions described hereunder, which shall be deemed to be accepted by
- * any user of the software and applicable on any use of the software:
- *
- * 1. K.U.Leuven Department of Electrical Engineering-ESAT/COSIC shall for
- * all purposes be considered the owner of the RIPEMD-160 software and of
- * all copyright, trade secret, patent or other intellectual property
- * rights therein.
- * 2. The RIPEMD-160 software is provided on an "as is" basis without
- * warranty of any sort, express or implied. K.U.Leuven makes no
- * representation that the use of the software will not infringe any
- * patent or proprietary right of third parties. User will indemnify
- * K.U.Leuven and hold K.U.Leuven harmless from any claims or liabilities
- * which may arise as a result of its use of the software. In no
- * circumstances K.U.Leuven R&D will be held liable for any deficiency,
- * fault or other mishappening with regard to the use or performance of
- * the software.
- * 3. User agrees to give due credit to K.U.Leuven in scientific publications
- * or communications in relation with the use of the RIPEMD-160 software
- * as follows: RIPEMD-160 software written by Antoon Bosselaers,
- * available at http://www.esat.kuleuven.be/~cosicart/ps/AB-9601/.
- *
- */
-
-
-/* $ModDesc: Allows for RIPEMD-160 encrypted oper passwords */
-
-/* macro definitions */
-
-#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
-
-#define RMDsize 160
-
-#ifndef HAS_STDINT
-typedef unsigned char byte;
-typedef unsigned int dword;
-#else
-typedef uint8_t byte;
-typedef uint32_t dword;
-#endif
-
-/* collect four bytes into one word: */
-#define BYTES_TO_DWORD(strptr) \
- (((dword) *((strptr)+3) << 24) | \
- ((dword) *((strptr)+2) << 16) | \
- ((dword) *((strptr)+1) << 8) | \
- ((dword) *(strptr)))
-
-/* ROL(x, n) cyclically rotates x over n bits to the left */
-/* x must be of an unsigned 32 bits type and 0 <= n < 32. */
-#define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-/* the five basic functions F(), G() and H() */
-#define F(x, y, z) ((x) ^ (y) ^ (z))
-#define G(x, y, z) (((x) & (y)) | (~(x) & (z)))
-#define H(x, y, z) (((x) | ~(y)) ^ (z))
-#define I(x, y, z) (((x) & (z)) | ((y) & ~(z)))
-#define J(x, y, z) ((x) ^ ((y) | ~(z)))
-
-/* the ten basic operations FF() through III() */
-
-#define FF(a, b, c, d, e, x, s) {\
- (a) += F((b), (c), (d)) + (x);\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define GG(a, b, c, d, e, x, s) {\
- (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define HH(a, b, c, d, e, x, s) {\
- (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define II(a, b, c, d, e, x, s) {\
- (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define JJ(a, b, c, d, e, x, s) {\
- (a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define FFF(a, b, c, d, e, x, s) {\
- (a) += F((b), (c), (d)) + (x);\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define GGG(a, b, c, d, e, x, s) {\
- (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define HHH(a, b, c, d, e, x, s) {\
- (a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define III(a, b, c, d, e, x, s) {\
- (a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-#define JJJ(a, b, c, d, e, x, s) {\
- (a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\
- (a) = ROL((a), (s)) + (e);\
- (c) = ROL((c), 10);\
- }
-
-
-class RIProv : public HashProvider
-{
- /** Final hash value
- */
- byte hashcode[RMDsize/8];
-
- void MDinit(dword *MDbuf, unsigned int* key)
- {
- if (key)
- {
- ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with custom mdbuf");
- MDbuf[0] = key[0];
- MDbuf[1] = key[1];
- MDbuf[2] = key[2];
- MDbuf[3] = key[3];
- MDbuf[4] = key[4];
- }
- else
- {
- ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with default mdbuf");
- MDbuf[0] = 0x67452301UL;
- MDbuf[1] = 0xefcdab89UL;
- MDbuf[2] = 0x98badcfeUL;
- MDbuf[3] = 0x10325476UL;
- MDbuf[4] = 0xc3d2e1f0UL;
- }
- return;
- }
-
-
- void compress(dword *MDbuf, dword *X)
- {
- dword aa = MDbuf[0], bb = MDbuf[1], cc = MDbuf[2],
- dd = MDbuf[3], ee = MDbuf[4];
- dword aaa = MDbuf[0], bbb = MDbuf[1], ccc = MDbuf[2],
- ddd = MDbuf[3], eee = MDbuf[4];
-
- /* round 1 */
- FF(aa, bb, cc, dd, ee, X[ 0], 11);
- FF(ee, aa, bb, cc, dd, X[ 1], 14);
- FF(dd, ee, aa, bb, cc, X[ 2], 15);
- FF(cc, dd, ee, aa, bb, X[ 3], 12);
- FF(bb, cc, dd, ee, aa, X[ 4], 5);
- FF(aa, bb, cc, dd, ee, X[ 5], 8);
- FF(ee, aa, bb, cc, dd, X[ 6], 7);
- FF(dd, ee, aa, bb, cc, X[ 7], 9);
- FF(cc, dd, ee, aa, bb, X[ 8], 11);
- FF(bb, cc, dd, ee, aa, X[ 9], 13);
- FF(aa, bb, cc, dd, ee, X[10], 14);
- FF(ee, aa, bb, cc, dd, X[11], 15);
- FF(dd, ee, aa, bb, cc, X[12], 6);
- FF(cc, dd, ee, aa, bb, X[13], 7);
- FF(bb, cc, dd, ee, aa, X[14], 9);
- FF(aa, bb, cc, dd, ee, X[15], 8);
-
- /* round 2 */
- GG(ee, aa, bb, cc, dd, X[ 7], 7);
- GG(dd, ee, aa, bb, cc, X[ 4], 6);
- GG(cc, dd, ee, aa, bb, X[13], 8);
- GG(bb, cc, dd, ee, aa, X[ 1], 13);
- GG(aa, bb, cc, dd, ee, X[10], 11);
- GG(ee, aa, bb, cc, dd, X[ 6], 9);
- GG(dd, ee, aa, bb, cc, X[15], 7);
- GG(cc, dd, ee, aa, bb, X[ 3], 15);
- GG(bb, cc, dd, ee, aa, X[12], 7);
- GG(aa, bb, cc, dd, ee, X[ 0], 12);
- GG(ee, aa, bb, cc, dd, X[ 9], 15);
- GG(dd, ee, aa, bb, cc, X[ 5], 9);
- GG(cc, dd, ee, aa, bb, X[ 2], 11);
- GG(bb, cc, dd, ee, aa, X[14], 7);
- GG(aa, bb, cc, dd, ee, X[11], 13);
- GG(ee, aa, bb, cc, dd, X[ 8], 12);
-
- /* round 3 */
- HH(dd, ee, aa, bb, cc, X[ 3], 11);
- HH(cc, dd, ee, aa, bb, X[10], 13);
- HH(bb, cc, dd, ee, aa, X[14], 6);
- HH(aa, bb, cc, dd, ee, X[ 4], 7);
- HH(ee, aa, bb, cc, dd, X[ 9], 14);
- HH(dd, ee, aa, bb, cc, X[15], 9);
- HH(cc, dd, ee, aa, bb, X[ 8], 13);
- HH(bb, cc, dd, ee, aa, X[ 1], 15);
- HH(aa, bb, cc, dd, ee, X[ 2], 14);
- HH(ee, aa, bb, cc, dd, X[ 7], 8);
- HH(dd, ee, aa, bb, cc, X[ 0], 13);
- HH(cc, dd, ee, aa, bb, X[ 6], 6);
- HH(bb, cc, dd, ee, aa, X[13], 5);
- HH(aa, bb, cc, dd, ee, X[11], 12);
- HH(ee, aa, bb, cc, dd, X[ 5], 7);
- HH(dd, ee, aa, bb, cc, X[12], 5);
-
- /* round 4 */
- II(cc, dd, ee, aa, bb, X[ 1], 11);
- II(bb, cc, dd, ee, aa, X[ 9], 12);
- II(aa, bb, cc, dd, ee, X[11], 14);
- II(ee, aa, bb, cc, dd, X[10], 15);
- II(dd, ee, aa, bb, cc, X[ 0], 14);
- II(cc, dd, ee, aa, bb, X[ 8], 15);
- II(bb, cc, dd, ee, aa, X[12], 9);
- II(aa, bb, cc, dd, ee, X[ 4], 8);
- II(ee, aa, bb, cc, dd, X[13], 9);
- II(dd, ee, aa, bb, cc, X[ 3], 14);
- II(cc, dd, ee, aa, bb, X[ 7], 5);
- II(bb, cc, dd, ee, aa, X[15], 6);
- II(aa, bb, cc, dd, ee, X[14], 8);
- II(ee, aa, bb, cc, dd, X[ 5], 6);
- II(dd, ee, aa, bb, cc, X[ 6], 5);
- II(cc, dd, ee, aa, bb, X[ 2], 12);
-
- /* round 5 */
- JJ(bb, cc, dd, ee, aa, X[ 4], 9);
- JJ(aa, bb, cc, dd, ee, X[ 0], 15);
- JJ(ee, aa, bb, cc, dd, X[ 5], 5);
- JJ(dd, ee, aa, bb, cc, X[ 9], 11);
- JJ(cc, dd, ee, aa, bb, X[ 7], 6);
- JJ(bb, cc, dd, ee, aa, X[12], 8);
- JJ(aa, bb, cc, dd, ee, X[ 2], 13);
- JJ(ee, aa, bb, cc, dd, X[10], 12);
- JJ(dd, ee, aa, bb, cc, X[14], 5);
- JJ(cc, dd, ee, aa, bb, X[ 1], 12);
- JJ(bb, cc, dd, ee, aa, X[ 3], 13);
- JJ(aa, bb, cc, dd, ee, X[ 8], 14);
- JJ(ee, aa, bb, cc, dd, X[11], 11);
- JJ(dd, ee, aa, bb, cc, X[ 6], 8);
- JJ(cc, dd, ee, aa, bb, X[15], 5);
- JJ(bb, cc, dd, ee, aa, X[13], 6);
-
- /* parallel round 1 */
- JJJ(aaa, bbb, ccc, ddd, eee, X[ 5], 8);
- JJJ(eee, aaa, bbb, ccc, ddd, X[14], 9);
- JJJ(ddd, eee, aaa, bbb, ccc, X[ 7], 9);
- JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11);
- JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13);
- JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15);
- JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15);
- JJJ(ddd, eee, aaa, bbb, ccc, X[ 4], 5);
- JJJ(ccc, ddd, eee, aaa, bbb, X[13], 7);
- JJJ(bbb, ccc, ddd, eee, aaa, X[ 6], 7);
- JJJ(aaa, bbb, ccc, ddd, eee, X[15], 8);
- JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11);
- JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14);
- JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14);
- JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12);
- JJJ(aaa, bbb, ccc, ddd, eee, X[12], 6);
-
- /* parallel round 2 */
- III(eee, aaa, bbb, ccc, ddd, X[ 6], 9);
- III(ddd, eee, aaa, bbb, ccc, X[11], 13);
- III(ccc, ddd, eee, aaa, bbb, X[ 3], 15);
- III(bbb, ccc, ddd, eee, aaa, X[ 7], 7);
- III(aaa, bbb, ccc, ddd, eee, X[ 0], 12);
- III(eee, aaa, bbb, ccc, ddd, X[13], 8);
- III(ddd, eee, aaa, bbb, ccc, X[ 5], 9);
- III(ccc, ddd, eee, aaa, bbb, X[10], 11);
- III(bbb, ccc, ddd, eee, aaa, X[14], 7);
- III(aaa, bbb, ccc, ddd, eee, X[15], 7);
- III(eee, aaa, bbb, ccc, ddd, X[ 8], 12);
- III(ddd, eee, aaa, bbb, ccc, X[12], 7);
- III(ccc, ddd, eee, aaa, bbb, X[ 4], 6);
- III(bbb, ccc, ddd, eee, aaa, X[ 9], 15);
- III(aaa, bbb, ccc, ddd, eee, X[ 1], 13);
- III(eee, aaa, bbb, ccc, ddd, X[ 2], 11);
-
- /* parallel round 3 */
- HHH(ddd, eee, aaa, bbb, ccc, X[15], 9);
- HHH(ccc, ddd, eee, aaa, bbb, X[ 5], 7);
- HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15);
- HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11);
- HHH(eee, aaa, bbb, ccc, ddd, X[ 7], 8);
- HHH(ddd, eee, aaa, bbb, ccc, X[14], 6);
- HHH(ccc, ddd, eee, aaa, bbb, X[ 6], 6);
- HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14);
- HHH(aaa, bbb, ccc, ddd, eee, X[11], 12);
- HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13);
- HHH(ddd, eee, aaa, bbb, ccc, X[12], 5);
- HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14);
- HHH(bbb, ccc, ddd, eee, aaa, X[10], 13);
- HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13);
- HHH(eee, aaa, bbb, ccc, ddd, X[ 4], 7);
- HHH(ddd, eee, aaa, bbb, ccc, X[13], 5);
-
- /* parallel round 4 */
- GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15);
- GGG(bbb, ccc, ddd, eee, aaa, X[ 6], 5);
- GGG(aaa, bbb, ccc, ddd, eee, X[ 4], 8);
- GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11);
- GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14);
- GGG(ccc, ddd, eee, aaa, bbb, X[11], 14);
- GGG(bbb, ccc, ddd, eee, aaa, X[15], 6);
- GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14);
- GGG(eee, aaa, bbb, ccc, ddd, X[ 5], 6);
- GGG(ddd, eee, aaa, bbb, ccc, X[12], 9);
- GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12);
- GGG(bbb, ccc, ddd, eee, aaa, X[13], 9);
- GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12);
- GGG(eee, aaa, bbb, ccc, ddd, X[ 7], 5);
- GGG(ddd, eee, aaa, bbb, ccc, X[10], 15);
- GGG(ccc, ddd, eee, aaa, bbb, X[14], 8);
-
- /* parallel round 5 */
- FFF(bbb, ccc, ddd, eee, aaa, X[12] , 8);
- FFF(aaa, bbb, ccc, ddd, eee, X[15] , 5);
- FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12);
- FFF(ddd, eee, aaa, bbb, ccc, X[ 4] , 9);
- FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12);
- FFF(bbb, ccc, ddd, eee, aaa, X[ 5] , 5);
- FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14);
- FFF(eee, aaa, bbb, ccc, ddd, X[ 7] , 6);
- FFF(ddd, eee, aaa, bbb, ccc, X[ 6] , 8);
- FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13);
- FFF(bbb, ccc, ddd, eee, aaa, X[13] , 6);
- FFF(aaa, bbb, ccc, ddd, eee, X[14] , 5);
- FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15);
- FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13);
- FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11);
- FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11);
-
- /* combine results */
- ddd += cc + MDbuf[1]; /* final result for MDbuf[0] */
- MDbuf[1] = MDbuf[2] + dd + eee;
- MDbuf[2] = MDbuf[3] + ee + aaa;
- MDbuf[3] = MDbuf[4] + aa + bbb;
- MDbuf[4] = MDbuf[0] + bb + ccc;
- MDbuf[0] = ddd;
-
- return;
- }
-
- void MDfinish(dword *MDbuf, byte *strptr, dword lswlen, dword mswlen)
- {
- unsigned int i; /* counter */
- dword X[16]; /* message words */
-
- memset(X, 0, sizeof(X));
-
- /* put bytes from strptr into X */
- for (i=0; i<(lswlen&63); i++) {
- /* byte i goes into word X[i div 4] at pos. 8*(i mod 4) */
- X[i>>2] ^= (dword) *strptr++ << (8 * (i&3));
- }
-
- /* append the bit m_n == 1 */
- X[(lswlen>>2)&15] ^= (dword)1 << (8*(lswlen&3) + 7);
-
- if ((lswlen & 63) > 55) {
- /* length goes to next block */
- compress(MDbuf, X);
- memset(X, 0, sizeof(X));
- }
-
- /* append length in bits*/
- X[14] = lswlen << 3;
- X[15] = (lswlen >> 29) | (mswlen << 3);
- compress(MDbuf, X);
-
- return;
- }
-
- byte *RMD(byte *message, dword length, unsigned int* key)
- {
- ServerInstance->Logs->Log("m_ripemd160", DEBUG, "RMD: '%s' length=%u", (const char*)message, length);
- dword MDbuf[RMDsize/32]; /* contains (A, B, C, D(E)) */
- dword X[16]; /* current 16-word chunk */
- unsigned int i; /* counter */
- dword nbytes; /* # of bytes not yet processed */
-
- /* initialize */
- MDinit(MDbuf, key);
-
- /* process message in 16-word chunks */
- for (nbytes=length; nbytes > 63; nbytes-=64) {
- for (i=0; i<16; i++) {
- X[i] = BYTES_TO_DWORD(message);
- message += 4;
- }
- compress(MDbuf, X);
- } /* length mod 64 bytes left */
-
- MDfinish(MDbuf, message, length, 0);
-
- for (i=0; i<RMDsize/8; i+=4) {
- hashcode[i] = MDbuf[i>>2]; /* implicit cast to byte */
- hashcode[i+1] = (MDbuf[i>>2] >> 8); /* extracts the 8 least */
- hashcode[i+2] = (MDbuf[i>>2] >> 16); /* significant bits. */
- hashcode[i+3] = (MDbuf[i>>2] >> 24);
- }
-
- return (byte *)hashcode;
- }
-public:
- std::string sum(const std::string& data)
- {
- char* rv = (char*)RMD((byte*)data.data(), data.length(), NULL);
- return std::string(rv, RMDsize / 8);
- }
-
- std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
- {
- return "";
- }
-
- RIProv(Module* m) : HashProvider(m, "hash/ripemd160", 20, 64) {}
-};
-
-class ModuleRIPEMD160 : public Module
-{
- public:
- RIProv mr;
- ModuleRIPEMD160() : mr(this)
- {
- ServerInstance->Modules->AddService(mr);
- }
-
- Version GetVersion()
- {
- return Version("Provides RIPEMD-160 hashing", VF_VENDOR);
- }
-
-};
-
-MODULE_INIT(ModuleRIPEMD160)
-
diff --git a/src/modules/m_rline.cpp b/src/modules/m_rline.cpp
index d1ab5d9ba..bf6a64d84 100644
--- a/src/modules/m_rline.cpp
+++ b/src/modules/m_rline.cpp
@@ -20,10 +20,9 @@
*/
-/* $ModDesc: RLINE: Regexp user banning. */
-
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
+#include "modules/stats.h"
#include "xline.h"
static bool ZlineOnMatch = false;
@@ -58,28 +57,30 @@ class RLine : public XLine
delete regex;
}
- bool Matches(User *u)
+ bool Matches(User* u) CXX11_OVERRIDE
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
- std::string compare = u->nick + "!" + u->ident + "@" + u->host + " " + u->fullname;
- return regex->Matches(compare);
+ const std::string host = u->nick + "!" + u->ident + "@" + u->GetRealHost() + " " + u->GetRealName();
+ const std::string ip = u->nick + "!" + u->ident + "@" + u->GetIPString() + " " + u->GetRealName();
+ return (regex->Matches(host) || regex->Matches(ip));
}
- bool Matches(const std::string &compare)
+ bool Matches(const std::string& compare) CXX11_OVERRIDE
{
return regex->Matches(compare);
}
- void Apply(User* u)
+ void Apply(User* u) CXX11_OVERRIDE
{
if (ZlineOnMatch)
{
ZLine* zl = new ZLine(ServerInstance->Time(), duration ? expiry - ServerInstance->Time() : 0, ServerInstance->Config->ServerName.c_str(), reason.c_str(), u->GetIPString());
if (ServerInstance->XLines->AddLine(zl, NULL))
{
- std::string timestr = ServerInstance->TimeString(zl->expiry);
+ std::string timestr = InspIRCd::TimeString(zl->expiry);
ServerInstance->SNO->WriteToSnoMask('x', "Z-line added due to R-line match on *@%s%s%s: %s",
zl->ipaddr.c_str(), zl->duration ? " to expire on " : "", zl->duration ? timestr.c_str() : "", zl->reason.c_str());
added_zline = true;
@@ -90,15 +91,9 @@ class RLine : public XLine
DefaultApply(u, "R", false);
}
- void DisplayExpiry()
- {
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired R-line %s (set by %s %ld seconds ago)",
- this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
- }
-
- const char* Displayable()
+ const std::string& Displayable() CXX11_OVERRIDE
{
- return matchtext.c_str();
+ return matchtext;
}
std::string matchtext;
@@ -116,10 +111,10 @@ class RLineFactory : public XLineFactory
RLineFactory(dynamic_reference<RegexFactory>& rx) : XLineFactory("R"), rxfactory(rx)
{
}
-
+
/** Generate a RLine
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
if (!rxfactory)
{
@@ -129,10 +124,6 @@ class RLineFactory : public XLineFactory
return new RLine(set_time, duration, source, reason, xline_specific_mask, rxfactory);
}
-
- ~RLineFactory()
- {
- }
};
/** Handle /RLINE
@@ -149,14 +140,14 @@ class CommandRLine : public Command
flags_needed = 'o'; this->syntax = "<regex> [<rline-duration>] :<reason>";
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
if (parameters.size() >= 3)
{
// Adding - XXX todo make this respect <insane> tag perhaps..
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
XLine *r = NULL;
try
@@ -165,7 +156,7 @@ class CommandRLine : public Command
}
catch (ModuleException &e)
{
- ServerInstance->SNO->WriteToSnoMask('a',"Could not add RLINE: %s", e.GetReason());
+ ServerInstance->SNO->WriteToSnoMask('a',"Could not add RLINE: " + e.GetReason());
}
if (r)
@@ -179,7 +170,7 @@ class CommandRLine : public Command
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x', "%s added timed R-line for %s to expire on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), parameters[2].c_str());
}
@@ -188,7 +179,7 @@ class CommandRLine : public Command
else
{
delete r;
- user->WriteServ("NOTICE %s :*** R-Line for %s already exists", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** R-Line for " + parameters[0] + " already exists");
}
}
}
@@ -200,14 +191,14 @@ class CommandRLine : public Command
}
else
{
- user->WriteServ("NOTICE %s :*** R-Line %s not found in list, try /stats R.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** R-Line " + parameters[0] + " not found in list, try /stats R.");
}
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
if (IS_LOCAL(user))
return ROUTE_LOCALONLY; // spanningtree will send ADDLINE
@@ -216,9 +207,8 @@ class CommandRLine : public Command
}
};
-class ModuleRLine : public Module
+class ModuleRLine : public Module, public Stats::EventListener
{
- private:
dynamic_reference<RegexFactory> rxfactory;
RLineFactory f;
CommandRLine r;
@@ -228,34 +218,31 @@ class ModuleRLine : public Module
public:
ModuleRLine()
- : rxfactory(this, "regex"), f(rxfactory), r(this, f)
+ : Stats::EventListener(this)
+ , rxfactory(this, "regex")
+ , f(rxfactory)
+ , r(this, f)
, initing(true)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(r);
ServerInstance->XLines->RegisterFactory(&f);
-
- Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnUserPostNick, I_OnStats, I_OnBackgroundTimer, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleRLine()
+ ~ModuleRLine()
{
ServerInstance->XLines->DelAll("R");
ServerInstance->XLines->UnregisterFactory(&f);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("RLINE: Regexp user banning.", VF_COMMON | VF_VENDOR, rxfactory ? rxfactory->name : "");
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
// Apply lines on user connect
XLine *rl = ServerInstance->XLines->MatchesLine("R", user);
@@ -269,7 +256,7 @@ class ModuleRLine : public Module
return MOD_RES_PASSTHRU;
}
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("rline");
@@ -302,16 +289,16 @@ class ModuleRLine : public Module
initing = false;
}
- virtual ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'R')
+ if (stats.GetSymbol() != 'R')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("R", 223, user, results);
+ ServerInstance->XLines->InvokeStats("R", 223, stats);
return MOD_RES_DENY;
}
- virtual void OnUserPostNick(User *user, const std::string &oldnick)
+ void OnUserPostNick(User *user, const std::string &oldnick) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return;
@@ -328,7 +315,7 @@ class ModuleRLine : public Module
}
}
- virtual void OnBackgroundTimer(time_t curtime)
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
{
if (added_zline)
{
@@ -337,7 +324,7 @@ class ModuleRLine : public Module
}
}
- void OnUnloadModule(Module* mod)
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
{
// If the regex engine became unavailable or has changed, remove all rlines
if (!rxfactory)
@@ -351,7 +338,7 @@ class ModuleRLine : public Module
}
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
Module* mod = ServerInstance->Modules->Find("m_cgiirc.so");
ServerInstance->Modules->SetPriority(this, I_OnUserRegister, PRIORITY_AFTER, mod);
diff --git a/src/modules/m_rmode.cpp b/src/modules/m_rmode.cpp
new file mode 100644
index 000000000..ce28630b4
--- /dev/null
+++ b/src/modules/m_rmode.cpp
@@ -0,0 +1,110 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "listmode.h"
+
+/** Handle /RMODE
+ */
+class CommandRMode : public Command
+{
+ public:
+ CommandRMode(Module* Creator) : Command(Creator,"RMODE", 2, 3)
+ {
+ allow_empty_last_param = false;
+ syntax = "<channel> <mode> [pattern]";
+ }
+
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ ModeHandler* mh;
+ Channel* chan = ServerInstance->FindChan(parameters[0]);
+ char modeletter = parameters[1][0];
+
+ if (chan == NULL)
+ {
+ user->WriteNotice("The channel " + parameters[0] + " does not exist.");
+ return CMD_FAILURE;
+ }
+
+ mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
+ if (mh == NULL || parameters[1].size() > 1)
+ {
+ user->WriteNotice(parameters[1] + " is not a valid channel mode.");
+ return CMD_FAILURE;
+ }
+
+ if (chan->GetPrefixValue(user) < mh->GetLevelRequired(false))
+ {
+ user->WriteNotice("You do not have access to unset " + ConvToStr(modeletter) + " on " + chan->name + ".");
+ return CMD_FAILURE;
+ }
+
+ std::string pattern = parameters.size() > 2 ? parameters[2] : "*";
+ PrefixMode* pm;
+ ListModeBase* lm;
+ ListModeBase::ModeList* ml;
+ Modes::ChangeList changelist;
+
+ if ((pm = mh->IsPrefixMode()))
+ {
+ // As user prefix modes don't have a GetList() method, let's iterate through the channel's users.
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = users.begin(); it != users.end(); ++it)
+ {
+ if (!InspIRCd::Match(it->first->nick, pattern))
+ continue;
+ if (it->second->HasMode(pm) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE)))
+ changelist.push_remove(mh, it->first->nick);
+ }
+ }
+ else if ((lm = mh->IsListModeBase()) && ((ml = lm->GetList(chan)) != NULL))
+ {
+ for (ListModeBase::ModeList::iterator it = ml->begin(); it != ml->end(); ++it)
+ {
+ if (!InspIRCd::Match(it->mask, pattern))
+ continue;
+ changelist.push_remove(mh, it->mask);
+ }
+ }
+ else
+ {
+ if (chan->IsModeSet(mh))
+ changelist.push_remove(mh);
+ }
+
+ ServerInstance->Modes->Process(user, chan, NULL, changelist);
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleRMode : public Module
+{
+ CommandRMode cmd;
+
+ public:
+ ModuleRMode() : cmd(this) { }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Allows glob-based removal of list modes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleRMode)
diff --git a/src/modules/m_sajoin.cpp b/src/modules/m_sajoin.cpp
index 7ac465732..9aa8837e0 100644
--- a/src/modules/m_sajoin.cpp
+++ b/src/modules/m_sajoin.cpp
@@ -21,62 +21,71 @@
#include "inspircd.h"
-/* $ModDesc: Provides command SAJOIN to allow opers to force-join users to channels */
-
/** Handle /SAJOIN
*/
class CommandSajoin : public Command
{
public:
- CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 2)
+ CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 1)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ flags_needed = 'o'; syntax = "[<nick>] <channel>[,<channel>]";
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
+ const unsigned int channelindex = (parameters.size() > 1) ? 1 : 0;
+ if (CommandParser::LoopCall(user, this, parameters, channelindex))
+ return CMD_FAILURE;
+
+ const std::string& channel = parameters[channelindex];
+ const std::string& nickname = parameters.size() > 1 ? parameters[0] : user->nick;
+
+ User* dest = ServerInstance->FindNick(nickname);
if ((dest) && (dest->registered == REG_ALL))
{
- if (ServerInstance->ULine(dest->server))
+ if (user != dest && !user->HasPrivPermission("users/sajoin-others", false))
+ {
+ user->WriteNotice("*** You are not allowed to /SAJOIN other users (the privilege users/sajoin-others is needed to /SAJOIN others).");
+ return CMD_FAILURE;
+ }
+
+ if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
- if (IS_LOCAL(user) && !ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (IS_LOCAL(user) && !ServerInstance->IsChannel(channel))
{
/* we didn't need to check this for each character ;) */
- user->WriteServ("NOTICE "+user->nick+" :*** Invalid characters in channel name or name too long");
+ user->WriteNotice("*** Invalid characters in channel name or name too long");
return CMD_FAILURE;
}
- /* For local users, we send the JoinUser which may create a channel and set its TS.
+ Channel* chan = ServerInstance->FindChan(channel);
+ if ((chan) && (chan->HasUser(dest)))
+ {
+ user->WriteRemoteNotice("*** " + dest->nick + " is already on " + channel);
+ return CMD_FAILURE;
+ }
+
+ /* For local users, we call Channel::JoinUser which may create a channel and set its TS.
* For non-local users, we just return CMD_SUCCESS, knowing this will propagate it where it needs to be
- * and then that server will generate the users JOIN or FJOIN instead.
+ * and then that server will handle the command.
*/
- if (IS_LOCAL(dest))
+ LocalUser* localuser = IS_LOCAL(dest);
+ if (localuser)
{
- Channel::JoinUser(dest, parameters[1].c_str(), true, "", false, ServerInstance->Time());
- /* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propagate */
- Channel* n = ServerInstance->FindChan(parameters[1]);
- if (n)
+ chan = Channel::JoinUser(localuser, channel, true);
+ if (chan)
{
- if (n->HasUser(dest))
- {
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAJOIN to make "+dest->nick+" join "+parameters[1]);
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteServ("NOTICE "+user->nick+" :*** Could not join "+dest->nick+" to "+parameters[1]+" (User is probably banned, or blocking modes)");
- return CMD_FAILURE;
- }
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAJOIN to make "+dest->nick+" join "+channel);
+ return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE "+user->nick+" :*** Could not join "+dest->nick+" to "+parameters[1]);
+ user->WriteNotice("*** Could not join "+dest->nick+" to "+channel);
return CMD_FAILURE;
}
}
@@ -87,17 +96,14 @@ class CommandSajoin : public Command
}
else
{
- user->WriteServ("NOTICE "+user->nick+" :*** No such nickname "+parameters[0]);
+ user->WriteNotice("*** No such nickname "+nickname);
return CMD_FAILURE;
}
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -110,20 +116,10 @@ class ModuleSajoin : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSajoin()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command SAJOIN to allow opers to force-join users to channels", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSajoin)
diff --git a/src/modules/m_sakick.cpp b/src/modules/m_sakick.cpp
index afca49e25..6a9e11fe7 100644
--- a/src/modules/m_sakick.cpp
+++ b/src/modules/m_sakick.cpp
@@ -20,8 +20,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides a SAKICK command */
-
/** Handle /SAKICK
*/
class CommandSakick : public Command
@@ -29,30 +27,28 @@ class CommandSakick : public Command
public:
CommandSakick(Module* Creator) : Command(Creator,"SAKICK", 2, 3)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<channel> <nick> [reason]";
- TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
+ flags_needed = 'o'; syntax = "<channel> <nick> [reason]";
+ TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* dest = ServerInstance->FindNick(parameters[1]);
Channel* channel = ServerInstance->FindChan(parameters[0]);
- const char* reason = "";
if ((dest) && (dest->registered == REG_ALL) && (channel))
{
- if (parameters.size() > 2)
- {
- reason = parameters[2].c_str();
- }
- else
+ const std::string& reason = (parameters.size() > 2) ? parameters[2] : dest->nick;
+
+ if (dest->server->IsULine())
{
- reason = dest->nick.c_str();
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
+ return CMD_FAILURE;
}
- if (ServerInstance->ULine(dest->server))
+ if (!channel->HasUser(dest))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client", user->nick.c_str());
+ user->WriteNotice("*** " + dest->nick + " is not on " + channel->name);
return CMD_FAILURE;
}
@@ -62,31 +58,24 @@ class CommandSakick : public Command
*/
if (IS_LOCAL(dest))
{
+ // Target is on this server, kick them and send the snotice
channel->KickUser(ServerInstance->FakeClient, dest, reason);
- }
-
- if (IS_LOCAL(user))
- {
- /* Locally issued command; send the snomasks */
- ServerInstance->SNO->WriteGlobalSno('a', user->nick + " SAKICKed " + dest->nick + " on " + parameters[0]);
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick + " SAKICKed " + dest->nick + " on " + channel->name);
}
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick.c_str());
+ user->WriteNotice("*** Invalid nickname or channel");
}
return CMD_FAILURE;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[1]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[1]);
}
};
@@ -99,21 +88,10 @@ class ModuleSakick : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSakick()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a SAKICK command", VF_OPTCOMMON|VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSakick)
-
diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp
index ea2ae24ab..8f28a7e9c 100644
--- a/src/modules/m_samode.cpp
+++ b/src/modules/m_samode.cpp
@@ -20,42 +20,66 @@
*/
-/* $ModDesc: Provides command SAMODE to allow opers to change modes on channels and users */
-
#include "inspircd.h"
/** Handle /SAMODE
*/
class CommandSamode : public Command
{
+ bool logged;
+
public:
bool active;
CommandSamode(Module* Creator) : Command(Creator,"SAMODE", 2)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "<target> <modes> {<mode-parameters>}";
+ flags_needed = 'o'; syntax = "<target> <modes> {<mode-parameters>}";
active = false;
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
if (parameters[0].c_str()[0] != '#')
{
User* target = ServerInstance->FindNickOnly(parameters[0]);
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
+
+ // Changing the modes of another user requires a special permission
+ if ((target != user) && (!user->HasPrivPermission("users/samode-usermodes", true)))
+ return CMD_FAILURE;
}
+ // XXX: Make ModeParser clear LastParse
+ Modes::ChangeList emptychangelist;
+ ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
+
+ logged = false;
this->active = true;
- ServerInstance->Parser->CallHandler("MODE", parameters, user);
- if (ServerInstance->Modes->GetLastParse().length())
- ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
+ ServerInstance->Parser.CallHandler("MODE", parameters, user);
this->active = false;
+
+ if (!logged)
+ {
+ // If we haven't logged anything yet then the client queried the list of a listmode
+ // (e.g. /SAMODE #chan b), which was handled internally by the MODE command handler.
+ //
+ // Viewing the modes of a user or a channel could also result in this, but
+ // that is not possible with /SAMODE because we require at least 2 parameters.
+ LogUsage(user, stdalgo::string::join(parameters));
+ }
+
return CMD_SUCCESS;
}
+
+ void LogUsage(const User* user, const std::string& text)
+ {
+ logged = true;
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + text);
+ }
};
class ModuleSaMode : public Module
@@ -67,32 +91,41 @@ class ModuleSaMode : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->Attach(I_OnPreMode, this);
- }
-
- ~ModuleSaMode()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command SAMODE to allow opers to change modes on channels and users", VF_VENDOR);
}
- ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (cmd.active)
return MOD_RES_ALLOW;
return MOD_RES_PASSTHRU;
}
- void Prioritize()
+ void OnMode(User* user, User* destuser, Channel* destchan, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) CXX11_OVERRIDE
+ {
+ if (!cmd.active)
+ return;
+
+ std::string logtext = (destuser ? destuser->nick : destchan->name);
+ logtext.push_back(' ');
+ logtext += ClientProtocol::Messages::Mode::ToModeLetters(modes);
+
+ for (Modes::ChangeList::List::const_iterator i = modes.getlist().begin(); i != modes.getlist().end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ if (!item.param.empty())
+ logtext.append(1, ' ').append(item.param);
+ }
+
+ cmd.LogUsage(user, logtext);
+ }
+
+ void Prioritize() CXX11_OVERRIDE
{
Module *override = ServerInstance->Modules->Find("m_override.so");
- ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, &override);
+ ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, override);
}
};
diff --git a/src/modules/m_sanick.cpp b/src/modules/m_sanick.cpp
index 4e4be77ae..4744ca1de 100644
--- a/src/modules/m_sanick.cpp
+++ b/src/modules/m_sanick.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for SANICK command */
-
/** Handle /SANICK
*/
class CommandSanick : public Command
@@ -31,32 +29,32 @@ class CommandSanick : public Command
CommandSanick(Module* Creator) : Command(Creator,"SANICK", 2)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <new-nick>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ flags_needed = 'o'; syntax = "<nick> <new-nick>";
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* target = ServerInstance->FindNick(parameters[0]);
/* Do local sanity checks and bails */
if (IS_LOCAL(user))
{
- if (target && ServerInstance->ULine(target->server))
+ if (target && target->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
return CMD_FAILURE;
}
- if (!ServerInstance->IsNick(parameters[1].c_str(), ServerInstance->Config->Limits.NickMax))
+ if (!ServerInstance->IsNick(parameters[1]))
{
- user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNotice("*** Invalid nickname '" + parameters[1] + "'");
return CMD_FAILURE;
}
}
@@ -66,7 +64,7 @@ class CommandSanick : public Command
{
std::string oldnick = user->nick;
std::string newnick = target->nick;
- if (target->ChangeNick(parameters[1], true))
+ if (target->ChangeNick(parameters[1]))
{
ServerInstance->SNO->WriteGlobalSno('a', oldnick+" used SANICK to change "+newnick+" to "+parameters[1]);
}
@@ -79,12 +77,9 @@ class CommandSanick : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -98,20 +93,10 @@ class ModuleSanick : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSanick()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for SANICK command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSanick)
diff --git a/src/modules/m_sapart.cpp b/src/modules/m_sapart.cpp
index 89256e0e4..0cd82fa15 100644
--- a/src/modules/m_sapart.cpp
+++ b/src/modules/m_sapart.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides command SAPART to force-part users from a channel. */
-
/** Handle /SAPART
*/
class CommandSapart : public Command
@@ -30,12 +28,15 @@ class CommandSapart : public Command
public:
CommandSapart(Module* Creator) : Command(Creator,"SAPART", 2, 3)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel> [reason]";
- TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
+ flags_needed = 'o'; syntax = "<nick> <channel>[,<channel>] [reason]";
+ TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
+ if (CommandParser::LoopCall(user, this, parameters, 1))
+ return CMD_FAILURE;
+
User* dest = ServerInstance->FindNick(parameters[0]);
Channel* channel = ServerInstance->FindChan(parameters[1]);
std::string reason;
@@ -45,9 +46,15 @@ class CommandSapart : public Command
if (parameters.size() > 2)
reason = parameters[2];
- if (ServerInstance->ULine(dest->server))
+ if (dest->server->IsULine())
+ {
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
+ return CMD_FAILURE;
+ }
+
+ if (!channel->HasUser(dest))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNotice("*** " + dest->nick + " is not on " + channel->name);
return CMD_FAILURE;
}
@@ -58,44 +65,22 @@ class CommandSapart : public Command
if (IS_LOCAL(dest))
{
channel->PartUser(dest, reason);
-
- Channel* n = ServerInstance->FindChan(parameters[1]);
- if (!n)
- {
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+parameters[1]);
- return CMD_SUCCESS;
- }
- else
- {
- if (!n->HasUser(dest))
- {
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+parameters[1]);
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteServ("NOTICE %s :*** Unable to make %s part %s",user->nick.c_str(), dest->nick.c_str(), parameters[1].c_str());
- return CMD_FAILURE;
- }
- }
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+channel->name);
}
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick.c_str());
+ user->WriteNotice("*** Invalid nickname or channel");
}
return CMD_FAILURE;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -109,21 +94,10 @@ class ModuleSapart : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSapart()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command SAPART to force-part users from a channel.", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSapart)
-
diff --git a/src/modules/m_saquit.cpp b/src/modules/m_saquit.cpp
index 909a026ab..9034016d2 100644
--- a/src/modules/m_saquit.cpp
+++ b/src/modules/m_saquit.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */
-
/** Handle /SAQUIT
*/
class CommandSaquit : public Command
@@ -30,25 +28,25 @@ class CommandSaquit : public Command
public:
CommandSaquit(Module* Creator) : Command(Creator, "SAQUIT", 2, 2)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <reason>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ flags_needed = 'o'; syntax = "<nick> <reason>";
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* dest = ServerInstance->FindNick(parameters[0]);
- if ((dest) && (!IS_SERVER(dest)) && (dest->registered == REG_ALL))
+ if ((dest) && (dest->registered == REG_ALL))
{
- if (ServerInstance->ULine(dest->server))
+ if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
// Pass the command on, so the client's server can quit it properly.
if (!IS_LOCAL(dest))
return CMD_SUCCESS;
-
+
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAQUIT to make "+dest->nick+" quit with a reason of "+parameters[1]);
ServerInstance->Users->QuitUser(dest, parameters[1]);
@@ -56,17 +54,14 @@ class CommandSaquit : public Command
}
else
{
- user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** Invalid nickname '" + parameters[0] + "'");
return CMD_FAILURE;
}
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -79,20 +74,10 @@ class ModuleSaquit : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSaquit()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for an SAQUIT command, exits user with a reason", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSaquit)
diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp
index 0ef93ec5a..480f8f6db 100644
--- a/src/modules/m_sasl.cpp
+++ b/src/modules/m_sasl.cpp
@@ -19,26 +19,146 @@
#include "inspircd.h"
-#include "m_cap.h"
-#include "account.h"
-#include "sasl.h"
-#include "ssl.h"
+#include "modules/cap.h"
+#include "modules/account.h"
+#include "modules/sasl.h"
+#include "modules/ssl.h"
+#include "modules/server.h"
-/* $ModDesc: Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE. */
+enum
+{
+ // From IRCv3 sasl-3.1
+ RPL_SASLSUCCESS = 903,
+ ERR_SASLFAIL = 904,
+ ERR_SASLTOOLONG = 905,
+ ERR_SASLABORTED = 906,
+ RPL_SASLMECHS = 908
+};
+
+static std::string sasl_target;
+
+class ServerTracker : public ServerEventListener
+{
+ bool online;
+
+ void Update(const Server* server, bool linked)
+ {
+ if (sasl_target == "*")
+ return;
+
+ if (InspIRCd::Match(server->GetName(), sasl_target))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_VERBOSE, "SASL target server \"%s\" %s", sasl_target.c_str(), (linked ? "came online" : "went offline"));
+ online = linked;
+ }
+ }
+
+ void OnServerLink(const Server* server) CXX11_OVERRIDE
+ {
+ Update(server, true);
+ }
+
+ void OnServerSplit(const Server* server) CXX11_OVERRIDE
+ {
+ Update(server, false);
+ }
+
+ public:
+ ServerTracker(Module* mod)
+ : ServerEventListener(mod)
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ if (sasl_target == "*")
+ {
+ online = true;
+ return;
+ }
+
+ online = false;
+
+ ProtocolInterface::ServerList servers;
+ ServerInstance->PI->GetServerList(servers);
+ for (ProtocolInterface::ServerList::const_iterator i = servers.begin(); i != servers.end(); ++i)
+ {
+ const ProtocolInterface::ServerInfo& server = *i;
+ if (InspIRCd::Match(server.servername, sasl_target))
+ {
+ online = true;
+ break;
+ }
+ }
+ }
+
+ bool IsOnline() const { return online; }
+};
+
+class SASLCap : public Cap::Capability
+{
+ std::string mechlist;
+ const ServerTracker& servertracker;
+
+ bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
+ {
+ // Requesting this cap is allowed anytime
+ if (adding)
+ return true;
+
+ // But removing it can only be done when unregistered
+ return (user->registered != REG_ALL);
+ }
+
+ bool OnList(LocalUser* user) CXX11_OVERRIDE
+ {
+ return servertracker.IsOnline();
+ }
+
+ const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
+ {
+ return &mechlist;
+ }
+
+ public:
+ SASLCap(Module* mod, const ServerTracker& tracker)
+ : Cap::Capability(mod, "sasl")
+ , servertracker(tracker)
+ {
+ }
+
+ void SetMechlist(const std::string& newmechlist)
+ {
+ if (mechlist == newmechlist)
+ return;
+
+ mechlist = newmechlist;
+ NotifyValueChange();
+ }
+};
enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
-static std::string sasl_target = "*";
+static Events::ModuleEventProvider* saslevprov;
-static void SendSASL(const parameterlist& params)
+static void SendSASL(LocalUser* user, const std::string& agent, char mode, const std::vector<std::string>& parameters)
{
- if (!ServerInstance->PI->SendEncapsulatedData(params))
+ CommandBase::Params params;
+ params.push_back(user->uuid);
+ params.push_back(agent);
+ params.push_back(ConvToStr(mode));
+ params.insert(params.end(), parameters.begin(), parameters.end());
+
+ if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
{
- SASLFallback(NULL, params);
+ FOREACH_MOD_CUSTOM(*saslevprov, SASLEventListener, OnSASLAuth, (params));
}
}
+static ClientProtocol::EventProvider* g_protoev;
+
/**
* Tracks SASL authentication state like charybdis does. --nenolod
*/
@@ -46,95 +166,35 @@ class SaslAuthenticator
{
private:
std::string agent;
- User *user;
+ LocalUser* user;
SaslState state;
SaslResult result;
bool state_announced;
- /* taken from m_services_account */
- static bool ReadCGIIRCExt(const char* extname, User* user, std::string& out)
- {
- ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
- if (!wiext)
- return false;
-
- if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
- return false;
-
- StringExtItem* stringext = static_cast<StringExtItem*>(wiext);
- std::string* addr = stringext->get(user);
- if (!addr)
- return false;
-
- out = *addr;
- return true;
- }
-
-
void SendHostIP()
{
- std::string host, ip;
-
- if (!ReadCGIIRCExt("cgiirc_webirc_hostname", user, host))
- {
- host = user->host;
- }
- if (!ReadCGIIRCExt("cgiirc_webirc_ip", user, ip))
- {
- ip = user->GetIPString();
- }
- else
- {
- /* IP addresses starting with a : on irc are a Bad Thing (tm) */
- if (ip.c_str()[0] == ':')
- ip.insert(ip.begin(),1,'0');
- }
-
- parameterlist params;
- params.push_back(sasl_target);
- params.push_back("SASL");
- params.push_back(user->uuid);
- params.push_back("*");
- params.push_back("H");
- params.push_back(host);
- params.push_back(ip);
-
- LocalUser* lu = IS_LOCAL(user);
- if (lu)
- {
- // NOTE: SaslAuthenticator instances are only created for local
- // users so this parameter will always be appended.
- SocketCertificateRequest req(&lu->eh, ServerInstance->Modules->Find("m_sasl.so"));
- params.push_back(req.cert ? "S" : "P");
- }
+ std::vector<std::string> params;
+ params.push_back(user->GetRealHost());
+ params.push_back(user->GetIPString());
+ params.push_back(SSLIOHook::IsSSL(&user->eh) ? "S" : "P");
- SendSASL(params);
+ SendSASL(user, "*", 'H', params);
}
public:
- SaslAuthenticator(User* user_, const std::string& method)
+ SaslAuthenticator(LocalUser* user_, const std::string& method)
: user(user_), state(SASL_INIT), state_announced(false)
{
SendHostIP();
- parameterlist params;
- params.push_back(sasl_target);
- params.push_back("SASL");
- params.push_back(user->uuid);
- params.push_back("*");
- params.push_back("S");
+ std::vector<std::string> params;
params.push_back(method);
- if (method == "EXTERNAL" && IS_LOCAL(user_))
- {
- SocketCertificateRequest req(&((LocalUser*)user_)->eh, ServerInstance->Modules->Find("m_sasl.so"));
- std::string fp = req.GetFingerprint();
-
- if (fp.size())
- params.push_back(fp);
- }
+ const std::string fp = SSLClientCert::GetFingerprint(&user->eh);
+ if (fp.size())
+ params.push_back(fp);
- SendSASL(params);
+ SendSASL(user, "*", 'S', params);
}
SaslResult GetSaslResult(const std::string &result_)
@@ -149,7 +209,7 @@ class SaslAuthenticator
}
/* checks for and deals with a state change. */
- SaslState ProcessInboundMessage(const std::vector<std::string> &msg)
+ SaslState ProcessInboundMessage(const CommandBase::Params& msg)
{
switch (this->state)
{
@@ -165,53 +225,47 @@ class SaslAuthenticator
return this->state;
if (msg[2] == "C")
- this->user->Write("AUTHENTICATE %s", msg[3].c_str());
+ {
+ ClientProtocol::Message authmsg("AUTHENTICATE");
+ authmsg.PushParamRef(msg[3]);
+
+ ClientProtocol::Event authevent(*g_protoev, authmsg);
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (localuser)
+ localuser->Send(authevent);
+ }
else if (msg[2] == "D")
{
this->state = SASL_DONE;
this->result = this->GetSaslResult(msg[3]);
}
else if (msg[2] == "M")
- this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
+ this->user->WriteNumeric(RPL_SASLMECHS, msg[3], "are available SASL mechanisms");
else
- ServerInstance->Logs->Log("m_sasl", DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
break;
case SASL_DONE:
break;
default:
- ServerInstance->Logs->Log("m_sasl", DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
break;
}
return this->state;
}
- void Abort(void)
- {
- this->state = SASL_DONE;
- this->result = SASL_ABORT;
- }
-
bool SendClientMessage(const std::vector<std::string>& parameters)
{
if (this->state != SASL_COMM)
return true;
- parameterlist params;
- params.push_back(sasl_target);
- params.push_back("SASL");
- params.push_back(this->user->uuid);
- params.push_back(this->agent);
- params.push_back("C");
-
- params.insert(params.end(), parameters.begin(), parameters.end());
-
- SendSASL(params);
+ SendSASL(this->user, this->agent, 'C', parameters);
if (parameters[0].c_str()[0] == '*')
{
- this->Abort();
+ this->state = SASL_DONE;
+ this->result = SASL_ABORT;
return false;
}
@@ -226,13 +280,13 @@ class SaslAuthenticator
switch (this->result)
{
case SASL_OK:
- this->user->WriteNumeric(903, "%s :SASL authentication successful", this->user->nick.c_str());
+ this->user->WriteNumeric(RPL_SASLSUCCESS, "SASL authentication successful");
break;
case SASL_ABORT:
- this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
+ this->user->WriteNumeric(ERR_SASLABORTED, "SASL authentication aborted");
break;
case SASL_FAIL:
- this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
+ this->user->WriteNumeric(ERR_SASLFAIL, "SASL authentication failed");
break;
default:
break;
@@ -242,29 +296,39 @@ class SaslAuthenticator
}
};
-class CommandAuthenticate : public Command
+class CommandAuthenticate : public SplitCommand
{
+ private:
+ // The maximum length of an AUTHENTICATE request.
+ static const size_t MAX_AUTHENTICATE_SIZE = 400;
+
public:
SimpleExtItem<SaslAuthenticator>& authExt;
- GenericCap& cap;
- CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, GenericCap& Cap)
- : Command(Creator, "AUTHENTICATE", 1), authExt(ext), cap(Cap)
+ Cap::Capability& cap;
+ CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, Cap::Capability& Cap)
+ : SplitCommand(Creator, "AUTHENTICATE", 1)
+ , authExt(ext)
+ , cap(Cap)
{
works_before_reg = true;
allow_empty_last_param = false;
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
- /* Only allow AUTHENTICATE on unregistered clients */
- if (user->registered != REG_ALL)
{
- if (!cap.ext.get(user))
+ if (!cap.get(user))
return CMD_FAILURE;
if (parameters[0].find(' ') != std::string::npos || parameters[0][0] == ':')
return CMD_FAILURE;
+ if (parameters[0].length() > MAX_AUTHENTICATE_SIZE)
+ {
+ user->WriteNumeric(ERR_SASLTOOLONG, "SASL message too long");
+ return CMD_FAILURE;
+ }
+
SaslAuthenticator *sasl = authExt.get(user);
if (!sasl)
authExt.set(user, new SaslAuthenticator(user, parameters[0]));
@@ -287,12 +351,12 @@ class CommandSASL : public Command
this->flags_needed = FLAG_SERVERONLY; // should not be called by users
}
- CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* target = ServerInstance->FindNick(parameters[1]);
- if ((!target) || (IS_SERVER(target)))
+ User* target = ServerInstance->FindUUID(parameters[1]);
+ if (!target)
{
- ServerInstance->Logs->Log("m_sasl", DEBUG,"User not found in sasl ENCAP event: %s", parameters[1].c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User not found in sasl ENCAP event: %s", parameters[1].c_str());
return CMD_FAILURE;
}
@@ -309,7 +373,7 @@ class CommandSASL : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
@@ -318,51 +382,52 @@ class CommandSASL : public Command
class ModuleSASL : public Module
{
SimpleExtItem<SaslAuthenticator> authExt;
- GenericCap cap;
+ ServerTracker servertracker;
+ SASLCap cap;
CommandAuthenticate auth;
CommandSASL sasl;
+ Events::ModuleEventProvider sasleventprov;
+ ClientProtocol::EventProvider protoev;
+
public:
ModuleSASL()
- : authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
+ : authExt("sasl_auth", ExtensionItem::EXT_USER, this)
+ , servertracker(this)
+ , cap(this, servertracker)
+ , auth(this, authExt, cap)
+ , sasl(this, authExt)
+ , sasleventprov(this, "event/sasl")
+ , protoev(this, auth.name)
{
+ saslevprov = &sasleventprov;
+ g_protoev = &protoev;
}
- void init()
+ void init() CXX11_OVERRIDE
{
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnEvent, I_OnUserConnect, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- ServiceProvider* providelist[] = { &auth, &sasl, &authExt };
- ServerInstance->Modules->AddServices(providelist, 3);
-
if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
- ServerInstance->Logs->Log("m_sasl", DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: m_services_account and m_cap are not loaded! m_sasl will NOT function correctly until these two modules are loaded!");
}
- void OnRehash(User*)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
- }
+ std::string target = ServerInstance->Config->ConfValue("sasl")->getString("target");
+ if (target.empty())
+ throw ModuleException("<sasl:target> must be set to the name of your services server!");
- void OnUserConnect(LocalUser *user)
- {
- SaslAuthenticator *sasl_ = authExt.get(user);
- if (sasl_)
- {
- sasl_->Abort();
- authExt.unset(user);
- }
+ sasl_target = target;
+ servertracker.Reset();
}
- Version GetVersion()
+ void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string& extdata) CXX11_OVERRIDE
{
- return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
+ if ((target == NULL) && (extname == "saslmechlist"))
+ cap.SetMechlist(extdata);
}
- void OnEvent(Event &ev)
+ Version GetVersion() CXX11_OVERRIDE
{
- cap.HandleEvent(ev);
+ return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
}
};
diff --git a/src/modules/m_satopic.cpp b/src/modules/m_satopic.cpp
index ae1c19d91..8c8629221 100644
--- a/src/modules/m_satopic.cpp
+++ b/src/modules/m_satopic.cpp
@@ -17,8 +17,6 @@
*/
-/* $ModDesc: Provides a SATOPIC command */
-
#include "inspircd.h"
/** Handle /SATOPIC
@@ -28,10 +26,10 @@ class CommandSATopic : public Command
public:
CommandSATopic(Module* Creator) : Command(Creator,"SATOPIC", 2, 2)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<target> <topic>";
+ flags_needed = 'o'; syntax = "<target> <topic>";
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
/*
* Handles a SATOPIC request. Notifies all +s users.
@@ -40,17 +38,21 @@ class CommandSATopic : public Command
if(target)
{
- std::string newTopic = parameters[1];
+ const std::string newTopic(parameters[1], 0, ServerInstance->Config->Limits.MaxTopic);
+ if (target->topic == newTopic)
+ {
+ user->WriteNotice(InspIRCd::Format("The topic on %s is already what you are trying to change it to.", target->name.c_str()));
+ return CMD_SUCCESS;
+ }
- // 3rd parameter overrides access checks
- target->SetTopic(user, newTopic, true);
+ target->SetTopic(user, newTopic, ServerInstance->Time(), NULL);
ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SATOPIC on " + target->name + ", new topic: " + newTopic);
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
return CMD_FAILURE;
}
}
@@ -65,16 +67,7 @@ class ModuleSATopic : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSATopic()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a SATOPIC command", VF_VENDOR);
}
diff --git a/src/modules/m_securelist.cpp b/src/modules/m_securelist.cpp
index 5d11d27f7..aa14707b1 100644
--- a/src/modules/m_securelist.cpp
+++ b/src/modules/m_securelist.cpp
@@ -20,32 +20,21 @@
#include "inspircd.h"
-
-/* $ModDesc: Disallows /LIST for recently connected clients to hinder spam bots */
+#include "modules/account.h"
class ModuleSecureList : public Module
{
- private:
std::vector<std::string> allowlist;
- time_t WaitTime;
- public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
+ bool exemptregistered;
+ unsigned int WaitTime;
- virtual ~ModuleSecureList()
- {
- }
-
- virtual Version GetVersion()
+ public:
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Disallows /LIST for recently connected clients to hinder spam bots", VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
allowlist.clear();
@@ -53,7 +42,9 @@ class ModuleSecureList : public Module
for (ConfigIter i = tags.first; i != tags.second; ++i)
allowlist.push_back(i->second->getString("exception"));
- WaitTime = ServerInstance->Config->ConfValue("securelist")->getInt("waittime", 60);
+ ConfigTag* tag = ServerInstance->Config->ConfValue("securelist");
+ exemptregistered = tag->getBool("exemptregistered");
+ WaitTime = tag->getDuration("waittime", 60, 1);
}
@@ -61,34 +52,38 @@ class ModuleSecureList : public Module
* OnPreCommand()
* Intercept the LIST command.
*/
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return MOD_RES_PASSTHRU;
- if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user)))
+ if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!user->IsOper()))
{
/* Normally wouldnt be allowed here, are they exempt? */
for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++)
if (InspIRCd::Match(user->MakeHost(), *x, ascii_case_insensitive_map))
return MOD_RES_PASSTHRU;
+ const AccountExtItem* ext = GetAccountExtItem();
+ if (exemptregistered && ext && ext->get(user))
+ return MOD_RES_PASSTHRU;
+
/* Not exempt, BOOK EM DANNO! */
- user->WriteServ("NOTICE %s :*** You cannot list within the first %lu seconds of connecting. Please try again later.",user->nick.c_str(), (unsigned long) WaitTime);
+ user->WriteNotice("*** You cannot list within the first " + ConvToStr(WaitTime) + " seconds of connecting. Please try again later.");
/* Some clients (e.g. mIRC, various java chat applets) muck up if they don't
* receive these numerics whenever they send LIST, so give them an empty LIST to mull over.
*/
- user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
- user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
+ user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+ user->WriteNumeric(RPL_LISTEND, "End of channel list.");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" SECURELIST");
+ tokens["SECURELIST"];
}
};
diff --git a/src/modules/m_seenicks.cpp b/src/modules/m_seenicks.cpp
index 95872b5b2..bff3516f1 100644
--- a/src/modules/m_seenicks.cpp
+++ b/src/modules/m_seenicks.cpp
@@ -21,24 +21,20 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks 'n' and 'N'. */
-
class ModuleSeeNicks : public Module
{
public:
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->SNO->EnableSnomask('n',"NICK");
- Implementation eventlist[] = { I_OnUserPostNick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for seeing local and remote nickchanges via snomasks", VF_VENDOR);
}
- virtual void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick.c_str());
}
diff --git a/src/modules/m_serverban.cpp b/src/modules/m_serverban.cpp
index cf77ae9ba..f51d1d373 100644
--- a/src/modules/m_serverban.cpp
+++ b/src/modules/m_serverban.cpp
@@ -19,43 +19,28 @@
#include "inspircd.h"
-/* $ModDesc: Implements extban +b s: - server name bans */
-
class ModuleServerBan : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleServerBan()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Extban 's' - server ban",VF_OPTCOMMON|VF_VENDOR);
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 's') && (mask[1] == ':'))
{
- if (InspIRCd::Match(user->server, mask.substr(2)))
+ if (InspIRCd::Match(user->server->GetName(), mask.substr(2)))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('s');
+ tokens["EXTBAN"].push_back('s');
}
};
-
MODULE_INIT(ModuleServerBan)
-
diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp
index 154968e9e..c3b84dbba 100644
--- a/src/modules/m_services_account.cpp
+++ b/src/modules/m_services_account.cpp
@@ -22,10 +22,26 @@
*/
-/* $ModDesc: Provides support for ircu-style services accounts, including chmode +R, etc. */
-
#include "inspircd.h"
-#include "account.h"
+#include "modules/account.h"
+#include "modules/exemption.h"
+#include "modules/whois.h"
+
+enum
+{
+ // From UnrealIRCd.
+ RPL_WHOISREGNICK = 307,
+
+ // From ircu.
+ RPL_WHOISACCOUNT = 330,
+
+ // From ircd-hybrid?
+ ERR_NEEDREGGEDNICK = 477,
+
+ // From IRCv3 sasl-3.1.
+ RPL_LOGGEDIN = 900,
+ RPL_LOGGEDOUT = 901
+};
/** Channel mode +r - mark a channel as identified
*/
@@ -34,21 +50,21 @@ class Channel_r : public ModeHandler
public:
Channel_r(Module* Creator) : ModeHandler(Creator, "c_registered", 'r', PARAM_NONE, MODETYPE_CHANNEL) { }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
// only a u-lined server may add or remove the +r mode.
if (!IS_LOCAL(source))
{
// Only change the mode if it's not redundant
- if ((adding != channel->IsModeSet('r')))
+ if ((adding != channel->IsModeSet(this)))
{
- channel->SetMode('r',adding);
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
}
else
{
- source->WriteNumeric(500, "%s :Only a server may modify the +r channel mode", source->nick.c_str());
+ source->WriteNumeric(ERR_NOPRIVILEGES, "Only a server may modify the +r channel mode");
}
return MODEACTION_DENY;
}
@@ -62,128 +78,118 @@ class User_r : public ModeHandler
public:
User_r(Module* Creator) : ModeHandler(Creator, "u_registered", 'r', PARAM_NONE, MODETYPE_USER) { }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
if (!IS_LOCAL(source))
{
- if ((adding != dest->IsModeSet('r')))
+ if ((adding != dest->IsModeSet(this)))
{
- dest->SetMode('r',adding);
+ dest->SetMode(this, adding);
return MODEACTION_ALLOW;
}
}
else
{
- source->WriteNumeric(500, "%s :Only a server may modify the +r user mode", source->nick.c_str());
+ source->WriteNumeric(ERR_NOPRIVILEGES, "Only a server may modify the +r user mode");
}
return MODEACTION_DENY;
}
};
-/** Channel mode +R - unidentified users cannot join
- */
-class AChannel_R : public SimpleChannelModeHandler
-{
- public:
- AChannel_R(Module* Creator) : SimpleChannelModeHandler(Creator, "reginvite", 'R') { }
-};
-
-/** User mode +R - unidentified users cannot message
- */
-class AUser_R : public SimpleUserModeHandler
+class AccountExtItemImpl : public AccountExtItem
{
- public:
- AUser_R(Module* Creator) : SimpleUserModeHandler(Creator, "regdeaf", 'R') { }
-};
+ Events::ModuleEventProvider eventprov;
-/** Channel mode +M - unidentified users cannot message channel
- */
-class AChannel_M : public SimpleChannelModeHandler
-{
public:
- AChannel_M(Module* Creator) : SimpleChannelModeHandler(Creator, "regmoderated", 'M') { }
-};
-
-class ModuleServicesAccount : public Module
-{
- AChannel_R m1;
- AChannel_M m2;
- AUser_R m3;
- Channel_r m4;
- User_r m5;
- AccountExtItem accountname;
- bool checking_ban;
+ AccountExtItemImpl(Module* mod)
+ : AccountExtItem("accountname", ExtensionItem::EXT_USER, mod)
+ , eventprov(mod, "event/account")
+ {
+ }
- static bool ReadCGIIRCExt(const char* extname, User* user, const std::string*& out)
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
{
- ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
- if (!wiext)
- return false;
+ User* user = static_cast<User*>(container);
- if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
- return false;
+ StringExtItem::unserialize(format, container, value);
- StringExtItem* stringext = static_cast<StringExtItem*>(wiext);
- std::string* addr = stringext->get(user);
- if (!addr)
- return false;
+ // If we are being reloaded then don't send the numeric or run the event
+ if (format == FORMAT_INTERNAL)
+ return;
- out = addr;
- return true;
- }
+ if (IS_LOCAL(user))
+ {
+ if (value.empty())
+ {
+ // Logged out.
+ user->WriteNumeric(RPL_LOGGEDOUT, user->GetFullHost(), "You are now logged out");
+ }
+ else
+ {
+ // Logged in.
+ user->WriteNumeric(RPL_LOGGEDIN, user->GetFullHost(), value, InspIRCd::Format("You are now logged in as %s", value.c_str()));
+ }
+ }
- public:
- ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this),
- accountname("accountname", this), checking_ban(false)
- {
+ FOREACH_MOD_CUSTOM(eventprov, AccountEventListener, OnAccountChange, (user, value));
}
+};
- void init()
+class ModuleServicesAccount : public Module, public Whois::EventListener
+{
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler m1;
+ SimpleChannelModeHandler m2;
+ SimpleUserModeHandler m3;
+ Channel_r m4;
+ User_r m5;
+ AccountExtItemImpl accountname;
+ bool checking_ban;
+ public:
+ ModuleServicesAccount()
+ : Whois::EventListener(this)
+ , exemptionprov(this)
+ , m1(this, "reginvite", 'R')
+ , m2(this, "regmoderated", 'M')
+ , m3(this, "regdeaf", 'R')
+ , m4(this)
+ , m5(this)
+ , accountname(this)
+ , checking_ban(false)
{
- ServiceProvider* providerlist[] = { &m1, &m2, &m3, &m4, &m5, &accountname };
- ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
- Implementation eventlist[] = { I_OnWhois, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreJoin, I_OnCheckBan,
- I_OnDecodeMetaData, I_On005Numeric, I_OnUserPostNick, I_OnSetConnectClass };
-
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void On005Numeric(std::string &t)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('R');
- ServerInstance->AddExtBanChar('U');
+ tokens["EXTBAN"].push_back('R');
+ tokens["EXTBAN"].push_back('U');
}
/* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
- void OnWhois(User* source, User* dest)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- std::string *account = accountname.get(dest);
+ std::string* account = accountname.get(whois.GetTarget());
if (account)
{
- ServerInstance->SendWhoisLine(source, dest, 330, "%s %s %s :is logged in as", source->nick.c_str(), dest->nick.c_str(), account->c_str());
+ whois.SendLine(RPL_WHOISACCOUNT, *account, "is logged in as");
}
- if (dest->IsModeSet('r'))
+ if (whois.GetTarget()->IsModeSet(m5))
{
/* user is registered */
- ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick.c_str(), dest->nick.c_str());
+ whois.SendLine(RPL_WHOISREGNICK, "is a registered nick");
}
}
- void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
/* On nickchange, if they have +r, remove it */
- if (user->IsModeSet('r') && assign(user->nick) != oldnick)
- {
- std::vector<std::string> modechange;
- modechange.push_back(user->nick);
- modechange.push_back("-r");
- ServerInstance->SendMode(modechange, ServerInstance->FakeClient);
- }
+ if ((user->IsModeSet(m5)) && (ServerInstance->FindNickOnly(oldnick) != user))
+ m5.RemoveMode(user);
}
- ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
@@ -191,33 +197,33 @@ class ModuleServicesAccount : public Module
std::string *account = accountname.get(user);
bool is_registered = account && !account->empty();
- if (target_type == TYPE_CHANNEL)
+ if (target.type == MessageTarget::TYPE_CHANNEL)
{
- Channel* c = (Channel*)dest;
- ModResult res = ServerInstance->OnCheckExemption(user,c,"regmoderated");
+ Channel* c = target.Get<Channel>();
+ ModResult res = CheckExemption::Call(exemptionprov, user, c, "regmoderated");
- if (c->IsModeSet('M') && !is_registered && res != MOD_RES_ALLOW)
+ if (c->IsModeSet(m2) && !is_registered && res != MOD_RES_ALLOW)
{
// user messaging a +M channel and is not registered
- user->WriteNumeric(477, user->nick+" "+c->name+" :You need to be identified to a registered account to message this channel");
+ user->WriteNumeric(ERR_NEEDREGGEDNICK, c->name, "You need to be identified to a registered account to message this channel");
return MOD_RES_DENY;
}
}
- else if (target_type == TYPE_USER)
+ else if (target.type == MessageTarget::TYPE_USER)
{
- User* u = (User*)dest;
+ User* u = target.Get<User>();
- if (u->IsModeSet('R') && !is_registered)
+ if (u->IsModeSet(m3) && !is_registered)
{
// user messaging a +R user and is not registered
- user->WriteNumeric(477, ""+ user->nick +" "+ u->nick +" :You need to be identified to a registered account to message this user");
+ user->WriteNumeric(ERR_NEEDREGGEDNICK, u->nick, "You need to be identified to a registered account to message this user");
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
+ ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
{
if (checking_ban)
return MOD_RES_PASSTHRU;
@@ -253,27 +259,19 @@ class ModuleServicesAccount : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
- {
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
std::string *account = accountname.get(user);
bool is_registered = account && !account->empty();
if (chan)
{
- if (chan->IsModeSet('R'))
+ if (chan->IsModeSet(m1))
{
if (!is_registered)
{
// joining a +R channel and not identified
- user->WriteNumeric(477, user->nick + " " + chan->name + " :You need to be identified to a registered account to join this channel");
+ user->WriteNumeric(ERR_NEEDREGGEDNICK, chan->name, "You need to be identified to a registered account to join this channel");
return MOD_RES_DENY;
}
}
@@ -281,56 +279,14 @@ class ModuleServicesAccount : public Module
return MOD_RES_PASSTHRU;
}
- // Whenever the linking module receives metadata from another server and doesnt know what
- // to do with it (of course, hence the 'meta') it calls this method, and it is up to each
- // module in turn to figure out if this metadata key belongs to them, and what they want
- // to do with it.
- // In our case we're only sending a single string around, so we just construct a std::string.
- // Some modules will probably get much more complex and format more detailed structs and classes
- // in a textual way for sending over the link.
- void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata)
- {
- User* dest = dynamic_cast<User*>(target);
- // check if its our metadata key, and its associated with a user
- if (dest && (extname == "accountname"))
- {
- std::string *account = accountname.get(dest);
- if (account && !account->empty())
- {
- trim(*account);
-
- if (IS_LOCAL(dest))
- {
- const std::string* host = &dest->dhost;
- if (dest->registered != REG_ALL)
- {
- if (!ReadCGIIRCExt("cgiirc_webirc_hostname", dest, host))
- {
- ReadCGIIRCExt("cgiirc_webirc_ip", dest, host);
- }
- }
-
- dest->WriteNumeric(900, "%s %s!%s@%s %s :You are now logged in as %s",
- dest->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), host->c_str(), account->c_str(), account->c_str());
- }
-
- AccountEvent(this, dest, *account).Send();
- }
- else
- {
- AccountEvent(this, dest, "").Send();
- }
- }
- }
-
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
if (myclass->config->getBool("requireaccount") && !accountname.get(user))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for ircu-style services accounts, including chmode +R, etc.",VF_OPTCOMMON|VF_VENDOR);
}
diff --git a/src/modules/m_servprotect.cpp b/src/modules/m_servprotect.cpp
index b4f2b5bbd..a5d7a82e4 100644
--- a/src/modules/m_servprotect.cpp
+++ b/src/modules/m_servprotect.cpp
@@ -20,8 +20,16 @@
#include "inspircd.h"
+#include "modules/whois.h"
-/* $ModDesc: Provides usermode +k to protect services from kicks, kills and mode changes. */
+enum
+{
+ // From AustHex.
+ RPL_WHOISSERVICE = 310,
+
+ // From UnrealIRCd.
+ ERR_KILLDENY = 485
+};
/** Handles user mode +k
*/
@@ -30,7 +38,7 @@ class ServProtectMode : public ModeHandler
public:
ServProtectMode(Module* Creator) : ModeHandler(Creator, "servprotect", 'k', PARAM_NONE, MODETYPE_USER) { oper = true; }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
/* Because this returns MODEACTION_DENY all the time, there is only ONE
* way to add this mode and that is at client introduction in the UID command,
@@ -44,47 +52,41 @@ class ServProtectMode : public ModeHandler
}
};
-class ModuleServProtectMode : public Module
+class ModuleServProtectMode : public Module, public Whois::EventListener, public Whois::LineEventListener
{
ServProtectMode bm;
public:
ModuleServProtectMode()
- : bm(this)
- {
- }
-
- void init()
+ : Whois::EventListener(this)
+ , Whois::LineEventListener(this)
+ , bm(this)
{
- ServerInstance->Modules->AddService(bm);
- Implementation eventlist[] = { I_OnWhois, I_OnKill, I_OnWhoisLine, I_OnRawMode, I_OnUserPreKick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
-
- ~ModuleServProtectMode()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides usermode +k to protect services from kicks, kills, and mode changes.", VF_VENDOR);
}
- void OnWhois(User* src, User* dst)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (dst->IsModeSet('k'))
+ if (whois.GetTarget()->IsModeSet(bm))
{
- ServerInstance->SendWhoisLine(src, dst, 310, src->nick+" "+dst->nick+" :is an "+ServerInstance->Config->Network+" Service");
+ whois.SendLine(RPL_WHOISSERVICE, "is a Network Service on " + ServerInstance->Config->Network);
}
}
- ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt)
+ ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
{
/* Check that the mode is not a server mode, it is being removed, the user making the change is local, there is a parameter,
* and the user making the change is not a uline
*/
- if (!adding && chan && IS_LOCAL(user) && !param.empty() && !ServerInstance->ULine(user->server))
+ if (!adding && chan && IS_LOCAL(user) && !param.empty())
{
+ const PrefixMode* const pm = mh->IsPrefixMode();
+ if (!pm)
+ return MOD_RES_PASSTHRU;
+
/* Check if the parameter is a valid nick/uuid
*/
User *u = ServerInstance->FindNick(param);
@@ -95,10 +97,10 @@ class ModuleServProtectMode : public Module
* This includes any prefix permission mode, even those registered in other modules, e.g. +qaohv. Using ::ModeString()
* here means that the number of modes is restricted to only modes the user has, limiting it to as short a loop as possible.
*/
- if (u->IsModeSet('k') && memb && memb->modes.find(mode) != std::string::npos)
+ if ((u->IsModeSet(bm)) && (memb) && (memb->HasMode(pm)))
{
/* BZZZT, Denied! */
- user->WriteNumeric(482, "%s %s :You are not permitted to remove privileges from %s services", user->nick.c_str(), chan->name.c_str(), ServerInstance->Config->Network.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You are not permitted to remove privileges from %s services", ServerInstance->Config->Network.c_str()));
return MOD_RES_DENY;
}
}
@@ -107,37 +109,35 @@ class ModuleServProtectMode : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnKill(User* src, User* dst, const std::string &reason)
+ ModResult OnKill(User* src, User* dst, const std::string &reason) CXX11_OVERRIDE
{
if (src == NULL)
return MOD_RES_PASSTHRU;
- if (dst->IsModeSet('k'))
+ if (dst->IsModeSet(bm))
{
- src->WriteNumeric(485, "%s :You are not permitted to kill %s services!", src->nick.c_str(), ServerInstance->Config->Network.c_str());
+ src->WriteNumeric(ERR_KILLDENY, InspIRCd::Format("You are not permitted to kill %s services!", ServerInstance->Config->Network.c_str()));
ServerInstance->SNO->WriteGlobalSno('a', src->nick+" tried to kill service "+dst->nick+" ("+reason+")");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreKick(User *src, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User *src, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
- if (memb->user->IsModeSet('k'))
+ if (memb->user->IsModeSet(bm))
{
- src->WriteNumeric(484, "%s %s :You are not permitted to kick services",
- src->nick.c_str(), memb->chan->name.c_str());
+ src->WriteNumeric(ERR_RESTRICTED, memb->chan->name, "You are not permitted to kick services");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnWhoisLine(User* src, User* dst, int &numeric, std::string &text)
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- return ((src != dst) && (numeric == 319) && dst->IsModeSet('k')) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ return ((numeric.GetNumeric() == 319) && whois.GetTarget()->IsModeSet(bm)) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
};
-
MODULE_INIT(ModuleServProtectMode)
diff --git a/src/modules/m_sethost.cpp b/src/modules/m_sethost.cpp
index 2ef0c0548..87eed4022 100644
--- a/src/modules/m_sethost.cpp
+++ b/src/modules/m_sethost.cpp
@@ -21,43 +21,40 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the SETHOST command */
-
/** Handle /SETHOST
*/
class CommandSethost : public Command
{
- private:
- char* hostmap;
public:
- CommandSethost(Module* Creator, char* hmap) : Command(Creator,"SETHOST", 1), hostmap(hmap)
+ std::bitset<UCHAR_MAX> hostmap;
+
+ CommandSethost(Module* Creator)
+ : Command(Creator,"SETHOST", 1)
{
allow_empty_last_param = false;
flags_needed = 'o'; syntax = "<new-hostname>";
- TRANSLATE2(TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- size_t len = 0;
- for (std::string::const_iterator x = parameters[0].begin(); x != parameters[0].end(); x++, len++)
+ if (parameters[0].length() > ServerInstance->Config->Limits.MaxHost)
{
- if (!hostmap[(const unsigned char)*x])
- {
- user->WriteServ("NOTICE "+user->nick+" :*** SETHOST: Invalid characters in hostname");
- return CMD_FAILURE;
- }
+ user->WriteNotice("*** SETHOST: Host too long");
+ return CMD_FAILURE;
}
- if (len > 64)
+ for (std::string::const_iterator x = parameters[0].begin(); x != parameters[0].end(); x++)
{
- user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick.c_str());
- return CMD_FAILURE;
+ if (!hostmap.test(static_cast<unsigned char>(*x)))
+ {
+ user->WriteNotice("*** SETHOST: Invalid characters in hostname");
+ return CMD_FAILURE;
+ }
}
- if (user->ChangeDisplayedHost(parameters[0].c_str()))
+ if (user->ChangeDisplayedHost(parameters[0]))
{
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SETHOST to change their displayed host to "+user->dhost);
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SETHOST to change their displayed host to "+user->GetDisplayedHost());
return CMD_SUCCESS;
}
@@ -69,39 +66,26 @@ class CommandSethost : public Command
class ModuleSetHost : public Module
{
CommandSethost cmd;
- char hostmap[256];
+
public:
ModuleSetHost()
- : cmd(this, hostmap)
- {
- }
-
- void init()
+ : cmd(this)
{
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
- memset(hostmap, 0, sizeof(hostmap));
+ cmd.hostmap.reset();
for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
- hostmap[(unsigned char)*n] = 1;
+ cmd.hostmap.set(static_cast<unsigned char>(*n));
}
- virtual ~ModuleSetHost()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the SETHOST command", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSetHost)
diff --git a/src/modules/m_setident.cpp b/src/modules/m_setident.cpp
index f63be1381..11ce8f015 100644
--- a/src/modules/m_setident.cpp
+++ b/src/modules/m_setident.cpp
@@ -22,8 +22,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the SETIDENT command */
-
/** Handle /SETIDENT
*/
class CommandSetident : public Command
@@ -33,31 +31,29 @@ class CommandSetident : public Command
{
allow_empty_last_param = false;
flags_needed = 'o'; syntax = "<new-ident>";
- TRANSLATE2(TR_TEXT, TR_END);
}
- CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
if (parameters[0].size() > ServerInstance->Config->Limits.IdentMax)
{
- user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick.c_str());
+ user->WriteNotice("*** SETIDENT: Ident is too long");
return CMD_FAILURE;
}
- if (!ServerInstance->IsIdent(parameters[0].c_str()))
+ if (!ServerInstance->IsIdent(parameters[0]))
{
- user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick.c_str());
+ user->WriteNotice("*** SETIDENT: Invalid characters in ident");
return CMD_FAILURE;
}
- user->ChangeIdent(parameters[0].c_str());
+ user->ChangeIdent(parameters[0]);
ServerInstance->SNO->WriteGlobalSno('a', "%s used SETIDENT to change their ident to '%s'", user->nick.c_str(), user->ident.c_str());
return CMD_SUCCESS;
}
};
-
class ModuleSetIdent : public Module
{
CommandSetident cmd;
@@ -67,21 +63,10 @@ class ModuleSetIdent : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSetIdent()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the SETIDENT command", VF_VENDOR);
}
-
};
-
MODULE_INIT(ModuleSetIdent)
diff --git a/src/modules/m_setidle.cpp b/src/modules/m_setidle.cpp
index fdb29d14f..37984030b 100644
--- a/src/modules/m_setidle.cpp
+++ b/src/modules/m_setidle.cpp
@@ -21,25 +21,29 @@
#include "inspircd.h"
-/* $ModDesc: Allows opers to set their idle time */
+enum
+{
+ // InspIRCd-specific.
+ ERR_INVALIDIDLETIME = 948,
+ RPL_IDLETIMESET = 944
+};
/** Handle /SETIDLE
*/
-class CommandSetidle : public Command
+class CommandSetidle : public SplitCommand
{
public:
- CommandSetidle(Module* Creator) : Command(Creator,"SETIDLE", 1)
+ CommandSetidle(Module* Creator) : SplitCommand(Creator,"SETIDLE", 1)
{
flags_needed = 'o'; syntax = "<duration>";
- TRANSLATE2(TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
- time_t idle = ServerInstance->Duration(parameters[0]);
+ int idle = InspIRCd::Duration(parameters[0]);
if (idle < 1)
{
- user->WriteNumeric(948, "%s :Invalid idle time.",user->nick.c_str());
+ user->WriteNumeric(ERR_INVALIDIDLETIME, "Invalid idle time.");
return CMD_FAILURE;
}
user->idle_lastmsg = (ServerInstance->Time() - idle);
@@ -47,7 +51,7 @@ class CommandSetidle : public Command
if (user->signon > user->idle_lastmsg)
user->signon = user->idle_lastmsg;
ServerInstance->SNO->WriteToSnoMask('a', user->nick+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds");
- user->WriteNumeric(944, "%s :Idle time set.",user->nick.c_str());
+ user->WriteNumeric(RPL_IDLETIMESET, "Idle time set.");
return CMD_SUCCESS;
}
@@ -63,16 +67,7 @@ class ModuleSetIdle : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSetIdle()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows opers to set their idle time", VF_VENDOR);
}
diff --git a/src/modules/m_setname.cpp b/src/modules/m_setname.cpp
index d0610853b..efe7028b7 100644
--- a/src/modules/m_setname.cpp
+++ b/src/modules/m_setname.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the SETNAME command */
-
class CommandSetname : public Command
@@ -31,21 +29,20 @@ class CommandSetname : public Command
CommandSetname(Module* Creator) : Command(Creator,"SETNAME", 1, 1)
{
allow_empty_last_param = false;
- syntax = "<new-gecos>";
- TRANSLATE2(TR_TEXT, TR_END);
+ syntax = "<new real name>";
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (parameters[0].size() > ServerInstance->Config->Limits.MaxGecos)
+ if (parameters[0].size() > ServerInstance->Config->Limits.MaxReal)
{
- user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick.c_str());
+ user->WriteNotice("*** SETNAME: Real name is too long");
return CMD_FAILURE;
}
- if (user->ChangeName(parameters[0].c_str()))
+ if (user->ChangeRealName(parameters[0]))
{
- ServerInstance->SNO->WriteGlobalSno('a', "%s used SETNAME to change their GECOS to '%s'", user->nick.c_str(), parameters[0].c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "%s used SETNAME to change their real name to '%s'", user->nick.c_str(), parameters[0].c_str());
}
return CMD_SUCCESS;
@@ -62,16 +59,7 @@ class ModuleSetName : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSetName()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the SETNAME command", VF_VENDOR);
}
diff --git a/src/modules/m_sha1.cpp b/src/modules/m_sha1.cpp
new file mode 100644
index 000000000..561a7b6cb
--- /dev/null
+++ b/src/modules/m_sha1.cpp
@@ -0,0 +1,199 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+union CHAR64LONG16
+{
+ unsigned char c[64];
+ uint32_t l[16];
+};
+
+inline static uint32_t rol(uint32_t value, uint32_t bits) { return (value << bits) | (value >> (32 - bits)); }
+
+// blk0() and blk() perform the initial expand.
+// I got the idea of expanding during the round function from SSLeay
+static bool big_endian;
+inline static uint32_t blk0(CHAR64LONG16& block, uint32_t i)
+{
+ if (big_endian)
+ return block.l[i];
+ else
+ return block.l[i] = (rol(block.l[i], 24) & 0xFF00FF00) | (rol(block.l[i], 8) & 0x00FF00FF);
+}
+inline static uint32_t blk(CHAR64LONG16 &block, uint32_t i) { return block.l[i & 15] = rol(block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15] ^ block.l[(i + 2) & 15] ^ block.l[i & 15],1); }
+
+// (R0+R1), R2, R3, R4 are the different operations used in SHA1
+inline static void R0(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); }
+inline static void R1(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); }
+inline static void R2(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); }
+inline static void R3(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); w = rol(w, 30); }
+inline static void R4(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30); }
+
+static const uint32_t sha1_iv[5] =
+{
+ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
+};
+
+class SHA1Context
+{
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+ unsigned char digest[20];
+
+ void Transform(const unsigned char buf[64])
+ {
+ uint32_t a, b, c, d, e;
+
+ CHAR64LONG16 block;
+ memcpy(block.c, buf, 64);
+
+ // Copy state[] to working vars
+ a = this->state[0];
+ b = this->state[1];
+ c = this->state[2];
+ d = this->state[3];
+ e = this->state[4];
+
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(block, a, b, c, d, e, 0); R0(block, e, a, b, c, d, 1); R0(block, d, e, a, b, c, 2); R0(block, c, d, e, a, b, 3);
+ R0(block, b, c, d, e, a, 4); R0(block, a, b, c, d, e, 5); R0(block, e, a, b, c, d, 6); R0(block, d, e, a, b, c, 7);
+ R0(block, c, d, e, a, b, 8); R0(block, b, c, d, e, a, 9); R0(block, a, b, c, d, e, 10); R0(block, e, a, b, c, d, 11);
+ R0(block, d, e, a, b, c, 12); R0(block, c, d, e, a, b, 13); R0(block, b, c, d, e, a, 14); R0(block, a, b, c, d, e, 15);
+ R1(block, e, a, b, c, d, 16); R1(block, d, e, a, b, c, 17); R1(block, c, d, e, a, b, 18); R1(block, b, c, d, e, a, 19);
+ R2(block, a, b, c, d, e, 20); R2(block, e, a, b, c, d, 21); R2(block, d, e, a, b, c, 22); R2(block, c, d, e, a, b, 23);
+ R2(block, b, c, d, e, a, 24); R2(block, a, b, c, d, e, 25); R2(block, e, a, b, c, d, 26); R2(block, d, e, a, b, c, 27);
+ R2(block, c, d, e, a, b, 28); R2(block, b, c, d, e, a, 29); R2(block, a, b, c, d, e, 30); R2(block, e, a, b, c, d, 31);
+ R2(block, d, e, a, b, c, 32); R2(block, c, d, e, a, b, 33); R2(block, b, c, d, e, a, 34); R2(block, a, b, c, d, e, 35);
+ R2(block, e, a, b, c, d, 36); R2(block, d, e, a, b, c, 37); R2(block, c, d, e, a, b, 38); R2(block, b, c, d, e, a, 39);
+ R3(block, a, b, c, d, e, 40); R3(block, e, a, b, c, d, 41); R3(block, d, e, a, b, c, 42); R3(block, c, d, e, a, b, 43);
+ R3(block, b, c, d, e, a, 44); R3(block, a, b, c, d, e, 45); R3(block, e, a, b, c, d, 46); R3(block, d, e, a, b, c, 47);
+ R3(block, c, d, e, a, b, 48); R3(block, b, c, d, e, a, 49); R3(block, a, b, c, d, e, 50); R3(block, e, a, b, c, d, 51);
+ R3(block, d, e, a, b, c, 52); R3(block, c, d, e, a, b, 53); R3(block, b, c, d, e, a, 54); R3(block, a, b, c, d, e, 55);
+ R3(block, e, a, b, c, d, 56); R3(block, d, e, a, b, c, 57); R3(block, c, d, e, a, b, 58); R3(block, b, c, d, e, a, 59);
+ R4(block, a, b, c, d, e, 60); R4(block, e, a, b, c, d, 61); R4(block, d, e, a, b, c, 62); R4(block, c, d, e, a, b, 63);
+ R4(block, b, c, d, e, a, 64); R4(block, a, b, c, d, e, 65); R4(block, e, a, b, c, d, 66); R4(block, d, e, a, b, c, 67);
+ R4(block, c, d, e, a, b, 68); R4(block, b, c, d, e, a, 69); R4(block, a, b, c, d, e, 70); R4(block, e, a, b, c, d, 71);
+ R4(block, d, e, a, b, c, 72); R4(block, c, d, e, a, b, 73); R4(block, b, c, d, e, a, 74); R4(block, a, b, c, d, e, 75);
+ R4(block, e, a, b, c, d, 76); R4(block, d, e, a, b, c, 77); R4(block, c, d, e, a, b, 78); R4(block, b, c, d, e, a, 79);
+ // Add the working vars back into state[]
+ this->state[0] += a;
+ this->state[1] += b;
+ this->state[2] += c;
+ this->state[3] += d;
+ this->state[4] += e;
+ }
+
+ public:
+ SHA1Context()
+ {
+ for (int i = 0; i < 5; ++i)
+ this->state[i] = sha1_iv[i];
+
+ this->count[0] = this->count[1] = 0;
+ memset(this->buffer, 0, sizeof(this->buffer));
+ memset(this->digest, 0, sizeof(this->digest));
+ }
+
+ void Update(const unsigned char* data, size_t len)
+ {
+ uint32_t i, j;
+
+ j = (this->count[0] >> 3) & 63;
+ if ((this->count[0] += len << 3) < (len << 3))
+ ++this->count[1];
+ this->count[1] += len >> 29;
+ if (j + len > 63)
+ {
+ memcpy(&this->buffer[j], data, (i = 64 - j));
+ this->Transform(this->buffer);
+ for (; i + 63 < len; i += 64)
+ this->Transform(&data[i]);
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&this->buffer[j], &data[i], len - i);
+ }
+
+ void Finalize()
+ {
+ uint32_t i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; ++i)
+ finalcount[i] = static_cast<unsigned char>((this->count[i >= 4 ? 0 : 1] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
+ this->Update(reinterpret_cast<const unsigned char *>("\200"), 1);
+ while ((this->count[0] & 504) != 448)
+ this->Update(reinterpret_cast<const unsigned char *>("\0"), 1);
+ this->Update(finalcount, 8); // Should cause a SHA1Transform()
+ for (i = 0; i < 20; ++i)
+ this->digest[i] = static_cast<unsigned char>((this->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255);
+
+ this->Transform(this->buffer);
+ }
+
+ std::string GetRaw() const
+ {
+ return std::string((const char*)digest, sizeof(digest));
+ }
+};
+
+class SHA1HashProvider : public HashProvider
+{
+ public:
+ SHA1HashProvider(Module* mod)
+ : HashProvider(mod, "hash/sha1", 20, 64)
+ {
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ SHA1Context ctx;
+ ctx.Update(reinterpret_cast<const unsigned char*>(data.data()), data.length());
+ ctx.Finalize();
+ return ctx.GetRaw();
+ }
+};
+
+class ModuleSHA1 : public Module
+{
+ SHA1HashProvider sha1;
+
+ public:
+ ModuleSHA1()
+ : sha1(this)
+ {
+ big_endian = (htonl(1337) == 1337);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Implements SHA-1 hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleSHA1)
diff --git a/src/modules/m_sha256.cpp b/src/modules/m_sha256.cpp
index 86970968a..d905ab9b4 100644
--- a/src/modules/m_sha256.cpp
+++ b/src/modules/m_sha256.cpp
@@ -56,17 +56,8 @@
* SUCH DAMAGE.
*/
-/* $ModDesc: Allows for SHA-256 encrypted oper passwords */
-
#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
-
-#ifndef HAS_STDINT
-typedef unsigned int uint32_t;
-#endif
+#include "modules/hash.h"
#define SHA256_DIGEST_SIZE (256 / 8)
#define SHA256_BLOCK_SIZE (512 / 8)
@@ -256,19 +247,14 @@ class HashSHA256 : public HashProvider
}
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
{
unsigned char bytes[SHA256_DIGEST_SIZE];
SHA256(data.data(), bytes, data.length());
return std::string((char*)bytes, SHA256_DIGEST_SIZE);
}
- std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
- {
- return "";
- }
-
- HashSHA256(Module* parent) : HashProvider(parent, "hash/sha256", 32, 64) {}
+ HashSHA256(Module* parent) : HashProvider(parent, "sha256", 32, 64) {}
};
class ModuleSHA256 : public Module
@@ -277,10 +263,9 @@ class ModuleSHA256 : public Module
public:
ModuleSHA256() : sha(this)
{
- ServerInstance->Modules->AddService(sha);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements SHA-256 hashing", VF_VENDOR);
}
diff --git a/src/modules/m_showfile.cpp b/src/modules/m_showfile.cpp
new file mode 100644
index 000000000..99c545140
--- /dev/null
+++ b/src/modules/m_showfile.cpp
@@ -0,0 +1,179 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+enum
+{
+ // From UnrealIRCd.
+ RPL_RULES = 232,
+ RPL_RULESTART = 308,
+ RPL_RULESEND = 309,
+ ERR_NORULES = 434
+};
+
+class CommandShowFile : public Command
+{
+ enum Method
+ {
+ SF_MSG,
+ SF_NOTICE,
+ SF_NUMERIC
+ };
+
+ std::string introtext;
+ std::string endtext;
+ unsigned int intronumeric;
+ unsigned int textnumeric;
+ unsigned int endnumeric;
+ file_cache contents;
+ Method method;
+
+ public:
+ CommandShowFile(Module* parent, const std::string& cmdname)
+ : Command(parent, cmdname)
+ {
+ }
+
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if (method == SF_NUMERIC)
+ {
+ if (!introtext.empty())
+ user->WriteRemoteNumeric(intronumeric, introtext);
+
+ for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+ user->WriteRemoteNumeric(textnumeric, InspIRCd::Format("- %s", i->c_str()));
+
+ user->WriteRemoteNumeric(endnumeric, endtext.c_str());
+ }
+ else if (IS_LOCAL(user))
+ {
+ LocalUser* const localuser = IS_LOCAL(user);
+ for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+ {
+ const std::string& line = *i;
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, line, ((method == SF_MSG) ? MSG_PRIVMSG : MSG_NOTICE));
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+ }
+ }
+ return CMD_SUCCESS;
+ }
+
+ void UpdateSettings(ConfigTag* tag, const std::vector<std::string>& filecontents)
+ {
+ introtext = tag->getString("introtext", "Showing " + name);
+ endtext = tag->getString("endtext", "End of " + name);
+ intronumeric = tag->getUInt("intronumeric", RPL_RULESTART, 0, 999);
+ textnumeric = tag->getUInt("numeric", RPL_RULES, 0, 999);
+ endnumeric = tag->getUInt("endnumeric", RPL_RULESEND, 0, 999);
+ std::string smethod = tag->getString("method");
+
+ method = SF_NUMERIC;
+ if (smethod == "msg")
+ method = SF_MSG;
+ else if (smethod == "notice")
+ method = SF_NOTICE;
+
+ contents = filecontents;
+ InspIRCd::ProcessColors(contents);
+ }
+};
+
+class ModuleShowFile : public Module
+{
+ std::vector<CommandShowFile*> cmds;
+
+ void ReadTag(ConfigTag* tag, std::vector<CommandShowFile*>& newcmds)
+ {
+ std::string cmdname = tag->getString("name");
+ if (cmdname.empty())
+ throw ModuleException("Empty value for 'name'");
+
+ std::transform(cmdname.begin(), cmdname.end(), cmdname.begin(), ::toupper);
+
+ const std::string file = tag->getString("file", cmdname);
+ if (file.empty())
+ throw ModuleException("Empty value for 'file'");
+ FileReader reader(file);
+
+ CommandShowFile* sfcmd;
+ Command* handler = ServerInstance->Parser.GetHandler(cmdname);
+ if (handler)
+ {
+ // Command exists, check if it is ours
+ if (handler->creator != this)
+ throw ModuleException("Command " + cmdname + " already exists");
+
+ // This is our command, make sure we don't have the same entry twice
+ sfcmd = static_cast<CommandShowFile*>(handler);
+ if (stdalgo::isin(newcmds, sfcmd))
+ throw ModuleException("Command " + cmdname + " is already used in a <showfile> tag");
+ }
+ else
+ {
+ // Command doesn't exist, create it
+ sfcmd = new CommandShowFile(this, cmdname);
+ ServerInstance->Modules->AddService(*sfcmd);
+ }
+
+ sfcmd->UpdateSettings(tag, reader.GetVector());
+ newcmds.push_back(sfcmd);
+ }
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ std::vector<CommandShowFile*> newcmds;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("showfile");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ try
+ {
+ ReadTag(tag, newcmds);
+ }
+ catch (CoreException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error: " + ex.GetReason() + " at " + tag->getTagLocation());
+ }
+ }
+
+ // Remove all commands that were removed from the config
+ std::vector<CommandShowFile*> removed(cmds.size());
+ std::sort(newcmds.begin(), newcmds.end());
+ std::set_difference(cmds.begin(), cmds.end(), newcmds.begin(), newcmds.end(), removed.begin());
+
+ stdalgo::delete_all(removed);
+ cmds.swap(newcmds);
+ }
+
+ ~ModuleShowFile()
+ {
+ stdalgo::delete_all(cmds);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for showing text files to users", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleShowFile)
diff --git a/src/modules/m_showwhois.cpp b/src/modules/m_showwhois.cpp
index 398ebf571..d01e1a83b 100644
--- a/src/modules/m_showwhois.cpp
+++ b/src/modules/m_showwhois.cpp
@@ -22,17 +22,21 @@
#include "inspircd.h"
-
-/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */
+#include "modules/whois.h"
/** Handle user mode +W
*/
class SeeWhois : public SimpleUserModeHandler
{
public:
- SeeWhois(Module* Creator, bool IsOpersOnly) : SimpleUserModeHandler(Creator, "showwhois", 'W')
+ SeeWhois(Module* Creator)
+ : SimpleUserModeHandler(Creator, "showwhois", 'W')
{
- oper = IsOpersOnly;
+ }
+
+ void SetOperOnly(bool operonly)
+ {
+ oper = operonly;
}
};
@@ -46,12 +50,12 @@ class WhoisNoticeCmd : public Command
void HandleFast(User* dest, User* src)
{
- dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you",
- dest->nick.c_str(), src->nick.c_str(), src->ident.c_str(),
- dest->HasPrivPermission("users/auspex") ? src->host.c_str() : src->dhost.c_str());
+ dest->WriteNotice("*** " + src->nick + " (" + src->ident + "@" +
+ src->GetHost(dest->HasPrivPermission("users/auspex")) +
+ ") did a /whois on you");
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* dest = ServerInstance->FindNick(parameters[0]);
if (!dest)
@@ -66,49 +70,42 @@ class WhoisNoticeCmd : public Command
}
};
-class ModuleShowwhois : public Module
+class ModuleShowwhois : public Module, public Whois::EventListener
{
bool ShowWhoisFromOpers;
- SeeWhois* sw;
+ SeeWhois sw;
WhoisNoticeCmd cmd;
public:
ModuleShowwhois()
- : sw(NULL), cmd(this)
+ : Whois::EventListener(this)
+ , sw(this)
+ , cmd(this)
{
}
- void init()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("showwhois");
- bool OpersOnly = tag->getBool("opersonly", true);
+ sw.SetOperOnly(tag->getBool("opersonly", true));
ShowWhoisFromOpers = tag->getBool("showfromopers", true);
-
- sw = new SeeWhois(this, OpersOnly);
- ServerInstance->Modules->AddService(*sw);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnWhois };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ~ModuleShowwhois()
- {
- delete sw;
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows opers to set +W to see when a user uses WHOIS on them",VF_OPTCOMMON|VF_VENDOR);
}
- void OnWhois(User* source, User* dest)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (!dest->IsModeSet('W') || source == dest)
+ User* const source = whois.GetSource();
+ User* const dest = whois.GetTarget();
+ if (!dest->IsModeSet(sw) || whois.IsSelfWhois())
return;
- if (!ShowWhoisFromOpers && IS_OPER(source))
+ if (!ShowWhoisFromOpers && source->IsOper())
return;
if (IS_LOCAL(dest))
@@ -117,15 +114,12 @@ class ModuleShowwhois : public Module
}
else
{
- std::vector<std::string> params;
- params.push_back(dest->server);
- params.push_back("WHOISNOTICE");
+ CommandBase::Params params;
params.push_back(dest->uuid);
params.push_back(source->uuid);
- ServerInstance->PI->SendEncapsulatedData(params);
+ ServerInstance->PI->SendEncapsulatedData(dest->server->GetName(), cmd.name, params);
}
}
-
};
MODULE_INIT(ModuleShowwhois)
diff --git a/src/modules/m_shun.cpp b/src/modules/m_shun.cpp
index 98e63f026..da090e4f8 100644
--- a/src/modules/m_shun.cpp
+++ b/src/modules/m_shun.cpp
@@ -22,54 +22,9 @@
#include "inspircd.h"
#include "xline.h"
+#include "modules/shun.h"
+#include "modules/stats.h"
-/* $ModDesc: Provides the /SHUN command, which stops a user from executing all except configured commands. */
-
-class Shun : public XLine
-{
-public:
- std::string matchtext;
-
- Shun(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& shunmask)
- : XLine(s_time, d, src, re, "SHUN")
- , matchtext(shunmask)
- {
- }
-
- ~Shun()
- {
- }
-
- bool Matches(User *u)
- {
- // E: overrides shun
- if (u->exempt)
- return false;
-
- if (InspIRCd::Match(u->GetFullHost(), matchtext) || InspIRCd::Match(u->GetFullRealHost(), matchtext) || InspIRCd::Match(u->nick+"!"+u->ident+"@"+u->GetIPString(), matchtext))
- return true;
-
- return false;
- }
-
- bool Matches(const std::string &s)
- {
- if (matchtext == s)
- return true;
- return false;
- }
-
- void DisplayExpiry()
- {
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired shun %s (set by %s %ld seconds ago)",
- this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
- }
-
- const char* Displayable()
- {
- return matchtext.c_str();
- }
-};
/** An XLineFactory specialized to generate shun pointers
*/
@@ -80,12 +35,12 @@ class ShunFactory : public XLineFactory
/** Generate a shun
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
return new Shun(set_time, duration, source, reason, xline_specific_mask);
}
- bool AutoApplyToUserList(XLine *x)
+ bool AutoApplyToUserList(XLine* x) CXX11_OVERRIDE
{
return false;
}
@@ -101,13 +56,13 @@ class CommandShun : public Command
flags_needed = 'o'; this->syntax = "<nick!user@hostmask> [<shun-duration>] :<reason>";
}
- CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
/* syntax: SHUN nick!user@host time :reason goes here */
/* 'time' is a human-readable timestring, like 2d3h2s. */
std::string target = parameters[0];
-
+
User *find = ServerInstance->FindNick(target);
if ((find) && (find->registered == REG_ALL))
target = std::string("*!*@") + find->GetIPString();
@@ -120,22 +75,22 @@ class CommandShun : public Command
}
else if (ServerInstance->XLines->DelLine(target.c_str(), "SHUN", user))
{
- ServerInstance->SNO->WriteToSnoMask('x',"%s removed SHUN on %s",user->nick.c_str(),target.c_str());
+ ServerInstance->SNO->WriteToSnoMask('x',"%s removed SHUN on %s", user->nick.c_str(), target.c_str());
}
else
{
- user->WriteServ("NOTICE %s :*** Shun %s not found in list, try /stats H.", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** Shun " + parameters[0] + " not found in list, try /stats H.");
return CMD_FAILURE;
}
}
else
{
// Adding - XXX todo make this respect <insane> tag perhaps..
- long duration;
+ unsigned long duration;
std::string expr;
if (parameters.size() > 2)
{
- duration = ServerInstance->Duration(parameters[1]);
+ duration = InspIRCd::Duration(parameters[1]);
expr = parameters[2];
}
else
@@ -155,7 +110,7 @@ class CommandShun : public Command
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x', "%s added timed SHUN for %s to expire on %s: %s",
user->nick.c_str(), target.c_str(), timestr.c_str(), expr.c_str());
}
@@ -163,14 +118,14 @@ class CommandShun : public Command
else
{
delete r;
- user->WriteServ("NOTICE %s :*** Shun for %s already exists", user->nick.c_str(), target.c_str());
+ user->WriteNotice("*** Shun for " + target + " already exists");
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
if (IS_LOCAL(user))
return ROUTE_LOCALONLY; // spanningtree will send ADDLINE
@@ -179,51 +134,48 @@ class CommandShun : public Command
}
};
-class ModuleShun : public Module
+class ModuleShun : public Module, public Stats::EventListener
{
CommandShun cmd;
ShunFactory f;
- std::set<std::string> ShunEnabledCommands;
+ insp::flat_set<std::string> ShunEnabledCommands;
bool NotifyOfShun;
bool affectopers;
public:
- ModuleShun() : cmd(this)
+ ModuleShun()
+ : Stats::EventListener(this)
+ , cmd(this)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->XLines->RegisterFactory(&f);
- ServerInstance->Modules->AddService(cmd);
-
- Implementation eventlist[] = { I_OnStats, I_OnPreCommand, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- virtual ~ModuleShun()
+ ~ModuleShun()
{
ServerInstance->XLines->DelAll("SHUN");
ServerInstance->XLines->UnregisterFactory(&f);
}
- void Prioritize()
+ void Prioritize() CXX11_OVERRIDE
{
Module* alias = ServerInstance->Modules->Find("m_alias.so");
- ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, &alias);
+ ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, alias);
}
- virtual ModResult OnStats(char symbol, User* user, string_list& out)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'H')
+ if (stats.GetSymbol() != 'H')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("SHUN", 223, user, out);
+ ServerInstance->XLines->InvokeStats("SHUN", 223, stats);
return MOD_RES_DENY;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("shun");
std::string cmds = tag->getString("enabledcommands");
@@ -234,10 +186,10 @@ class ModuleShun : public Module
ShunEnabledCommands.clear();
- std::stringstream dcmds(cmds);
+ irc::spacesepstream dcmds(cmds);
std::string thiscmd;
- while (dcmds >> thiscmd)
+ while (dcmds.GetToken(thiscmd))
{
ShunEnabledCommands.insert(thiscmd);
}
@@ -246,7 +198,7 @@ class ModuleShun : public Module
affectopers = tag->getBool("affectopers", false);
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
if (validated)
return MOD_RES_PASSTHRU;
@@ -257,18 +209,16 @@ class ModuleShun : public Module
return MOD_RES_PASSTHRU;
}
- if (!affectopers && IS_OPER(user))
+ if (!affectopers && user->IsOper())
{
/* Don't do anything if the user is an operator and affectopers isn't set */
return MOD_RES_PASSTHRU;
}
- std::set<std::string>::iterator i = ShunEnabledCommands.find(command);
-
- if (i == ShunEnabledCommands.end())
+ if (!ShunEnabledCommands.count(command))
{
if (NotifyOfShun)
- user->WriteServ("NOTICE %s :*** Command %s not processed, as you have been blocked from issuing commands (SHUN)", user->nick.c_str(), command.c_str());
+ user->WriteNotice("*** Command " + command + " not processed, as you have been blocked from issuing commands (SHUN)");
return MOD_RES_DENY;
}
@@ -287,11 +237,10 @@ class ModuleShun : public Module
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the /SHUN command, which stops a user from executing all except configured commands.",VF_VENDOR|VF_COMMON);
}
};
MODULE_INIT(ModuleShun)
-
diff --git a/src/modules/m_silence.cpp b/src/modules/m_silence.cpp
index c82ab3f9d..165e083bb 100644
--- a/src/modules/m_silence.cpp
+++ b/src/modules/m_silence.cpp
@@ -23,8 +23,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for the /SILENCE command */
-
/* Improved drop-in replacement for the /SILENCE command
* syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude>
*
@@ -47,8 +45,8 @@
// pair of hostmask and flags
typedef std::pair<std::string, int> silenceset;
-// deque list of pairs
-typedef std::deque<silenceset> silencelist;
+// list of pairs
+typedef std::vector<silenceset> silencelist;
// intmasks for flags
static int SILENCE_PRIVATE = 0x0001; /* p private messages */
@@ -59,6 +57,17 @@ 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 */
+enum
+{
+ // From ircu?
+ RPL_SILELIST = 271,
+ RPL_ENDOFSILELIST = 272,
+
+ // InspIRCd-specific.
+ RPL_UNSILENCED = 950,
+ RPL_SILENCED = 951,
+ ERR_NOTSILENCED = 952
+};
class CommandSVSSilence : public Command
{
@@ -66,10 +75,10 @@ class CommandSVSSilence : public Command
CommandSVSSilence(Module* Creator) : Command(Creator,"SVSSILENCE", 2)
{
syntax = "<target> {[+|-]<mask> <p|c|i|n|t|a|x>}";
- TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
+ TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
/*
* XXX: thought occurs to me
@@ -78,7 +87,7 @@ class CommandSVSSilence : public Command
* style command so services can modify lots of entries at once.
* leaving it backwards compatible for now as it's late. -- w
*/
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
return CMD_FAILURE;
User *u = ServerInstance->FindNick(parameters[0]);
@@ -87,18 +96,16 @@ class CommandSVSSilence : public Command
if (IS_LOCAL(u))
{
- ServerInstance->Parser->CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
+ CommandBase::Params params(parameters.begin() + 1, parameters.end());
+ ServerInstance->Parser.CallHandler("SILENCE", params, u);
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* target = ServerInstance->FindNick(parameters[0]);
- if (target)
- return ROUTE_OPT_UCAST(target->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -108,16 +115,16 @@ class CommandSilence : public Command
public:
SimpleExtItem<silencelist> ext;
CommandSilence(Module* Creator, unsigned int &max) : Command(Creator, "SILENCE", 0),
- maxsilence(max), ext("silence_list", Creator)
+ maxsilence(max)
+ , ext("silence_list", ExtensionItem::EXT_USER, Creator)
{
allow_empty_last_param = false;
syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (!parameters.size())
+ if (parameters.empty())
{
// no parameters, show the current silence list.
silencelist* sl = ext.get(user);
@@ -127,17 +134,17 @@ class CommandSilence : public Command
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
std::string decomppattern = DecompPattern(c->second);
- user->WriteNumeric(271, "%s %s %s %s",user->nick.c_str(), user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
+ user->WriteNumeric(RPL_SILELIST, user->nick, c->first, decomppattern);
}
}
- user->WriteNumeric(272, "%s :End of Silence List",user->nick.c_str());
+ user->WriteNumeric(RPL_ENDOFSILELIST, "End of Silence List");
return CMD_SUCCESS;
}
- else if (parameters.size() > 0)
+ else
{
// one or more parameters, add or delete entry from the list (only the first parameter is used)
- std::string mask = parameters[0].substr(1);
+ std::string mask(parameters[0], 1);
char action = parameters[0][0];
// Default is private and notice so clients do not break
int pattern = CompilePattern("pn");
@@ -149,7 +156,7 @@ class CommandSilence : public Command
if (pattern == 0)
{
- user->WriteServ("NOTICE %s :Bad SILENCE pattern",user->nick.c_str());
+ user->WriteNotice("Bad SILENCE pattern");
return CMD_INVALID;
}
@@ -172,11 +179,11 @@ class CommandSilence : public Command
for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
{
// search through for the item
- irc::string listitem = i->first.c_str();
- if (listitem == mask && i->second == pattern)
+ const std::string& listitem = i->first;
+ if ((irc::equals(listitem, mask)) && (i->second == pattern))
{
sl->erase(i);
- user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(RPL_UNSILENCED, user->nick, InspIRCd::Format("Removed %s %s from silence list", mask.c_str(), decomppattern.c_str()));
if (!sl->size())
{
ext.unset(user);
@@ -185,7 +192,7 @@ class CommandSilence : public Command
}
}
}
- user->WriteNumeric(952, "%s %s :%s %s does not exist on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(ERR_NOTSILENCED, user->nick, InspIRCd::Format("%s %s does not exist on your silence list", mask.c_str(), decomppattern.c_str()));
}
else if (action == '+')
{
@@ -198,29 +205,29 @@ class CommandSilence : public Command
}
if (sl->size() > maxsilence)
{
- user->WriteNumeric(952, "%s %s :Your silence list is full",user->nick.c_str(), user->nick.c_str());
+ user->WriteNumeric(ERR_NOTSILENCED, user->nick, "Your silence list is full");
return CMD_FAILURE;
}
std::string decomppattern = DecompPattern(pattern);
for (silencelist::iterator n = sl->begin(); n != sl->end(); n++)
{
- irc::string listitem = n->first.c_str();
- if (listitem == mask && n->second == pattern)
+ const std::string& listitem = n->first;
+ if ((irc::equals(listitem, mask)) && (n->second == pattern))
{
- user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(ERR_NOTSILENCED, user->nick, InspIRCd::Format("%s %s is already on your silence list", mask.c_str(), decomppattern.c_str()));
return CMD_FAILURE;
}
}
if (((pattern & SILENCE_EXCLUDE) > 0))
{
- sl->push_front(silenceset(mask,pattern));
+ sl->insert(sl->begin(), silenceset(mask, pattern));
}
else
{
sl->push_back(silenceset(mask,pattern));
}
- user->WriteNumeric(951, "%s %s :Added %s %s to silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(RPL_SILENCED, user->nick, InspIRCd::Format("Added %s %s to silence list", mask.c_str(), decomppattern.c_str()));
return CMD_SUCCESS;
}
}
@@ -293,6 +300,7 @@ class CommandSilence : public Command
class ModuleSilence : public Module
{
unsigned int maxsilence;
+ bool ExemptULine;
CommandSilence cmdsilence;
CommandSVSSilence cmdsvssilence;
public:
@@ -302,36 +310,26 @@ class ModuleSilence : public Module
{
}
- void init()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmdsilence);
- ServerInstance->Modules->AddService(cmdsvssilence);
- ServerInstance->Modules->AddService(cmdsilence.ext);
+ ConfigTag* tag = ServerInstance->Config->ConfValue("silence");
- Implementation eventlist[] = { I_OnRehash, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage, I_OnUserPreInvite };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ maxsilence = tag->getUInt("maxentries", 32, 1);
+ ExemptULine = tag->getBool("exemptuline", true);
}
- void OnRehash(User* user)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- maxsilence = ServerInstance->Config->ConfValue("silence")->getInt("maxentries", 32);
- if (!maxsilence)
- maxsilence = 32;
+ tokens["ESILENCE"];
+ tokens["SILENCE"] = ConvToStr(maxsilence);
}
- void On005Numeric(std::string &output)
- {
- // we don't really have a limit...
- output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
- }
-
- void OnBuildExemptList(MessageType message_type, Channel* chan, User* sender, char status, CUList &exempt_list, const std::string &text)
+ void BuildExemptList(MessageType message_type, Channel* chan, User* sender, CUList& exempt_list)
{
int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
- const UserMembList *ulist = chan->GetUsers();
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
if (IS_LOCAL(i->first))
{
@@ -343,43 +341,29 @@ class ModuleSilence : public Module
}
}
- ModResult PreText(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
- if (target_type == TYPE_USER && IS_LOCAL(((User*)dest)))
+ if (target.type == MessageTarget::TYPE_USER && IS_LOCAL(target.Get<User>()))
{
- return MatchPattern((User*)dest, user, silence_type);
+ return MatchPattern(target.Get<User>(), user, ((details.type == MSG_PRIVMSG) ? SILENCE_PRIVATE : SILENCE_NOTICE));
}
- else if (target_type == TYPE_CHANNEL)
+ else if (target.type == MessageTarget::TYPE_CHANNEL)
{
- Channel* chan = (Channel*)dest;
- if (chan)
- {
- this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list, "");
- }
+ Channel* chan = target.Get<Channel>();
+ BuildExemptList(details.type, chan, user, details.exemptions);
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
- }
-
- ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
- }
-
- ModResult OnUserPreInvite(User* source,User* dest,Channel* channel, time_t timeout)
+ ModResult OnUserPreInvite(User* source,User* dest,Channel* channel, time_t timeout) CXX11_OVERRIDE
{
return MatchPattern(dest, source, SILENCE_INVITE);
}
ModResult MatchPattern(User* dest, User* source, int pattern)
{
- /* Server source */
- if (!source || !dest)
- return MOD_RES_ALLOW;
+ if (ExemptULine && source->server->IsULine())
+ return MOD_RES_PASSTHRU;
silencelist* sl = cmdsilence.ext.get(dest);
if (sl)
@@ -393,11 +377,7 @@ class ModuleSilence : public Module
return MOD_RES_PASSTHRU;
}
- ~ModuleSilence()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the /SILENCE command", VF_OPTCOMMON | VF_VENDOR);
}
diff --git a/src/modules/m_spanningtree/addline.cpp b/src/modules/m_spanningtree/addline.cpp
index 16043b2aa..00ef5b8d1 100644
--- a/src/modules/m_spanningtree/addline.cpp
+++ b/src/modules/m_spanningtree/addline.cpp
@@ -20,60 +20,37 @@
#include "inspircd.h"
#include "xline.h"
-#include "treesocket.h"
#include "treeserver.h"
#include "utils.h"
+#include "commands.h"
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::AddLine(const std::string &prefix, parameterlist &params)
+CmdResult CommandAddLine::Handle(User* usr, Params& params)
{
- if (params.size() < 6)
- {
- std::string servername = MyRoot->GetName();
- ServerInstance->SNO->WriteToSnoMask('d', "%s sent me a malformed ADDLINE", servername.c_str());
- return true;
- }
-
XLineFactory* xlf = ServerInstance->XLines->GetFactory(params[0]);
-
- std::string setter = "<unknown>";
- User* usr = ServerInstance->FindNick(prefix);
- if (usr)
- setter = usr->nick;
- else
- {
- TreeServer* t = Utils->FindServer(prefix);
- if (t)
- setter = t->GetName();
- }
+ const std::string& setter = usr->nick;
if (!xlf)
{
- ServerInstance->SNO->WriteToSnoMask('d',"%s sent me an unknown ADDLINE type (%s).",setter.c_str(),params[0].c_str());
- return true;
+ ServerInstance->SNO->WriteToSnoMask('x',"%s sent me an unknown ADDLINE type (%s).",setter.c_str(),params[0].c_str());
+ return CMD_FAILURE;
}
- long created = atol(params[3].c_str()), expires = atol(params[4].c_str());
- if (created < 0 || expires < 0)
- return true;
-
XLine* xl = NULL;
try
{
- xl = xlf->Generate(ServerInstance->Time(), expires, params[2], params[5], params[1]);
+ xl = xlf->Generate(ServerInstance->Time(), ConvToInt(params[4]), params[2], params[5], params[1]);
}
catch (ModuleException &e)
{
- ServerInstance->SNO->WriteToSnoMask('d',"Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason());
- return true;
+ ServerInstance->SNO->WriteToSnoMask('x',"Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason().c_str());
+ return CMD_FAILURE;
}
- xl->SetCreateTime(created);
+ xl->SetCreateTime(ConvToInt(params[3]));
if (ServerInstance->XLines->AddLine(xl, NULL))
{
if (xl->duration)
{
- std::string timestr = ServerInstance->TimeString(xl->expiry);
+ std::string timestr = InspIRCd::TimeString(xl->expiry);
ServerInstance->SNO->WriteToSnoMask('X',"%s added %s%s on %s to expire on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "",
params[1].c_str(), timestr.c_str(), params[5].c_str());
}
@@ -82,20 +59,29 @@ bool TreeSocket::AddLine(const std::string &prefix, parameterlist &params)
ServerInstance->SNO->WriteToSnoMask('X',"%s added permanent %s%s on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "",
params[1].c_str(),params[5].c_str());
}
- params[5] = ":" + params[5];
- User* u = ServerInstance->FindNick(prefix);
- Utils->DoOneToAllButSender(prefix, "ADDLINE", params, u ? u->server : prefix);
- TreeServer *remoteserver = Utils->FindServer(u ? u->server : prefix);
+ TreeServer* remoteserver = TreeServer::Get(usr);
- if (!remoteserver->bursting)
+ if (!remoteserver->IsBursting())
{
ServerInstance->XLines->ApplyLines();
}
+ return CMD_SUCCESS;
}
else
+ {
delete xl;
-
- return true;
+ return CMD_FAILURE;
+ }
}
+CommandAddLine::Builder::Builder(XLine* xline, User* user)
+ : CmdBuilder(user, "ADDLINE")
+{
+ push(xline->type);
+ push(xline->Displayable());
+ push(xline->source);
+ push_int(xline->set_time);
+ push_int(xline->duration);
+ push_last(xline->reason);
+}
diff --git a/src/modules/m_spanningtree/away.cpp b/src/modules/m_spanningtree/away.cpp
index ed97c48cd..282f52a35 100644
--- a/src/modules/m_spanningtree/away.cpp
+++ b/src/modules/m_spanningtree/away.cpp
@@ -21,32 +21,32 @@
#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
-bool TreeSocket::Away(const std::string &prefix, parameterlist &params)
+CmdResult CommandAway::HandleRemote(::RemoteUser* u, Params& params)
{
- User* u = ServerInstance->FindNick(prefix);
- if ((!u) || (IS_SERVER(u)))
- return true;
- if (params.size())
+ if (!params.empty())
{
- FOREACH_MOD(I_OnSetAway, OnSetAway(u, params[params.size() - 1]));
-
if (params.size() > 1)
- u->awaytime = atoi(params[0].c_str());
+ u->awaytime = ConvToInt(params[0]);
else
u->awaytime = ServerInstance->Time();
- u->awaymsg = params[params.size() - 1];
-
- params[params.size() - 1] = ":" + params[params.size() - 1];
+ u->awaymsg = params.back();
+ FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserAway, (u));
}
else
{
- FOREACH_MOD(I_OnSetAway, OnSetAway(u, ""));
+ u->awaytime = 0;
u->awaymsg.clear();
+ FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserBack, (u));
}
- Utils->DoOneToAllButSender(prefix,"AWAY",params,u->server);
- return true;
+ return CMD_SUCCESS;
+}
+
+CommandAway::Builder::Builder(User* user)
+ : CmdBuilder(user, "AWAY")
+{
+ if (user->awaymsg.empty())
+ push_int(user->awaytime).push_last(user->awaymsg);
}
diff --git a/src/modules/m_spanningtree/cachetimer.h b/src/modules/m_spanningtree/cachetimer.h
index bad1b7419..489194b86 100644
--- a/src/modules/m_spanningtree/cachetimer.h
+++ b/src/modules/m_spanningtree/cachetimer.h
@@ -17,25 +17,13 @@
*/
-#ifndef M_SPANNINGTREE_CACHETIMER_H
-#define M_SPANNINGTREE_CACHETIMER_H
+#pragma once
-#include "timer.h"
-
-class ModuleSpanningTree;
-class SpanningTreeUtilities;
-
-/** Create a timer which recurs every second, we inherit from Timer.
- * Timer is only one-shot however, so at the end of each Tick() we simply
- * insert another of ourselves into the pending queue :)
+/** Timer that fires when we need to refresh the IP cache of servers
*/
class CacheRefreshTimer : public Timer
{
- private:
- SpanningTreeUtilities *Utils;
public:
- CacheRefreshTimer(SpanningTreeUtilities* Util);
- virtual void Tick(time_t TIME);
+ CacheRefreshTimer();
+ bool Tick(time_t TIME) CXX11_OVERRIDE;
};
-
-#endif
diff --git a/src/modules/m_spanningtree/capab.cpp b/src/modules/m_spanningtree/capab.cpp
index 0ab815fef..256a3d069 100644
--- a/src/modules/m_spanningtree/capab.cpp
+++ b/src/modules/m_spanningtree/capab.cpp
@@ -20,39 +20,61 @@
#include "inspircd.h"
-#include "xline.h"
-#include "treesocket.h"
#include "treeserver.h"
#include "utils.h"
#include "link.h"
#include "main.h"
-#include "../hash.h"
-std::string TreeSocket::MyModules(int filter)
+struct CompatMod
+{
+ const char* name;
+ ModuleFlags listflag;
+};
+
+static CompatMod compatmods[] =
{
- std::vector<std::string> modlist = ServerInstance->Modules->GetAllModuleNames(filter);
+ { "m_watch.so", VF_OPTCOMMON }
+};
- if (filter == VF_COMMON && proto_version != ProtocolVersion)
- CompatAddModules(modlist);
+std::string TreeSocket::MyModules(int filter)
+{
+ const ModuleManager::ModuleMap& modlist = ServerInstance->Modules->GetModules();
std::string capabilities;
- sort(modlist.begin(),modlist.end());
- for (std::vector<std::string>::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
+ for (ModuleManager::ModuleMap::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
{
- if (i != modlist.begin())
- capabilities.push_back(proto_version > 1201 ? ' ' : ',');
- capabilities.append(*i);
- Module* m = ServerInstance->Modules->Find(*i);
- if (m && proto_version > 1201)
+ Module* const mod = i->second;
+ // 3.0 advertises its settings for the benefit of services
+ // 2.0 would bork on this
+ if (proto_version < 1205 && i->second->ModuleSourceFile == "m_kicknorejoin.so")
+ continue;
+
+ bool do_compat_include = false;
+ if (proto_version < 1205)
{
- Version v = m->GetVersion();
- if (!v.link_data.empty())
+ for (size_t j = 0; j < sizeof(compatmods)/sizeof(compatmods[0]); j++)
{
- capabilities.push_back('=');
- capabilities.append(v.link_data);
+ if ((compatmods[j].listflag & filter) && (mod->ModuleSourceFile == compatmods[j].name))
+ {
+ do_compat_include = true;
+ break;
+ }
}
}
+
+ Version v = i->second->GetVersion();
+ if ((!do_compat_include) && (!(v.Flags & filter)))
+ continue;
+
+ if (i != modlist.begin())
+ capabilities.push_back(' ');
+ capabilities.append(i->first);
+ if (!v.link_data.empty())
+ {
+ capabilities.push_back('=');
+ capabilities.append(v.link_data);
+ }
}
return capabilities;
}
@@ -60,23 +82,23 @@ std::string TreeSocket::MyModules(int filter)
static std::string BuildModeList(ModeType type)
{
std::vector<std::string> modes;
- for(char c='A'; c <= 'z'; c++)
+ const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes.GetModes(type);
+ for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, type);
- if (mh)
+ const ModeHandler* const mh = i->second;
+ std::string mdesc = mh->name;
+ mdesc.push_back('=');
+ const PrefixMode* const pm = mh->IsPrefixMode();
+ if (pm)
{
- std::string mdesc = mh->name;
- mdesc.push_back('=');
- if (mh->GetPrefix())
- mdesc.push_back(mh->GetPrefix());
- if (mh->GetModeChar())
- mdesc.push_back(mh->GetModeChar());
- modes.push_back(mdesc);
+ if (pm->GetPrefix())
+ mdesc.push_back(pm->GetPrefix());
}
+ mdesc.push_back(mh->GetModeChar());
+ modes.push_back(mdesc);
}
- sort(modes.begin(), modes.end());
- irc::stringjoiner line(" ", modes, 0, modes.size() - 1);
- return line.GetJoined();
+ std::sort(modes.begin(), modes.end());
+ return stdalgo::string::join(modes);
}
void TreeSocket::SendCapabilities(int phase)
@@ -91,7 +113,7 @@ void TreeSocket::SendCapabilities(int phase)
if (phase < 2)
return;
- char sep = proto_version > 1201 ? ' ' : ',';
+ const char sep = ' ';
irc::sepstream modulelist(MyModules(VF_COMMON), sep);
irc::sepstream optmodulelist(MyModules(VF_OPTCOMMON), sep);
/* Send module names, split at 509 length */
@@ -135,13 +157,18 @@ void TreeSocket::SendCapabilities(int phase)
std::string extra;
/* Do we have sha256 available? If so, we send a challenge */
- if (Utils->ChallengeResponse && (ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256")))
+ if (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256"))
{
SetOurChallenge(ServerInstance->GenRandomStr(20));
extra = " CHALLENGE=" + this->GetOurChallenge();
}
- if (proto_version < 1202)
- extra += ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL) ? " HALFOP=1" : " HALFOP=0";
+
+ // 2.0 needs these keys.
+ if (proto_version == 1202)
+ {
+ extra.append(" PROTOCOL="+ConvToStr(ProtocolVersion));
+ extra.append(" MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxReal));
+ }
this->WriteLine("CAPAB CAPABILITIES " /* Preprocessor does this one. */
":NICKMAX="+ConvToStr(ServerInstance->Config->Limits.NickMax)+
@@ -151,20 +178,21 @@ void TreeSocket::SendCapabilities(int phase)
" MAXQUIT="+ConvToStr(ServerInstance->Config->Limits.MaxQuit)+
" MAXTOPIC="+ConvToStr(ServerInstance->Config->Limits.MaxTopic)+
" MAXKICK="+ConvToStr(ServerInstance->Config->Limits.MaxKick)+
- " MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxGecos)+
+ " MAXREAL="+ConvToStr(ServerInstance->Config->Limits.MaxReal)+
" MAXAWAY="+ConvToStr(ServerInstance->Config->Limits.MaxAway)+
- " IP6SUPPORT=1"+
- " PROTOCOL="+ConvToStr(ProtocolVersion)+extra+
+ " MAXHOST="+ConvToStr(ServerInstance->Config->Limits.MaxHost)+
+ extra+
" PREFIX="+ServerInstance->Modes->BuildPrefixes()+
- " CHANMODES="+ServerInstance->Modes->GiveModeList(MASK_CHANNEL)+
- " USERMODES="+ServerInstance->Modes->GiveModeList(MASK_USER)+
+ " CHANMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL)+
+ " USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER)+
+ " CASEMAPPING="+ServerInstance->Config->CaseMapping+
// XXX: Advertise the presence or absence of m_globops in CAPAB CAPABILITIES.
// Services want to know about it, and since m_globops was not marked as VF_(OPT)COMMON
// in 2.0, we advertise it here to not break linking to previous versions.
// Protocol version 1201 (1.2) does not have this issue because we advertise m_globops
// to 1201 protocol servers irrespectively of its module flags.
- (ServerInstance->Modules->Find("m_globops.so") != NULL ? " GLOBOPS=1" : " GLOBOPS=0")+
- " SVSPART=1");
+ (ServerInstance->Modules->Find("m_globops.so") != NULL ? " GLOBOPS=1" : " GLOBOPS=0")
+ );
this->WriteLine("CAPAB END");
}
@@ -196,7 +224,7 @@ void TreeSocket::ListDifference(const std::string &one, const std::string &two,
}
}
-bool TreeSocket::Capab(const parameterlist &params)
+bool TreeSocket::Capab(const CommandBase::Params& params)
{
if (params.size() < 1)
{
@@ -209,7 +237,23 @@ bool TreeSocket::Capab(const parameterlist &params)
capab->OptModuleList.clear();
capab->CapKeys.clear();
if (params.size() > 1)
- proto_version = atoi(params[1].c_str());
+ proto_version = ConvToInt(params[1]);
+
+ if (proto_version < MinCompatProtocol)
+ {
+ SendError("CAPAB negotiation failed: Server is using protocol version " + (proto_version ? ConvToStr(proto_version) : "1201 or older")
+ + " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
+ + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")"));
+ return false;
+ }
+
+ // Special case, may be removed in the future
+ if (proto_version == 1203 || proto_version == 1204)
+ {
+ SendError("CAPAB negotiation failed: InspIRCd 2.1 beta is not supported");
+ return false;
+ }
+
SendCapabilities(2);
}
else if (params[0] == "END")
@@ -219,7 +263,7 @@ bool TreeSocket::Capab(const parameterlist &params)
if ((this->capab->ModuleList != this->MyModules(VF_COMMON)) && (this->capab->ModuleList.length()))
{
std::string diffIneed, diffUneed;
- ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), proto_version > 1201 ? ' ' : ',', diffIneed, diffUneed);
+ ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), ' ', diffIneed, diffUneed);
if (diffIneed.length() || diffUneed.length())
{
reason = "Modules incorrectly matched on these servers.";
@@ -257,21 +301,6 @@ bool TreeSocket::Capab(const parameterlist &params)
}
}
- if (this->capab->CapKeys.find("PROTOCOL") == this->capab->CapKeys.end())
- {
- reason = "Protocol version not specified";
- }
- else
- {
- proto_version = atoi(capab->CapKeys.find("PROTOCOL")->second.c_str());
- if (proto_version < MinCompatProtocol)
- {
- reason = "Server is using protocol version " + ConvToStr(proto_version) +
- " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
- + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")");
- }
- }
-
if(this->capab->CapKeys.find("PREFIX") != this->capab->CapKeys.end() && this->capab->CapKeys.find("PREFIX")->second != ServerInstance->Modes->BuildPrefixes())
reason = "One or more of the prefixes on the remote server are invalid on this server.";
@@ -293,7 +322,7 @@ bool TreeSocket::Capab(const parameterlist &params)
}
else if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end())
{
- if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MASK_CHANNEL))
+ if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL))
reason = "One or more of the channel modes on the remote server are invalid on this server.";
}
@@ -315,13 +344,31 @@ bool TreeSocket::Capab(const parameterlist &params)
}
else if (this->capab->CapKeys.find("USERMODES") != this->capab->CapKeys.end())
{
- if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MASK_USER))
+ if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MODETYPE_USER))
reason = "One or more of the user modes on the remote server are invalid on this server.";
}
+ else
+ {
+ // We default to rfc1459 here because if this key is not sent then
+ // the remote server is running the 2.0 protocol which uses rfc1459
+ // by default.
+ std::string casemapping = "rfc1459";
+ std::map<std::string, std::string>::iterator citer = this->capab->CapKeys.find("CASEMAPPING");
+ if (citer != this->capab->CapKeys.end())
+ casemapping = citer->second;
+
+ if (casemapping != ServerInstance->Config->CaseMapping)
+ {
+ reason = "The casemapping of the remote server differs to that of the local server."
+ " Local casemapping: " + ServerInstance->Config->CaseMapping +
+ " Remote casemapping: " + casemapping;
+ }
+
+ }
/* Challenge response, store their challenge for our password */
std::map<std::string,std::string>::iterator n = this->capab->CapKeys.find("CHALLENGE");
- if (Utils->ChallengeResponse && (n != this->capab->CapKeys.end()) && (ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256")))
+ if ((n != this->capab->CapKeys.end()) && (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256")))
{
/* Challenge-response is on now */
this->SetTheirChallenge(n->second);
@@ -333,7 +380,7 @@ bool TreeSocket::Capab(const parameterlist &params)
}
else
{
- /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
+ // They didn't specify a challenge or we don't have sha256, we use plaintext
if (this->LinkState == CONNECTING)
{
this->SendCapabilities(2);
@@ -355,7 +402,7 @@ bool TreeSocket::Capab(const parameterlist &params)
}
else
{
- capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ',');
+ capab->ModuleList.push_back(' ');
capab->ModuleList.append(params[1]);
}
}
@@ -381,7 +428,7 @@ bool TreeSocket::Capab(const parameterlist &params)
}
else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
{
- irc::tokenstream capabs(params[1]);
+ irc::spacesepstream capabs(params[1]);
std::string item;
while (capabs.GetToken(item))
{
@@ -389,12 +436,11 @@ bool TreeSocket::Capab(const parameterlist &params)
std::string::size_type equals = item.find('=');
if (equals != std::string::npos)
{
- std::string var = item.substr(0, equals);
- std::string value = item.substr(equals+1, item.length());
+ std::string var(item, 0, equals);
+ std::string value(item, equals+1);
capab->CapKeys[var] = value;
}
}
}
return true;
}
-
diff --git a/src/modules/m_spanningtree/commandbuilder.h b/src/modules/m_spanningtree/commandbuilder.h
new file mode 100644
index 000000000..59de84052
--- /dev/null
+++ b/src/modules/m_spanningtree/commandbuilder.h
@@ -0,0 +1,149 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "utils.h"
+
+class TreeServer;
+
+class CmdBuilder
+{
+ protected:
+ std::string content;
+
+ public:
+ CmdBuilder(const char* cmd)
+ : content(1, ':')
+ {
+ content.append(ServerInstance->Config->GetSID());
+ push(cmd);
+ }
+
+ CmdBuilder(const std::string& src, const char* cmd)
+ : content(1, ':')
+ {
+ content.append(src);
+ push(cmd);
+ }
+
+ CmdBuilder(User* src, const char* cmd)
+ : content(1, ':')
+ {
+ content.append(src->uuid);
+ push(cmd);
+ }
+
+ CmdBuilder& push_raw(const std::string& s)
+ {
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push_raw(const char* s)
+ {
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push_raw(char c)
+ {
+ content.push_back(c);
+ return *this;
+ }
+
+ template <typename T>
+ CmdBuilder& push_raw_int(T i)
+ {
+ content.append(ConvToStr(i));
+ return *this;
+ }
+
+ template <typename InputIterator>
+ CmdBuilder& push_raw(InputIterator first, InputIterator last)
+ {
+ content.append(first, last);
+ return *this;
+ }
+
+ CmdBuilder& push(const std::string& s)
+ {
+ content.push_back(' ');
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push(const char* s)
+ {
+ content.push_back(' ');
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push(char c)
+ {
+ content.push_back(' ');
+ content.push_back(c);
+ return *this;
+ }
+
+ template <typename T>
+ CmdBuilder& push_int(T i)
+ {
+ content.push_back(' ');
+ content.append(ConvToStr(i));
+ return *this;
+ }
+
+ CmdBuilder& push_last(const std::string& s)
+ {
+ content.push_back(' ');
+ content.push_back(':');
+ content.append(s);
+ return *this;
+ }
+
+ template<typename T>
+ CmdBuilder& insert(const T& cont)
+ {
+ for (typename T::const_iterator i = cont.begin(); i != cont.end(); ++i)
+ push(*i);
+ return *this;
+ }
+
+ void push_back(const std::string& s) { push(s); }
+
+ const std::string& str() const { return content; }
+ operator const std::string&() const { return str(); }
+
+ void Broadcast() const
+ {
+ Utils->DoOneToMany(*this);
+ }
+
+ void Forward(TreeServer* omit) const
+ {
+ Utils->DoOneToAllButSender(*this, omit);
+ }
+
+ void Unicast(User* target) const
+ {
+ Utils->DoOneToOne(*this, target->server);
+ }
+};
diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h
index 3b5b499c1..434528e46 100644
--- a/src/modules/m_spanningtree/commands.h
+++ b/src/modules/m_spanningtree/commands.h
@@ -17,126 +17,390 @@
*/
-#ifndef M_SPANNINGTREE_COMMANDS_H
-#define M_SPANNINGTREE_COMMANDS_H
+#pragma once
-#include "main.h"
+#include "servercommand.h"
+#include "commandbuilder.h"
+#include "remoteuser.h"
+#include "modules/away.h"
+
+namespace SpanningTree
+{
+ class CommandAway;
+ class CommandNick;
+ class CommandPing;
+ class CommandPong;
+ class CommandServer;
+}
+
+using SpanningTree::CommandAway;
+using SpanningTree::CommandNick;
+using SpanningTree::CommandPing;
+using SpanningTree::CommandPong;
+using SpanningTree::CommandServer;
/** Handle /RCONNECT
*/
class CommandRConnect : public Command
{
- SpanningTreeUtilities* Utils; /* Utility class */
public:
- CommandRConnect (Module* Callback, SpanningTreeUtilities* Util);
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+ CommandRConnect(Module* Creator);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
};
class CommandRSQuit : public Command
{
- SpanningTreeUtilities* Utils; /* Utility class */
public:
- CommandRSQuit(Module* Callback, SpanningTreeUtilities* Util);
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
- void NoticeUser(User* user, const std::string &msg);
+ CommandRSQuit(Module* Creator);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
};
-class CommandSVSJoin : public Command
+class CommandMap : public Command
{
public:
- CommandSVSJoin(Module* Creator) : Command(Creator, "SVSJOIN", 2) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+ CommandMap(Module* Creator);
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
};
-class CommandSVSPart : public Command
+
+class CommandSVSJoin : public ServerCommand
{
public:
- CommandSVSPart(Module* Creator) : Command(Creator, "SVSPART", 2) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+ CommandSVSJoin(Module* Creator) : ServerCommand(Creator, "SVSJOIN", 2) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
};
-class CommandSVSNick : public Command
+
+class CommandSVSPart : public ServerCommand
{
public:
- CommandSVSNick(Module* Creator) : Command(Creator, "SVSNICK", 3) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+ CommandSVSPart(Module* Creator) : ServerCommand(Creator, "SVSPART", 2) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
};
-class CommandMetadata : public Command
+
+class CommandSVSNick : public ServerCommand
{
public:
- CommandMetadata(Module* Creator) : Command(Creator, "METADATA", 2) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandSVSNick(Module* Creator) : ServerCommand(Creator, "SVSNICK", 3) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
};
-class CommandUID : public Command
+
+class CommandMetadata : public ServerCommand
{
public:
- CommandUID(Module* Creator) : Command(Creator, "UID", 10) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandMetadata(Module* Creator) : ServerCommand(Creator, "METADATA", 2) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user, const std::string& key, const std::string& val);
+ Builder(Channel* chan, const std::string& key, const std::string& val);
+ Builder(const std::string& key, const std::string& val);
+ };
};
-class CommandOpertype : public Command
+
+class CommandUID : public ServerOnlyServerCommand<CommandUID>
{
public:
- CommandOpertype(Module* Creator) : Command(Creator, "OPERTYPE", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandUID(Module* Creator) : ServerOnlyServerCommand<CommandUID>(Creator, "UID", 10) { }
+ CmdResult HandleServer(TreeServer* server, CommandBase::Params& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user);
+ };
};
-class CommandFJoin : public Command
+
+class CommandOpertype : public UserOnlyServerCommand<CommandOpertype>
{
public:
- CommandFJoin(Module* Creator) : Command(Creator, "FJOIN", 3) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandOpertype(Module* Creator) : UserOnlyServerCommand<CommandOpertype>(Creator, "OPERTYPE", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, Params& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user);
+ };
+};
+
+class TreeSocket;
+class FwdFJoinBuilder;
+class CommandFJoin : public ServerCommand
+{
/** Remove all modes from a channel, including statusmodes (+qaovh etc), simplemodes, parameter modes.
* This does not update the timestamp of the target channel, this must be done seperately.
*/
- void RemoveStatus(User* source, parameterlist &params);
+ static void RemoveStatus(Channel* c);
+
+ /**
+ * Lowers the TS on the given channel: removes all modes, unsets all extensions,
+ * clears the topic and removes all pending invites.
+ * @param chan The target channel whose TS to lower
+ * @param TS The new TS to set
+ * @param newname The new name of the channel; must be the same or a case change of the current name
+ */
+ static void LowerTS(Channel* chan, time_t TS, const std::string& newname);
+ void ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin);
+ public:
+ CommandFJoin(Module* Creator) : ServerCommand(Creator, "FJOIN", 3) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_LOCALONLY; }
+
+ class Builder : public CmdBuilder
+ {
+ /** Maximum possible Membership::Id length in decimal digits, used for determining whether a user will fit into
+ * a message or not
+ */
+ static const size_t membid_max_digits = 20;
+ static const size_t maxline = 510;
+ std::string::size_type pos;
+
+ protected:
+ void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+ bool has_room(std::string::size_type nummodes) const;
+
+ public:
+ Builder(Channel* chan, TreeServer* source = Utils->TreeRoot);
+ void add(Membership* memb)
+ {
+ add(memb, memb->modes.begin(), memb->modes.end());
+ }
+
+ bool has_room(Membership* memb) const
+ {
+ return has_room(memb->modes.size());
+ }
+
+ void clear();
+ const std::string& finalize();
+ };
+};
+
+class CommandFMode : public ServerCommand
+{
+ public:
+ CommandFMode(Module* Creator) : ServerCommand(Creator, "FMODE", 3) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
};
-class CommandFMode : public Command
+
+class CommandFTopic : public ServerCommand
{
public:
- CommandFMode(Module* Creator) : Command(Creator, "FMODE", 3) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandFTopic(Module* Creator) : ServerCommand(Creator, "FTOPIC", 4, 5) { }
+ CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(Channel* chan);
+ Builder(User* user, Channel* chan);
+ };
};
-class CommandFTopic : public Command
+
+class CommandFHost : public UserOnlyServerCommand<CommandFHost>
+{
+ public:
+ CommandFHost(Module* Creator) : UserOnlyServerCommand<CommandFHost>(Creator, "FHOST", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, Params& params);
+};
+
+class CommandFIdent : public UserOnlyServerCommand<CommandFIdent>
{
public:
- CommandFTopic(Module* Creator) : Command(Creator, "FTOPIC", 4) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandFIdent(Module* Creator) : UserOnlyServerCommand<CommandFIdent>(Creator, "FIDENT", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, Params& params);
};
-class CommandFHost : public Command
+
+class CommandFName : public UserOnlyServerCommand<CommandFName>
{
public:
- CommandFHost(Module* Creator) : Command(Creator, "FHOST", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandFName(Module* Creator) : UserOnlyServerCommand<CommandFName>(Creator, "FNAME", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, Params& params);
};
-class CommandFIdent : public Command
+
+class CommandIJoin : public UserOnlyServerCommand<CommandIJoin>
{
public:
- CommandFIdent(Module* Creator) : Command(Creator, "FIDENT", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 2) { }
+ CmdResult HandleRemote(RemoteUser* user, Params& params);
};
-class CommandFName : public Command
+
+class CommandResync : public ServerOnlyServerCommand<CommandResync>
{
public:
- CommandFName(Module* Creator) : Command(Creator, "FNAME", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_LOCALONLY; }
+};
+
+class SpanningTree::CommandAway : public UserOnlyServerCommand<SpanningTree::CommandAway>
+{
+ private:
+ Away::EventProvider awayevprov;
+
+ public:
+ CommandAway(Module* Creator)
+ : UserOnlyServerCommand<SpanningTree::CommandAway>(Creator, "AWAY", 0, 2)
+ , awayevprov(Creator)
+ {
+ }
+ CmdResult HandleRemote(::RemoteUser* user, Params& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user);
+ };
+};
+
+class XLine;
+class CommandAddLine : public ServerCommand
+{
+ public:
+ CommandAddLine(Module* Creator) : ServerCommand(Creator, "ADDLINE", 6, 6) { }
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(XLine* xline, User* user = ServerInstance->FakeClient);
+ };
+};
+
+class CommandDelLine : public ServerCommand
+{
+ public:
+ CommandDelLine(Module* Creator) : ServerCommand(Creator, "DELLINE", 2, 2) { }
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandEncap : public ServerCommand
+{
+ public:
+ CommandEncap(Module* Creator) : ServerCommand(Creator, "ENCAP", 2) { }
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandIdle : public UserOnlyServerCommand<CommandIdle>
+{
+ public:
+ CommandIdle(Module* Creator) : UserOnlyServerCommand<CommandIdle>(Creator, "IDLE", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, Params& parameters);
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class SpanningTree::CommandNick : public UserOnlyServerCommand<SpanningTree::CommandNick>
+{
+ public:
+ CommandNick(Module* Creator) : UserOnlyServerCommand<SpanningTree::CommandNick>(Creator, "NICK", 2) { }
+ CmdResult HandleRemote(::RemoteUser* user, Params& parameters);
+};
+
+class SpanningTree::CommandPing : public ServerCommand
+{
+ public:
+ CommandPing(Module* Creator) : ServerCommand(Creator, "PING", 1) { }
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class SpanningTree::CommandPong : public ServerOnlyServerCommand<SpanningTree::CommandPong>
+{
+ public:
+ CommandPong(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandPong>(Creator, "PONG", 1) { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandSave : public ServerCommand
+{
+ public:
+ /** Timestamp of the uuid nick of all users who collided and got their nick changed to uuid
+ */
+ static const time_t SavedTimestamp = 100;
+
+ CommandSave(Module* Creator) : ServerCommand(Creator, "SAVE", 2) { }
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+};
+
+class SpanningTree::CommandServer : public ServerOnlyServerCommand<SpanningTree::CommandServer>
+{
+ static void HandleExtra(TreeServer* newserver, Params& params);
+
+ public:
+ CommandServer(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandServer>(Creator, "SERVER", 3) { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ void push_property(const char* key, const std::string& val)
+ {
+ push(key).push_raw('=').push_raw(val);
+ }
+ public:
+ Builder(TreeServer* server);
+ };
+};
+
+class CommandSQuit : public ServerOnlyServerCommand<CommandSQuit>
+{
+ public:
+ CommandSQuit(Module* Creator) : ServerOnlyServerCommand<CommandSQuit>(Creator, "SQUIT", 2) { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+};
+
+class CommandSNONotice : public ServerCommand
+{
+ public:
+ CommandSNONotice(Module* Creator) : ServerCommand(Creator, "SNONOTICE", 2) { }
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
+{
+ public:
+ CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+};
+
+class CommandSInfo : public ServerOnlyServerCommand<CommandSInfo>
+{
+ public:
+ CommandSInfo(Module* Creator) : ServerOnlyServerCommand<CommandSInfo>(Creator, "SINFO", 2) { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(TreeServer* server, const char* type, const std::string& value);
+ };
+};
+
+class CommandNum : public ServerOnlyServerCommand<CommandNum>
+{
+ public:
+ CommandNum(Module* Creator) : ServerOnlyServerCommand<CommandNum>(Creator, "NUM", 3) { }
+ CmdResult HandleServer(TreeServer* server, Params& parameters);
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric);
+ };
};
class SpanningTreeCommands
{
public:
- CommandRConnect rconnect;
- CommandRSQuit rsquit;
CommandSVSJoin svsjoin;
CommandSVSPart svspart;
CommandSVSNick svsnick;
@@ -144,12 +408,27 @@ class SpanningTreeCommands
CommandUID uid;
CommandOpertype opertype;
CommandFJoin fjoin;
+ CommandIJoin ijoin;
+ CommandResync resync;
CommandFMode fmode;
CommandFTopic ftopic;
CommandFHost fhost;
CommandFIdent fident;
CommandFName fname;
+ SpanningTree::CommandAway away;
+ CommandAddLine addline;
+ CommandDelLine delline;
+ CommandEncap encap;
+ CommandIdle idle;
+ SpanningTree::CommandNick nick;
+ SpanningTree::CommandPing ping;
+ SpanningTree::CommandPong pong;
+ CommandSave save;
+ SpanningTree::CommandServer server;
+ CommandSQuit squit;
+ CommandSNONotice snonotice;
+ CommandEndBurst endburst;
+ CommandSInfo sinfo;
+ CommandNum num;
SpanningTreeCommands(ModuleSpanningTree* module);
};
-
-#endif
diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp
index ec0cdb036..b2967af3b 100644
--- a/src/modules/m_spanningtree/compat.cpp
+++ b/src/modules/m_spanningtree/compat.cpp
@@ -20,177 +20,561 @@
#include "inspircd.h"
#include "main.h"
#include "treesocket.h"
+#include "treeserver.h"
-static const char* const forge_common_1201[] = {
- "m_allowinvite.so",
- "m_alltime.so",
- "m_auditorium.so",
- "m_banexception.so",
- "m_blockcaps.so",
- "m_blockcolor.so",
- "m_botmode.so",
- "m_censor.so",
- "m_chanfilter.so",
- "m_chanhistory.so",
- "m_channelban.so",
- "m_chanprotect.so",
- "m_chghost.so",
- "m_chgname.so",
- "m_commonchans.so",
- "m_customtitle.so",
- "m_deaf.so",
- "m_delayjoin.so",
- "m_delaymsg.so",
- "m_exemptchanops.so",
- "m_gecosban.so",
- "m_globops.so",
- "m_helpop.so",
- "m_hidechans.so",
- "m_hideoper.so",
- "m_invisible.so",
- "m_inviteexception.so",
- "m_joinflood.so",
- "m_kicknorejoin.so",
- "m_knock.so",
- "m_messageflood.so",
- "m_muteban.so",
- "m_nickflood.so",
- "m_nicklock.so",
- "m_noctcp.so",
- "m_nokicks.so",
- "m_nonicks.so",
- "m_nonotice.so",
- "m_nopartmsg.so",
- "m_ojoin.so",
- "m_operprefix.so",
- "m_permchannels.so",
- "m_redirect.so",
- "m_regex_glob.so",
- "m_regex_pcre.so",
- "m_regex_posix.so",
- "m_regex_tre.so",
- "m_remove.so",
- "m_sajoin.so",
- "m_sakick.so",
- "m_sanick.so",
- "m_sapart.so",
- "m_saquit.so",
- "m_serverban.so",
- "m_services_account.so",
- "m_servprotect.so",
- "m_setident.so",
- "m_showwhois.so",
- "m_silence.so",
- "m_sslmodes.so",
- "m_stripcolor.so",
- "m_swhois.so",
- "m_uninvite.so",
- "m_watch.so"
-};
-
-static std::string wide_newline("\r\n");
static std::string newline("\n");
-void TreeSocket::CompatAddModules(std::vector<std::string>& modlist)
+void TreeSocket::WriteLineNoCompat(const std::string& line)
{
- if (proto_version < 1202)
- {
- // you MUST have chgident loaded in order to be able to translate FIDENT
- modlist.push_back("m_chgident.so");
- for(int i=0; i * sizeof(char*) < sizeof(forge_common_1201); i++)
- {
- if (ServerInstance->Modules->Find(forge_common_1201[i]))
- modlist.push_back(forge_common_1201[i]);
- }
- // module was merged
- if (ServerInstance->Modules->Find("m_operchans.so"))
- {
- modlist.push_back("m_operchans.so");
- modlist.push_back("m_operinvex.so");
- }
- }
+ ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
+ this->WriteData(line);
+ this->WriteData(newline);
}
-void TreeSocket::WriteLine(std::string line)
+void TreeSocket::WriteLine(const std::string& original_line)
{
if (LinkState == CONNECTED)
{
- if (line[0] != ':')
+ if (original_line.c_str()[0] != ':')
{
- ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Sending line without server prefix!");
- line = ":" + ServerInstance->Config->GetSID() + " " + line;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sending line without server prefix!");
+ WriteLine(":" + ServerInstance->Config->GetSID() + " " + original_line);
+ return;
}
if (proto_version != ProtocolVersion)
{
+ std::string line = original_line;
std::string::size_type a = line.find(' ');
std::string::size_type b = line.find(' ', a + 1);
- std::string command = line.substr(a + 1, b-a-1);
+ std::string command(line, a + 1, b-a-1);
// now try to find a translation entry
// TODO a more efficient lookup method will be needed later
- if (proto_version < 1202 && command == "FIDENT")
- {
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting FIDENT for 1201-protocol server");
- line = ":" + ServerInstance->Config->GetSID() + " CHGIDENT " + line.substr(1,a-1) + line.substr(b);
- }
- else if (proto_version < 1202 && command == "SAVE")
- {
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting SAVE for 1201-protocol server");
- std::string::size_type c = line.find(' ', b + 1);
- std::string uid = line.substr(b, c - b);
- line = ":" + ServerInstance->Config->GetSID() + " SVSNICK" + uid + line.substr(b);
- }
- else if (proto_version < 1202 && command == "AWAY")
+ if (proto_version < 1205)
{
- if (b != std::string::npos)
+ if (command == "IJOIN")
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Stripping AWAY timestamp for 1201-protocol server");
+ // Convert
+ // :<uid> IJOIN <chan> <membid> [<ts> [<flags>]]
+ // to
+ // :<sid> FJOIN <chan> <ts> + [<flags>],<uuid>
std::string::size_type c = line.find(' ', b + 1);
- line.erase(b,c-b);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ // Erase membership id first
+ line.erase(c, d-c);
+ if (d == std::string::npos)
+ {
+ // No TS or modes in the command
+ // :22DAAAAAB IJOIN #chan
+ const std::string channame(line, b+1, c-b-1);
+ Channel* chan = ServerInstance->FindChan(channame);
+ if (!chan)
+ return;
+
+ line.push_back(' ');
+ line.append(ConvToStr(chan->age));
+ line.append(" + ,");
+ }
+ else
+ {
+ d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ {
+ // TS present, no modes
+ // :22DAAAAAC IJOIN #chan 12345
+ line.append(" + ,");
+ }
+ else
+ {
+ // Both TS and modes are present
+ // :22DAAAAAC IJOIN #chan 12345 ov
+ std::string::size_type e = line.find(' ', d + 1);
+ if (e != std::string::npos)
+ line.erase(e);
+
+ line.insert(d, " +");
+ line.push_back(',');
+ }
+ }
+
+ // Move the uuid to the end and replace the I with an F
+ line.append(line.substr(1, 9));
+ line.erase(4, 6);
+ line[5] = 'F';
}
- }
- else if (proto_version < 1202 && command == "ENCAP")
- {
- // :src ENCAP target command [args...]
- // A B C D
- // Therefore B and C cannot be npos in a valid command
- if (b == std::string::npos)
- return;
- std::string::size_type c = line.find(' ', b + 1);
- if (c == std::string::npos)
+ else if (command == "RESYNC")
return;
- std::string::size_type d = line.find(' ', c + 1);
- std::string subcmd = line.substr(c + 1, d - c - 1);
+ else if (command == "METADATA")
+ {
+ // Drop TS for channel METADATA, translate METADATA operquit into an OPERQUIT command
+ // :sid METADATA #target TS extname ...
+ // A B C D
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
+ if (line[b + 1] == '#')
+ {
+ // We're sending channel metadata
+ line.erase(c, d-c);
+ }
+ else if (!line.compare(c, d-c, " operquit", 9))
+ {
+ // ":22D METADATA 22DAAAAAX operquit :message" -> ":22DAAAAAX OPERQUIT :message"
+ line = ":" + line.substr(b+1, c-b) + "OPERQUIT" + line.substr(d);
+ }
+ }
+ else if (command == "FTOPIC")
+ {
+ // Drop channel TS for FTOPIC
+ // :sid FTOPIC #target TS TopicTS setter :newtopic
+ // A B C D E F
+ // :uid FTOPIC #target TS TopicTS :newtopic
+ // A B C D E
+ if (b == std::string::npos)
+ return;
- if (subcmd == "CHGIDENT" && d != std::string::npos)
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
+ std::string::size_type e = line.find(' ', d + 1);
+ if (line[e+1] == ':')
+ {
+ line.erase(c, e-c);
+ line.erase(a+1, 1);
+ }
+ else
+ line.erase(c, d-c);
+ }
+ else if ((command == "PING") || (command == "PONG"))
+ {
+ // :22D PING 20D
+ if (line.length() < 13)
+ return;
+
+ // Insert the source SID (and a space) between the command and the first parameter
+ line.insert(10, line.substr(1, 4));
+ }
+ else if (command == "OPERTYPE")
{
+ std::string::size_type colon = line.find(':', b);
+ if (colon != std::string::npos)
+ {
+ for (std::string::iterator i = line.begin()+colon; i != line.end(); ++i)
+ {
+ if (*i == ' ')
+ *i = '_';
+ }
+ line.erase(colon, 1);
+ }
+ }
+ else if (command == "INVITE")
+ {
+ // :22D INVITE 22DAAAAAN #chan TS ExpirationTime
+ // A B C D E
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
std::string::size_type e = line.find(' ', d + 1);
- if (e == std::string::npos)
- return; // not valid
- std::string target = line.substr(d + 1, e - d - 1);
+ // If there is no expiration time then everything will be erased from 'd'
+ line.erase(d, e-d);
+ }
+ else if (command == "FJOIN")
+ {
+ // Strip membership ids
+ // :22D FJOIN #chan 1234 +f 4:3 :o,22DAAAAAB:15 o,22DAAAAAA:15
+ // :22D FJOIN #chan 1234 +f 4:3 o,22DAAAAAB:15
+ // :22D FJOIN #chan 1234 +Pf 4:3 :
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Forging acceptance of CHGIDENT from 1201-protocol server");
- recvq.insert(0, ":" + target + " FIDENT " + line.substr(e) + "\n");
+ // If the last parameter is prefixed by a colon then it's a userlist which may have 0 or more users;
+ // if it isn't, then it is a single member
+ std::string::size_type spcolon = line.find(" :");
+ if (spcolon != std::string::npos)
+ {
+ spcolon++;
+ // Loop while there is a ':' in the userlist, this is never true if the channel is empty
+ std::string::size_type pos = std::string::npos;
+ while ((pos = line.rfind(':', pos-1)) > spcolon)
+ {
+ // Find the next space after the ':'
+ std::string::size_type sp = line.find(' ', pos);
+ // Erase characters between the ':' and the next space after it, including the ':' but not the space;
+ // if there is no next space, everything will be erased between pos and the end of the line
+ line.erase(pos, sp-pos);
+ }
+ }
+ else
+ {
+ // Last parameter is a single member
+ std::string::size_type sp = line.rfind(' ');
+ std::string::size_type colon = line.find(':', sp);
+ line.erase(colon);
+ }
}
+ else if (command == "KICK")
+ {
+ // Strip membership id if the KICK has one
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
- Command* thiscmd = ServerInstance->Parser->GetHandler(subcmd);
- if (thiscmd && subcmd != "WHOISNOTICE")
+ std::string::size_type d = line.find(' ', c + 1);
+ if ((d < line.size()-1) && (original_line[d+1] != ':'))
+ {
+ // There is a third parameter which doesn't begin with a colon, erase it
+ std::string::size_type e = line.find(' ', d + 1);
+ line.erase(d, e-d);
+ }
+ }
+ else if (command == "SINFO")
{
- Version ver = thiscmd->creator->GetVersion();
- if (ver.Flags & VF_OPTCOMMON)
+ // :22D SINFO version :InspIRCd-3.0
+ // A B C
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ // Only translating SINFO version, discard everything else
+ if (line.compare(b, 9, " version ", 9))
+ return;
+
+ line = line.substr(0, 5) + "VERSION" + line.substr(c);
+ }
+ else if (command == "SERVER")
+ {
+ // :001 SERVER inspircd.test 002 [<anything> ...] :description
+ // A B C
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = c + 4;
+ std::string::size_type spcolon = line.find(" :", d);
+ if (spcolon == std::string::npos)
+ return;
+
+ line.erase(d, spcolon-d);
+ line.insert(c, " * 0");
+
+ if (burstsent)
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Removing ENCAP on '%s' for 1201-protocol server",
- subcmd.c_str());
- line.erase(a, c-a);
+ WriteLineNoCompat(line);
+
+ // Synthesize a :<newserver> BURST <time> message
+ spcolon = line.find(" :");
+ line = CmdBuilder(line.substr(spcolon-3, 3), "BURST").push_int(ServerInstance->Time()).str();
}
}
+ else if (command == "NUM")
+ {
+ // :<sid> NUM <numeric source sid> <target uuid> <3 digit number> <params>
+ // Translate to
+ // :<sid> PUSH <target uuid> :<numeric source name> <3 digit number> <target nick> <params>
+
+ TreeServer* const numericsource = Utils->FindServerID(line.substr(9, 3));
+ if (!numericsource)
+ return;
+
+ // The nick of the target is necessary for building the PUSH message
+ User* const target = ServerInstance->FindUUID(line.substr(13, UIDGenerator::UUID_LENGTH));
+ if (!target)
+ return;
+
+ std::string push = InspIRCd::Format(":%.*s PUSH %s ::%s %.*s %s", 3, line.c_str()+1, target->uuid.c_str(), numericsource->GetName().c_str(), 3, line.c_str()+23, target->nick.c_str());
+ push.append(line, 26, std::string::npos);
+ push.swap(line);
+ }
}
+ WriteLineNoCompat(line);
+ return;
}
}
- ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
- this->WriteData(line);
- if (proto_version < 1202)
- this->WriteData(wide_newline);
- else
- this->WriteData(newline);
+ WriteLineNoCompat(original_line);
+}
+
+namespace
+{
+ bool InsertCurrentChannelTS(CommandBase::Params& params, unsigned int chanindex = 0, unsigned int pos = 1)
+ {
+ Channel* chan = ServerInstance->FindChan(params[chanindex]);
+ if (!chan)
+ return false;
+
+ // Insert the current TS of the channel after the pos-th parameter
+ params.insert(params.begin()+pos, ConvToStr(chan->age));
+ return true;
+ }
+}
+
+bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params)
+{
+ if ((cmd == "METADATA") && (params.size() >= 3) && (params[0][0] == '#'))
+ {
+ // :20D METADATA #channel extname :extdata
+ return InsertCurrentChannelTS(params);
+ }
+ else if ((cmd == "FTOPIC") && (params.size() >= 4))
+ {
+ // :20D FTOPIC #channel 100 Attila :topic text
+ return InsertCurrentChannelTS(params);
+ }
+ else if ((cmd == "PING") || (cmd == "PONG"))
+ {
+ if (params.size() == 1)
+ {
+ // If it's a PING with 1 parameter, reply with a PONG now, if it's a PONG with 1 parameter (weird), do nothing
+ if (cmd[1] == 'I')
+ this->WriteData(":" + ServerInstance->Config->GetSID() + " PONG " + params[0] + newline);
+
+ // Don't process this message further
+ return false;
+ }
+
+ // :20D PING 20D 22D
+ // :20D PONG 20D 22D
+ // Drop the first parameter
+ params.erase(params.begin());
+
+ // If the target is a server name, translate it to a SID
+ if (!InspIRCd::IsSID(params[0]))
+ {
+ TreeServer* server = Utils->FindServer(params[0]);
+ if (!server)
+ {
+ // We've no idea what this is, log and stop processing
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received a " + cmd + " with an unknown target: \"" + params[0] + "\", command dropped");
+ return false;
+ }
+
+ params[0] = server->GetID();
+ }
+ }
+ else if ((cmd == "GLINE") || (cmd == "KLINE") || (cmd == "ELINE") || (cmd == "ZLINE") || (cmd == "QLINE"))
+ {
+ // Fix undocumented protocol usage: translate GLINE, ZLINE, etc. into ADDLINE or DELLINE
+ if ((params.size() != 1) && (params.size() != 3))
+ return false;
+
+ CommandBase::Params p;
+ p.push_back(cmd.substr(0, 1));
+ p.push_back(params[0]);
+
+ if (params.size() == 3)
+ {
+ cmd = "ADDLINE";
+ p.push_back(who->nick);
+ p.push_back(ConvToStr(ServerInstance->Time()));
+ p.push_back(ConvToStr(InspIRCd::Duration(params[1])));
+ p.push_back(params[2]);
+ }
+ else
+ cmd = "DELLINE";
+
+ params.swap(p);
+ }
+ else if (cmd == "SVSMODE")
+ {
+ cmd = "MODE";
+ }
+ else if (cmd == "OPERQUIT")
+ {
+ // Translate OPERQUIT into METADATA
+ if (params.empty())
+ return false;
+
+ cmd = "METADATA";
+ params.insert(params.begin(), who->uuid);
+ params.insert(params.begin()+1, "operquit");
+ who = MyRoot->ServerUser;
+ }
+ else if ((cmd == "TOPIC") && (params.size() >= 2))
+ {
+ // :20DAAAAAC TOPIC #chan :new topic
+ cmd = "FTOPIC";
+ if (!InsertCurrentChannelTS(params))
+ return false;
+
+ params.insert(params.begin()+2, ConvToStr(ServerInstance->Time()));
+ }
+ else if (cmd == "MODENOTICE")
+ {
+ // MODENOTICE is always supported by 2.0 but it's optional in 3.0.
+ params.insert(params.begin(), "*");
+ params.insert(params.begin()+1, cmd);
+ cmd = "ENCAP";
+ }
+ else if (cmd == "RULES")
+ {
+ return false;
+ }
+ else if (cmd == "INVITE")
+ {
+ // :20D INVITE 22DAAABBB #chan
+ // :20D INVITE 22DAAABBB #chan 123456789
+ // Insert channel timestamp after the channel name; the 3rd parameter, if there, is the invite expiration time
+ return InsertCurrentChannelTS(params, 1, 2);
+ }
+ else if (cmd == "VERSION")
+ {
+ // :20D VERSION :InspIRCd-2.0
+ // change to
+ // :20D SINFO version :InspIRCd-2.0
+ cmd = "SINFO";
+ params.insert(params.begin(), "version");
+ }
+ else if (cmd == "JOIN")
+ {
+ // 2.0 allows and forwards legacy JOINs but we don't, so translate them to FJOINs before processing
+ if ((params.size() != 1) || (IS_SERVER(who)))
+ return false; // Huh?
+
+ cmd = "FJOIN";
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ params.push_back(ConvToStr(chan ? chan->age : ServerInstance->Time()));
+ params.push_back("+");
+ params.push_back(",");
+ params.back().append(who->uuid);
+ who = TreeServer::Get(who)->ServerUser;
+ }
+ else if ((cmd == "FMODE") && (params.size() >= 2))
+ {
+ // Translate user mode changes with timestamp to MODE
+ if (params[0][0] != '#')
+ {
+ User* user = ServerInstance->FindUUID(params[0]);
+ if (!user)
+ return false;
+
+ // Emulate the old nonsensical behavior
+ if (user->age < ServerCommand::ExtractTS(params[1]))
+ return false;
+
+ cmd = "MODE";
+ params.erase(params.begin()+1);
+ }
+ }
+ else if ((cmd == "SERVER") && (params.size() > 4))
+ {
+ // This does not affect the initial SERVER line as it is sent before the link state is CONNECTED
+ // :20D SERVER <name> * 0 <sid> <desc>
+ // change to
+ // :20D SERVER <name> <sid> <desc>
+
+ params[1].swap(params[3]);
+ params.erase(params.begin()+2, params.begin()+4);
+
+ // If the source of this SERVER message is not bursting, then new servers it introduces are bursting
+ TreeServer* server = TreeServer::Get(who);
+ if (!server->IsBursting())
+ params.insert(params.begin()+2, "burst=" + ConvToStr(((uint64_t)ServerInstance->Time())*1000));
+ }
+ else if (cmd == "BURST")
+ {
+ // A server is introducing another one, drop unnecessary BURST
+ return false;
+ }
+ else if (cmd == "SVSWATCH")
+ {
+ // SVSWATCH was removed because nothing was using it, but better be sure
+ return false;
+ }
+ else if (cmd == "PUSH")
+ {
+ if ((params.size() != 2) || (!this->MyRoot))
+ return false; // Huh?
+
+ irc::tokenstream ts(params.back());
+
+ std::string srcstr;
+ ts.GetMiddle(srcstr);
+ srcstr.erase(0, 1);
+
+ std::string token;
+ ts.GetMiddle(token);
+
+ // See if it's a numeric being sent to the target via PUSH
+ unsigned int numeric_number = 0;
+ if (token.length() == 3)
+ numeric_number = ConvToInt(token);
+
+ if ((numeric_number > 0) && (numeric_number < 1000))
+ {
+ // It's a numeric, translate to NUM
+
+ // srcstr must be a valid server name
+ TreeServer* const numericsource = Utils->FindServer(srcstr);
+ if (!numericsource)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unable to translate PUSH numeric %s to user %s from 1202 protocol server %s: source \"%s\" doesn't exist", token.c_str(), params[0].c_str(), this->MyRoot->GetName().c_str(), srcstr.c_str());
+ return false;
+ }
+
+ cmd = "NUM";
+
+ // Second parameter becomes the target uuid
+ params[0].swap(params[1]);
+ // Replace first param (now the PUSH payload, not needed) with the source sid
+ params[0] = numericsource->GetID();
+
+ params.push_back(InspIRCd::Format("%03u", numeric_number));
+
+ // Ignore the nickname in the numeric in PUSH
+ ts.GetMiddle(token);
+
+ // Rest of the tokens are the numeric parameters, add them to NUM
+ while (ts.GetTrailing(token))
+ params.push_back(token);
+ }
+ else if ((token == "PRIVMSG") || (token == "NOTICE"))
+ {
+ // Command is a PRIVMSG/NOTICE
+ cmd.swap(token);
+
+ // Check if the PRIVMSG/NOTICE target is a nickname
+ ts.GetMiddle(token);
+ if (token.c_str()[0] == '#')
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unable to translate PUSH %s to user %s from 1202 protocol server %s, target \"%s\"", cmd.c_str(), params[0].c_str(), this->MyRoot->GetName().c_str(), token.c_str());
+ return false;
+ }
+
+ // Replace second parameter with the message
+ ts.GetTrailing(params[1]);
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unable to translate PUSH to user %s from 1202 protocol server %s", params[0].c_str(), this->MyRoot->GetName().c_str());
+ return false;
+ }
+
+ return true;
+ }
+
+ return true; // Passthru
}
diff --git a/src/modules/m_spanningtree/delline.cpp b/src/modules/m_spanningtree/delline.cpp
index 540ca5079..c64bec654 100644
--- a/src/modules/m_spanningtree/delline.cpp
+++ b/src/modules/m_spanningtree/delline.cpp
@@ -20,38 +20,18 @@
#include "inspircd.h"
#include "xline.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.h"
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-
-bool TreeSocket::DelLine(const std::string &prefix, parameterlist &params)
+CmdResult CommandDelLine::Handle(User* user, Params& params)
{
- if (params.size() < 2)
- return true;
-
- std::string setter = "<unknown>";
-
- User* user = ServerInstance->FindNick(prefix);
- if (user)
- setter = user->nick;
- else
- {
- TreeServer* t = Utils->FindServer(prefix);
- if (t)
- setter = t->GetName();
- }
+ const std::string& setter = user->nick;
-
- /* NOTE: No check needed on 'user', this function safely handles NULL */
+ // XLineManager::DelLine() returns true if the xline existed, false if it didn't
if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], user))
{
ServerInstance->SNO->WriteToSnoMask('X',"%s removed %s%s on %s", setter.c_str(),
params[0].c_str(), params[0].length() == 1 ? "-line" : "", params[1].c_str());
- Utils->DoOneToAllButSender(prefix,"DELLINE", params, prefix);
+ return CMD_SUCCESS;
}
- return true;
+ return CMD_FAILURE;
}
-
diff --git a/src/modules/m_spanningtree/encap.cpp b/src/modules/m_spanningtree/encap.cpp
index dabfc086b..4bc321065 100644
--- a/src/modules/m_spanningtree/encap.cpp
+++ b/src/modules/m_spanningtree/encap.cpp
@@ -18,32 +18,39 @@
#include "inspircd.h"
-#include "xline.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.h"
+#include "main.h"
/** ENCAP */
-void TreeSocket::Encap(User* who, parameterlist &params)
+CmdResult CommandEncap::Handle(User* user, Params& params)
{
- if (params.size() > 1)
+ if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
{
- if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
- {
- parameterlist plist(params.begin() + 2, params.end());
- ServerInstance->Parser->CallHandler(params[1], plist, who);
- // discard return value, ENCAP shall succeed even if the command does not exist
- }
-
- params[params.size() - 1] = ":" + params[params.size() - 1];
+ CommandBase::Params plist(params.begin() + 2, params.end());
- if (params[0].find_first_of("*?") != std::string::npos)
+ // XXX: Workaround for SVS* commands provided by spanningtree not being registered in the core
+ if ((params[1] == "SVSNICK") || (params[1] == "SVSJOIN") || (params[1] == "SVSPART"))
{
- Utils->DoOneToAllButSender(who->uuid, "ENCAP", params, who->server);
+ ServerCommand* const scmd = Utils->Creator->CmdManager.GetHandler(params[1]);
+ if (scmd)
+ scmd->Handle(user, plist);
+ return CMD_SUCCESS;
}
- else
- Utils->DoOneToOne(who->uuid, "ENCAP", params, params[0]);
+
+ Command* cmd = NULL;
+ ServerInstance->Parser.CallHandler(params[1], plist, user, &cmd);
+ // Discard return value, ENCAP shall succeed even if the command does not exist
+
+ if ((cmd) && (cmd->force_manual_route))
+ return CMD_FAILURE;
}
+ return CMD_SUCCESS;
}
+RouteDescriptor CommandEncap::GetRouting(User* user, const Params& params)
+{
+ if (params[0].find_first_of("*?") != std::string::npos)
+ return ROUTE_BROADCAST;
+ return ROUTE_UNICAST(params[0]);
+}
diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp
index 4ec6e1dbb..a6c52e41b 100644
--- a/src/modules/m_spanningtree/fjoin.cpp
+++ b/src/modules/m_spanningtree/fjoin.cpp
@@ -25,11 +25,26 @@
#include "treeserver.h"
#include "treesocket.h"
+/** FJOIN builder for rebuilding incoming FJOINs and splitting them up into multiple messages if necessary
+ */
+class FwdFJoinBuilder : public CommandFJoin::Builder
+{
+ TreeServer* const sourceserver;
+
+ public:
+ FwdFJoinBuilder(Channel* chan, TreeServer* server)
+ : CommandFJoin::Builder(chan, server)
+ , sourceserver(server)
+ {
+ }
+
+ void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+};
+
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
-CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandFJoin::Handle(User* srcuser, Params& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
- /* 1.1 FJOIN works as follows:
+ /* 1.1+ FJOIN works as follows:
*
* Each FJOIN is sent along with a timestamp, and the side with the lowest
* timestamp 'wins'. From this point on we will refer to this side as the
@@ -54,204 +69,277 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src
* The winning side on the other hand will ignore all user modes from the
* losing side, so only its own modes get applied. Life is simple for those
* who succeed at internets. :-)
+ *
+ * Outside of netbursts, the winning side also resyncs the losing side if it
+ * detects that the other side recreated the channel.
+ *
+ * Syntax:
+ * :<sid> FJOIN <chan> <TS> <modes> :[<member> [<member> ...]]
+ * The last parameter is a list consisting of zero or more channel members
+ * (permanent channels may have zero users). Each entry on the list is in the
+ * following format:
+ * [[<modes>,]<uuid>[:<membid>]
+ * <modes> is a concatenation of the mode letters the user has on the channel
+ * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters
+ * are not important but if a server ecounters an unknown mode letter, it will
+ * drop the link to avoid desync.
+ *
+ * InspIRCd 2.0 and older required a comma before the uuid even if the user
+ * had no prefix modes on the channel, InspIRCd 3.0 and later does not require
+ * a comma in this case anymore.
+ *
+ * <membid> is a positive integer representing the id of the membership.
+ * If not present (in FJOINs coming from pre-1205 servers), 0 is assumed.
+ *
+ * Forwarding:
+ * FJOIN messages are forwarded with the new TS and modes. Prefix modes of
+ * members on the losing side are not forwarded.
+ * This is required to only have one server on each side of the network who
+ * decides the fate of a channel during a network merge. Otherwise, if the
+ * clock of a server is slightly off it may make a different decision than
+ * the rest of the network and desync.
+ * The prefix modes are always forwarded as-is, or not at all.
+ * One incoming FJOIN may result in more than one FJOIN being generated
+ * and forwarded mainly due to compatibility reasons with non-InspIRCd
+ * servers that don't handle more than 512 char long lines.
+ *
+ * Forwarding examples:
+ * Existing channel #chan with TS 1000, modes +n.
+ * Incoming: :220 FJOIN #chan 1000 +t :o,220AAAAAB:0
+ * Forwarded: :220 FJOIN #chan 1000 +nt :o,220AAAAAB:0
+ * Merge modes and forward the result. Forward their prefix modes as well.
+ *
+ * Existing channel #chan with TS 1000, modes +nt.
+ * Incoming: :220 FJOIN #CHAN 2000 +i :ov,220AAAAAB:0 o,220AAAAAC:20
+ * Forwarded: :220 FJOIN #chan 1000 +nt :,220AAAAAB:0 ,220AAAAAC:20
+ * Drop their modes, forward our modes and TS, use our channel name
+ * capitalization. Don't forward prefix modes.
+ *
*/
- irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
- User* who = NULL; /* User we are currently checking */
- std::string channel = params[0]; /* Channel name, as a string */
- time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */
- irc::tokenstream users((params.size() > 3) ? params[params.size() - 1] : ""); /* users from the user list */
- bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
- Channel* chan = ServerInstance->FindChan(channel); /* The channel we're sending joins to */
- bool created = !chan; /* True if the channel doesnt exist here yet */
- std::string item; /* One item in the list of nicks */
-
- TreeServer* src_server = Utils->FindServer(srcuser->server);
- TreeSocket* src_socket = src_server->GetRoute()->GetSocket();
+ time_t TS = ServerCommand::ExtractTS(params[1]);
- if (!TS)
- {
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
- ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str());
- return CMD_INVALID;
- }
+ const std::string& channel = params[0];
+ Channel* chan = ServerInstance->FindChan(channel);
+ bool apply_other_sides_modes = true;
+ TreeServer* const sourceserver = TreeServer::Get(srcuser);
- if (created)
+ if (!chan)
{
chan = new Channel(channel, TS);
- ServerInstance->SNO->WriteToSnoMask('d', "Creation FJOIN received for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
}
else
{
time_t ourTS = chan->age;
-
if (TS != ourTS)
- ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %ld",
- chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (long)(ourTS - TS));
- /* If our TS is less than theirs, we dont accept their modes */
- if (ourTS < TS)
{
- ServerInstance->SNO->WriteToSnoMask('d', "NOT Applying modes from other side");
- apply_other_sides_modes = false;
- }
- else if (ourTS > TS)
- {
- /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
- ServerInstance->SNO->WriteToSnoMask('d', "Removing our modes, accepting remote");
- parameterlist param_list;
- if (Utils->AnnounceTSChange)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS);
- // while the name is equal in case-insensitive compare, it might differ in case; use the remote version
- chan->name = channel;
- chan->age = TS;
- chan->ClearInvites();
- param_list.push_back(channel);
- this->RemoveStatus(ServerInstance->FakeClient, param_list);
-
- // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
- // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
- // deleted later) as soon as the permchan mode is removed from them.
- if (ServerInstance->FindChan(channel) == NULL)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %ld",
+ chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (long)(ourTS - TS));
+ /* If our TS is less than theirs, we dont accept their modes */
+ if (ourTS < TS)
{
- chan = new Channel(channel, TS);
+ // If the source server isn't bursting then this FJOIN is the result of them recreating the channel with a higher TS.
+ // This happens if the last user on the channel hops and before the PART propagates a user on another server joins. Fix it by doing a resync.
+ // Servers behind us won't react this way because the forwarded FJOIN will have the correct TS.
+ if (!sourceserver->IsBursting())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s recreated channel %s with higher TS, resyncing", sourceserver->GetName().c_str(), chan->name.c_str());
+ sourceserver->GetSocket()->SyncChannel(chan);
+ }
+ apply_other_sides_modes = false;
+ }
+ else if (ourTS > TS)
+ {
+ // Our TS is greater than theirs, remove all modes, extensions, etc. from the channel
+ LowerTS(chan, TS, channel);
+
+ // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
+ // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
+ // deleted later) as soon as the permchan mode is removed from them.
+ if (ServerInstance->FindChan(channel) == NULL)
+ {
+ chan = new Channel(channel, TS);
+ }
}
}
- // The silent case here is ourTS == TS, we don't need to remove modes here, just to merge them later on.
}
- /* First up, apply their modes if they won the TS war */
+ // Apply their channel modes if we have to
+ Modes::ChangeList modechangelist;
if (apply_other_sides_modes)
{
- // Need to use a modestacker here due to maxmodes
- irc::modestacker stack(true);
- std::vector<std::string>::const_iterator paramit = params.begin() + 3;
- const std::vector<std::string>::const_iterator lastparamit = ((params.size() > 3) ? (params.end() - 1) : params.end());
- for (std::string::const_iterator i = params[2].begin(); i != params[2].end(); ++i)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
- if (!mh)
- continue;
+ ServerInstance->Modes.ModeParamsToChangeList(srcuser, MODETYPE_CHANNEL, params, modechangelist, 2, params.size() - 1);
+ ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+ // Reuse for prefix modes
+ modechangelist.clear();
+ }
- std::string modeparam;
- if ((paramit != lastparamit) && (mh->GetNumParams(true)))
- {
- modeparam = *paramit;
- ++paramit;
- }
+ // Build a new FJOIN for forwarding. Put the correct TS in it and the current modes of the channel
+ // after applying theirs. If they lost, the prefix modes from their message are not forwarded.
+ FwdFJoinBuilder fwdfjoin(chan, sourceserver);
- stack.Push(*i, modeparam);
- }
+ // Process every member in the message
+ irc::spacesepstream users(params.back());
+ std::string item;
+ Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
+ while (users.GetToken(item))
+ {
+ ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr, fwdfjoin);
+ }
- std::vector<std::string> modelist;
+ fwdfjoin.finalize();
+ fwdfjoin.Forward(sourceserver->GetRoute());
- // Mode parser needs to know what channel to act on.
- modelist.push_back(params[0]);
+ // Set prefix modes on their users if we lost the FJOIN or had equal TS
+ if (apply_other_sides_modes)
+ ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);
- while (stack.GetStackedLine(modelist))
- {
- ServerInstance->Modes->Process(modelist, srcuser, true);
- modelist.erase(modelist.begin() + 1, modelist.end());
- }
+ return CMD_SUCCESS;
+}
+
+void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin)
+{
+ std::string::size_type comma = item.find(',');
- ServerInstance->Modes->Process(modelist, srcuser, true);
+ // Comma not required anymore if the user has no modes
+ const std::string::size_type ubegin = (comma == std::string::npos ? 0 : comma+1);
+ std::string uuid(item, ubegin, UIDGenerator::UUID_LENGTH);
+ User* who = ServerInstance->FindUUID(uuid);
+ if (!who)
+ {
+ // Probably KILLed, ignore
+ return;
}
- /* Now, process every 'modes,nick' pair */
- while (users.GetToken(item))
+ TreeSocket* src_socket = sourceserver->GetSocket();
+ /* Check that the user's 'direction' is correct */
+ TreeServer* route_back_again = TreeServer::Get(who);
+ if (route_back_again->GetSocket() != src_socket)
{
- const char* usr = item.c_str();
- if (usr && *usr)
+ return;
+ }
+
+ std::string::const_iterator modeendit = item.begin(); // End of the "ov" mode string
+ /* Check if the user received at least one mode */
+ if ((modechangelist) && (comma != std::string::npos))
+ {
+ modeendit += comma;
+ /* Iterate through the modes and see if they are valid here, if so, apply */
+ for (std::string::const_iterator i = item.begin(); i != modeendit; ++i)
{
- const char* unparsedmodes = usr;
- std::string modes;
+ ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
+ if (!mh)
+ throw ProtocolException("Unrecognised mode '" + std::string(1, *i) + "'");
+ /* Add any modes this user had to the mode stack */
+ modechangelist->push_add(mh, who->nick);
+ }
+ }
- /* Iterate through all modes for this user and check they are valid. */
- while ((*unparsedmodes) && (*unparsedmodes != ','))
- {
- ModeHandler *mh = ServerInstance->Modes->FindMode(*unparsedmodes, MODETYPE_CHANNEL);
- if (!mh)
- {
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised mode %c, dropping link", *unparsedmodes);
- return CMD_INVALID;
- }
+ Membership* memb = chan->ForceJoin(who, NULL, sourceserver->IsBursting());
+ if (!memb)
+ {
+ // User was already on the channel, forward because of the modes they potentially got
+ memb = chan->GetUser(who);
+ if (memb)
+ fwdfjoin.add(memb, item.begin(), modeendit);
+ return;
+ }
- modes += *unparsedmodes;
- usr++;
- unparsedmodes++;
- }
+ // Assign the id to the new Membership
+ Membership::Id membid = 0;
+ const std::string::size_type colon = item.rfind(':');
+ if (colon != std::string::npos)
+ membid = Membership::IdFromString(item.substr(colon+1));
+ memb->id = membid;
- /* Advance past the comma, to the nick */
- usr++;
+ // Add member to fwdfjoin with prefix modes
+ fwdfjoin.add(memb, item.begin(), modeendit);
+}
- /* Check the user actually exists */
- who = ServerInstance->FindUUID(usr);
- if (who)
- {
- /* Check that the user's 'direction' is correct */
- TreeServer* route_back_again = Utils->BestRouteTo(who->server);
- if ((!route_back_again) || (route_back_again->GetSocket() != src_socket))
- continue;
+void CommandFJoin::RemoveStatus(Channel* c)
+{
+ Modes::ChangeList changelist;
- /* Add any modes this user had to the mode stack */
- for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
- modestack.Push(*x, who->nick);
+ const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+ for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
+ {
+ ModeHandler* mh = i->second;
- Channel::JoinUser(who, channel.c_str(), true, "", src_server->bursting, TS);
- }
- else
- {
- ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistent user %s in fjoin to %s (probably quit?)", usr, channel.c_str());
- continue;
- }
- }
+ // Add the removal of this mode to the changelist. This handles all kinds of modes, including prefix modes.
+ mh->RemoveMode(c, changelist);
}
- /* Flush mode stacker if we lost the FJOIN or had equal TS */
- if (apply_other_sides_modes)
- {
- parameterlist stackresult;
- stackresult.push_back(channel);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, changelist, ModeParser::MODE_LOCALONLY);
+}
- while (modestack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, srcuser);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
- }
- return CMD_SUCCESS;
+void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
+{
+ if (Utils->AnnounceTSChange)
+ chan->WriteNotice(InspIRCd::Format("Creation time of %s changed from %s to %s", newname.c_str(),
+ InspIRCd::TimeString(chan->age).c_str(), InspIRCd::TimeString(TS).c_str()));
+
+ // While the name is equal in case-insensitive compare, it might differ in case; use the remote version
+ chan->name = newname;
+ chan->age = TS;
+
+ // Clear all modes
+ CommandFJoin::RemoveStatus(chan);
+
+ // Unset all extensions
+ chan->FreeAllExtItems();
+
+ // Clear the topic
+ chan->SetTopic(ServerInstance->FakeClient, std::string(), 0);
+ chan->setby.clear();
}
-void CommandFJoin::RemoveStatus(User* srcuser, parameterlist &params)
+CommandFJoin::Builder::Builder(Channel* chan, TreeServer* source)
+ : CmdBuilder(source->GetID(), "FJOIN")
{
- if (params.size() < 1)
- return;
+ push(chan->name).push_int(chan->age).push_raw(" +");
+ pos = str().size();
+ push_raw(chan->ChanModes(true)).push_raw(" :");
+}
- Channel* c = ServerInstance->FindChan(params[0]);
+void CommandFJoin::Builder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
+{
+ push_raw(mbegin, mend).push_raw(',').push_raw(memb->user->uuid);
+ push_raw(':').push_raw_int(memb->id);
+ push_raw(' ');
+}
- if (c)
- {
- irc::modestacker stack(false);
- parameterlist stackresult;
- stackresult.push_back(c->name);
+bool CommandFJoin::Builder::has_room(std::string::size_type nummodes) const
+{
+ return ((str().size() + nummodes + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
+}
- for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
-
- /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
- * rather than applied immediately. Module unloads require this to be done immediately,
- * for this function we require tidyness instead. Fixes bug #493
- */
- if (mh)
- mh->RemoveMode(c, &stack);
- }
+void CommandFJoin::Builder::clear()
+{
+ content.erase(pos);
+ push_raw(" :");
+}
- while (stack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, srcuser);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
- }
+const std::string& CommandFJoin::Builder::finalize()
+{
+ if (*content.rbegin() == ' ')
+ content.erase(content.size()-1);
+ return str();
}
+void FwdFJoinBuilder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
+{
+ // Pseudoserver compatibility:
+ // Some pseudoservers do not handle lines longer than 512 so we split long FJOINs into multiple messages.
+ // The forwarded FJOIN can end up being longer than the original one if we have more modes set and won, for example.
+
+ // Check if the member fits into the current message. If not, send it and prepare a new one.
+ if (!has_room(std::distance(mbegin, mend)))
+ {
+ finalize();
+ Forward(sourceserver);
+ clear();
+ }
+ // Add the member and their modes exactly as they sent them
+ CommandFJoin::Builder::add(memb, mbegin, mend);
+}
diff --git a/src/modules/m_spanningtree/fmode.cpp b/src/modules/m_spanningtree/fmode.cpp
index c1e452db6..a15b5ddc2 100644
--- a/src/modules/m_spanningtree/fmode.cpp
+++ b/src/modules/m_spanningtree/fmode.cpp
@@ -21,73 +21,35 @@
#include "inspircd.h"
#include "commands.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-/** FMODE command - server mode with timestamp checks */
-CmdResult CommandFMode::Handle(const std::vector<std::string>& params, User *who)
+/** FMODE command - channel mode change with timestamp checks */
+CmdResult CommandFMode::Handle(User* who, Params& params)
{
- std::string sourceserv = who->server;
-
- std::vector<std::string> modelist;
- time_t TS = 0;
- for (unsigned int q = 0; (q < params.size()) && (q < 64); q++)
- {
- if (q == 1)
- {
- /* The timestamp is in this position.
- * We don't want to pass that up to the
- * server->client protocol!
- */
- TS = atoi(params[q].c_str());
- }
- else
- {
- /* Everything else is fine to append to the modelist */
- modelist.push_back(params[q]);
- }
+ time_t TS = ServerCommand::ExtractTS(params[1]);
- }
- /* Extract the TS value of the object, either User or Channel */
- User* dst = ServerInstance->FindNick(params[0]);
- Channel* chan = NULL;
- time_t ourTS = 0;
+ Channel* const chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ // Channel doesn't exist
+ return CMD_FAILURE;
- if (dst)
- {
- ourTS = dst->age;
- }
- else
- {
- chan = ServerInstance->FindChan(params[0]);
- if (chan)
- {
- ourTS = chan->age;
- }
- else
- /* Oops, channel doesnt exist! */
- return CMD_FAILURE;
- }
+ // Extract the TS of the channel in question
+ time_t ourTS = chan->age;
- if (!TS)
- {
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
- ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str());
- return CMD_INVALID;
- }
-
- /* TS is equal or less: Merge the mode changes into ours and pass on.
+ /* If the TS is greater than ours, we drop the mode and don't pass it anywhere.
*/
- if (TS <= ourTS)
- {
- bool merge = (TS == ourTS) && IS_SERVER(who);
- ServerInstance->Modes->Process(modelist, who, merge);
- return CMD_SUCCESS;
- }
- /* If the TS is greater than ours, we drop the mode and dont pass it anywhere.
+ if (TS > ourTS)
+ return CMD_FAILURE;
+
+ /* TS is equal or less: apply the mode change locally and forward the message
*/
- return CMD_FAILURE;
-}
+ // Turn modes into a Modes::ChangeList; may have more elements than max modes
+ Modes::ChangeList changelist;
+ ServerInstance->Modes.ModeParamsToChangeList(who, MODETYPE_CHANNEL, params, changelist, 2);
+
+ ModeParser::ModeProcessFlag flags = ModeParser::MODE_LOCALONLY;
+ if ((TS == ourTS) && IS_SERVER(who))
+ flags |= ModeParser::MODE_MERGE;
+ ServerInstance->Modes->Process(who, chan, NULL, changelist, flags);
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/ftopic.cpp b/src/modules/m_spanningtree/ftopic.cpp
index d559c6ae5..01826e8f6 100644
--- a/src/modules/m_spanningtree/ftopic.cpp
+++ b/src/modules/m_spanningtree/ftopic.cpp
@@ -21,31 +21,69 @@
#include "inspircd.h"
#include "commands.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
/** FTOPIC command */
-CmdResult CommandFTopic::Handle(const std::vector<std::string>& params, User *user)
+CmdResult CommandFTopic::Handle(User* user, Params& params)
{
- time_t ts = atoi(params[1].c_str());
Channel* c = ServerInstance->FindChan(params[0]);
- if (c)
+ if (!c)
+ return CMD_FAILURE;
+
+ if (c->age < ServerCommand::ExtractTS(params[1]))
+ // Our channel TS is older, nothing to do
+ return CMD_FAILURE;
+
+ // Channel::topicset is initialized to 0 on channel creation, so their ts will always win if we never had a topic
+ time_t ts = ServerCommand::ExtractTS(params[2]);
+ if (ts < c->topicset)
+ return CMD_FAILURE;
+
+ // The topic text is always the last parameter
+ const std::string& newtopic = params.back();
+
+ // If there is a setter in the message use that, otherwise use the message source
+ const std::string& setter = ((params.size() > 4) ? params[3] : (ServerInstance->Config->FullHostInTopic ? user->GetFullHost() : user->nick));
+
+ /*
+ * If the topics were updated at the exact same second, accept
+ * the remote only when it's "bigger" than ours as defined by
+ * string comparision, so non-empty topics always overridde
+ * empty topics if their timestamps are equal
+ *
+ * Similarly, if the topic texts are equal too, keep one topic
+ * setter and discard the other
+ */
+ if (ts == c->topicset)
{
- if ((ts >= c->topicset) || (c->topic.empty()))
- {
- if (c->topic != params[3])
- {
- // Update topic only when it differs from current topic
- c->topic.assign(params[3], 0, ServerInstance->Config->Limits.MaxTopic);
- c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
- }
-
- // Always update setter and settime.
- c->setby.assign(params[2], 0, 127);
- c->topicset = ts;
- }
+ // Discard if their topic text is "smaller"
+ if (c->topic > newtopic)
+ return CMD_FAILURE;
+
+ // If the texts are equal in addition to the timestamps, decide which setter to keep
+ if ((c->topic == newtopic) && (c->setby >= setter))
+ return CMD_FAILURE;
}
+
+ c->SetTopic(user, newtopic, ts, &setter);
return CMD_SUCCESS;
}
+// Used when bursting and in reply to RESYNC, contains topic setter as the 4th parameter
+CommandFTopic::Builder::Builder(Channel* chan)
+ : CmdBuilder("FTOPIC")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push_int(chan->topicset);
+ push(chan->setby);
+ push_last(chan->topic);
+}
+
+// Used when changing the topic, the setter is the message source
+CommandFTopic::Builder::Builder(User* user, Channel* chan)
+ : CmdBuilder(user, "FTOPIC")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push_int(chan->topicset);
+ push_last(chan->topic);
+}
diff --git a/src/modules/m_spanningtree/hmac.cpp b/src/modules/m_spanningtree/hmac.cpp
index d990e1fbf..2001d560d 100644
--- a/src/modules/m_spanningtree/hmac.cpp
+++ b/src/modules/m_spanningtree/hmac.cpp
@@ -19,18 +19,12 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "../hash.h"
-#include "../ssl.h"
-#include "socketengine.h"
+#include "modules/hash.h"
+#include "modules/ssl.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
#include "link.h"
#include "treesocket.h"
-#include "resolvers.h"
const std::string& TreeSocket::GetOurChallenge()
{
@@ -57,44 +51,15 @@ std::string TreeSocket::MakePass(const std::string &password, const std::string
/* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for
* suggesting the use of HMAC to secure the password against various attacks.
*
- * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no
+ * Note: If an sha256 provider is not available, we MUST fall back to plaintext with no
* HMAC challenge/response.
*/
HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
- if (Utils->ChallengeResponse && sha256 && !challenge.empty())
- {
- if (proto_version < 1202)
- {
- /* This is how HMAC is done in InspIRCd 1.2:
- *
- * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
- *
- * 5c and 36 were chosen as part of the HMAC standard, because they
- * flip the bits in a way likely to strengthen the function.
- */
- std::string hmac1, hmac2;
-
- for (size_t n = 0; n < password.length(); n++)
- {
- hmac1.push_back(static_cast<char>(password[n] ^ 0x5C));
- hmac2.push_back(static_cast<char>(password[n] ^ 0x36));
- }
-
- hmac2.append(challenge);
- hmac2 = sha256->hexsum(hmac2);
-
- std::string hmac = hmac1 + hmac2;
- hmac = sha256->hexsum(hmac);
-
- return "HMAC-SHA256:"+ hmac;
- }
- else
- {
- return "AUTH:" + BinToBase64(sha256->hmac(password, challenge));
- }
- }
- else if (!challenge.empty() && !sha256)
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
+ if (sha256 && !challenge.empty())
+ return "AUTH:" + BinToBase64(sha256->hmac(password, challenge));
+
+ if (!challenge.empty() && !sha256)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Not authenticating to server using SHA256/HMAC because we don't have an SHA256 provider (e.g. m_sha256) loaded!");
return password;
}
@@ -104,13 +69,16 @@ bool TreeSocket::ComparePass(const Link& link, const std::string &theirs)
capab->auth_fingerprint = !link.Fingerprint.empty();
capab->auth_challenge = !capab->ourchallenge.empty() && !capab->theirchallenge.empty();
- std::string fp;
- if (GetIOHook())
+ std::string fp = SSLClientCert::GetFingerprint(this);
+ if (capab->auth_fingerprint)
{
- SocketCertificateRequest req(this, Utils->Creator);
- if (req.cert)
+ /* Require fingerprint to exist and match */
+ if (link.Fingerprint != fp)
{
- fp = req.cert->GetFingerprint();
+ ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL certificate fingerprint on link %s: need \"%s\" got \"%s\"",
+ link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
+ SendError("Invalid SSL certificate fingerprint " + fp + " - expected " + link.Fingerprint);
+ return false;
}
}
@@ -118,32 +86,24 @@ bool TreeSocket::ComparePass(const Link& link, const std::string &theirs)
{
std::string our_hmac = MakePass(link.RecvPass, capab->ourchallenge);
- /* Straight string compare of hashes */
- if (our_hmac != theirs)
+ // Use the timing-safe compare function to compare the hashes
+ if (!InspIRCd::TimingSafeCompare(our_hmac, theirs))
return false;
}
else
{
- /* Straight string compare of plaintext */
- if (link.RecvPass != theirs)
+ // Use the timing-safe compare function to compare the passwords
+ if (!InspIRCd::TimingSafeCompare(link.RecvPass, theirs))
return false;
}
- if (capab->auth_fingerprint)
+ // Tell opers to set up fingerprint verification if it's not already set up and the SSL mod gave us a fingerprint
+ // this time
+ if ((!capab->auth_fingerprint) && (!fp.empty()))
{
- /* Require fingerprint to exist and match */
- if (link.Fingerprint != fp)
- {
- ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL fingerprint on link %s: need \"%s\" got \"%s\"",
- link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
- SendError("Provided invalid SSL fingerprint " + fp + " - expected " + link.Fingerprint);
- return false;
- }
- }
- else if (!fp.empty())
- {
- ServerInstance->SNO->WriteToSnoMask('l', "SSL fingerprint for link %s is \"%s\". "
+ ServerInstance->SNO->WriteToSnoMask('l', "SSL certificate fingerprint for link %s is \"%s\". "
"You can improve security by specifying this in <link:fingerprint>.", link.Name.c_str(), fp.c_str());
}
+
return true;
}
diff --git a/src/modules/m_spanningtree/idle.cpp b/src/modules/m_spanningtree/idle.cpp
index 18aeb0ad5..11e665531 100644
--- a/src/modules/m_spanningtree/idle.cpp
+++ b/src/modules/m_spanningtree/idle.cpp
@@ -18,67 +18,53 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
-bool TreeSocket::Whois(const std::string &prefix, parameterlist &params)
+CmdResult CommandIdle::HandleRemote(RemoteUser* issuer, Params& params)
{
- if (params.size() < 1)
- return true;
- User* u = ServerInstance->FindNick(prefix);
- if (u)
+ /**
+ * There are two forms of IDLE: request and reply. Requests have one parameter,
+ * replies have more than one.
+ *
+ * If this is a request, 'issuer' did a /whois and its server wants to learn the
+ * idle time of the user in params[0].
+ *
+ * If this is a reply, params[0] is the user who did the whois and params.back() is
+ * the number of seconds 'issuer' has been idle.
+ */
+
+ User* target = ServerInstance->FindUUID(params[0]);
+ if ((!target) || (target->registered != REG_ALL))
+ return CMD_FAILURE;
+
+ LocalUser* localtarget = IS_LOCAL(target);
+ if (!localtarget)
{
- // an incoming request
- if (params.size() == 1)
- {
- User* x = ServerInstance->FindNick(params[0]);
- if ((x) && (IS_LOCAL(x)))
- {
- long idle = labs((long)((x->idle_lastmsg) - ServerInstance->Time()));
- parameterlist par;
- par.push_back(prefix);
- par.push_back(ConvToStr(x->signon));
- par.push_back(ConvToStr(idle));
- // ours, we're done, pass it BACK
- Utils->DoOneToOne(params[0], "IDLE", par, u->server);
- }
- else
- {
- // not ours pass it on
- if (x)
- Utils->DoOneToOne(prefix, "IDLE", params, x->server);
- }
- }
- else if (params.size() == 3)
- {
- std::string who_did_the_whois = params[0];
- User* who_to_send_to = ServerInstance->FindNick(who_did_the_whois);
- if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)) && (who_to_send_to->registered == REG_ALL))
- {
- // an incoming reply to a whois we sent out
- std::string nick_whoised = prefix;
- unsigned long signon = atoi(params[1].c_str());
- unsigned long idle = atoi(params[2].c_str());
- if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
- {
- ServerInstance->DoWhois(who_to_send_to, u, signon, idle, nick_whoised.c_str());
- }
- }
- else
- {
- // not ours, pass it on
- if (who_to_send_to)
- Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server);
- }
- }
+ // Forward to target's server
+ return CMD_SUCCESS;
}
- return true;
-}
+ if (params.size() >= 2)
+ {
+ ServerInstance->Parser.CallHandler("WHOIS", params, issuer);
+ }
+ else
+ {
+ // A server is asking us the idle time of our user
+ unsigned int idle;
+ if (localtarget->idle_lastmsg >= ServerInstance->Time())
+ // Possible case when our clock ticked backwards
+ idle = 0;
+ else
+ idle = ((unsigned int) (ServerInstance->Time() - localtarget->idle_lastmsg));
+
+ CmdBuilder reply(params[0], "IDLE");
+ reply.push_back(issuer->uuid);
+ reply.push_back(ConvToStr(target->signon));
+ reply.push_back(ConvToStr(idle));
+ reply.Unicast(issuer);
+ }
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/ijoin.cpp b/src/modules/m_spanningtree/ijoin.cpp
new file mode 100644
index 000000000..d33ef3d4e
--- /dev/null
+++ b/src/modules/m_spanningtree/ijoin.cpp
@@ -0,0 +1,75 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "commands.h"
+#include "utils.h"
+#include "treeserver.h"
+#include "treesocket.h"
+
+CmdResult CommandIJoin::HandleRemote(RemoteUser* user, Params& params)
+{
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ {
+ // Desync detected, recover
+ // Ignore the join and send RESYNC, this will result in the remote server sending all channel data to us
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received IJOIN for non-existant channel: " + params[0]);
+
+ CmdBuilder("RESYNC").push(params[0]).Unicast(user);
+
+ return CMD_FAILURE;
+ }
+
+ bool apply_modes;
+ if (params.size() > 3)
+ {
+ time_t RemoteTS = ServerCommand::ExtractTS(params[2]);
+ apply_modes = (RemoteTS <= chan->age);
+ }
+ else
+ apply_modes = false;
+
+ // Join the user and set the membership id to what they sent
+ Membership* memb = chan->ForceJoin(user, apply_modes ? &params[3] : NULL);
+ if (!memb)
+ return CMD_FAILURE;
+
+ memb->id = Membership::IdFromString(params[1]);
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandResync::HandleServer(TreeServer* server, CommandBase::Params& params)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resyncing " + params[0]);
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ {
+ // This can happen for a number of reasons, safe to ignore
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel does not exist");
+ return CMD_FAILURE;
+ }
+
+ if (!server->IsLocal())
+ throw ProtocolException("RESYNC from a server that is not directly connected");
+
+ // Send all known information about the channel
+ server->GetSocket()->SyncChannel(chan);
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/link.h b/src/modules/m_spanningtree/link.h
index 797f108d8..5b9361fcd 100644
--- a/src/modules/m_spanningtree/link.h
+++ b/src/modules/m_spanningtree/link.h
@@ -18,23 +18,22 @@
*/
-#ifndef M_SPANNINGTREE_LINK_H
-#define M_SPANNINGTREE_LINK_H
+#pragma once
class Link : public refcountbase
{
public:
reference<ConfigTag> tag;
- irc::string Name;
+ std::string Name;
std::string IPAddr;
- int Port;
+ unsigned int Port;
std::string SendPass;
std::string RecvPass;
std::string Fingerprint;
- std::string AllowMask;
+ std::vector<std::string> AllowMasks;
bool HiddenFromStats;
std::string Hook;
- int Timeout;
+ unsigned int Timeout;
std::string Bind;
bool Hidden;
Link(ConfigTag* Tag) : tag(Tag) {}
@@ -51,5 +50,3 @@ class Autoconnect : public refcountbase
int position;
Autoconnect(ConfigTag* Tag) : tag(Tag) {}
};
-
-#endif
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index 78d202c47..0ff180a83 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -21,13 +21,12 @@
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "socket.h"
#include "xline.h"
+#include "iohook.h"
+#include "modules/server.h"
-#include "cachetimer.h"
#include "resolvers.h"
#include "main.h"
#include "utils.h"
@@ -35,60 +34,71 @@
#include "link.h"
#include "treesocket.h"
#include "commands.h"
-#include "protocolinterface.h"
+#include "translate.h"
ModuleSpanningTree::ModuleSpanningTree()
- : KeepNickTS(false)
+ : Away::EventListener(this)
+ , Stats::EventListener(this)
+ , rconnect(this)
+ , rsquit(this)
+ , map(this)
+ , commands(this)
+ , currmembid(0)
+ , eventprov(this, "event/server")
+ , DNS(this, "DNS")
+ , loopCall(false)
{
- Utils = new SpanningTreeUtilities(this);
- commands = new SpanningTreeCommands(this);
- RefreshTimer = NULL;
}
SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
- : rconnect(module, module->Utils), rsquit(module, module->Utils),
- svsjoin(module), svspart(module), svsnick(module), metadata(module),
- uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
- fhost(module), fident(module), fname(module)
+ : svsjoin(module), svspart(module), svsnick(module), metadata(module),
+ uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
+ fmode(module), ftopic(module), fhost(module), fident(module), fname(module),
+ away(module), addline(module), delline(module), encap(module), idle(module),
+ nick(module), ping(module), pong(module), save(module),
+ server(module), squit(module), snonotice(module),
+ endburst(module), sinfo(module), num(module)
{
}
+namespace
+{
+ void SetLocalUsersServer(Server* newserver)
+ {
+ // Does not change the server of quitting users because those are not in the list
+
+ ServerInstance->FakeClient->server = newserver;
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ (*i)->server = newserver;
+ }
+
+ void ResetMembershipIds()
+ {
+ // Set all membership ids to 0
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ for (User::ChanList::iterator j = user->chans.begin(); j != user->chans.end(); ++j)
+ (*j)->id = 0;
+ }
+ }
+}
+
void ModuleSpanningTree::init()
{
- ServerInstance->Modules->AddService(commands->rconnect);
- ServerInstance->Modules->AddService(commands->rsquit);
- ServerInstance->Modules->AddService(commands->svsjoin);
- ServerInstance->Modules->AddService(commands->svspart);
- ServerInstance->Modules->AddService(commands->svsnick);
- ServerInstance->Modules->AddService(commands->metadata);
- ServerInstance->Modules->AddService(commands->uid);
- ServerInstance->Modules->AddService(commands->opertype);
- ServerInstance->Modules->AddService(commands->fjoin);
- ServerInstance->Modules->AddService(commands->fmode);
- ServerInstance->Modules->AddService(commands->ftopic);
- ServerInstance->Modules->AddService(commands->fhost);
- ServerInstance->Modules->AddService(commands->fident);
- ServerInstance->Modules->AddService(commands->fname);
- RefreshTimer = new CacheRefreshTimer(Utils);
- ServerInstance->Timers->AddTimer(RefreshTimer);
-
- Implementation eventlist[] =
- {
- I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange,
- I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin,
- I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule,
- I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash,
- I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats,
- I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection
- };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- delete ServerInstance->PI;
- ServerInstance->PI = new SpanningTreeProtocolInterface(Utils);
- loopCall = false;
-
- // update our local user count
- Utils->TreeRoot->SetUserCount(ServerInstance->Users->LocalUserCount());
+ ServerInstance->SNO->EnableSnomask('l', "LINK");
+
+ ResetMembershipIds();
+
+ Utils = new SpanningTreeUtilities(this);
+ Utils->TreeRoot = new TreeServer;
+
+ ServerInstance->PI = &protocolinterface;
+
+ delete ServerInstance->FakeClient->server;
+ SetLocalUsersServer(Utils->TreeRoot);
}
void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
@@ -98,44 +108,39 @@ void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
{
Parent = Current->GetParent()->GetName();
}
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+
+ const TreeServer::ChildServers& children = Current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName()))))
+ TreeServer* server = *i;
+ if ((server->Hidden) || ((Utils->HideULines) && (server->IsULine())))
{
- if (IS_OPER(user))
+ if (user->IsOper())
{
- ShowLinks(Current->GetChild(q),user,hops+1);
+ ShowLinks(server, user, hops+1);
}
}
else
{
- ShowLinks(Current->GetChild(q),user,hops+1);
+ ShowLinks(server, user, hops+1);
}
}
/* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
- if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!IS_OPER(user)))
+ if ((Utils->HideULines) && (Current->IsULine()) && (!user->IsOper()))
return;
/* Or if the server is hidden and they're not an oper */
- else if ((Current->Hidden) && (!IS_OPER(user)))
+ else if ((Current->Hidden) && (!user->IsOper()))
return;
- std::string servername = Current->GetName();
- user->WriteNumeric(364, "%s %s %s :%d %s", user->nick.c_str(), servername.c_str(),
- (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
- (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
- Current->GetDesc().c_str());
+ user->WriteNumeric(RPL_LINKS, Current->GetName(),
+ (((Utils->FlatLinks) && (!user->IsOper())) ? ServerInstance->Config->ServerName : Parent),
+ InspIRCd::Format("%d %s", (((Utils->FlatLinks) && (!user->IsOper())) ? 0 : hops), Current->GetDesc().c_str()));
}
-int ModuleSpanningTree::CountServs()
-{
- return Utils->serverlist.size();
-}
-
-void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
+void ModuleSpanningTree::HandleLinks(const CommandBase::Params& parameters, User* user)
{
ShowLinks(Utils->TreeRoot,user,0);
- user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
- return;
+ user->WriteNumeric(RPL_ENDOFLINKS, '*', "End of /LINKS list.");
}
std::string ModuleSpanningTree::TimeToStr(time_t secs)
@@ -152,79 +157,6 @@ std::string ModuleSpanningTree::TimeToStr(time_t secs)
+ ConvToStr(secs) + "s");
}
-void ModuleSpanningTree::DoPingChecks(time_t curtime)
-{
- /*
- * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
- * This prevents lost REMOTECONNECT notices
- */
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-
-restart:
- for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
- {
- TreeServer *s = i->second;
-
- if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING)
- {
- s->GetSocket()->Close();
- goto restart;
- }
-
- // Fix for bug #792, do not ping servers that are not connected yet!
- // Remote servers have Socket == NULL and local connected servers have
- // Socket->LinkState == CONNECTED
- if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED)
- continue;
-
- // Now do PING checks on all servers
- TreeServer *mts = Utils->BestRouteTo(s->GetID());
-
- if (mts)
- {
- // Only ping if this server needs one
- if (curtime >= s->NextPingTime())
- {
- // And if they answered the last
- if (s->AnsweredLastPing())
- {
- // They did, send a ping to them
- s->SetNextPingTime(curtime + Utils->PingFreq);
- TreeSocket *tsock = mts->GetSocket();
-
- // ... if we can find a proper route to them
- if (tsock)
- {
- tsock->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " +
- ServerInstance->Config->GetSID() + " " + s->GetID());
- s->LastPingMsec = ts;
- }
- }
- else
- {
- // They didn't answer the last ping, if they are locally connected, get rid of them.
- TreeSocket *sock = s->GetSocket();
- if (sock)
- {
- sock->SendError("Ping timeout");
- sock->Close();
- goto restart;
- }
- }
- }
-
- // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
- if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
- {
- /* The server hasnt responded, send a warning to opers */
- std::string servername = s->GetName();
- ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", servername.c_str(), Utils->PingWarnTime);
- s->Warned = true;
- }
- }
- }
-}
-
void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
{
if (!a)
@@ -266,14 +198,21 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
{
bool ipvalid = true;
- if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name), rfc_case_insensitive_map))
+ if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map))
{
ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Not connecting to myself.");
return;
}
- QueryType start_type = DNS_QUERY_AAAA;
- if (strchr(x->IPAddr.c_str(),':'))
+#ifndef _WIN32
+ if (x->IPAddr.find('/') != std::string::npos)
+ {
+ struct stat sb;
+ if (stat(x->IPAddr.c_str(), &sb) == -1 || !S_ISSOCK(sb.st_mode))
+ ipvalid = false;
+ }
+#endif
+ if (x->IPAddr.find(':') != std::string::npos)
{
in6_addr n;
if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
@@ -282,15 +221,15 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
else
{
in_addr n;
- if (inet_aton(x->IPAddr.c_str(),&n) < 1)
+ if (inet_pton(AF_INET, x->IPAddr.c_str(),&n) < 1)
ipvalid = false;
}
/* Do we already have an IP? If so, no need to resolve it. */
if (ipvalid)
{
- /* Gave a hook, but it wasnt one we know */
- TreeSocket* newsocket = new TreeSocket(Utils, x, y, x->IPAddr);
+ // Create a TreeServer object that will start connecting immediately in the background
+ TreeSocket* newsocket = new TreeSocket(x, y, x->IPAddr);
if (newsocket->GetFd() > -1)
{
/* Handled automatically on success */
@@ -302,17 +241,30 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
ServerInstance->GlobalCulls.AddItem(newsocket);
}
}
+ else if (!DNS)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and core_dns is not loaded, unable to resolve.", x->Name.c_str());
+ }
else
{
+ // Guess start_type from bindip aftype
+ DNS::QueryType start_type = DNS::QUERY_AAAA;
+ irc::sockets::sockaddrs bind;
+ if ((!x->Bind.empty()) && (irc::sockets::aptosa(x->Bind, 0, bind)))
+ {
+ if (bind.family() == AF_INET)
+ start_type = DNS::QUERY_A;
+ }
+
+ ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
try
{
- bool cached = false;
- ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
- ServerInstance->AddResolver(snr, cached);
+ DNS->Process(snr);
}
- catch (ModuleException& e)
+ catch (DNS::Exception& e)
{
- ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
+ delete snr;
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason().c_str());
ConnectServer(y, false);
}
}
@@ -333,12 +285,12 @@ void ModuleSpanningTree::AutoConnectServers(time_t curtime)
void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
{
- std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = Utils->timeoutlist.begin();
+ SpanningTreeUtilities::TimeoutList::iterator i = Utils->timeoutlist.begin();
while (i != Utils->timeoutlist.end())
{
TreeSocket* s = i->first;
- std::pair<std::string, int> p = i->second;
- std::map<TreeSocket*, std::pair<std::string, int> >::iterator me = i;
+ std::pair<std::string, unsigned int> p = i->second;
+ SpanningTreeUtilities::TimeoutList::iterator me = i;
i++;
if (s->GetLinkState() == DYING)
{
@@ -347,233 +299,139 @@ void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
}
else if (curtime > s->age + p.second)
{
- ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %d seconds)",p.first.c_str(),p.second);
+ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %u seconds)",p.first.c_str(),p.second);
Utils->timeoutlist.erase(me);
s->Close();
}
}
}
-ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleVersion(const CommandBase::Params& parameters, User* user)
{
- // we've already checked if pcnt > 0, so this is safe
+ // We've already confirmed that !parameters.empty(), so this is safe
TreeServer* found = Utils->FindServerMask(parameters[0]);
if (found)
{
- std::string Version = found->GetVersion();
- user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
if (found == Utils->TreeRoot)
{
- ServerInstance->Config->Send005(user);
+ // Pass to default VERSION handler.
+ return MOD_RES_PASSTHRU;
}
+
+ // If an oper wants to see the version then show the full version string instead of the normal,
+ // but only if it is non-empty.
+ // If it's empty it might be that the server is still syncing (full version hasn't arrived yet)
+ // or the server is a 2.0 server and does not send a full version.
+ bool showfull = ((user->IsOper()) && (!found->GetFullVersion().empty()));
+ const std::string& Version = (showfull ? found->GetFullVersion() : found->GetVersion());
+ user->WriteNumeric(RPL_VERSION, Version);
}
else
{
- user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server");
}
return MOD_RES_DENY;
}
-/* This method will attempt to get a message to a remote user.
- */
-void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
-{
- char text[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, format);
- vsnprintf(text, MAXBUF, format, argsPtr);
- va_end(argsPtr);
-
- if (IS_LOCAL(user))
- user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
- else
- ServerInstance->PI->SendUserNotice(user, text);
-}
-
-ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleConnect(const CommandBase::Params& parameters, User* user)
{
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
{
Link* x = *i;
- if (InspIRCd::Match(x->Name.c_str(),parameters[0], rfc_case_insensitive_map))
+ if (InspIRCd::Match(x->Name, parameters[0], ascii_case_insensitive_map))
{
- if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name), rfc_case_insensitive_map))
+ if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map))
{
- RemoteMessage(user, "*** CONNECT: Server \002%s\002 is ME, not connecting.",x->Name.c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Server \002%s\002 is ME, not connecting.", x->Name.c_str()));
return MOD_RES_DENY;
}
- TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
+ TreeServer* CheckDupe = Utils->FindServer(x->Name);
if (!CheckDupe)
{
- RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Connecting to server: \002%s\002 (%s:%d)", x->Name.c_str(), (x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()), x->Port));
ConnectServer(x);
return MOD_RES_DENY;
}
else
{
- std::string servername = CheckDupe->GetParent()->GetName();
- RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), servername.c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str()));
return MOD_RES_DENY;
}
}
}
- RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: No server matching \002%s\002 could be found in the config file.", parameters[0].c_str()));
return MOD_RES_DENY;
}
-void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
-{
- TreeServer* s = Utils->FindServer(servername);
- if (s)
- {
- description = s->GetDesc();
- }
-}
-
-void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
+void ModuleSpanningTree::OnUserInvite(User* source, User* dest, Channel* channel, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts)
{
if (IS_LOCAL(source))
{
- parameterlist params;
+ CmdBuilder params(source, "INVITE");
params.push_back(dest->uuid);
params.push_back(channel->name);
+ params.push_int(channel->age);
params.push_back(ConvToStr(expiry));
- Utils->DoOneToMany(source->uuid,"INVITE",params);
+ params.Broadcast();
}
}
-void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
-{
- // Drop remote events on the floor.
- if (!IS_LOCAL(user))
- return;
-
- parameterlist params;
- params.push_back(chan->name);
- params.push_back(":"+topic);
- Utils->DoOneToMany(user->uuid,"TOPIC",params);
-}
-
-void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
+ModResult ModuleSpanningTree::OnPreTopicChange(User* user, Channel* chan, const std::string& topic)
{
- if (IS_LOCAL(user))
+ // XXX: Deny topic changes if the current topic set time is the current time or is in the future because
+ // other servers will drop our FTOPIC. This restriction will be removed when the protocol is updated.
+ if ((chan->topicset >= ServerInstance->Time()) && (Utils->serverlist.size() > 1))
{
- parameterlist params;
- params.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"WALLOPS",params);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, "Retry topic change later");
+ return MOD_RES_DENY;
}
+ return MOD_RES_PASSTHRU;
}
-void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
{
- /* Server origin */
- if (user == NULL)
+ // Drop remote events on the floor.
+ if (!IS_LOCAL(user))
return;
- if (target_type == TYPE_USER)
- {
- User* d = (User*)dest;
- if (!IS_LOCAL(d) && IS_LOCAL(user))
- {
- parameterlist params;
- params.push_back(d->uuid);
- params.push_back(":"+text);
- Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
- }
- }
- else if (target_type == TYPE_CHANNEL)
- {
- if (IS_LOCAL(user))
- {
- Channel *c = (Channel*)dest;
- if (c)
- {
- std::string cname = c->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- Utils->GetListOfServersForChannel(c,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
- }
- }
- }
- }
- else if (target_type == TYPE_SERVER)
- {
- if (IS_LOCAL(user))
- {
- char* target = (char*)dest;
- parameterlist par;
- par.push_back(target);
- par.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"NOTICE",par);
- }
- }
+ CommandFTopic::Builder(user, chan).Broadcast();
}
-void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+void ModuleSpanningTree::OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details)
{
- /* Server origin */
- if (user == NULL)
+ if (!IS_LOCAL(user))
return;
- if (target_type == TYPE_USER)
+ const char* message_type = (details.type == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
+ if (target.type == MessageTarget::TYPE_USER)
{
- // route private messages which are targetted at clients only to the server
- // which needs to receive them
- User* d = (User*)dest;
- if (!IS_LOCAL(d) && (IS_LOCAL(user)))
+ User* d = target.Get<User>();
+ if (!IS_LOCAL(d))
{
- parameterlist params;
+ CmdBuilder params(user, message_type);
params.push_back(d->uuid);
- params.push_back(":"+text);
- Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
+ params.push_last(details.text);
+ params.Unicast(d);
}
}
- else if (target_type == TYPE_CHANNEL)
+ else if (target.type == MessageTarget::TYPE_CHANNEL)
{
- if (IS_LOCAL(user))
- {
- Channel *c = (Channel*)dest;
- if (c)
- {
- std::string cname = c->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- Utils->GetListOfServersForChannel(c,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
- }
- }
- }
+ Utils->SendChannelMessage(user->uuid, target.Get<Channel>(), details.text, target.status, details.exemptions, message_type);
}
- else if (target_type == TYPE_SERVER)
+ else if (target.type == MessageTarget::TYPE_SERVER)
{
- if (IS_LOCAL(user))
- {
- char* target = (char*)dest;
- parameterlist par;
- par.push_back(target);
- par.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
- }
+ const std::string* serverglob = target.Get<std::string>();
+ CmdBuilder par(user, message_type);
+ par.push_back(*serverglob);
+ par.push_last(details.text);
+ par.Broadcast();
}
}
void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
{
AutoConnectServers(curtime);
- DoPingChecks(curtime);
DoConnectTimeout(curtime);
}
@@ -582,25 +440,10 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user)
if (user->quitting)
return;
- parameterlist params;
- params.push_back(user->uuid);
- params.push_back(ConvToStr(user->age));
- params.push_back(user->nick);
- params.push_back(user->host);
- params.push_back(user->dhost);
- params.push_back(user->ident);
- params.push_back(user->GetIPString());
- params.push_back(ConvToStr(user->signon));
- params.push_back("+"+std::string(user->FormatModes(true)));
- params.push_back(":"+user->fullname);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
+ CommandUID::Builder(user).Broadcast();
- if (IS_OPER(user))
- {
- params.clear();
- params.push_back(user->oper->name);
- Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
- }
+ if (user->IsOper())
+ CommandOpertype::Builder(user).Broadcast();
for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
{
@@ -610,23 +453,36 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user)
ServerInstance->PI->SendMetaData(user, item->name, value);
}
- Utils->TreeRoot->SetUserCount(1); // increment by 1
+ Utils->TreeRoot->UserCount++;
}
-void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
{
// Only do this for local users
- if (IS_LOCAL(memb->user))
+ if (!IS_LOCAL(memb->user))
+ return;
+
+ // Assign the current membership id to the new Membership and increase it
+ memb->id = currmembid++;
+
+ if (created_by_local)
+ {
+ CommandFJoin::Builder params(memb->chan);
+ params.add(memb);
+ params.finalize();
+ params.Broadcast();
+ }
+ else
{
- parameterlist params;
- // set up their permissions and the channel TS with FJOIN.
- // All users are FJOINed now, because a module may specify
- // new joining permissions for the user.
+ CmdBuilder params(memb->user, "IJOIN");
params.push_back(memb->chan->name);
- params.push_back(ConvToStr(memb->chan->age));
- params.push_back(std::string("+") + memb->chan->ChanModes(true));
- params.push_back(memb->modes+","+memb->user->uuid);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+ params.push_int(memb->id);
+ if (!memb->modes.empty())
+ {
+ params.push_back(ConvToStr(memb->chan->age));
+ params.push_back(memb->modes);
+ }
+ params.Broadcast();
}
}
@@ -635,19 +491,15 @@ void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(newhost);
- Utils->DoOneToMany(user->uuid,"FHOST",params);
+ CmdBuilder(user, "FHOST").push(newhost).Broadcast();
}
-void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
+void ModuleSpanningTree::OnChangeRealName(User* user, const std::string& real)
{
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(":" + gecos);
- Utils->DoOneToMany(user->uuid,"FNAME",params);
+ CmdBuilder(user, "FNAME").push_last(real).Broadcast();
}
void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
@@ -655,101 +507,77 @@ void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(ident);
- Utils->DoOneToMany(user->uuid,"FIDENT",params);
+ CmdBuilder(user, "FIDENT").push(ident).Broadcast();
}
void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
{
if (IS_LOCAL(memb->user))
{
- parameterlist params;
+ CmdBuilder params(memb->user, "PART");
params.push_back(memb->chan->name);
if (!partmessage.empty())
- params.push_back(":"+partmessage);
- Utils->DoOneToMany(memb->user->uuid,"PART",params);
+ params.push_last(partmessage);
+ params.Broadcast();
}
}
void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
{
- if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+ if (IS_LOCAL(user))
{
- parameterlist params;
-
if (oper_message != reason)
+ ServerInstance->PI->SendMetaData(user, "operquit", oper_message);
+
+ CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
+ }
+ else
+ {
+ // Hide the message if one of the following is true:
+ // - User is being quit due to a netsplit and quietbursts is on
+ // - Server is a silent uline
+ TreeServer* server = TreeServer::Get(user);
+ bool hide = (((server->IsDead()) && (Utils->quiet_bursts)) || (server->IsSilentULine()));
+ if (!hide)
{
- params.push_back(":"+oper_message);
- Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
+ ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]",
+ user->server->GetName().c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_message.c_str());
}
- params.clear();
- params.push_back(":"+reason);
- Utils->DoOneToMany(user->uuid,"QUIT",params);
}
- // Regardless, We need to modify the user Counts..
- TreeServer* SourceServer = Utils->FindServer(user->server);
- if (SourceServer)
- {
- SourceServer->SetUserCount(-1); // decrement by 1
- }
+ // Regardless, update the UserCount
+ TreeServer::Get(user)->UserCount--;
}
void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
{
if (IS_LOCAL(user))
{
- parameterlist params;
+ // The nick TS is updated by the core, we don't do it
+ CmdBuilder params(user, "NICK");
params.push_back(user->nick);
-
- /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
- */
- if ((irc::string(user->nick.c_str()) != assign(oldnick)) && (!this->KeepNickTS))
- user->age = ServerInstance->Time();
-
params.push_back(ConvToStr(user->age));
- Utils->DoOneToMany(user->uuid,"NICK",params);
- this->KeepNickTS = false;
+ params.Broadcast();
}
- else if (!loopCall && user->nick == user->uuid)
+ else if (!loopCall)
{
- parameterlist params;
- params.push_back(user->uuid);
- params.push_back(ConvToStr(user->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Changed nick of remote user %s from %s to %s TS %lu by ourselves!", user->uuid.c_str(), oldnick.c_str(), user->nick.c_str(), (unsigned long) user->age);
}
}
void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
{
- parameterlist params;
+ if ((!IS_LOCAL(source)) && (source != ServerInstance->FakeClient))
+ return;
+
+ CmdBuilder params(source, "KICK");
params.push_back(memb->chan->name);
params.push_back(memb->user->uuid);
- params.push_back(":"+reason);
- if (IS_LOCAL(source))
- {
- Utils->DoOneToMany(source->uuid,"KICK",params);
- }
- else if (source == ServerInstance->FakeClient)
- {
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
- }
-}
-
-void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
-{
- if (!IS_LOCAL(source))
- return; // Only start routing if we're origin.
-
- ServerInstance->OperQuit.set(dest, operreason);
- parameterlist params;
- params.push_back(":"+operreason);
- Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
- params.clear();
- params.push_back(dest->uuid);
- params.push_back(":"+reason);
- Utils->DoOneToMany(source->uuid,"KILL",params);
+ // If a remote user is being kicked by us then send the membership id in the kick too
+ if (!IS_LOCAL(memb->user))
+ params.push_int(memb->id);
+ params.push_last(reason);
+ params.Broadcast();
}
void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
@@ -757,19 +585,29 @@ void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
if (loopCall)
return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one
- ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str());
// Send out to other servers
if (!parameter.empty() && parameter[0] != '-')
{
- parameterlist params;
+ CmdBuilder params((user ? user->uuid : ServerInstance->Config->GetSID()), "REHASH");
params.push_back(parameter);
- Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
+ params.Forward(user ? TreeServer::Get(user)->GetRoute() : NULL);
}
}
-void ModuleSpanningTree::OnRehash(User* user)
+void ModuleSpanningTree::ReadConfig(ConfigStatus& status)
{
+ // Did this rehash change the description of this server?
+ const std::string& newdesc = ServerInstance->Config->ServerDesc;
+ if (newdesc != Utils->TreeRoot->GetDesc())
+ {
+ // Broadcast a SINFO desc message to let the network know about the new description. This is the description
+ // string that is sent in the SERVER message initially and shown for example in WHOIS.
+ // We don't need to update the field itself in the Server object - the core does that.
+ CommandSInfo::Builder(Utils->TreeRoot, "desc", newdesc).Broadcast();
+ }
+
// Re-read config stuff
try
{
@@ -783,8 +621,8 @@ void ModuleSpanningTree::OnRehash(User* user)
std::string msg = "Error in configuration: ";
msg.append(e.GetReason());
ServerInstance->SNO->WriteToSnoMask('l', msg);
- if (user && !IS_LOCAL(user))
- ServerInstance->PI->SendSNONotice("L", msg);
+ if (status.srcuser && !IS_LOCAL(status.srcuser))
+ ServerInstance->PI->SendSNONotice('L', msg);
}
}
@@ -799,24 +637,41 @@ void ModuleSpanningTree::OnLoadModule(Module* mod)
data.push_back('=');
data.append(v.link_data);
}
- ServerInstance->PI->SendMetaData(NULL, "modules", data);
+ ServerInstance->PI->SendMetaData("modules", data);
}
void ModuleSpanningTree::OnUnloadModule(Module* mod)
{
- ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile);
+ if (!Utils)
+ return;
+ ServerInstance->PI->SendMetaData("modules", "-" + mod->ModuleSourceFile);
+
+ if (mod == this)
+ {
+ // We are being unloaded, inform modules about all servers splitting which cannot be done later when the servers are actually disconnected
+ const server_hash& servers = Utils->serverlist;
+ for (server_hash::const_iterator i = servers.begin(); i != servers.end(); ++i)
+ {
+ TreeServer* server = i->second;
+ if (!server->IsRoot())
+ FOREACH_MOD_CUSTOM(GetEventProvider(), ServerEventListener, OnServerSplit, (server));
+ }
+ return;
+ }
+
+ // Some other module is being unloaded. If it provides an IOHook we use, we must close that server connection now.
restart:
- unsigned int items = Utils->TreeRoot->ChildCount();
- for(unsigned int x = 0; x < items; x++)
+ // Close all connections which use an IO hook provided by this module
+ const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i)
{
- TreeServer* srv = Utils->TreeRoot->GetChild(x);
- TreeSocket* sock = srv->GetSocket();
- if (sock && sock->GetIOHook() == mod)
+ TreeSocket* sock = (*i)->GetSocket();
+ if (sock->GetModHook(mod))
{
sock->SendError("SSL module unloaded");
sock->Close();
- // XXX: The list we're iterating is modified by TreeSocket::Squit() which is called by Close()
+ // XXX: The list we're iterating is modified by TreeServer::SQuit() which is called by Close()
goto restart;
}
}
@@ -824,169 +679,100 @@ restart:
for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
{
TreeSocket* sock = i->first;
- if (sock->GetIOHook() == mod)
+ if (sock->GetModHook(mod))
sock->Close();
}
}
-// note: the protocol does not allow direct umode +o except
-// via NICK with 8 params. sending OPERTYPE infers +o modechange
-// locally.
void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
{
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(opertype);
- Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
+
+ // Note: The protocol does not allow direct umode +o;
+ // sending OPERTYPE infers +o modechange locally.
+ CommandOpertype::Builder(user).Broadcast();
}
void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
{
- if (!x->IsBurstable() || loopCall)
+ if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(x->type);
- params.push_back(x->Displayable());
- params.push_back(x->source);
- params.push_back(ConvToStr(x->set_time));
- params.push_back(ConvToStr(x->duration));
- params.push_back(":" + x->reason);
-
if (!user)
- {
- /* Server-set lines */
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
- }
- else if (IS_LOCAL(user))
- {
- /* User-set lines */
- Utils->DoOneToMany(user->uuid, "ADDLINE", params);
- }
+ user = ServerInstance->FakeClient;
+
+ CommandAddLine::Builder(x, user).Broadcast();
}
void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
{
- if (!x->IsBurstable() || loopCall)
+ if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(x->type);
- params.push_back(x->Displayable());
-
if (!user)
- {
- /* Server-unset lines */
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
- }
- else if (IS_LOCAL(user))
- {
- /* User-unset lines */
- Utils->DoOneToMany(user->uuid, "DELLINE", params);
- }
-}
-
-void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
-{
- if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
- {
- parameterlist params;
- std::string output_text;
-
- ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
+ user = ServerInstance->FakeClient;
- if (target_type == TYPE_USER)
- {
- User* u = (User*)dest;
- params.push_back(u->uuid);
- params.push_back(output_text);
- Utils->DoOneToMany(user->uuid, "MODE", params);
- }
- else
- {
- Channel* c = (Channel*)dest;
- params.push_back(c->name);
- params.push_back(ConvToStr(c->age));
- params.push_back(output_text);
- Utils->DoOneToMany(user->uuid, "FMODE", params);
- }
- }
+ CmdBuilder params(user, "DELLINE");
+ params.push_back(x->type);
+ params.push_back(x->Displayable());
+ params.Broadcast();
}
-ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
+void ModuleSpanningTree::OnUserAway(User* user)
{
if (IS_LOCAL(user))
- {
- parameterlist params;
- if (!awaymsg.empty())
- {
- params.push_back(ConvToStr(ServerInstance->Time()));
- params.push_back(":" + awaymsg);
- }
- Utils->DoOneToMany(user->uuid, "AWAY", params);
- }
-
- return MOD_RES_PASSTHRU;
+ CommandAway::Builder(user).Broadcast();
}
-void ModuleSpanningTree::OnRequest(Request& request)
+void ModuleSpanningTree::OnUserBack(User* user)
{
- if (!strcmp(request.id, "rehash"))
- Utils->Rehash();
+ if (IS_LOCAL(user))
+ CommandAway::Builder(user).Broadcast();
}
-void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
+void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags)
{
- TreeSocket* s = (TreeSocket*)opaque;
- std::string output_text;
+ if (processflags & ModeParser::MODE_LOCALONLY)
+ return;
- ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
+ if (u)
+ {
+ if (u->registered != REG_ALL)
+ return;
- if (target)
+ CmdBuilder params(source, "MODE");
+ params.push(u->uuid);
+ params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
+ params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+ params.Broadcast();
+ }
+ else
{
- if (target_type == TYPE_USER)
- {
- User* u = (User*)target;
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text);
- }
- else if (target_type == TYPE_CHANNEL)
- {
- Channel* c = (Channel*)target;
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
- }
+ CmdBuilder params(source, "FMODE");
+ params.push(c->name);
+ params.push_int(c->age);
+ params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
+ params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+ params.Broadcast();
}
}
-void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata)
-{
- TreeSocket* s = static_cast<TreeSocket*>(opaque);
- User* u = dynamic_cast<User*>(target);
- Channel* c = dynamic_cast<Channel*>(target);
- if (u)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
- else if (c)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
- else if (!target)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
-}
-
CullResult ModuleSpanningTree::cull()
{
- Utils->cull();
- ServerInstance->Timers->DelTimer(RefreshTimer);
+ if (Utils)
+ Utils->cull();
return this->Module::cull();
}
ModuleSpanningTree::~ModuleSpanningTree()
{
- delete ServerInstance->PI;
- ServerInstance->PI = new ProtocolInterface;
+ ServerInstance->PI = &ServerInstance->DefaultProtocolInterface;
- /* This will also free the listeners */
- delete Utils;
+ Server* newsrv = new Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
+ SetLocalUsersServer(newsrv);
- delete commands;
+ delete Utils;
}
Version ModuleSpanningTree::GetVersion()
@@ -998,12 +784,13 @@ Version ModuleSpanningTree::GetVersion()
* so that any activity it sees is FINAL, e.g. we arent going to send out
* a NICK message before m_cloaking has finished putting the +x on the user,
* etc etc.
- * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
+ * Therefore, we set our priority to PRIORITY_LAST to make sure we end up at the END of
* the module call queue.
*/
void ModuleSpanningTree::Prioritize()
{
ServerInstance->Modules->SetPriority(this, PRIORITY_LAST);
+ ServerInstance->Modules.SetPriority(this, I_OnPreTopicChange, PRIORITY_FIRST);
}
MODULE_INIT(ModuleSpanningTree)
diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h
index 3e0a83111..d619c43bc 100644
--- a/src/modules/m_spanningtree/main.h
+++ b/src/modules/m_spanningtree/main.h
@@ -21,11 +21,15 @@
*/
-#ifndef M_SPANNINGTREE_MAIN_H
-#define M_SPANNINGTREE_MAIN_H
+#pragma once
#include "inspircd.h"
-#include <stdarg.h>
+#include "event.h"
+#include "modules/dns.h"
+#include "modules/stats.h"
+#include "servercommand.h"
+#include "commands.h"
+#include "protocolinterface.h"
/** If you make a change which breaks the protocol, increment this.
* If you completely change the protocol, completely change the number.
@@ -36,12 +40,11 @@
* Failure to document your protocol changes will result in a painfully
* painful death by pain. You have been warned.
*/
-const long ProtocolVersion = 1202;
-const long MinCompatProtocol = 1201;
+const long ProtocolVersion = 1205;
+const long MinCompatProtocol = 1202;
/** Forward declarations
*/
-class SpanningTreeCommands;
class SpanningTreeUtilities;
class CacheRefreshTimer;
class TreeServer;
@@ -50,60 +53,63 @@ class Autoconnect;
/** This is the main class for the spanningtree module
*/
-class ModuleSpanningTree : public Module
+class ModuleSpanningTree
+ : public Module
+ , public Away::EventListener
+ , public Stats::EventListener
{
- SpanningTreeCommands* commands;
+ /** Client to server commands, registered in the core
+ */
+ CommandRConnect rconnect;
+ CommandRSQuit rsquit;
+ CommandMap map;
+
+ /** Server to server only commands, not registered in the core
+ */
+ SpanningTreeCommands commands;
+
+ /** Next membership id assigned when a local user joins a channel
+ */
+ Membership::Id currmembid;
+
+ /** The specialized ProtocolInterface that is assigned to ServerInstance->PI on load
+ */
+ SpanningTreeProtocolInterface protocolinterface;
+
+ /** Event provider for our events
+ */
+ Events::ModuleEventProvider eventprov;
public:
- SpanningTreeUtilities* Utils;
+ dynamic_reference<DNS::Manager> DNS;
+
+ ServerCommandManager CmdManager;
- CacheRefreshTimer *RefreshTimer;
/** Set to true if inside a spanningtree call, to prevent sending
* xlines and other things back to their source
*/
bool loopCall;
- /** If true OnUserPostNick() won't update the nick TS before sending the NICK,
- * used when handling SVSNICK.
- */
- bool KeepNickTS;
-
/** Constructor
*/
ModuleSpanningTree();
- void init();
+ void init() CXX11_OVERRIDE;
/** Shows /LINKS
*/
void ShowLinks(TreeServer* Current, User* user, int hops);
- /** Counts local and remote servers
- */
- int CountServs();
-
/** Handle LINKS command
*/
- void HandleLinks(const std::vector<std::string>& parameters, User* user);
-
- /** Show MAP output to a user (recursive)
- */
- void ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats);
-
- /** Handle MAP command
- */
- bool HandleMap(const std::vector<std::string>& parameters, User* user);
+ void HandleLinks(const CommandBase::Params& parameters, User* user);
/** Handle SQUIT
*/
- ModResult HandleSquit(const std::vector<std::string>& parameters, User* user);
+ ModResult HandleSquit(const CommandBase::Params& parameters, User* user);
/** Handle remote WHOIS
*/
- ModResult HandleRemoteWhois(const std::vector<std::string>& parameters, User* user);
-
- /** Ping all local servers
- */
- void DoPingChecks(time_t curtime);
+ ModResult HandleRemoteWhois(const CommandBase::Params& parameters, User* user);
/** Connect a server locally
*/
@@ -123,66 +129,52 @@ class ModuleSpanningTree : public Module
/** Handle remote VERSON
*/
- ModResult HandleVersion(const std::vector<std::string>& parameters, User* user);
+ ModResult HandleVersion(const CommandBase::Params& parameters, User* user);
/** Handle CONNECT
*/
- ModResult HandleConnect(const std::vector<std::string>& parameters, User* user);
-
- /** Attempt to send a message to a user
- */
- void RemoteMessage(User* user, const char* format, ...) CUSTOM_PRINTF(3, 4);
-
- /** Returns oper-specific MAP information
- */
- const std::string MapOperInfo(TreeServer* Current);
+ ModResult HandleConnect(const CommandBase::Params& parameters, User* user);
/** Display a time as a human readable string
*/
- std::string TimeToStr(time_t secs);
+ static std::string TimeToStr(time_t secs);
+
+ const Events::ModuleEventProvider& GetEventProvider() const { return eventprov; }
/**
** *** MODULE EVENTS ***
**/
- ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line);
- void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
- void OnGetServerDescription(const std::string &servername,std::string &description);
- void OnUserConnect(LocalUser* source);
- void OnUserInvite(User* source,User* dest,Channel* channel, time_t);
- void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
- void OnWallops(User* user, const std::string &text);
- void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
- void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
- void OnBackgroundTimer(time_t curtime);
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts);
- void OnChangeHost(User* user, const std::string &newhost);
- void OnChangeName(User* user, const std::string &gecos);
- void OnChangeIdent(User* user, const std::string &ident);
- void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts);
- void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message);
- void OnUserPostNick(User* user, const std::string &oldnick);
- void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts);
- void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
- void OnPreRehash(User* user, const std::string &parameter);
- void OnRehash(User* user);
- void OnOper(User* user, const std::string &opertype);
- void OnLine(User* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
- void OnAddLine(User *u, XLine *x);
- void OnDelLine(User *u, XLine *x);
- void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
- ModResult OnStats(char statschar, User* user, string_list &results);
- ModResult OnSetAway(User* user, const std::string &awaymsg);
- void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
- void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
- void OnLoadModule(Module* mod);
- void OnUnloadModule(Module* mod);
- ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
- void OnRequest(Request& request);
- CullResult cull();
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE;
+ void OnPostCommand(Command*, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop) CXX11_OVERRIDE;
+ void OnUserConnect(LocalUser* source) CXX11_OVERRIDE;
+ void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE;
+ ModResult OnPreTopicChange(User* user, Channel* chan, const std::string& topic) CXX11_OVERRIDE;
+ void OnPostTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE;
+ void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE;
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE;
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE;
+ void OnChangeHost(User* user, const std::string &newhost) CXX11_OVERRIDE;
+ void OnChangeRealName(User* user, const std::string& real) CXX11_OVERRIDE;
+ void OnChangeIdent(User* user, const std::string &ident) CXX11_OVERRIDE;
+ void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE;
+ void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE;
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE;
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE;
+ void OnPreRehash(User* user, const std::string &parameter) CXX11_OVERRIDE;
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+ void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE;
+ void OnAddLine(User *u, XLine *x) CXX11_OVERRIDE;
+ void OnDelLine(User *u, XLine *x) CXX11_OVERRIDE;
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
+ void OnUserAway(User* user) CXX11_OVERRIDE;
+ void OnUserBack(User* user) CXX11_OVERRIDE;
+ void OnLoadModule(Module* mod) CXX11_OVERRIDE;
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
+ ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+ void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) CXX11_OVERRIDE;
+ CullResult cull() CXX11_OVERRIDE;
~ModuleSpanningTree();
- Version GetVersion();
- void Prioritize();
+ Version GetVersion() CXX11_OVERRIDE;
+ void Prioritize() CXX11_OVERRIDE;
};
-
-#endif
diff --git a/src/modules/m_spanningtree/metadata.cpp b/src/modules/m_spanningtree/metadata.cpp
index a584f8fa8..52267c522 100644
--- a/src/modules/m_spanningtree/metadata.cpp
+++ b/src/modules/m_spanningtree/metadata.cpp
@@ -21,39 +21,76 @@
#include "inspircd.h"
#include "commands.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-CmdResult CommandMetadata::Handle(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandMetadata::Handle(User* srcuser, Params& params)
{
- std::string value = params.size() < 3 ? "" : params[2];
- ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
if (params[0] == "*")
{
- FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(NULL,params[1],value));
+ std::string value = params.size() < 3 ? "" : params[2];
+ FOREACH_MOD(OnDecodeMetaData, (NULL,params[1],value));
+ return CMD_SUCCESS;
}
- else if (*(params[0].c_str()) == '#')
+
+ if (params[0][0] == '#')
{
+ // Channel METADATA has an additional parameter: the channel TS
+ // :22D METADATA #channel 12345 extname :extdata
+ if (params.size() < 3)
+ throw ProtocolException("Insufficient parameters for channel METADATA");
+
Channel* c = ServerInstance->FindChan(params[0]);
- if (c)
- {
- if (item)
- item->unserialize(FORMAT_NETWORK, c, value);
- FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(c,params[1],value));
- }
+ if (!c)
+ return CMD_FAILURE;
+
+ time_t ChanTS = ServerCommand::ExtractTS(params[1]);
+ if (c->age < ChanTS)
+ // Their TS is newer than ours, discard this command and do not propagate
+ return CMD_FAILURE;
+
+ std::string value = params.size() < 4 ? "" : params[3];
+
+ ExtensionItem* item = ServerInstance->Extensions.GetItem(params[2]);
+ if ((item) && (item->type == ExtensionItem::EXT_CHANNEL))
+ item->unserialize(FORMAT_NETWORK, c, value);
+ FOREACH_MOD(OnDecodeMetaData, (c,params[2],value));
}
- else if (*(params[0].c_str()) != '#')
+ else
{
User* u = ServerInstance->FindUUID(params[0]);
- if ((u) && (!IS_SERVER(u)))
+ if (u)
{
- if (item)
+ ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
+ std::string value = params.size() < 3 ? "" : params[2];
+
+ if ((item) && (item->type == ExtensionItem::EXT_USER))
item->unserialize(FORMAT_NETWORK, u, value);
- FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(u,params[1],value));
+ FOREACH_MOD(OnDecodeMetaData, (u,params[1],value));
}
}
return CMD_SUCCESS;
}
+CommandMetadata::Builder::Builder(User* user, const std::string& key, const std::string& val)
+ : CmdBuilder("METADATA")
+{
+ push(user->uuid);
+ push(key);
+ push_last(val);
+}
+
+CommandMetadata::Builder::Builder(Channel* chan, const std::string& key, const std::string& val)
+ : CmdBuilder("METADATA")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push(key);
+ push_last(val);
+}
+
+CommandMetadata::Builder::Builder(const std::string& key, const std::string& val)
+ : CmdBuilder("METADATA")
+{
+ push("*");
+ push(key);
+ push_last(val);
+}
diff --git a/src/modules/m_spanningtree/misccommands.cpp b/src/modules/m_spanningtree/misccommands.cpp
new file mode 100644
index 000000000..8fc1b178f
--- /dev/null
+++ b/src/modules/m_spanningtree/misccommands.cpp
@@ -0,0 +1,42 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2007-2008, 2012 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "main.h"
+#include "commands.h"
+#include "treeserver.h"
+
+CmdResult CommandSNONotice::Handle(User* user, Params& params)
+{
+ ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + user->nick + ": " + params[1]);
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandEndBurst::HandleServer(TreeServer* server, Params& params)
+{
+ server->FinishBurst();
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/netburst.cpp b/src/modules/m_spanningtree/netburst.cpp
index 3bce90eda..30a37baee 100644
--- a/src/modules/m_spanningtree/netburst.cpp
+++ b/src/modules/m_spanningtree/netburst.cpp
@@ -21,11 +21,80 @@
#include "inspircd.h"
#include "xline.h"
+#include "listmode.h"
#include "treesocket.h"
#include "treeserver.h"
-#include "utils.h"
#include "main.h"
+#include "commands.h"
+#include "modules/server.h"
+
+/**
+ * Creates FMODE messages, used only when syncing channels
+ */
+class FModeBuilder : public CmdBuilder
+{
+ static const size_t maxline = 480;
+ std::string params;
+ unsigned int modes;
+ std::string::size_type startpos;
+
+ public:
+ FModeBuilder(Channel* chan)
+ : CmdBuilder("FMODE"), modes(0)
+ {
+ push(chan->name).push_int(chan->age).push_raw(" +");
+ startpos = str().size();
+ }
+
+ /** Add a mode to the message
+ */
+ void push_mode(const char modeletter, const std::string& mask)
+ {
+ push_raw(modeletter);
+ params.push_back(' ');
+ params.append(mask);
+ modes++;
+ }
+
+ /** Remove all modes from the message
+ */
+ void clear()
+ {
+ content.erase(startpos);
+ params.clear();
+ modes = 0;
+ }
+
+ /** Prepare the message for sending, next mode can only be added after clear()
+ */
+ const std::string& finalize()
+ {
+ return push_raw(params);
+ }
+
+ /** Returns true if the given mask can be added to the message, false if the message
+ * has no room for the mask
+ */
+ bool has_room(const std::string& mask) const
+ {
+ return ((str().size() + params.size() + mask.size() + 2 <= maxline) &&
+ (modes < ServerInstance->Config->Limits.MaxModes));
+ }
+
+ /** Returns true if this message is empty (has no modes)
+ */
+ bool empty() const
+ {
+ return (modes == 0);
+ }
+};
+
+struct TreeSocket::BurstState
+{
+ SpanningTreeProtocolInterface::Server server;
+ BurstState(TreeSocket* sock) : server(sock) { }
+};
/** This function is called when we want to send a netburst to a local
* server. There is a set order we must do this, because for example
@@ -34,157 +103,101 @@
*/
void TreeSocket::DoBurst(TreeServer* s)
{
- std::string servername = s->GetName();
ServerInstance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s%s).",
- servername.c_str(),
- capab->auth_fingerprint ? "SSL Fingerprint and " : "",
+ s->GetName().c_str(),
+ capab->auth_fingerprint ? "SSL certificate fingerprint and " : "",
capab->auth_challenge ? "challenge-response" : "plaintext password");
this->CleanNegotiationInfo();
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " BURST " + ConvToStr(ServerInstance->Time()));
- /* send our version string */
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " VERSION :"+ServerInstance->GetVersionString());
- /* Send server tree */
- this->SendServers(Utils->TreeRoot,s,1);
- /* Send users and their oper status */
- this->SendUsers();
- /* Send everything else (channel modes, xlines etc) */
- this->SendChannelModes();
+ this->WriteLine(CmdBuilder("BURST").push_int(ServerInstance->Time()));
+ // Introduce all servers behind us
+ this->SendServers(Utils->TreeRoot, s);
+
+ BurstState bs(this);
+ // Introduce all users
+ this->SendUsers(bs);
+
+ // Sync all channels
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ SyncChannel(i->second, bs);
+
+ // Send all xlines
this->SendXLines();
- FOREACH_MOD(I_OnSyncNetwork,OnSyncNetwork(Utils->Creator,(void*)this));
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " ENDBURST");
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnSyncNetwork, (bs.server));
+ this->WriteLine(CmdBuilder("ENDBURST"));
ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2.");
+
+ this->burstsent = true;
+}
+
+void TreeSocket::SendServerInfo(TreeServer* from)
+{
+ // Send public version string
+ this->WriteLine(CommandSInfo::Builder(from, "version", from->GetVersion()));
+
+ // Send full version string that contains more information and is shown to opers
+ this->WriteLine(CommandSInfo::Builder(from, "fullversion", from->GetFullVersion()));
+
+ // Send the raw version string that just contains the base info
+ this->WriteLine(CommandSInfo::Builder(from, "rawversion", from->GetRawVersion()));
}
-/** Recursively send the server tree with distances as hops.
+/** Recursively send the server tree.
* This is used during network burst to inform the other server
* (and any of ITS servers too) of what servers we know about.
* If at any point any of these servers already exist on the other
- * end, our connection may be terminated. The hopcounts given
- * by this function are relative, this doesn't matter so long as
- * they are all >1, as all the remote servers re-calculate them
- * to be relative too, with themselves as hop 0.
+ * end, our connection may be terminated.
*/
-void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
+void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
{
- char command[MAXBUF];
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ SendServerInfo(Current);
+
+ const TreeServer::ChildServers& children = Current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- TreeServer* recursive_server = Current->GetChild(q);
+ TreeServer* recursive_server = *i;
if (recursive_server != s)
{
- std::string recursive_servername = recursive_server->GetName();
- snprintf(command, MAXBUF, ":%s SERVER %s * %d %s :%s", Current->GetID().c_str(), recursive_servername.c_str(), hops,
- recursive_server->GetID().c_str(),
- recursive_server->GetDesc().c_str());
- this->WriteLine(command);
- this->WriteLine(":"+recursive_server->GetID()+" VERSION :"+recursive_server->GetVersion());
+ this->WriteLine(CommandServer::Builder(recursive_server));
/* down to next level */
- this->SendServers(recursive_server, s, hops+1);
+ this->SendServers(recursive_server, s);
}
}
}
/** Send one or more FJOINs for a channel of users.
- * If the length of a single line is more than 480-NICKMAX
- * in length, it is split over multiple lines.
+ * If the length of a single line is too long, it is split over multiple lines.
*/
void TreeSocket::SendFJoins(Channel* c)
{
- std::string buffer;
- char list[MAXBUF];
-
- size_t curlen, headlen;
- curlen = headlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu +%s :",
- ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long)c->age, c->ChanModes(true));
- int numusers = 0;
- char* ptr = list + curlen;
- bool looped_once = false;
-
- const UserMembList *ulist = c->GetUsers();
- std::string modes;
- std::string params;
+ CommandFJoin::Builder fjoin(c);
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
- size_t ptrlen = 0;
- std::string modestr = i->second->modes;
-
- if ((curlen + modestr.length() + i->first->uuid.length() + 4) > 480)
+ Membership* memb = i->second;
+ if (!fjoin.has_room(memb))
{
- // remove the final space
- if (ptr[-1] == ' ')
- ptr[-1] = '\0';
- buffer.append(list).append("\r\n");
- curlen = headlen;
- ptr = list + headlen;
- numusers = 0;
+ // No room for this user, send the line and prepare a new one
+ this->WriteLine(fjoin.finalize());
+ fjoin.clear();
}
-
- ptrlen = snprintf(ptr, MAXBUF-curlen, "%s,%s ", modestr.c_str(), i->first->uuid.c_str());
-
- looped_once = true;
-
- curlen += ptrlen;
- ptr += ptrlen;
-
- numusers++;
+ fjoin.add(memb);
}
-
- // Okay, permanent channels will (of course) need this \r\n anyway, numusers check is if there
- // actually were people in the channel (looped_once == true)
- if (!looped_once || numusers > 0)
- {
- // remove the final space
- if (ptr[-1] == ' ')
- ptr[-1] = '\0';
- buffer.append(list).append("\r\n");
- }
-
- unsigned int linesize = 1;
- for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
- {
- unsigned int size = b->data.length() + 2; // "b" and " "
- unsigned int nextsize = linesize + size;
-
- if ((modes.length() >= ServerInstance->Config->Limits.MaxModes) || (nextsize > FMODE_MAX_LENGTH))
- {
- /* Wrap */
- buffer.append(":").append(ServerInstance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
-
- modes.clear();
- params.clear();
- linesize = 1;
- }
-
- modes.push_back('b');
-
- params.push_back(' ');
- params.append(b->data);
-
- linesize += size;
- }
-
- /* Only send these if there are any */
- if (!modes.empty())
- buffer.append(":").append(ServerInstance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
-
- this->WriteLine(buffer);
+ this->WriteLine(fjoin.finalize());
}
/** Send all XLines we know about */
void TreeSocket::SendXLines()
{
- char data[MAXBUF];
- std::string n = ServerInstance->Config->GetSID();
- const char* sn = n.c_str();
-
std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
- time_t current = ServerInstance->Time();
- for (std::vector<std::string>::iterator it = types.begin(); it != types.end(); ++it)
+ for (std::vector<std::string>::const_iterator it = types.begin(); it != types.end(); ++it)
{
+ /* Expired lines are removed in XLineManager::GetAll() */
XLineLookup* lookup = ServerInstance->XLines->GetAll(*it);
+ /* lookup cannot be NULL in this case but a check won't hurt */
if (lookup)
{
for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
@@ -195,96 +208,99 @@ void TreeSocket::SendXLines()
if (!i->second->IsBurstable())
break;
- /* If it's expired, don't bother to burst it
- */
- if (i->second->duration && current > i->second->expiry)
- continue;
-
- snprintf(data,MAXBUF,":%s ADDLINE %s %s %s %lu %lu :%s",sn, it->c_str(), i->second->Displayable(),
- i->second->source.c_str(),
- (unsigned long)i->second->set_time,
- (unsigned long)i->second->duration,
- i->second->reason.c_str());
- this->WriteLine(data);
+ this->WriteLine(CommandAddLine::Builder(i->second));
}
}
}
}
-/** Send channel topic, modes and metadata */
-void TreeSocket::SendChannelModes()
+void TreeSocket::SendListModes(Channel* chan)
{
- char data[MAXBUF];
- std::string n = ServerInstance->Config->GetSID();
- const char* sn = n.c_str();
-
- for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
+ FModeBuilder fmode(chan);
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
{
- SendFJoins(c->second);
- if (!c->second->topic.empty())
+ ListModeBase* mh = *i;
+ ListModeBase::ModeList* list = mh->GetList(chan);
+ if (!list)
+ continue;
+
+ // Add all items on the list to the FMODE, send it whenever it becomes too long
+ const char modeletter = mh->GetModeChar();
+ for (ListModeBase::ModeList::const_iterator j = list->begin(); j != list->end(); ++j)
{
- snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s", sn, c->second->name.c_str(), (unsigned long)c->second->topicset, c->second->setby.c_str(), c->second->topic.c_str());
- this->WriteLine(data);
+ const std::string& mask = j->mask;
+ if (!fmode.has_room(mask))
+ {
+ // No room for this mask, send the current line as-is then add the mask to a
+ // new, empty FMODE message
+ this->WriteLine(fmode.finalize());
+ fmode.clear();
+ }
+ fmode.push_mode(modeletter, mask);
}
+ }
- for(Extensible::ExtensibleStore::const_iterator i = c->second->GetExtList().begin(); i != c->second->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_NETWORK, c->second, i->second);
- if (!value.empty())
- Utils->Creator->ProtoSendMetaData(this, c->second, item->name, value);
- }
+ if (!fmode.empty())
+ this->WriteLine(fmode.finalize());
+}
+
+/** Send channel users, topic, modes and global metadata */
+void TreeSocket::SyncChannel(Channel* chan, BurstState& bs)
+{
+ SendFJoins(chan);
- FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,Utils->Creator,this));
+ // If the topic was ever set, send it, even if it's empty now
+ // because a new empty topic should override an old non-empty topic
+ if (chan->topicset != 0)
+ this->WriteLine(CommandFTopic::Builder(chan));
+
+ SendListModes(chan);
+
+ for (Extensible::ExtensibleStore::const_iterator i = chan->GetExtList().begin(); i != chan->GetExtList().end(); i++)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_NETWORK, chan, i->second);
+ if (!value.empty())
+ this->WriteLine(CommandMetadata::Builder(chan, item->name, value));
}
+
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnSyncChannel, (chan, bs.server));
}
-/** send all users and their oper state/modes */
-void TreeSocket::SendUsers()
+void TreeSocket::SyncChannel(Channel* chan)
{
- char data[MAXBUF];
- for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+ BurstState bs(this);
+ SyncChannel(chan, bs);
+}
+
+/** Send all users and their state, including oper and away status and global metadata */
+void TreeSocket::SendUsers(BurstState& bs)
+{
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
{
- if (u->second->registered == REG_ALL)
- {
- TreeServer* theirserver = Utils->FindServer(u->second->server);
- if (theirserver)
- {
- snprintf(data,MAXBUF,":%s UID %s %lu %s %s %s %s %s %lu +%s :%s",
- theirserver->GetID().c_str(), /* Prefix: SID */
- u->second->uuid.c_str(), /* 0: UUID */
- (unsigned long)u->second->age, /* 1: TS */
- u->second->nick.c_str(), /* 2: Nick */
- u->second->host.c_str(), /* 3: Displayed Host */
- u->second->dhost.c_str(), /* 4: Real host */
- u->second->ident.c_str(), /* 5: Ident */
- u->second->GetIPString(), /* 6: IP string */
- (unsigned long)u->second->signon, /* 7: Signon time for WHOWAS */
- u->second->FormatModes(true), /* 8...n: Modes and params */
- u->second->fullname.c_str()); /* size-1: GECOS */
- this->WriteLine(data);
- if (IS_OPER(u->second))
- {
- snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->uuid.c_str(), u->second->oper->name.c_str());
- this->WriteLine(data);
- }
- if (IS_AWAY(u->second))
- {
- snprintf(data,MAXBUF,":%s AWAY %ld :%s", u->second->uuid.c_str(), (long)u->second->awaytime, u->second->awaymsg.c_str());
- this->WriteLine(data);
- }
- }
+ User* user = u->second;
+ if (user->registered != REG_ALL)
+ continue;
- for(Extensible::ExtensibleStore::const_iterator i = u->second->GetExtList().begin(); i != u->second->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
- if (!value.empty())
- Utils->Creator->ProtoSendMetaData(this, u->second, item->name, value);
- }
+ this->WriteLine(CommandUID::Builder(user));
+
+ if (user->IsOper())
+ this->WriteLine(CommandOpertype::Builder(user));
+
+ if (user->IsAway())
+ this->WriteLine(CommandAway::Builder(user));
- FOREACH_MOD(I_OnSyncUser,OnSyncUser(u->second,Utils->Creator,this));
+ const Extensible::ExtensibleStore& exts = user->GetExtList();
+ for (Extensible::ExtensibleStore::const_iterator i = exts.begin(); i != exts.end(); ++i)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
+ if (!value.empty())
+ this->WriteLine(CommandMetadata::Builder(user, item->name, value));
}
+
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnSyncUser, (user, bs.server));
}
}
-
diff --git a/src/modules/m_spanningtree/nick.cpp b/src/modules/m_spanningtree/nick.cpp
new file mode 100644
index 000000000..4f53941ce
--- /dev/null
+++ b/src/modules/m_spanningtree/nick.cpp
@@ -0,0 +1,64 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2007-2008, 2012 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "main.h"
+#include "utils.h"
+#include "commands.h"
+#include "treeserver.h"
+
+CmdResult CommandNick::HandleRemote(::RemoteUser* user, Params& params)
+{
+ if ((isdigit(params[0][0])) && (params[0] != user->uuid))
+ throw ProtocolException("Attempted to change nick to an invalid or non-matching UUID");
+
+ // Timestamp of the new nick
+ time_t newts = ServerCommand::ExtractTS(params[1]);
+
+ /*
+ * On nick messages, check that the nick doesn't already exist here.
+ * If it does, perform collision logic.
+ */
+ User* x = ServerInstance->FindNickOnly(params[0]);
+ if ((x) && (x != user) && (x->registered == REG_ALL))
+ {
+ // 'x' is the already existing user using the same nick as params[0]
+ // 'user' is the user trying to change nick to the in use nick
+ bool they_change = Utils->DoCollision(x, TreeServer::Get(user), newts, user->ident, user->GetIPString(), user->uuid, "NICK");
+ if (they_change)
+ {
+ // Remote client lost, or both lost, rewrite this nick change as a change to uuid before
+ // calling ChangeNick() and forwarding the message
+ params[0] = user->uuid;
+ params[1] = ConvToStr(CommandSave::SavedTimestamp);
+ newts = CommandSave::SavedTimestamp;
+ }
+ }
+
+ user->ChangeNick(params[0], newts);
+
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/nickcollide.cpp b/src/modules/m_spanningtree/nickcollide.cpp
index 38d59affb..62e200921 100644
--- a/src/modules/m_spanningtree/nickcollide.cpp
+++ b/src/modules/m_spanningtree/nickcollide.cpp
@@ -19,23 +19,25 @@
#include "inspircd.h"
-#include "xline.h"
#include "treesocket.h"
#include "treeserver.h"
#include "utils.h"
-
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
+#include "commandbuilder.h"
+#include "commands.h"
/*
* Yes, this function looks a little ugly.
* However, in some circumstances we may not have a User, so we need to do things this way.
- * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both.
- * Sends SAVEs as appropriate and forces nickchanges too.
+ * Returns true if remote or both lost, false otherwise.
+ * Sends SAVEs as appropriate and forces nick change of the user 'u' if our side loses or if both lose.
+ * Does not change the nick of the user that is trying to claim the nick of 'u', i.e. the "remote" user.
*/
-int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remoteident, const std::string &remoteip, const std::string &remoteuid)
+bool SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid, const char* collidecmd)
{
+ // At this point we're sure that a collision happened, increment the counter regardless of who wins
+ ServerInstance->stats.Collisions++;
+
/*
* Under old protocol rules, we would have had to kill both clients.
* Really, this sucks.
@@ -56,21 +58,14 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei
bool bChangeLocal = true;
bool bChangeRemote = true;
- /* for brevity, don't use the User - use defines to avoid any copy */
- #define localts u->age
- #define localident u->ident
- #define localip u->GetIPString()
-
- /* mmk. let's do this again. */
- if (remotets == localts)
+ // If the timestamps are not equal only one of the users has to change nick,
+ // otherwise both have to change
+ const time_t localts = u->age;
+ if (remotets != localts)
{
- /* equal. fuck them both! do nada, let the handler at the bottom figure this out. */
- }
- else
- {
- /* fuck. now it gets complex. */
-
/* first, let's see if ident@host matches. */
+ const std::string& localident = u->ident;
+ const std::string& localip = u->GetIPString();
bool SamePerson = (localident == remoteident)
&& (localip == remoteip);
@@ -81,19 +76,22 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei
if((SamePerson && remotets < localts) ||
(!SamePerson && remotets > localts))
{
- /* remote needs to change */
+ // Only remote needs to change
bChangeLocal = false;
}
else
{
- /* ours needs to change */
+ // Only ours needs to change
bChangeRemote = false;
}
}
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Nick collision on \"%s\" caused by %s: %s/%lu/%s@%s %d <-> %s/%lu/%s@%s %d", u->nick.c_str(), collidecmd,
+ u->uuid.c_str(), (unsigned long)localts, u->ident.c_str(), u->GetIPString().c_str(), bChangeLocal,
+ remoteuid.c_str(), (unsigned long)remotets, remoteident.c_str(), remoteip.c_str(), bChangeRemote);
+
/*
- * Cheat a little here. Instead of a dedicated command to change UID,
- * use SAVE and accept the losing client with its UID (as we know the SAVE will
+ * Send SAVE and accept the losing client with its UID (as we know the SAVE will
* not fail under any circumstances -- UIDs are netwide exclusive).
*
* This means that each side of a collide will generate one extra NICK back to where
@@ -107,38 +105,23 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei
{
/*
* Local-side nick needs to change. Just in case we are hub, and
- * this "local" nick is actually behind us, send an SAVE out.
+ * this "local" nick is actually behind us, send a SAVE out.
*/
- parameterlist params;
+ CmdBuilder params("SAVE");
params.push_back(u->uuid);
params.push_back(ConvToStr(u->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
-
- u->ForceNickChange(u->uuid.c_str());
+ params.Broadcast();
- if (!bChangeRemote)
- return 1;
+ u->ChangeNick(u->uuid, CommandSave::SavedTimestamp);
}
if (bChangeRemote)
{
- User *remote = ServerInstance->FindUUID(remoteuid);
/*
- * remote side needs to change. If this happens, we will modify
- * the UID or halt the propagation of the nick change command,
- * so other servers don't need to see the SAVE
+ * Remote side needs to change. If this happens, we modify the UID or NICK and
+ * send back a SAVE to the source.
*/
- WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets));
-
- if (remote)
- {
- /* nick change collide. Force change their nick. */
- remote->ForceNickChange(remoteuid.c_str());
- }
-
- if (!bChangeLocal)
- return 2;
+ CmdBuilder("SAVE").push(remoteuid).push_int(remotets).Unicast(server->ServerUser);
}
- return 3;
+ return bChangeRemote;
}
-
diff --git a/src/modules/m_spanningtree/num.cpp b/src/modules/m_spanningtree/num.cpp
new file mode 100644
index 000000000..f83f91286
--- /dev/null
+++ b/src/modules/m_spanningtree/num.cpp
@@ -0,0 +1,62 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "utils.h"
+#include "commands.h"
+#include "remoteuser.h"
+
+CmdResult CommandNum::HandleServer(TreeServer* server, CommandBase::Params& params)
+{
+ User* const target = ServerInstance->FindUUID(params[1]);
+ if (!target)
+ return CMD_FAILURE;
+
+ LocalUser* const localtarget = IS_LOCAL(target);
+ if (!localtarget)
+ return CMD_SUCCESS;
+
+ Numeric::Numeric numeric(ConvToInt(params[2]));
+ // Passing NULL is ok, in that case the numeric source becomes this server
+ numeric.SetServer(Utils->FindServerID(params[0]));
+ numeric.GetParams().insert(numeric.GetParams().end(), params.begin()+3, params.end());
+
+ localtarget->WriteNumeric(numeric);
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandNum::GetRouting(User* user, const Params& params)
+{
+ return ROUTE_UNICAST(params[1]);
+}
+
+CommandNum::Builder::Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric)
+ : CmdBuilder("NUM")
+{
+ TreeServer* const server = (numeric.GetServer() ? (static_cast<TreeServer*>(numeric.GetServer())) : Utils->TreeRoot);
+ push(server->GetID()).push(target->uuid).push(InspIRCd::Format("%03u", numeric.GetNumeric()));
+ const CommandBase::Params& params = numeric.GetParams();
+ if (!params.empty())
+ {
+ for (CommandBase::Params::const_iterator i = params.begin(); i != params.end()-1; ++i)
+ push(*i);
+ push_last(params.back());
+ }
+}
diff --git a/src/modules/m_spanningtree/opertype.cpp b/src/modules/m_spanningtree/opertype.cpp
index 97a4de8c2..473cdb857 100644
--- a/src/modules/m_spanningtree/opertype.cpp
+++ b/src/modules/m_spanningtree/opertype.cpp
@@ -26,21 +26,20 @@
/** Because the core won't let users or even SERVERS set +o,
* we use the OPERTYPE command to do this.
*/
-CmdResult CommandOpertype::Handle(const std::vector<std::string>& params, User *u)
+CmdResult CommandOpertype::HandleRemote(RemoteUser* u, CommandBase::Params& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
- std::string opertype = params[0];
- if (!IS_OPER(u))
+ const std::string& opertype = params[0];
+ if (!u->IsOper())
ServerInstance->Users->all_opers.push_back(u);
- u->modes[UM_OPERATOR] = 1;
- OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(" " + opertype);
- if (iter != ServerInstance->Config->oper_blocks.end())
+
+ ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+ u->SetMode(opermh, true);
+
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(opertype);
+ if (iter != ServerInstance->Config->OperTypes.end())
u->oper = iter->second;
else
- {
- u->oper = new OperInfo;
- u->oper->name = opertype;
- }
+ u->oper = new OperInfo(opertype);
if (Utils->quiet_bursts)
{
@@ -48,12 +47,17 @@ CmdResult CommandOpertype::Handle(const std::vector<std::string>& params, User *
* If quiet bursts are enabled, and server is bursting or silent uline (i.e. services),
* then do nothing. -- w00t
*/
- TreeServer* remoteserver = Utils->FindServer(u->server);
- if (remoteserver->bursting || ServerInstance->SilentULine(u->server))
+ TreeServer* remoteserver = TreeServer::Get(u);
+ if (remoteserver->IsBehindBursting() || remoteserver->IsSilentULine())
return CMD_SUCCESS;
}
- ServerInstance->SNO->WriteToSnoMask('O',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server.c_str(), u->nick.c_str(),u->ident.c_str(), u->host.c_str(), irc::Spacify(opertype.c_str()));
+ ServerInstance->SNO->WriteToSnoMask('O',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server->GetName().c_str(), u->nick.c_str(),u->ident.c_str(), u->GetRealHost().c_str(), opertype.c_str());
return CMD_SUCCESS;
}
+CommandOpertype::Builder::Builder(User* user)
+ : CmdBuilder(user, "OPERTYPE")
+{
+ push_last(user->oper->name);
+}
diff --git a/src/modules/m_spanningtree/override_map.cpp b/src/modules/m_spanningtree/override_map.cpp
index 04fa4bcab..693b07bad 100644
--- a/src/modules/m_spanningtree/override_map.cpp
+++ b/src/modules/m_spanningtree/override_map.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2014 Adam <Adam@anope.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
* Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
@@ -19,178 +20,204 @@
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
-const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)
+CommandMap::CommandMap(Module* Creator)
+ : Command(Creator, "MAP", 0, 1)
{
- time_t secs_up = ServerInstance->Time() - Current->age;
- return " [Up: " + TimeToStr(secs_up) + (Current->rtt == 0 ? "]" : " Lag: " + ConvToStr(Current->rtt) + "ms]");
+ Penalty = 2;
}
-void ModuleSpanningTree::ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats)
+static inline bool IsHidden(User* user, TreeServer* server)
{
- ServerInstance->Logs->Log("map",DEBUG,"ShowMap depth %d on line %d", depth, line);
- float percent;
-
- if (ServerInstance->Users->clientlist->size() == 0)
+ if (!user->IsOper())
{
- // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
- percent = 0;
+ if (server->Hidden)
+ return true;
+ if (Utils->HideULines && server->IsULine())
+ return true;
}
- else
+
+ return false;
+}
+
+// Calculate the map depth the servers go, and the longest server name
+static void GetDepthAndLen(TreeServer* current, unsigned int depth, unsigned int& max_depth, unsigned int& max_len)
+{
+ if (depth > max_depth)
+ max_depth = depth;
+ if (current->GetName().length() > max_len)
+ max_len = current->GetName().length();
+
+ const TreeServer::ChildServers& servers = current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i)
{
- percent = Current->GetUserCount() * 100.0 / ServerInstance->Users->clientlist->size();
+ TreeServer* child = *i;
+ GetDepthAndLen(child, depth + 1, max_depth, max_len);
}
+}
- const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";
-
- char* myname = names + 100 * line;
- char* mystat = stats + 50 * line;
- memset(myname, ' ', depth);
- int w = depth;
+static std::vector<std::string> GetMap(User* user, TreeServer* current, unsigned int max_len, unsigned int depth)
+{
+ float percent = 0;
- std::string servername = Current->GetName();
- if (IS_OPER(user))
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ if (!users.empty())
{
- w += snprintf(myname + depth, 99 - depth, "%s (%s)", servername.c_str(), Current->GetID().c_str());
+ // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
+ percent = current->UserCount * 100.0 / users.size();
}
- else
+
+ std::string buffer = current->GetName();
+ if (user->IsOper())
{
- w += snprintf(myname + depth, 99 - depth, "%s", servername.c_str());
+ buffer += " (" + current->GetID();
+
+ const std::string& cur_vers = current->GetRawVersion();
+ if (!cur_vers.empty())
+ buffer += " " + cur_vers;
+
+ buffer += ")";
}
- memset(myname + w, ' ', 100 - w);
- if (w > maxnamew)
- maxnamew = w;
- snprintf(mystat, 49, "%5d [%5.2f%%]%s", Current->GetUserCount(), percent, operdata.c_str());
- line++;
+ // Pad with spaces until its at max len, max_len must always be >= my names length
+ buffer.append(max_len - current->GetName().length(), ' ');
- if (IS_OPER(user) || !Utils->FlatLinks)
- depth = depth + 2;
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ buffer += InspIRCd::Format("%5d [%5.2f%%]", current->UserCount, percent);
+
+ if (user->IsOper())
{
- TreeServer* child = Current->GetChild(q);
- if (!IS_OPER(user)) {
- if (child->Hidden)
- continue;
- if ((Utils->HideULines) && (ServerInstance->ULine(child->GetName())))
- continue;
- }
- ShowMap(child, user, depth, line, names, maxnamew, stats);
+ time_t secs_up = ServerInstance->Time() - current->age;
+ buffer += " [Up: " + ModuleSpanningTree::TimeToStr(secs_up) + (current->rtt == 0 ? "]" : " Lag: " + ConvToStr(current->rtt) + "ms]");
}
-}
+ std::vector<std::string> map;
+ map.push_back(buffer);
-// Ok, prepare to be confused.
-// After much mulling over how to approach this, it struck me that
-// the 'usual' way of doing a /MAP isnt the best way. Instead of
-// keeping track of a ton of ascii characters, and line by line
-// under recursion working out where to place them using multiplications
-// and divisons, we instead render the map onto a backplane of characters
-// (a character matrix), then draw the branches as a series of "L" shapes
-// from the nodes. This is not only friendlier on CPU it uses less stack.
-bool ModuleSpanningTree::HandleMap(const std::vector<std::string>& parameters, User* user)
-{
- if (parameters.size() > 0)
+ const TreeServer::ChildServers& servers = current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i)
{
- /* Remote MAP, the server is within the 1st parameter */
- TreeServer* s = Utils->FindServerMask(parameters[0]);
- bool ret = false;
- if (!s)
+ TreeServer* child = *i;
+
+ if (IsHidden(user, child))
+ continue;
+
+ bool last = true;
+ for (TreeServer::ChildServers::const_iterator j = i + 1; last && j != servers.end(); ++j)
+ if (!IsHidden(user, *j))
+ last = false;
+
+ unsigned int next_len;
+
+ if (user->IsOper() || !Utils->FlatLinks)
{
- user->WriteNumeric(ERR_NOSUCHSERVER, "%s %s :No such server", user->nick.c_str(), parameters[0].c_str());
- ret = true;
+ // This child is indented by us, so remove the depth from the max length to align the users properly
+ next_len = max_len - 2;
}
- else if (s && s != Utils->TreeRoot)
+ else
{
- parameterlist params;
- params.push_back(parameters[0]);
-
- params[0] = s->GetName();
- Utils->DoOneToOne(user->uuid, "MAP", params, s->GetName());
- ret = true;
+ // This user can not see depth, so max_len remains constant
+ next_len = max_len;
}
- // Don't return if s == Utils->TreeRoot (us)
- if (ret)
- return true;
- }
+ // Build the map for this child
+ std::vector<std::string> child_map = GetMap(user, child, next_len, depth + 1);
- // These arrays represent a virtual screen which we will
- // "scratch" draw to, as the console device of an irc
- // client does not provide for a proper terminal.
- int totusers = ServerInstance->Users->clientlist->size();
- int totservers = this->CountServs();
- int maxnamew = 0;
- int line = 0;
- char* names = new char[totservers * 100];
- char* stats = new char[totservers * 50];
-
- // The only recursive bit is called here.
- ShowMap(Utils->TreeRoot,user,0,line,names,maxnamew,stats);
-
- // Process each line one by one.
- for (int l = 1; l < line; l++)
- {
- char* myname = names + 100 * l;
- // scan across the line looking for the start of the
- // servername (the recursive part of the algorithm has placed
- // the servers at indented positions depending on what they
- // are related to)
- int first_nonspace = 0;
-
- while (myname[first_nonspace] == ' ')
+ for (std::vector<std::string>::const_iterator j = child_map.begin(); j != child_map.end(); ++j)
{
- first_nonspace++;
+ const char* prefix;
+
+ if (user->IsOper() || !Utils->FlatLinks)
+ {
+ // If this server is not the root child
+ if (j != child_map.begin())
+ {
+ // If this child is not my last child, then add |
+ // to be able to "link" the next server in my list to me, and to indent this childs servers
+ if (!last)
+ prefix = "| ";
+ // Otherwise this is my last child, so just use a space as theres nothing else linked to me below this
+ else
+ prefix = " ";
+ }
+ // If we get here, this server must be the root child
+ else
+ {
+ // If this is the last child, it gets a `-
+ if (last)
+ prefix = "`-";
+ // Otherwise this isn't the last child, so it gets |-
+ else
+ prefix = "|-";
+ }
+ }
+ else
+ // User can't see depth, so use no prefix
+ prefix = "";
+
+ // Add line to the map
+ map.push_back(prefix + *j);
}
+ }
- first_nonspace--;
-
- // Draw the `- (corner) section: this may be overwritten by
- // another L shape passing along the same vertical pane, becoming
- // a |- (branch) section instead.
-
- myname[first_nonspace] = '-';
- myname[first_nonspace-1] = '`';
- int l2 = l - 1;
+ return map;
+}
- // Draw upwards until we hit the parent server, causing possibly
- // other corners (`-) to become branches (|-)
- while ((names[l2 * 100 + first_nonspace-1] == ' ') || (names[l2 * 100 + first_nonspace-1] == '`'))
+CmdResult CommandMap::Handle(User* user, const Params& parameters)
+{
+ if (parameters.size() > 0)
+ {
+ // Remote MAP, the target server is the 1st parameter
+ TreeServer* s = Utils->FindServerMask(parameters[0]);
+ if (!s)
{
- names[l2 * 100 + first_nonspace-1] = '|';
- l2--;
+ user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server");
+ return CMD_FAILURE;
}
+
+ if (!s->IsRoot())
+ return CMD_SUCCESS;
}
- float avg_users = totusers * 1.0 / line;
+ // Max depth and max server name length
+ unsigned int max_depth = 0;
+ unsigned int max_len = 0;
+ GetDepthAndLen(Utils->TreeRoot, 0, max_depth, max_len);
- ServerInstance->Logs->Log("map",DEBUG,"local");
- for (int t = 0; t < line; t++)
+ unsigned int max;
+ if (user->IsOper() || !Utils->FlatLinks)
{
- // terminate the string at maxnamew characters
- names[100 * t + maxnamew] = '\0';
- user->SendText(":%s %03d %s :%s %s", ServerInstance->Config->ServerName.c_str(),
- RPL_MAP, user->nick.c_str(), names + 100 * t, stats + 50 * t);
+ // Each level of the map is indented by 2 characters, making the max possible line (max_depth * 2) + max_len
+ max = (max_depth * 2) + max_len;
}
- user->SendText(":%s %03d %s :%d server%s and %d user%s, average %.2f users per server",
- ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(),
- line, (line > 1 ? "s" : ""), totusers, (totusers > 1 ? "s" : ""), avg_users);
- user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(),
- RPL_ENDMAP, user->nick.c_str());
+ else
+ {
+ // This user can't see any depth
+ max = max_len;
+ }
+
+ std::vector<std::string> map = GetMap(user, Utils->TreeRoot, max, 0);
+ for (std::vector<std::string>::const_iterator i = map.begin(); i != map.end(); ++i)
+ user->WriteRemoteNumeric(RPL_MAP, *i);
- delete[] names;
- delete[] stats;
+ size_t totusers = ServerInstance->Users->GetUsers().size();
+ float avg_users = (float) totusers / Utils->serverlist.size();
- return true;
+ user->WriteRemoteNumeric(RPL_MAPUSERS, InspIRCd::Format("%u server%s and %u user%s, average %.2f users per server",
+ (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users));
+ user->WriteRemoteNumeric(RPL_ENDMAP, "End of /MAP");
+
+ return CMD_SUCCESS;
}
+RouteDescriptor CommandMap::GetRouting(User* user, const Params& parameters)
+{
+ if (!parameters.empty())
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
diff --git a/src/modules/m_spanningtree/override_squit.cpp b/src/modules/m_spanningtree/override_squit.cpp
index 7d01c8149..eb224660d 100644
--- a/src/modules/m_spanningtree/override_squit.cpp
+++ b/src/modules/m_spanningtree/override_squit.cpp
@@ -17,48 +17,38 @@
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "socket.h"
-#include "xline.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
#include "treesocket.h"
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-ModResult ModuleSpanningTree::HandleSquit(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleSquit(const CommandBase::Params& parameters, User* user)
{
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (s)
{
- if (s == Utils->TreeRoot)
+ if (s->IsRoot())
{
- user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (" + parameters[0] + " matches local server name)");
return MOD_RES_DENY;
}
- TreeSocket* sock = s->GetSocket();
-
- if (sock)
+ if (s->IsLocal())
{
ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0].c_str(),user->nick.c_str());
- sock->Squit(s,"Server quit by " + user->GetFullRealHost());
- ServerInstance->SE->DelFd(sock);
- sock->Close();
+ s->SQuit("Server quit by " + user->GetFullRealHost());
}
else
{
- user->WriteServ("NOTICE %s :*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead.",user->nick.c_str());
+ user->WriteNotice("*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead.");
}
}
else
{
- user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** SQUIT: The server \002" + parameters[0] + "\002 does not exist on the network.");
}
return MOD_RES_DENY;
}
-
diff --git a/src/modules/m_spanningtree/override_stats.cpp b/src/modules/m_spanningtree/override_stats.cpp
index 688661b80..9b73837cb 100644
--- a/src/modules/m_spanningtree/override_stats.cpp
+++ b/src/modules/m_spanningtree/override_stats.cpp
@@ -18,30 +18,42 @@
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
#include "main.h"
#include "utils.h"
-#include "treeserver.h"
#include "link.h"
-#include "treesocket.h"
-ModResult ModuleSpanningTree::OnStats(char statschar, User* user, string_list &results)
+ModResult ModuleSpanningTree::OnStats(Stats::Context& stats)
{
- if ((statschar == 'c') || (statschar == 'n'))
+ if ((stats.GetSymbol() == 'c') || (stats.GetSymbol() == 'n'))
{
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
{
Link* L = *i;
- results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(L->HiddenFromStats ? "<hidden>" : L->IPAddr)+" * "+(*i)->Name.c_str()+" "+ConvToStr(L->Port)+" "+(L->Hook.empty() ? "plaintext" : L->Hook));
- if (statschar == 'c')
- results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+L->Name.c_str());
+ std::string ipaddr = "*@";
+ if (L->HiddenFromStats)
+ ipaddr.append("<hidden>");
+ else
+ ipaddr.append(L->IPAddr);
+
+ const std::string hook = (L->Hook.empty() ? "plaintext" : L->Hook);
+ stats.AddRow(213, stats.GetSymbol(), ipaddr, '*', L->Name, L->Port, hook);
+ if (stats.GetSymbol() == 'c')
+ stats.AddRow(244, 'H', '*', '*', L->Name);
+ }
+ return MOD_RES_DENY;
+ }
+ else if (stats.GetSymbol() == 'U')
+ {
+ ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ std::string name = i->second->getString("server");
+ if (!name.empty())
+ stats.AddRow(248, 'U', name);
}
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
-
diff --git a/src/modules/m_spanningtree/override_whois.cpp b/src/modules/m_spanningtree/override_whois.cpp
index ad8c6a6ef..6a64a9403 100644
--- a/src/modules/m_spanningtree/override_whois.cpp
+++ b/src/modules/m_spanningtree/override_whois.cpp
@@ -16,39 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commandbuilder.h"
-ModResult ModuleSpanningTree::HandleRemoteWhois(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleRemoteWhois(const CommandBase::Params& parameters, User* user)
{
- if ((IS_LOCAL(user)) && (parameters.size() > 1))
+ User* remote = ServerInstance->FindNickOnly(parameters[1]);
+ if (remote && !IS_LOCAL(remote))
{
- User* remote = ServerInstance->FindNickOnly(parameters[1]);
- if (remote && !IS_LOCAL(remote))
- {
- parameterlist params;
- params.push_back(remote->uuid);
- Utils->DoOneToOne(user->uuid,"IDLE",params,remote->server);
- return MOD_RES_DENY;
- }
- else if (!remote)
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
- user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), parameters[1].c_str());
- return MOD_RES_DENY;
- }
+ CmdBuilder(user, "IDLE").push(remote->uuid).Unicast(remote);
+ return MOD_RES_DENY;
+ }
+ else if (!remote)
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ user->WriteNumeric(RPL_ENDOFWHOIS, parameters[0], "End of /WHOIS list.");
+ return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
-
diff --git a/src/modules/m_spanningtree/ping.cpp b/src/modules/m_spanningtree/ping.cpp
index aec680b23..844feb35b 100644
--- a/src/modules/m_spanningtree/ping.cpp
+++ b/src/modules/m_spanningtree/ping.cpp
@@ -18,44 +18,24 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
+#include "utils.h"
-bool TreeSocket::LocalPing(const std::string &prefix, parameterlist &params)
+CmdResult CommandPing::Handle(User* user, Params& params)
{
- if (params.size() < 1)
- return true;
- if (params.size() == 1)
- {
- std::string stufftobounce = params[0];
- this->WriteLine(":"+ServerInstance->Config->GetSID()+" PONG "+stufftobounce);
- return true;
- }
- else
+ if (params[0] == ServerInstance->Config->GetSID())
{
- std::string forwardto = params[1];
- if (forwardto == ServerInstance->Config->ServerName || forwardto == ServerInstance->Config->GetSID())
- {
- // this is a ping for us, send back PONG to the requesting server
- params[1] = params[0];
- params[0] = forwardto;
- Utils->DoOneToOne(ServerInstance->Config->GetSID(),"PONG",params,params[1]);
- }
- else
- {
- // not for us, pass it on :)
- Utils->DoOneToOne(prefix,"PING",params,forwardto);
- }
- return true;
+ // PING for us, reply with a PONG
+ CmdBuilder reply("PONG");
+ reply.push_back(user->uuid);
+ if (params.size() >= 2)
+ // If there is a second parameter, append it
+ reply.push_back(params[1]);
+
+ reply.Unicast(user);
}
+ return CMD_SUCCESS;
}
-
-
diff --git a/src/modules/m_spanningtree/pingtimer.cpp b/src/modules/m_spanningtree/pingtimer.cpp
new file mode 100644
index 000000000..1c96259bf
--- /dev/null
+++ b/src/modules/m_spanningtree/pingtimer.cpp
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "pingtimer.h"
+#include "treeserver.h"
+#include "commandbuilder.h"
+
+PingTimer::PingTimer(TreeServer* ts)
+ : Timer(Utils->PingFreq)
+ , server(ts)
+ , state(PS_SENDPING)
+{
+}
+
+PingTimer::State PingTimer::TickInternal()
+{
+ // Timer expired, take next action based on what happened last time
+ if (state == PS_SENDPING)
+ {
+ // Last ping was answered, send next ping
+ server->GetSocket()->WriteLine(CmdBuilder("PING").push(server->GetID()));
+ LastPingMsec = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ // Warn next unless warnings are disabled. If they are, jump straight to timeout.
+ if (Utils->PingWarnTime)
+ return PS_WARN;
+ else
+ return PS_TIMEOUT;
+ }
+ else if (state == PS_WARN)
+ {
+ // No pong arrived in PingWarnTime seconds, send a warning to opers
+ ServerInstance->SNO->WriteToSnoMask('l', "Server \002%s\002 has not responded to PING for %d seconds, high latency.", server->GetName().c_str(), GetInterval());
+ return PS_TIMEOUT;
+ }
+ else // PS_TIMEOUT
+ {
+ // They didn't answer the last ping, if they are locally connected, get rid of them
+ if (server->IsLocal())
+ {
+ TreeSocket* sock = server->GetSocket();
+ sock->SendError("Ping timeout");
+ sock->Close();
+ }
+
+ // If the server is non-locally connected, don't do anything until we get a PONG.
+ // This is to avoid pinging the server and warning opers more than once.
+ // If they do answer eventually, we will move to the PS_SENDPING state and ping them again.
+ return PS_IDLE;
+ }
+}
+
+void PingTimer::SetState(State newstate)
+{
+ state = newstate;
+
+ // Set when should the next Tick() happen based on the state
+ if (state == PS_SENDPING)
+ SetInterval(Utils->PingFreq);
+ else if (state == PS_WARN)
+ SetInterval(Utils->PingWarnTime);
+ else if (state == PS_TIMEOUT)
+ SetInterval(Utils->PingFreq - Utils->PingWarnTime);
+
+ // If state == PS_IDLE, do not set the timer, see above why
+}
+
+bool PingTimer::Tick(time_t currtime)
+{
+ if (server->IsDead())
+ return false;
+
+ SetState(TickInternal());
+ return false;
+}
+
+void PingTimer::OnPong()
+{
+ // Calculate RTT
+ long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ server->rtt = ts - LastPingMsec;
+
+ // Change state to send ping next, also reschedules the timer appropriately
+ SetState(PS_SENDPING);
+}
diff --git a/src/modules/m_spanningtree/pingtimer.h b/src/modules/m_spanningtree/pingtimer.h
new file mode 100644
index 000000000..753558689
--- /dev/null
+++ b/src/modules/m_spanningtree/pingtimer.h
@@ -0,0 +1,77 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class TreeServer;
+
+/** Handles PINGing servers and killing them on timeout
+ */
+class PingTimer : public Timer
+{
+ enum State
+ {
+ /** Send PING next */
+ PS_SENDPING,
+ /** Warn opers next */
+ PS_WARN,
+ /** Kill the server next due to ping timeout */
+ PS_TIMEOUT,
+ /** Do nothing */
+ PS_IDLE
+ };
+
+ /** Server the timer is interacting with
+ */
+ TreeServer* const server;
+
+ /** What to do when the timer ticks next
+ */
+ State state;
+
+ /** Last ping time in milliseconds, used to calculate round trip time
+ */
+ unsigned long LastPingMsec;
+
+ /** Update internal state and reschedule timer according to the new state
+ * @param newstate State to change to
+ */
+ void SetState(State newstate);
+
+ /** Process timer tick event
+ * @return State to change to
+ */
+ State TickInternal();
+
+ /** Called by the TimerManager when the timer expires
+ * @param currtime Time now
+ * @return Always false, we reschedule ourselves instead
+ */
+ bool Tick(time_t currtime) CXX11_OVERRIDE;
+
+ public:
+ /** Construct the timer. This doesn't schedule the timer.
+ * @param server TreeServer to interact with
+ */
+ PingTimer(TreeServer* server);
+
+ /** Register a PONG from the server
+ */
+ void OnPong();
+};
diff --git a/src/modules/m_spanningtree/pong.cpp b/src/modules/m_spanningtree/pong.cpp
index 5966d05d9..718d5f0bb 100644
--- a/src/modules/m_spanningtree/pong.cpp
+++ b/src/modules/m_spanningtree/pong.cpp
@@ -18,65 +18,24 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
+#include "utils.h"
-bool TreeSocket::LocalPong(const std::string &prefix, parameterlist &params)
+CmdResult CommandPong::HandleServer(TreeServer* server, CommandBase::Params& params)
{
- if (params.size() < 1)
- return true;
-
- if (params.size() == 1)
+ if (server->IsBursting())
{
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (ServerSource)
- {
- ServerSource->SetPingFlag();
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- ServerSource->rtt = ts - ServerSource->LastPingMsec;
- }
+ ServerInstance->SNO->WriteGlobalSno('l', "Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", server->GetName().c_str());
+ server->FinishBurst();
}
- else
- {
- std::string forwardto = params[1];
- if (forwardto == ServerInstance->Config->GetSID() || forwardto == ServerInstance->Config->ServerName)
- {
- /*
- * this is a PONG for us
- * if the prefix is a user, check theyre local, and if they are,
- * dump the PONG reply back to their fd. If its a server, do nowt.
- * Services might want to send these s->s, but we dont need to yet.
- */
- User* u = ServerInstance->FindNick(prefix);
- if (u)
- {
- u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
- }
- TreeServer *ServerSource = Utils->FindServer(params[0]);
-
- if (ServerSource)
- {
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- ServerSource->rtt = ts - ServerSource->LastPingMsec;
- ServerSource->SetPingFlag();
- }
- }
- else
- {
- // not for us, pass it on :)
- Utils->DoOneToOne(prefix,"PONG",params,forwardto);
- }
+ if (params[0] == ServerInstance->Config->GetSID())
+ {
+ // PONG for us
+ server->OnPong();
}
-
- return true;
+ return CMD_SUCCESS;
}
-
diff --git a/src/modules/m_spanningtree/postcommand.cpp b/src/modules/m_spanningtree/postcommand.cpp
index 3f5d427e1..c7b4707b3 100644
--- a/src/modules/m_spanningtree/postcommand.cpp
+++ b/src/modules/m_spanningtree/postcommand.cpp
@@ -17,69 +17,55 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
+#include "commandbuilder.h"
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-void ModuleSpanningTree::OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line)
+void ModuleSpanningTree::OnPostCommand(Command* command, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop)
{
if (result == CMD_SUCCESS)
Utils->RouteCommand(NULL, command, parameters, user);
}
-void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &command, const parameterlist& parameters, User *user)
+void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscmd, const CommandBase::Params& parameters, User* user)
{
- if (!ServerInstance->Parser->IsValidCommand(command, parameters.size(), user))
- return;
-
- /* We know it's non-null because IsValidCommand returned true */
- Command* thiscmd = ServerInstance->Parser->GetHandler(command);
-
+ const std::string& command = thiscmd->name;
RouteDescriptor routing = thiscmd->GetRouting(user, parameters);
-
- std::string sent_cmd = command;
- parameterlist params;
-
if (routing.type == ROUTE_TYPE_LOCALONLY)
- {
- /* Broadcast when it's a core command with the default route descriptor and the source is a
- * remote user or a remote server
- */
+ return;
- Version ver = thiscmd->creator->GetVersion();
- if ((!(ver.Flags & VF_CORE)) || (IS_LOCAL(user)) || (IS_SERVER(user) == ServerInstance->FakeClient))
- return;
+ const bool encap = ((routing.type == ROUTE_TYPE_OPT_BCAST) || (routing.type == ROUTE_TYPE_OPT_UCAST));
+ CmdBuilder params(user, encap ? "ENCAP" : command.c_str());
+ TreeServer* sdest = NULL;
- routing = ROUTE_BROADCAST;
- }
- else if (routing.type == ROUTE_TYPE_OPT_BCAST)
+ if (routing.type == ROUTE_TYPE_OPT_BCAST)
{
- params.push_back("*");
+ params.push('*');
params.push_back(command);
- sent_cmd = "ENCAP";
}
- else if (routing.type == ROUTE_TYPE_OPT_UCAST)
+ else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
{
- TreeServer* sdest = FindServer(routing.serverdest);
+ sdest = static_cast<TreeServer*>(routing.server);
if (!sdest)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Trying to route ENCAP to nonexistent server %s",
- routing.serverdest.c_str());
- return;
+ // Assume the command handler already validated routing.serverdest and have only returned success if the target is something that the
+ // user executing the command is allowed to look up e.g. target is not an uuid if user is local.
+ sdest = FindRouteTarget(routing.serverdest);
+ if (!sdest)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistent server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
+ return;
+ }
+ }
+
+ if (encap)
+ {
+ params.push_back(sdest->GetID());
+ params.push_back(command);
}
- params.push_back(sdest->GetID());
- params.push_back(command);
- sent_cmd = "ENCAP";
}
else
{
@@ -88,14 +74,13 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &
if (!(ver.Flags & (VF_COMMON | VF_CORE)) && srcmodule != Creator)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Routed command %s from non-VF_COMMON module %s",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Routed command %s from non-VF_COMMON module %s",
command.c_str(), srcmodule->ModuleSourceFile.c_str());
return;
}
}
- std::string output_text;
- ServerInstance->Parser->TranslateUIDs(thiscmd->translation, parameters, output_text, true, thiscmd);
+ std::string output_text = CommandParser::TranslateUIDs(thiscmd->translation, parameters, true, thiscmd);
params.push_back(output_text);
@@ -106,59 +91,40 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &
if (ServerInstance->Modes->FindPrefix(dest[0]))
{
pfx = dest[0];
- dest = dest.substr(1);
+ dest.erase(dest.begin());
}
if (dest[0] == '#')
{
Channel* c = ServerInstance->FindChan(dest);
if (!c)
return;
- TreeServerList list;
// TODO OnBuildExemptList hook was here
- GetListOfServersForChannel(c,list,pfx, CUList());
- std::string data = ":" + user->uuid + " " + sent_cmd;
- for (unsigned int x = 0; x < params.size(); x++)
- data += " " + params[x];
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (origin && origin->GetSocket() == Sock)
- continue;
- if (Sock)
- Sock->WriteLine(data);
- }
+ CUList exempts;
+ SendChannelMessage(user->uuid, c, parameters[1], pfx, exempts, command.c_str(), origin ? origin->GetSocket() : NULL);
}
else if (dest[0] == '$')
{
- if (origin)
- DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName());
- else
- DoOneToMany(user->uuid, sent_cmd, params);
+ params.Forward(origin);
}
else
{
// user target?
User* d = ServerInstance->FindNick(dest);
- if (!d)
+ if (!d || IS_LOCAL(d))
return;
- TreeServer* tsd = BestRouteTo(d->server);
+ TreeServer* tsd = TreeServer::Get(d)->GetRoute();
if (tsd == origin)
// huh? no routing stuff around in a circle, please.
return;
- DoOneToOne(user->uuid, sent_cmd, params, d->server);
+ params.Unicast(d);
}
}
else if (routing.type == ROUTE_TYPE_BROADCAST || routing.type == ROUTE_TYPE_OPT_BCAST)
{
- if (origin)
- DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName());
- else
- DoOneToMany(user->uuid, sent_cmd, params);
+ params.Forward(origin);
}
else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
{
- if (origin && routing.serverdest == origin->GetName())
- return;
- DoOneToOne(user->uuid, sent_cmd, params, routing.serverdest);
+ params.Unicast(sdest->ServerUser);
}
}
diff --git a/src/modules/m_spanningtree/precommand.cpp b/src/modules/m_spanningtree/precommand.cpp
index b331571ca..5db8aafe3 100644
--- a/src/modules/m_spanningtree/precommand.cpp
+++ b/src/modules/m_spanningtree/precommand.cpp
@@ -18,20 +18,11 @@
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line)
+ModResult ModuleSpanningTree::OnPreCommand(std::string &command, CommandBase::Params& parameters, LocalUser *user, bool validated)
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
@@ -45,10 +36,6 @@ ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std
{
return this->HandleSquit(parameters,user);
}
- else if (command == "MAP")
- {
- return this->HandleMap(parameters,user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
- }
else if (command == "LINKS")
{
this->HandleLinks(parameters,user);
@@ -64,9 +51,7 @@ ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std
}
else if ((command == "VERSION") && (parameters.size() > 0))
{
- this->HandleVersion(parameters,user);
- return MOD_RES_DENY;
+ return this->HandleVersion(parameters,user);
}
return MOD_RES_PASSTHRU;
}
-
diff --git a/src/modules/m_spanningtree/protocolinterface.cpp b/src/modules/m_spanningtree/protocolinterface.cpp
index ca4147fea..e54a528d9 100644
--- a/src/modules/m_spanningtree/protocolinterface.cpp
+++ b/src/modules/m_spanningtree/protocolinterface.cpp
@@ -19,166 +19,105 @@
#include "inspircd.h"
-#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
#include "protocolinterface.h"
+#include "commands.h"
/*
* For documentation on this class, see include/protocol.h.
*/
-void SpanningTreeProtocolInterface::GetServerList(ProtoServerList &sl)
+void SpanningTreeProtocolInterface::GetServerList(ServerList& sl)
{
- sl.clear();
for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
{
- ProtoServer ps;
+ ServerInfo ps;
ps.servername = i->second->GetName();
TreeServer* s = i->second->GetParent();
ps.parentname = s ? s->GetName() : "";
- ps.usercount = i->second->GetUserCount();
- ps.opercount = i->second->GetOperCount();
- ps.gecos = i->second->GetDesc();
+ ps.usercount = i->second->UserCount;
+ ps.opercount = i->second->OperCount;
+ ps.description = i->second->GetDesc();
ps.latencyms = i->second->rtt;
sl.push_back(ps);
}
}
-bool SpanningTreeProtocolInterface::SendEncapsulatedData(const parameterlist &encap)
+bool SpanningTreeProtocolInterface::SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const CommandBase::Params& params, User* source)
{
- if (encap[0].find_first_of("*?") != std::string::npos)
+ if (!source)
+ source = ServerInstance->FakeClient;
+
+ CmdBuilder encap(source, "ENCAP");
+
+ // Are there any wildcards in the target string?
+ if (targetmask.find_first_of("*?") != std::string::npos)
{
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ENCAP", encap);
- return true;
+ // Yes, send the target string as-is; servers will decide whether or not it matches them
+ encap.push(targetmask).push(cmd).insert(params).Broadcast();
}
- return Utils->DoOneToOne(ServerInstance->Config->GetSID(), "ENCAP", encap, encap[0]);
-}
-
-void SpanningTreeProtocolInterface::SendMetaData(Extensible* target, const std::string &key, const std::string &data)
-{
- parameterlist params;
-
- User* u = dynamic_cast<User*>(target);
- Channel* c = dynamic_cast<Channel*>(target);
- if (u)
- params.push_back(u->uuid);
- else if (c)
- params.push_back(c->name);
else
- params.push_back("*");
+ {
+ // No wildcards which means the target string has to be the name of a known server
+ TreeServer* server = Utils->FindServer(targetmask);
+ if (!server)
+ return false;
- params.push_back(key);
- params.push_back(":" + data);
+ // Use the SID of the target in the message instead of the server name
+ encap.push(server->GetID()).push(cmd).insert(params).Unicast(server->ServerUser);
+ }
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"METADATA",params);
+ return true;
}
-void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
+void SpanningTreeProtocolInterface::BroadcastEncap(const std::string& cmd, const CommandBase::Params& params, User* source, User* omit)
{
- parameterlist params;
-
- params.push_back(channel->name);
- params.push_back(ConvToStr(ServerInstance->Time()));
- params.push_back(ServerInstance->Config->ServerName);
- params.push_back(":" + topic);
+ if (!source)
+ source = ServerInstance->FakeClient;
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FTOPIC", params);
+ // If omit is non-NULL we pass the route belonging to the user to Forward(),
+ // otherwise we pass NULL, which is equivalent to Broadcast()
+ TreeServer* server = (omit ? TreeServer::Get(omit)->GetRoute() : NULL);
+ CmdBuilder(source, "ENCAP * ").push_raw(cmd).insert(params).Forward(server);
}
-void SpanningTreeProtocolInterface::SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &translate)
+void SpanningTreeProtocolInterface::SendMetaData(User* u, const std::string& key, const std::string& data)
{
- if (modedata.empty())
- return;
-
- std::string outdata;
- ServerInstance->Parser->TranslateUIDs(translate, modedata, outdata);
-
- std::string uidtarget;
- ServerInstance->Parser->TranslateUIDs(TR_NICK, target, uidtarget);
-
- parameterlist outlist;
- outlist.push_back(uidtarget);
- outlist.push_back(outdata);
-
- User* a = ServerInstance->FindNick(uidtarget);
- if (a)
- {
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",outlist);
- return;
- }
- else
- {
- Channel* c = ServerInstance->FindChan(target);
- if (c)
- {
- outlist.insert(outlist.begin() + 1, ConvToStr(c->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",outlist);
- }
- }
+ CommandMetadata::Builder(u, key, data).Broadcast();
}
-void SpanningTreeProtocolInterface::SendSNONotice(const std::string &snomask, const std::string &text)
+void SpanningTreeProtocolInterface::SendMetaData(Channel* c, const std::string& key, const std::string& data)
{
- parameterlist p;
- p.push_back(snomask);
- p.push_back(":" + text);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "SNONOTICE", p);
+ CommandMetadata::Builder(c, key, data).Broadcast();
}
-void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
+void SpanningTreeProtocolInterface::SendMetaData(const std::string& key, const std::string& data)
{
- parameterlist p;
- p.push_back(target->uuid);
- p.push_back(":" + rawline);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", p, target->server);
-}
-
-void SpanningTreeProtocolInterface::SendChannel(Channel* target, char status, const std::string &text)
-{
- TreeServerList list;
- CUList exempt_list;
- Utils->GetListOfServersForChannel(target,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(text);
- }
+ CommandMetadata::Builder(key, data).Broadcast();
}
-
-void SpanningTreeProtocolInterface::SendChannelPrivmsg(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::Server::SendMetaData(const std::string& key, const std::string& data)
{
- std::string cname = target->name;
- if (status)
- cname.insert(0, 1, status);
-
- SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" PRIVMSG "+cname+" :"+text);
+ sock->WriteLine(CommandMetadata::Builder(key, data));
}
-void SpanningTreeProtocolInterface::SendChannelNotice(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::SendSNONotice(char snomask, const std::string &text)
{
- std::string cname = target->name;
- if (status)
- cname.insert(0, 1, status);
-
- SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" NOTICE "+cname+" :"+text);
+ CmdBuilder("SNONOTICE").push(snomask).push_last(text).Broadcast();
}
-void SpanningTreeProtocolInterface::SendUserPrivmsg(User* target, const std::string &text)
+void SpanningTreeProtocolInterface::SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype)
{
- parameterlist p;
- p.push_back(target->uuid);
- p.push_back(":" + text);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PRIVMSG", p, target->server);
+ const char* cmd = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
+ CUList exempt_list;
+ Utils->SendChannelMessage(ServerInstance->Config->GetSID(), target, text, status, exempt_list, cmd);
}
-void SpanningTreeProtocolInterface::SendUserNotice(User* target, const std::string &text)
+void SpanningTreeProtocolInterface::SendMessage(User* target, const std::string& text, MessageType msgtype)
{
- parameterlist p;
+ CmdBuilder p(msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
p.push_back(target->uuid);
- p.push_back(":" + text);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "NOTICE", p, target->server);
+ p.push_last(text);
+ p.Unicast(target);
}
diff --git a/src/modules/m_spanningtree/protocolinterface.h b/src/modules/m_spanningtree/protocolinterface.h
index 297366893..969ed68bf 100644
--- a/src/modules/m_spanningtree/protocolinterface.h
+++ b/src/modules/m_spanningtree/protocolinterface.h
@@ -17,32 +17,27 @@
*/
-#ifndef M_SPANNINGTREE_PROTOCOLINTERFACE_H
-#define M_SPANNINGTREE_PROTOCOLINTERFACE_H
-
-class SpanningTreeUtilities;
-class ModuleSpanningTree;
+#pragma once
class SpanningTreeProtocolInterface : public ProtocolInterface
{
- SpanningTreeUtilities* Utils;
- void SendChannel(Channel* target, char status, const std::string &text);
public:
- SpanningTreeProtocolInterface(SpanningTreeUtilities* util) : Utils(util) { }
- virtual ~SpanningTreeProtocolInterface() { }
-
- virtual bool SendEncapsulatedData(const parameterlist &encap);
- virtual void SendMetaData(Extensible* target, const std::string &key, const std::string &data);
- virtual void SendTopic(Channel* channel, std::string &topic);
- virtual void SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &types);
- virtual void SendSNONotice(const std::string &snomask, const std::string &text);
- virtual void PushToClient(User* target, const std::string &rawline);
- virtual void SendChannelPrivmsg(Channel* target, char status, const std::string &text);
- virtual void SendChannelNotice(Channel* target, char status, const std::string &text);
- virtual void SendUserPrivmsg(User* target, const std::string &text);
- virtual void SendUserNotice(User* target, const std::string &text);
- virtual void GetServerList(ProtoServerList &sl);
-};
+ class Server : public ProtocolInterface::Server
+ {
+ TreeSocket* const sock;
-#endif
+ public:
+ Server(TreeSocket* s) : sock(s) { }
+ void SendMetaData(const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ };
+ bool SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const CommandBase::Params& params, User* source) CXX11_OVERRIDE;
+ void BroadcastEncap(const std::string& cmd, const CommandBase::Params& params, User* source, User* omit) CXX11_OVERRIDE;
+ void SendMetaData(User* user, const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ void SendMetaData(Channel* chan, const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ void SendMetaData(const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ void SendSNONotice(char snomask, const std::string& text) CXX11_OVERRIDE;
+ void SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype) CXX11_OVERRIDE;
+ void SendMessage(User* target, const std::string& text, MessageType msgtype) CXX11_OVERRIDE;
+ void GetServerList(ServerList& sl) CXX11_OVERRIDE;
+};
diff --git a/src/modules/m_spanningtree/push.cpp b/src/modules/m_spanningtree/push.cpp
deleted file mode 100644
index b791376ea..000000000
--- a/src/modules/m_spanningtree/push.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::Push(const std::string &prefix, parameterlist &params)
-{
- if (params.size() < 2)
- return true;
- User* u = ServerInstance->FindNick(params[0]);
- if (!u)
- return true;
- if (IS_LOCAL(u))
- {
- u->Write(params[1]);
- }
- else
- {
- // continue the raw onwards
- params[1] = ":" + params[1];
- Utils->DoOneToOne(prefix,"PUSH",params,u->server);
- }
- return true;
-}
-
diff --git a/src/modules/m_spanningtree/rconnect.cpp b/src/modules/m_spanningtree/rconnect.cpp
index d4254cac6..7779355e2 100644
--- a/src/modules/m_spanningtree/rconnect.cpp
+++ b/src/modules/m_spanningtree/rconnect.cpp
@@ -19,34 +19,25 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "resolvers.h"
#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "link.h"
-#include "treesocket.h"
#include "commands.h"
-CommandRConnect::CommandRConnect (Module* Creator, SpanningTreeUtilities* Util)
- : Command(Creator, "RCONNECT", 2), Utils(Util)
+CommandRConnect::CommandRConnect (Module* Creator)
+ : Command(Creator, "RCONNECT", 2)
{
flags_needed = 'o';
syntax = "<remote-server-mask> <target-server-mask>";
}
-CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandRConnect::Handle(User* user, const Params& parameters)
{
- if (IS_LOCAL(user))
+ /* First see if the server which is being asked to connect to another server in fact exists */
+ if (!Utils->FindServerMask(parameters[0]))
{
- if (!Utils->FindServerMask(parameters[0]))
- {
- user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick.c_str(),parameters[0].c_str(),parameters[1].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** RCONNECT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str()));
+ return CMD_FAILURE;
}
/* Is this aimed at our server? */
@@ -54,14 +45,29 @@ CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, U
{
/* Yes, initiate the given connect */
ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick.c_str(),parameters[0].c_str(),parameters[1].c_str());
- std::vector<std::string> para;
+ CommandBase::Params para;
para.push_back(parameters[1]);
((ModuleSpanningTree*)(Module*)creator)->HandleConnect(para, user);
}
+ else
+ {
+ /* It's not aimed at our server, but if the request originates from our user
+ * acknowledge that we sent the request.
+ *
+ * It's possible that we're asking a server for something that makes no sense
+ * (e.g. connect to itself or to an already connected server), but we don't check
+ * for those conditions here, as ModuleSpanningTree::HandleConnect() (which will run
+ * on the target) does all the checking and error reporting.
+ */
+ if (IS_LOCAL(user))
+ {
+ user->WriteNotice("*** RCONNECT: Sending remote connect to \002 " + parameters[0] + "\002 to connect server \002" + parameters[1] + "\002.");
+ }
+ }
return CMD_SUCCESS;
}
-RouteDescriptor CommandRConnect::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandRConnect::GetRouting(User* user, const Params& parameters)
{
return ROUTE_UNICAST(parameters[0]);
}
diff --git a/src/modules/account.h b/src/modules/m_spanningtree/remoteuser.cpp
index ba671ba0b..717a6fd9f 100644
--- a/src/modules/account.h
+++ b/src/modules/m_spanningtree/remoteuser.cpp
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -17,28 +17,17 @@
*/
-#ifndef ACCOUNT_H
-#define ACCOUNT_H
+#include "inspircd.h"
-#include <map>
-#include <string>
+#include "main.h"
+#include "remoteuser.h"
-class AccountEvent : public Event
+SpanningTree::RemoteUser::RemoteUser(const std::string& uid, Server* srv)
+ : ::RemoteUser(uid, srv)
{
- public:
- User* const user;
- const std::string account;
- AccountEvent(Module* me, User* u, const std::string& name)
- : Event(me, "account_login"), user(u), account(name)
- {
- }
-};
-
-typedef StringExtItem AccountExtItem;
+}
-inline AccountExtItem* GetAccountExtItem()
+void SpanningTree::RemoteUser::WriteRemoteNumeric(const Numeric::Numeric& numeric)
{
- return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname"));
+ CommandNum::Builder(this, numeric).Unicast(this);
}
-
-#endif
diff --git a/src/modules/sasl.h b/src/modules/m_spanningtree/remoteuser.h
index f67351104..416f2f760 100644
--- a/src/modules/sasl.h
+++ b/src/modules/m_spanningtree/remoteuser.h
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -17,18 +17,16 @@
*/
-#ifndef SASL_H
-#define SASL_H
+#pragma once
-class SASLFallback : public Event
+namespace SpanningTree
+{
+ class RemoteUser;
+}
+
+class SpanningTree::RemoteUser : public ::RemoteUser
{
public:
- const parameterlist& params;
- SASLFallback(Module* me, const parameterlist& p)
- : Event(me, "sasl_fallback"), params(p)
- {
- Send();
- }
+ RemoteUser(const std::string& uid, Server* srv);
+ void WriteRemoteNumeric(const Numeric::Numeric& numeric) CXX11_OVERRIDE;
};
-
-#endif
diff --git a/src/modules/m_spanningtree/resolvers.cpp b/src/modules/m_spanningtree/resolvers.cpp
index d7c4c5227..ded0573af 100644
--- a/src/modules/m_spanningtree/resolvers.cpp
+++ b/src/modules/m_spanningtree/resolvers.cpp
@@ -19,9 +19,8 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
+#include "cachetimer.h"
#include "resolvers.h"
#include "main.h"
#include "utils.h"
@@ -29,29 +28,35 @@
#include "link.h"
#include "treesocket.h"
-/* $ModDep: m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
-
/** This class is used to resolve server hostnames during /connect and autoconnect.
* As of 1.1, the resolver system is seperated out from BufferedSocket, so we must do this
* resolver step first ourselves if we need it. This is totally nonblocking, and will
* callback to OnLookupComplete or OnError when completed. Once it has completed we
* will have an IP address which we can then use to continue our connection.
*/
-ServernameResolver::ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac)
- : Resolver(hostname, qt, cached, Util->Creator), Utils(Util), query(qt), host(hostname), MyLink(x), myautoconnect(myac)
+ServernameResolver::ServernameResolver(DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt, Autoconnect* myac)
+ : DNS::Request(mgr, Utils->Creator, hostname, qt)
+ , query(qt), host(hostname), MyLink(x), myautoconnect(myac)
{
}
-void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void ServernameResolver::OnLookupComplete(const DNS::Query *r)
{
+ const DNS::ResourceRecord* const ans_record = r->FindAnswerOfType(this->question.type);
+ if (!ans_record)
+ {
+ OnError(r);
+ return;
+ }
+
/* Initiate the connection, now that we have an IP to use.
* Passing a hostname directly to BufferedSocket causes it to
* just bail and set its FD to -1.
*/
- TreeServer* CheckDupe = Utils->FindServer(MyLink->Name.c_str());
+ TreeServer* CheckDupe = Utils->FindServer(MyLink->Name);
if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
{
- TreeSocket* newsocket = new TreeSocket(Utils, MyLink, myautoconnect, result);
+ TreeSocket* newsocket = new TreeSocket(MyLink, myautoconnect, ans_record->rdata);
if (newsocket->GetFd() > -1)
{
/* We're all OK */
@@ -66,47 +71,83 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in
}
}
-void ServernameResolver::OnError(ResolverError e, const std::string &errormessage)
+void ServernameResolver::OnError(const DNS::Query *r)
{
- /* Ooops! */
- if (query == DNS_QUERY_AAAA)
+ if (r->error == DNS::ERROR_UNLOADED)
{
- bool cached = false;
- ServernameResolver* snr = new ServernameResolver(Utils, host, MyLink, cached, DNS_QUERY_A, myautoconnect);
- ServerInstance->AddResolver(snr, cached);
+ // We're being unloaded, skip the snotice and ConnectServer() below to prevent autoconnect creating new sockets
return;
}
- ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), errormessage.c_str() );
+
+ if (query == DNS::QUERY_AAAA)
+ {
+ ServernameResolver* snr = new ServernameResolver(this->manager, host, MyLink, DNS::QUERY_A, myautoconnect);
+ try
+ {
+ this->manager->Process(snr);
+ return;
+ }
+ catch (DNS::Exception &)
+ {
+ delete snr;
+ }
+ }
+
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str());
Utils->Creator->ConnectServer(myautoconnect, false);
}
-SecurityIPResolver::SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt)
- : Resolver(hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt)
+SecurityIPResolver::SecurityIPResolver(Module* me, DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt)
+ : DNS::Request(mgr, me, hostname, qt)
+ , MyLink(x), mine(me), host(hostname), query(qt)
{
}
-void SecurityIPResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void SecurityIPResolver::OnLookupComplete(const DNS::Query *r)
{
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
{
Link* L = *i;
if (L->IPAddr == host)
{
- Utils->ValidIPs.push_back(result);
+ for (std::vector<DNS::ResourceRecord>::const_iterator j = r->answers.begin(); j != r->answers.end(); ++j)
+ {
+ const DNS::ResourceRecord& ans_record = *j;
+ if (ans_record.type == this->question.type)
+ Utils->ValidIPs.push_back(ans_record.rdata);
+ }
break;
}
}
}
-void SecurityIPResolver::OnError(ResolverError e, const std::string &errormessage)
+void SecurityIPResolver::OnError(const DNS::Query *r)
{
- if (query == DNS_QUERY_AAAA)
+ // This can be called because of us being unloaded but we don't have to do anything differently
+ if (query == DNS::QUERY_AAAA)
{
- bool cached = false;
- SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, host, MyLink, cached, DNS_QUERY_A);
- ServerInstance->AddResolver(res, cached);
- return;
+ SecurityIPResolver* res = new SecurityIPResolver(mine, this->manager, host, MyLink, DNS::QUERY_A);
+ try
+ {
+ this->manager->Process(res);
+ return;
+ }
+ catch (DNS::Exception &)
+ {
+ delete res;
+ }
}
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Could not resolve IP associated with Link '%s': %s",
- MyLink->Name.c_str(),errormessage.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Could not resolve IP associated with Link '%s': %s",
+ MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str());
+}
+
+CacheRefreshTimer::CacheRefreshTimer()
+ : Timer(3600, true)
+{
+}
+
+bool CacheRefreshTimer::Tick(time_t TIME)
+{
+ Utils->RefreshIPCache();
+ return true;
}
diff --git a/src/modules/m_spanningtree/resolvers.h b/src/modules/m_spanningtree/resolvers.h
index 65b9e7249..782ac86ef 100644
--- a/src/modules/m_spanningtree/resolvers.h
+++ b/src/modules/m_spanningtree/resolvers.h
@@ -18,30 +18,27 @@
*/
-#ifndef M_SPANNINGTREE_RESOLVERS_H
-#define M_SPANNINGTREE_RESOLVERS_H
+#pragma once
-#include "socket.h"
#include "inspircd.h"
-#include "xline.h"
+#include "modules/dns.h"
#include "utils.h"
#include "link.h"
/** Handle resolving of server IPs for the cache
*/
-class SecurityIPResolver : public Resolver
+class SecurityIPResolver : public DNS::Request
{
private:
reference<Link> MyLink;
- SpanningTreeUtilities* Utils;
Module* mine;
std::string host;
- QueryType query;
+ DNS::QueryType query;
public:
- SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt);
- void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
- void OnError(ResolverError e, const std::string &errormessage);
+ SecurityIPResolver(Module* me, DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt);
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE;
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE;
};
/** This class is used to resolve server hostnames during /connect and autoconnect.
@@ -50,18 +47,15 @@ class SecurityIPResolver : public Resolver
* callback to OnLookupComplete or OnError when completed. Once it has completed we
* will have an IP address which we can then use to continue our connection.
*/
-class ServernameResolver : public Resolver
+class ServernameResolver : public DNS::Request
{
private:
- SpanningTreeUtilities* Utils;
- QueryType query;
+ DNS::QueryType query;
std::string host;
reference<Link> MyLink;
reference<Autoconnect> myautoconnect;
public:
- ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac);
- void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
- void OnError(ResolverError e, const std::string &errormessage);
+ ServernameResolver(DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt, Autoconnect* myac);
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE;
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE;
};
-
-#endif
diff --git a/src/modules/m_spanningtree/rsquit.cpp b/src/modules/m_spanningtree/rsquit.cpp
index 027ae02ab..7ede80b4c 100644
--- a/src/modules/m_spanningtree/rsquit.cpp
+++ b/src/modules/m_spanningtree/rsquit.cpp
@@ -19,76 +19,48 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
#include "commands.h"
-CommandRSQuit::CommandRSQuit (Module* Creator, SpanningTreeUtilities* Util)
- : Command(Creator, "RSQUIT", 1), Utils(Util)
+CommandRSQuit::CommandRSQuit(Module* Creator)
+ : Command(Creator, "RSQUIT", 1)
{
flags_needed = 'o';
syntax = "<target-server-mask> [reason]";
}
-CmdResult CommandRSQuit::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandRSQuit::Handle(User* user, const Params& parameters)
{
TreeServer *server_target; // Server to squit
- TreeServer *server_linked; // Server target is linked to
server_target = Utils->FindServerMask(parameters[0]);
if (!server_target)
{
- user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick.c_str(), parameters[0].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** RSQUIT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str()));
return CMD_FAILURE;
}
- if (server_target == Utils->TreeRoot)
+ if (server_target->IsRoot())
{
- NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+parameters[0]+" matches local server name)");
+ user->WriteRemoteNotice(InspIRCd::Format("*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)", parameters[0].c_str()));
return CMD_FAILURE;
}
- server_linked = server_target->GetParent();
-
- if (server_linked == Utils->TreeRoot)
+ if (server_target->IsLocal())
{
// We have been asked to remove server_target.
- TreeSocket* sock = server_target->GetSocket();
- if (sock)
- {
- const char *reason = parameters.size() == 2 ? parameters[1].c_str() : "No reason";
- ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s (%s)", parameters[0].c_str(), user->nick.c_str(), reason);
- sock->Squit(server_target, "Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
- sock->Close();
- }
+ const char* reason = parameters.size() == 2 ? parameters[1].c_str() : "No reason";
+ ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s (%s)", parameters[0].c_str(), user->nick.c_str(), reason);
+ server_target->SQuit("Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
}
return CMD_SUCCESS;
}
-RouteDescriptor CommandRSQuit::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandRSQuit::GetRouting(User* user, const Params& parameters)
{
return ROUTE_UNICAST(parameters[0]);
}
-
-// XXX use protocol interface instead of rolling our own :)
-void CommandRSQuit::NoticeUser(User* user, const std::string &msg)
-{
- if (IS_LOCAL(user))
- {
- user->WriteServ("NOTICE %s :%s",user->nick.c_str(),msg.c_str());
- }
- else
- {
- parameterlist params;
- params.push_back(user->nick);
- params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", params, user->server);
- }
-}
-
diff --git a/src/modules/m_spanningtree/save.cpp b/src/modules/m_spanningtree/save.cpp
index 92999b422..be3a0a687 100644
--- a/src/modules/m_spanningtree/save.cpp
+++ b/src/modules/m_spanningtree/save.cpp
@@ -18,38 +18,24 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
-#include "treeserver.h"
#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
/**
* SAVE command - force nick change to UID on timestamp match
*/
-bool TreeSocket::ForceNick(const std::string &prefix, parameterlist &params)
+CmdResult CommandSave::Handle(User* user, Params& params)
{
- if (params.size() < 2)
- return true;
-
- User* u = ServerInstance->FindNick(params[0]);
- time_t ts = atol(params[1].c_str());
+ User* u = ServerInstance->FindUUID(params[0]);
+ if (!u)
+ return CMD_FAILURE;
- if ((u) && (!IS_SERVER(u)) && (u->age == ts))
- {
- Utils->DoOneToAllButSender(prefix,"SAVE",params,prefix);
+ time_t ts = ConvToNum<time_t>(params[1]);
- if (!u->ForceNickChange(u->uuid.c_str()))
- {
- ServerInstance->Users->QuitUser(u, "Nickname collision");
- }
- }
+ if (u->age == ts)
+ u->ChangeNick(u->uuid, SavedTimestamp);
- return true;
+ return CMD_SUCCESS;
}
-
diff --git a/src/modules/m_spanningtree/server.cpp b/src/modules/m_spanningtree/server.cpp
index d3033799e..07004a1e8 100644
--- a/src/modules/m_spanningtree/server.cpp
+++ b/src/modules/m_spanningtree/server.cpp
@@ -19,113 +19,102 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
+#include "modules/ssl.h"
#include "main.h"
#include "utils.h"
#include "link.h"
#include "treeserver.h"
#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h m_spanningtree/link.h */
+#include "commands.h"
/*
* Some server somewhere in the network introducing another server.
* -- w
*/
-bool TreeSocket::RemoteServer(const std::string &prefix, parameterlist &params)
+CmdResult CommandServer::HandleServer(TreeServer* ParentOfThis, Params& params)
{
- if (params.size() < 5)
- {
- SendError("Protocol error - Not enough parameters for SERVER command");
- return false;
- }
+ const std::string& servername = params[0];
+ const std::string& sid = params[1];
+ const std::string& description = params.back();
+ TreeSocket* socket = ParentOfThis->GetSocket();
- std::string servername = params[0];
- // password is not used for a remote server
- // hopcount is not used (ever)
- std::string sid = params[3];
- std::string description = params[4];
- TreeServer* ParentOfThis = Utils->FindServer(prefix);
-
- if (!ParentOfThis)
- {
- this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
- return false;
- }
- if (!ServerInstance->IsSID(sid))
+ if (!InspIRCd::IsSID(sid))
{
- this->SendError("Invalid format server ID: "+sid+"!");
- return false;
+ socket->SendError("Invalid format server ID: "+sid+"!");
+ return CMD_FAILURE;
}
TreeServer* CheckDupe = Utils->FindServer(servername);
if (CheckDupe)
{
- this->SendError("Server "+servername+" already exists!");
+ socket->SendError("Server "+servername+" already exists!");
ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+CheckDupe->GetName()+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, already exists. Closing link with " + ParentOfThis->GetName());
- return false;
+ return CMD_FAILURE;
}
CheckDupe = Utils->FindServer(sid);
if (CheckDupe)
{
- this->SendError("Server ID "+sid+" already exists! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
+ socket->SendError("Server ID "+sid+" already exists! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+servername+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, server ID already exists on the network. Closing link with " + ParentOfThis->GetName());
- return false;
+ return CMD_FAILURE;
}
Link* lnk = Utils->FindLink(servername);
- TreeServer *Node = new TreeServer(Utils, servername, description, sid, ParentOfThis,NULL, lnk ? lnk->Hidden : false);
+ TreeServer* Node = new TreeServer(servername, description, sid, ParentOfThis, ParentOfThis->GetSocket(), lnk ? lnk->Hidden : false);
+
+ HandleExtra(Node, params);
- ParentOfThis->AddChild(Node);
- params[4] = ":" + params[4];
- Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+ParentOfThis->GetName()+"\002 introduced server \002"+servername+"\002 ("+description+")");
- return true;
+ return CMD_SUCCESS;
}
+void CommandServer::HandleExtra(TreeServer* newserver, Params& params)
+{
+ for (CommandBase::Params::const_iterator i = params.begin() + 2; i != params.end() - 1; ++i)
+ {
+ const std::string& prop = *i;
+ std::string::size_type p = prop.find('=');
-/*
- * This is used after the other side of a connection has accepted our credentials.
- * They are then introducing themselves to us, BEFORE either of us burst. -- w
- */
-bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
+ std::string key = prop;
+ std::string val;
+ if (p != std::string::npos)
+ {
+ key.erase(p);
+ val.assign(prop, p+1, std::string::npos);
+ }
+
+ if (key == "burst")
+ newserver->BeginBurst(ConvToNum<uint64_t>(val));
+ }
+}
+
+Link* TreeSocket::AuthRemote(const CommandBase::Params& params)
{
if (params.size() < 5)
{
SendError("Protocol error - Not enough parameters for SERVER command");
- return false;
+ return NULL;
}
- irc::string servername = params[0].c_str();
- std::string sname = params[0];
- std::string password = params[1];
- std::string sid = params[3];
- std::string description = params[4];
- int hops = atoi(params[2].c_str());
+ const std::string& sname = params[0];
+ const std::string& password = params[1];
+ const std::string& sid = params[3];
+ const std::string& description = params.back();
this->SendCapabilities(2);
- if (hops)
- {
- this->SendError("Server too far away for authentication");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
- return false;
- }
-
if (!ServerInstance->IsSID(sid))
{
this->SendError("Invalid format server ID: "+sid+"!");
- return false;
+ return NULL;
}
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
{
Link* x = *i;
- if (x->Name != servername && x->Name != "*") // open link allowance
+ if ((!stdalgo::string::equalsci(x->Name, sname)) && (x->Name != "*")) // open link allowance
continue;
if (!ComparePass(*x, password))
@@ -134,22 +123,36 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
continue;
}
- TreeServer* CheckDupe = Utils->FindServer(sname);
- if (CheckDupe)
- {
- std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>";
- SendError("Server "+sname+" already exists on server "+pname+"!");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname);
- return false;
- }
- CheckDupe = Utils->FindServer(sid);
- if (CheckDupe)
+ if (!CheckDuplicate(sname, sid))
+ return NULL;
+
+ ServerInstance->SNO->WriteToSnoMask('l',"Verified server connection " + linkID + " ("+description+")");
+
+ const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(this);
+ if (ssliohook)
{
- this->SendError("Server ID "+sid+" already exists on the network! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
- ServerInstance->SNO->WriteToSnoMask('l',"Server \2"+assign(servername)+"\2 being introduced denied, server ID already exists on the network. Closing link.");
- return false;
+ std::string ciphersuite;
+ ssliohook->GetCiphersuite(ciphersuite);
+ ServerInstance->SNO->WriteToSnoMask('l', "Negotiated ciphersuite %s on link %s", ciphersuite.c_str(), x->Name.c_str());
}
+ return x;
+ }
+
+ this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
+ ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
+ return NULL;
+}
+
+/*
+ * This is used after the other side of a connection has accepted our credentials.
+ * They are then introducing themselves to us, BEFORE either of us burst. -- w
+ */
+bool TreeSocket::Outbound_Reply_Server(CommandBase::Params& params)
+{
+ const Link* x = AuthRemote(params);
+ if (x)
+ {
/*
* They're in WAIT_AUTH_2 (having accepted our credentials).
* Set our state to CONNECTED (since everything's peachy so far) and send our
@@ -158,32 +161,17 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
* While we're at it, create a treeserver object so we know about them.
* -- w
*/
- this->LinkState = CONNECTED;
-
- Utils->timeoutlist.erase(this);
- linkID = sname;
-
- MyRoot = new TreeServer(Utils, sname, description, sid, Utils->TreeRoot, this, x->Hidden);
- Utils->TreeRoot->AddChild(MyRoot);
- this->DoBurst(MyRoot);
-
- params[4] = ":" + params[4];
-
- /* IMPORTANT: Take password/hmac hash OUT of here before we broadcast the introduction! */
- params[1] = "*";
- Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(),"SERVER",params,sname);
+ FinishAuth(params[0], params[3], params.back(), x->Hidden);
return true;
}
- this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid)
{
- /* Check for fully initialized instances of the server by name */
+ // Check if the server name is not in use by a server that's already fully connected
TreeServer* CheckDupe = Utils->FindServer(sname);
if (CheckDupe)
{
@@ -193,8 +181,8 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
return false;
}
- /* Check for fully initialized instances of the server by id */
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Looking for dupe SID %s", sid.c_str());
+ // Check if the id is not in use by a server that's already fully connected
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Looking for dupe SID %s", sid.c_str());
CheckDupe = Utils->FindServerID(sid);
if (CheckDupe)
@@ -212,60 +200,16 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
* Someone else is attempting to connect to us if this is called. Validate their credentials etc.
* -- w
*/
-bool TreeSocket::Inbound_Server(parameterlist &params)
+bool TreeSocket::Inbound_Server(CommandBase::Params& params)
{
- if (params.size() < 5)
- {
- SendError("Protocol error - Missing SID");
- return false;
- }
-
- irc::string servername = params[0].c_str();
- std::string sname = params[0];
- std::string password = params[1];
- std::string sid = params[3];
- std::string description = params[4];
- int hops = atoi(params[2].c_str());
-
- this->SendCapabilities(2);
-
- if (hops)
- {
- this->SendError("Server too far away for authentication");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
- return false;
- }
-
- if (!ServerInstance->IsSID(sid))
+ const Link* x = AuthRemote(params);
+ if (x)
{
- this->SendError("Invalid format server ID: "+sid+"!");
- return false;
- }
-
- for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
- {
- Link* x = *i;
- if (x->Name != servername && x->Name != "*") // open link allowance
- continue;
-
- if (!ComparePass(*x, password))
- {
- ServerInstance->SNO->WriteToSnoMask('l',"Invalid password on link: %s", x->Name.c_str());
- continue;
- }
-
- if (!CheckDuplicate(sname, sid))
- return false;
-
- ServerInstance->SNO->WriteToSnoMask('l',"Verified incoming server connection " + linkID + " ("+description+")");
-
- this->SendCapabilities(2);
-
// Save these for later, so when they accept our credentials (indicated by BURST) we remember them
this->capab->hidden = x->Hidden;
- this->capab->sid = sid;
- this->capab->description = description;
- this->capab->name = sname;
+ this->capab->sid = params[3];
+ this->capab->description = params.back();
+ this->capab->name = params[0];
// Send our details: Our server name and description and hopcount of 0,
// along with the sendpass from this block.
@@ -276,8 +220,15 @@ bool TreeSocket::Inbound_Server(parameterlist &params)
return true;
}
- this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
+CommandServer::Builder::Builder(TreeServer* server)
+ : CmdBuilder(server->GetParent()->GetID(), "SERVER")
+{
+ push(server->GetName());
+ push(server->GetID());
+ if (server->IsBursting())
+ push_property("burst", ConvToStr(server->StartBurst));
+ push_last(server->GetDesc());
+}
diff --git a/src/modules/m_spanningtree/servercommand.cpp b/src/modules/m_spanningtree/servercommand.cpp
new file mode 100644
index 000000000..5b8152846
--- /dev/null
+++ b/src/modules/m_spanningtree/servercommand.cpp
@@ -0,0 +1,60 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "main.h"
+#include "servercommand.h"
+
+ServerCommand::ServerCommand(Module* Creator, const std::string& Name, unsigned int MinParams, unsigned int MaxParams)
+ : CommandBase(Creator, Name, MinParams, MaxParams)
+{
+}
+
+void ServerCommand::RegisterService()
+{
+ ModuleSpanningTree* st = static_cast<ModuleSpanningTree*>(static_cast<Module*>(creator));
+ st->CmdManager.AddCommand(this);
+}
+
+RouteDescriptor ServerCommand::GetRouting(User* user, const Params& parameters)
+{
+ // Broadcast server-to-server commands unless overridden
+ return ROUTE_BROADCAST;
+}
+
+time_t ServerCommand::ExtractTS(const std::string& tsstr)
+{
+ time_t TS = ConvToInt(tsstr);
+ if (!TS)
+ throw ProtocolException("Invalid TS");
+ return TS;
+}
+
+ServerCommand* ServerCommandManager::GetHandler(const std::string& command) const
+{
+ ServerCommandMap::const_iterator it = commands.find(command);
+ if (it != commands.end())
+ return it->second;
+ return NULL;
+}
+
+bool ServerCommandManager::AddCommand(ServerCommand* cmd)
+{
+ return commands.insert(std::make_pair(cmd->name, cmd)).second;
+}
diff --git a/src/modules/m_spanningtree/servercommand.h b/src/modules/m_spanningtree/servercommand.h
new file mode 100644
index 000000000..6ea5a9251
--- /dev/null
+++ b/src/modules/m_spanningtree/servercommand.h
@@ -0,0 +1,104 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "utils.h"
+#include "treeserver.h"
+
+class ProtocolException : public ModuleException
+{
+ public:
+ ProtocolException(const std::string& msg)
+ : ModuleException("Protocol violation: " + msg)
+ {
+ }
+};
+
+/** Base class for server-to-server commands that may have a (remote) user source or server source.
+ */
+class ServerCommand : public CommandBase
+{
+ public:
+ ServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0);
+
+ /** Register this object in the ServerCommandManager
+ */
+ void RegisterService() CXX11_OVERRIDE;
+
+ virtual CmdResult Handle(User* user, Params& parameters) = 0;
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+ /**
+ * Extract the TS from a string.
+ * @param tsstr The string containing the TS.
+ * @return The raw timestamp value.
+ * This function throws a ProtocolException if it considers the TS invalid. Note that the detection of
+ * invalid timestamps is not designed to be bulletproof, only some cases - like "0" - trigger an exception.
+ */
+ static time_t ExtractTS(const std::string& tsstr);
+};
+
+/** Base class for server-to-server command handlers which are only valid if their source is a user.
+ * When a server sends a command of this type and the source is a server (sid), the link is aborted.
+ */
+template <class T>
+class UserOnlyServerCommand : public ServerCommand
+{
+ public:
+ UserOnlyServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0)
+ : ServerCommand(Creator, Name, MinPara, MaxPara) { }
+
+ CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE
+ {
+ RemoteUser* remoteuser = IS_REMOTE(user);
+ if (!remoteuser)
+ throw ProtocolException("Invalid source");
+ return static_cast<T*>(this)->HandleRemote(remoteuser, parameters);
+ }
+};
+
+/** Base class for server-to-server command handlers which are only valid if their source is a server.
+ * When a server sends a command of this type and the source is a user (uuid), the link is aborted.
+ */
+template <class T>
+class ServerOnlyServerCommand : public ServerCommand
+{
+ public:
+ ServerOnlyServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0)
+ : ServerCommand(Creator, Name, MinPara, MaxPara) { }
+
+ CmdResult Handle(User* user, CommandBase::Params& parameters) CXX11_OVERRIDE
+ {
+ if (!IS_SERVER(user))
+ throw ProtocolException("Invalid source");
+ TreeServer* server = TreeServer::Get(user);
+ return static_cast<T*>(this)->HandleServer(server, parameters);
+ }
+};
+
+class ServerCommandManager
+{
+ typedef TR1NS::unordered_map<std::string, ServerCommand*> ServerCommandMap;
+ ServerCommandMap commands;
+
+ public:
+ ServerCommand* GetHandler(const std::string& command) const;
+ bool AddCommand(ServerCommand* cmd);
+};
diff --git a/src/modules/m_spanningtree/sinfo.cpp b/src/modules/m_spanningtree/sinfo.cpp
new file mode 100644
index 000000000..a5dae783c
--- /dev/null
+++ b/src/modules/m_spanningtree/sinfo.cpp
@@ -0,0 +1,55 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+
+#include "treeserver.h"
+#include "commands.h"
+
+CmdResult CommandSInfo::HandleServer(TreeServer* server, CommandBase::Params& params)
+{
+ const std::string& key = params.front();
+ const std::string& value = params.back();
+
+ if (key == "fullversion")
+ {
+ server->SetFullVersion(value);
+ }
+ else if (key == "version")
+ {
+ server->SetVersion(value);
+ }
+ else if (key == "rawversion")
+ {
+ server->SetRawVersion(value);
+ }
+ else if (key == "desc")
+ {
+ // Only sent when the description of a server changes because of a rehash; not sent on burst
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Server description of " + server->GetName() + " changed: " + value);
+ server->SetDesc(value);
+ }
+
+ return CMD_SUCCESS;
+}
+
+CommandSInfo::Builder::Builder(TreeServer* server, const char* key, const std::string& val)
+ : CmdBuilder(server->GetID(), "SINFO")
+{
+ push(key).push_last(val);
+}
diff --git a/src/modules/m_spanningtree/svsjoin.cpp b/src/modules/m_spanningtree/svsjoin.cpp
index 416502369..92187ddf7 100644
--- a/src/modules/m_spanningtree/svsjoin.cpp
+++ b/src/modules/m_spanningtree/svsjoin.cpp
@@ -19,19 +19,13 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
#include "commands.h"
-CmdResult CommandSVSJoin::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSJoin::Handle(User* user, Params& parameters)
{
// Check for valid channel name
- if (!ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(parameters[1]))
return CMD_FAILURE;
// Check target exists
@@ -40,15 +34,25 @@ CmdResult CommandSVSJoin::Handle(const std::vector<std::string>& parameters, Use
return CMD_FAILURE;
/* only join if it's local, otherwise just pass it on! */
- if (IS_LOCAL(u))
- Channel::JoinUser(u, parameters[1].c_str(), false, "", false, ServerInstance->Time());
+ LocalUser* localuser = IS_LOCAL(u);
+ if (localuser)
+ {
+ bool override = false;
+ std::string key;
+ if (parameters.size() >= 3)
+ {
+ key = parameters[2];
+ if (key.empty())
+ override = true;
+ }
+
+ Channel::JoinUser(localuser, parameters[1], override, key);
+ }
+
return CMD_SUCCESS;
}
-RouteDescriptor CommandSVSJoin::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandSVSJoin::GetRouting(User* user, const Params& parameters)
{
- User* u = ServerInstance->FindUUID(parameters[0]);
- if (u)
- return ROUTE_OPT_UCAST(u->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
diff --git a/src/modules/m_spanningtree/svsnick.cpp b/src/modules/m_spanningtree/svsnick.cpp
index 59973202d..2514dfd6f 100644
--- a/src/modules/m_spanningtree/svsnick.cpp
+++ b/src/modules/m_spanningtree/svsnick.cpp
@@ -21,50 +21,56 @@
#include "inspircd.h"
#include "main.h"
-#include "utils.h"
#include "commands.h"
-CmdResult CommandSVSNick::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSNick::Handle(User* user, Params& parameters)
{
User* u = ServerInstance->FindNick(parameters[0]);
if (u && IS_LOCAL(u))
{
+ // The 4th parameter is optional and it is the expected nick TS of the target user. If this parameter is
+ // present and it doesn't match the user's nick TS, the SVSNICK is not acted upon.
+ // This makes it possible to detect the case when services wants to change the nick of a user, but the
+ // user changes their nick before the SVSNICK arrives, making the SVSNICK nick change (usually to a guest nick)
+ // unnecessary. Consider the following for example:
+ //
+ // 1. test changes nick to Attila which is protected by services
+ // 2. Services SVSNICKs the user to Guest12345
+ // 3. Attila changes nick to Attila_ which isn't protected by services
+ // 4. SVSNICK arrives
+ // 5. Attila_ gets his nick changed to Guest12345 unnecessarily
+ //
+ // In this case when the SVSNICK is processed the target has already changed his nick to something
+ // which isn't protected, so changing the nick again to a Guest nick is not desired.
+ // However, if the expected nick TS parameter is present in the SVSNICK then the nick change in step 5
+ // won't happen because the timestamps won't match.
+ if (parameters.size() > 3)
+ {
+ time_t ExpectedTS = ConvToInt(parameters[3]);
+ if (u->age != ExpectedTS)
+ return CMD_FAILURE; // Ignore SVSNICK
+ }
+
std::string nick = parameters[1];
if (isdigit(nick[0]))
nick = u->uuid;
- // Don't update the TS if the nick is exactly the same
- if (u->nick == nick)
- return CMD_FAILURE;
-
time_t NickTS = ConvToInt(parameters[2]);
if (NickTS <= 0)
return CMD_FAILURE;
- ModuleSpanningTree* st = (ModuleSpanningTree*)(Module*)creator;
- st->KeepNickTS = true;
- u->age = NickTS;
-
- if (!u->ForceNickChange(nick.c_str()))
+ if (!u->ChangeNick(nick, NickTS))
{
- /* buh. UID them */
- if (!u->ForceNickChange(u->uuid.c_str()))
- {
- ServerInstance->Users->QuitUser(u, "Nickname collision");
- }
+ // Changing to 'nick' failed (it may already be in use), change to the uuid
+ u->ChangeNick(u->uuid);
}
-
- st->KeepNickTS = false;
}
return CMD_SUCCESS;
}
-RouteDescriptor CommandSVSNick::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandSVSNick::GetRouting(User* user, const Params& parameters)
{
- User* u = ServerInstance->FindNick(parameters[0]);
- if (u)
- return ROUTE_OPT_UCAST(u->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
diff --git a/src/modules/m_spanningtree/svspart.cpp b/src/modules/m_spanningtree/svspart.cpp
index 3bdf13b25..505a033e3 100644
--- a/src/modules/m_spanningtree/svspart.cpp
+++ b/src/modules/m_spanningtree/svspart.cpp
@@ -19,16 +19,10 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
#include "commands.h"
-CmdResult CommandSVSPart::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSPart::Handle(User* user, Params& parameters)
{
User* u = ServerInstance->FindUUID(parameters[0]);
if (!u)
@@ -46,10 +40,7 @@ CmdResult CommandSVSPart::Handle(const std::vector<std::string>& parameters, Use
return CMD_SUCCESS;
}
-RouteDescriptor CommandSVSPart::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandSVSPart::GetRouting(User* user, const Params& parameters)
{
- User* u = ServerInstance->FindUUID(parameters[0]);
- if (u)
- return ROUTE_OPT_UCAST(u->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
diff --git a/src/modules/m_spanningtree/cachetimer.cpp b/src/modules/m_spanningtree/translate.cpp
index be438651d..66e1bb35b 100644
--- a/src/modules/m_spanningtree/cachetimer.cpp
+++ b/src/modules/m_spanningtree/translate.cpp
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -18,24 +18,31 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
+#include "translate.h"
-#include "cachetimer.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "link.h"
-#include "treesocket.h"
+std::string Translate::ModeChangeListToParams(const Modes::ChangeList::List& modes)
+{
+ std::string ret;
+ for (Modes::ChangeList::List::const_iterator i = modes.begin(); i != modes.end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+ if (!mh->NeedsParam(item.adding))
+ continue;
-/* $ModDep: m_spanningtree/cachetimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
+ ret.push_back(' ');
-CacheRefreshTimer::CacheRefreshTimer(SpanningTreeUtilities *Util) : Timer(3600, ServerInstance->Time(), true), Utils(Util)
-{
-}
+ if (mh->IsPrefixMode())
+ {
+ User* target = ServerInstance->FindNick(item.param);
+ if (target)
+ {
+ ret.append(target->uuid);
+ continue;
+ }
+ }
-void CacheRefreshTimer::Tick(time_t TIME)
-{
- Utils->RefreshIPCache();
+ ret.append(item.param);
+ }
+ return ret;
}
-
diff --git a/src/modules/m_spanningtree/translate.h b/src/modules/m_spanningtree/translate.h
new file mode 100644
index 000000000..a2bc6df78
--- /dev/null
+++ b/src/modules/m_spanningtree/translate.h
@@ -0,0 +1,30 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace Translate
+{
+ /** Generate a list of mode parameters suitable for FMODE/MODE from a Modes::ChangeList::List
+ * @param modes List of mode changes
+ * @return List of mode parameters built from the input. Does not include the modes themselves,
+ * only the parameters.
+ */
+ std::string ModeChangeListToParams(const Modes::ChangeList::List& modes);
+}
diff --git a/src/modules/m_spanningtree/treeserver.cpp b/src/modules/m_spanningtree/treeserver.cpp
index 493b05ebf..ca7959ea7 100644
--- a/src/modules/m_spanningtree/treeserver.cpp
+++ b/src/modules/m_spanningtree/treeserver.cpp
@@ -21,56 +21,47 @@
#include "inspircd.h"
-#include "socket.h"
#include "xline.h"
#include "main.h"
-#include "../spanningtree.h"
+#include "modules/server.h"
#include "utils.h"
#include "treeserver.h"
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */
-
/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
* represents our own server. Therefore, it has no route, no parent, and
* no socket associated with it. Its version string is our own local version.
*/
-TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id)
- : ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util), ServerUser(ServerInstance->FakeClient)
+TreeServer::TreeServer()
+ : Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc)
+ , Parent(NULL), Route(NULL)
+ , VersionString(ServerInstance->GetVersionString())
+ , fullversion(ServerInstance->GetVersionString(true))
+ , rawversion(INSPIRCD_VERSION)
+ , Socket(NULL), sid(ServerInstance->Config->GetSID()), behind_bursting(0), isdead(false)
+ , pingtimer(this)
+ , ServerUser(ServerInstance->FakeClient)
+ , age(ServerInstance->Time()), UserCount(ServerInstance->Users.LocalUserCount())
+ , OperCount(0), rtt(0), StartBurst(0), Hidden(false)
{
- age = ServerInstance->Time();
- bursting = false;
- Parent = NULL;
- VersionString.clear();
- ServerUserCount = ServerOperCount = 0;
- VersionString = ServerInstance->GetVersionString();
- Route = NULL;
- Socket = NULL; /* Fix by brain */
- StartBurst = rtt = 0;
- Warned = Hidden = false;
AddHashEntry();
- SetID(id);
}
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
- * its ping counters so that it will be pinged one minute from now.
+ * the ping timer for the server.
*/
-TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide)
- : Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), ServerUser(new FakeUser(id, Name)), Hidden(Hide)
+TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide)
+ : Server(Name, Desc)
+ , Parent(Above), Socket(Sock), sid(id), behind_bursting(Parent->behind_bursting), isdead(false)
+ , pingtimer(this)
+ , ServerUser(new FakeUser(id, this))
+ , age(ServerInstance->Time()), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(Hide)
{
- age = ServerInstance->Time();
- bursting = true;
- VersionString.clear();
- ServerUserCount = ServerOperCount = 0;
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
- Warned = false;
- rtt = 0;
-
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- this->StartBurst = ts;
- ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Started bursting at time %lu", ts);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "New server %s behind_bursting %u", GetName().c_str(), behind_bursting);
+ CheckULine();
+
+ ServerInstance->Timers.AddTimer(&pingtimer);
/* find the 'route' for this server (e.g. the one directly connected
* to the local server, which we can use to reach it)
@@ -124,246 +115,180 @@ TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::strin
*/
this->AddHashEntry();
+ Parent->Children.push_back(this);
- SetID(id);
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnServerLink, (this));
}
-const std::string& TreeServer::GetID()
+void TreeServer::BeginBurst(uint64_t startms)
{
- return sid;
+ behind_bursting++;
+
+ uint64_t now = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ // If the start time is in the future (clocks are not synced) then use current time
+ if ((!startms) || (startms > now))
+ startms = now;
+ this->StartBurst = startms;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %s behind_bursting %u", sid.c_str(), ConvToStr(startms).c_str(), behind_bursting);
}
void TreeServer::FinishBurstInternal()
{
- this->bursting = false;
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
- for(unsigned int q=0; q < ChildCount(); q++)
+ // Check is needed because 1202 protocol servers don't send the bursting state of a server, so servers
+ // introduced during a netburst may later send ENDBURST which would normally decrease this counter
+ if (behind_bursting > 0)
+ behind_bursting--;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "FinishBurstInternal() %s behind_bursting %u", GetName().c_str(), behind_bursting);
+
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
{
- TreeServer* child = GetChild(q);
+ TreeServer* child = *i;
child->FinishBurstInternal();
}
}
void TreeServer::FinishBurst()
{
- FinishBurstInternal();
ServerInstance->XLines->ApplyLines();
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ uint64_t ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
unsigned long bursttime = ts - this->StartBurst;
ServerInstance->SNO->WriteToSnoMask(Parent == Utils->TreeRoot ? 'l' : 'L', "Received end of netburst from \2%s\2 (burst time: %lu %s)",
- ServerName.c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
- AddServerEvent(Utils->Creator, ServerName.c_str());
-}
+ GetName().c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
-void TreeServer::SetID(const std::string &id)
-{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Setting SID to " + id);
- sid = id;
- Utils->sidlist[sid] = this;
+ StartBurst = 0;
+ FinishBurstInternal();
}
-int TreeServer::QuitUsers(const std::string &reason)
+void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
{
- const char* reason_s = reason.c_str();
- std::vector<User*> time_to_die;
- for (user_hash::iterator n = ServerInstance->Users->clientlist->begin(); n != ServerInstance->Users->clientlist->end(); n++)
+ stdalgo::erase(Children, server);
+
+ if (IsRoot())
{
- if (n->second->server == ServerName)
- {
- time_to_die.push_back(n->second);
- }
+ // Server split from us, generate a SQUIT message and broadcast it
+ ServerInstance->SNO->WriteGlobalSno('l', "Server \002" + server->GetName() + "\002 split: " + reason);
+ CmdBuilder("SQUIT").push(server->GetID()).push_last(reason).Broadcast();
}
- for (std::vector<User*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++)
+ else
{
- User* a = (User*)*n;
- if (!IS_LOCAL(a))
- {
- if (this->Utils->quiet_bursts)
- a->quietquit = true;
-
- if (ServerInstance->Config->HideSplits)
- ServerInstance->Users->QuitUser(a, "*.net *.split", reason_s);
- else
- ServerInstance->Users->QuitUser(a, reason_s);
- }
+ ServerInstance->SNO->WriteToSnoMask('L', "Server \002" + server->GetName() + "\002 split from server \002" + GetName() + "\002 with reason: " + reason);
}
- return time_to_die.size();
-}
-
-/** This method is used to add the structure to the
- * hash_map for linear searches. It is only called
- * by the constructors.
- */
-void TreeServer::AddHashEntry()
-{
- server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
- if (iter == Utils->serverlist.end())
- Utils->serverlist[this->ServerName.c_str()] = this;
-}
-
-/** This method removes the reference to this object
- * from the hash_map which is used for linear searches.
- * It is only called by the default destructor.
- */
-void TreeServer::DelHashEntry()
-{
- server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
- if (iter != Utils->serverlist.end())
- Utils->serverlist.erase(iter);
-}
-
-/** These accessors etc should be pretty self-
- * explanitory.
- */
-TreeServer* TreeServer::GetRoute()
-{
- return Route;
-}
-
-std::string TreeServer::GetName()
-{
- return ServerName.c_str();
-}
-
-const std::string& TreeServer::GetDesc()
-{
- return ServerDesc;
-}
-
-const std::string& TreeServer::GetVersion()
-{
- return VersionString;
-}
-
-void TreeServer::SetNextPingTime(time_t t)
-{
- this->NextPing = t;
- LastPingWasGood = false;
-}
-time_t TreeServer::NextPingTime()
-{
- return NextPing;
-}
+ unsigned int num_lost_servers = 0;
+ server->SQuitInternal(num_lost_servers);
-bool TreeServer::AnsweredLastPing()
-{
- return LastPingWasGood;
-}
+ const std::string quitreason = GetName() + " " + server->GetName();
+ unsigned int num_lost_users = QuitUsers(quitreason);
-void TreeServer::SetPingFlag()
-{
- LastPingWasGood = true;
-}
+ ServerInstance->SNO->WriteToSnoMask(IsRoot() ? 'l' : 'L', "Netsplit complete, lost \002%u\002 user%s on \002%u\002 server%s.",
+ num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
-unsigned int TreeServer::GetUserCount()
-{
- return ServerUserCount;
-}
+ // No-op if the socket is already closed (i.e. it called us)
+ if (server->IsLocal())
+ server->GetSocket()->Close();
-void TreeServer::SetUserCount(int diff)
-{
- ServerUserCount += diff;
+ // Add the server to the cull list, the servers behind it are handled by cull() and the destructor
+ ServerInstance->GlobalCulls.AddItem(server);
}
-void TreeServer::SetOperCount(int diff)
+void TreeServer::SQuitInternal(unsigned int& num_lost_servers)
{
- ServerOperCount += diff;
-}
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s lost in split", GetName().c_str());
-unsigned int TreeServer::GetOperCount()
-{
- return ServerOperCount;
-}
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ {
+ TreeServer* server = *i;
+ server->SQuitInternal(num_lost_servers);
+ }
-TreeSocket* TreeServer::GetSocket()
-{
- return Socket;
-}
+ // Mark server as dead
+ isdead = true;
+ num_lost_servers++;
+ RemoveHash();
-TreeServer* TreeServer::GetParent()
-{
- return Parent;
+ if (!Utils->Creator->dying)
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnServerSplit, (this));
}
-void TreeServer::SetVersion(const std::string &Version)
+unsigned int TreeServer::QuitUsers(const std::string& reason)
{
- VersionString = Version;
-}
+ std::string publicreason = ServerInstance->Config->HideSplits ? "*.net *.split" : reason;
-unsigned int TreeServer::ChildCount()
-{
- return Children.size();
-}
-
-TreeServer* TreeServer::GetChild(unsigned int n)
-{
- if (n < Children.size())
- {
- /* Make sure they cant request
- * an out-of-range object. After
- * all we know what these programmer
- * types are like *grin*.
- */
- return Children[n];
- }
- else
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ unsigned int original_size = users.size();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); )
{
- return NULL;
+ User* user = i->second;
+ // Increment the iterator now because QuitUser() removes the user from the container
+ ++i;
+ TreeServer* server = TreeServer::Get(user);
+ if (server->IsDead())
+ ServerInstance->Users->QuitUser(user, publicreason, &reason);
}
+ return original_size - users.size();
}
-void TreeServer::AddChild(TreeServer* Child)
+void TreeServer::CheckULine()
{
- Children.push_back(Child);
-}
+ uline = silentuline = false;
-bool TreeServer::DelChild(TreeServer* Child)
-{
- std::vector<TreeServer*>::iterator it = std::find(Children.begin(), Children.end(), Child);
- if (it != Children.end())
+ ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- Children.erase(it);
- return true;
+ ConfigTag* tag = i->second;
+ std::string server = tag->getString("server");
+ if (!strcasecmp(server.c_str(), GetName().c_str()))
+ {
+ if (this->IsRoot())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Servers should not uline themselves (at " + tag->getTagLocation() + ")");
+ return;
+ }
+
+ uline = true;
+ silentuline = tag->getBool("silent");
+ break;
+ }
}
- return false;
}
-/** Removes child nodes of this node, and of that node, etc etc.
- * This is used during netsplits to automatically tidy up the
- * server tree. It is slow, we don't use it for much else.
+/** This method is used to add the server to the
+ * maps for linear searches. It is only called
+ * by the constructors.
*/
-bool TreeServer::Tidy()
+void TreeServer::AddHashEntry()
{
- while (1)
- {
- std::vector<TreeServer*>::iterator a = Children.begin();
- if (a == Children.end())
- return true;
- TreeServer* s = *a;
- s->Tidy();
- s->cull();
- Children.erase(a);
- delete s;
- }
+ Utils->serverlist[GetName()] = this;
+ Utils->sidlist[sid] = this;
}
CullResult TreeServer::cull()
{
- if (ServerUser != ServerInstance->FakeClient)
+ // Recursively cull all servers that are under us in the tree
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ {
+ TreeServer* server = *i;
+ server->cull();
+ }
+
+ if (!IsRoot())
ServerUser->cull();
return classbase::cull();
}
TreeServer::~TreeServer()
{
- /* We'd better tidy up after ourselves, eh? */
- this->DelHashEntry();
- if (ServerUser != ServerInstance->FakeClient)
+ // Recursively delete all servers that are under us in the tree first
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ delete *i;
+
+ // Delete server user unless it's us
+ if (!IsRoot())
delete ServerUser;
+}
- server_hash::iterator iter = Utils->sidlist.find(GetID());
- if (iter != Utils->sidlist.end())
- Utils->sidlist.erase(iter);
+void TreeServer::RemoveHash()
+{
+ Utils->sidlist.erase(sid);
+ Utils->serverlist.erase(GetName());
}
diff --git a/src/modules/m_spanningtree/treeserver.h b/src/modules/m_spanningtree/treeserver.h
index 60b6d1def..037edd194 100644
--- a/src/modules/m_spanningtree/treeserver.h
+++ b/src/modules/m_spanningtree/treeserver.h
@@ -19,10 +19,10 @@
*/
-#ifndef M_SPANNINGTREE_TREESERVER_H
-#define M_SPANNINGTREE_TREESERVER_H
+#pragma once
#include "treesocket.h"
+#include "pingtimer.h"
/** Each server in the tree is represented by one class of
* type TreeServer. A locally connected TreeServer can
@@ -38,90 +38,116 @@
* TreeServer items, deleting and inserting them as they
* are created and destroyed.
*/
-class TreeServer : public classbase
+class TreeServer : public Server
{
TreeServer* Parent; /* Parent entry */
TreeServer* Route; /* Route entry */
std::vector<TreeServer*> Children; /* List of child objects */
- irc::string ServerName; /* Server's name */
- std::string ServerDesc; /* Server's description */
std::string VersionString; /* Version string or empty string */
- unsigned int ServerUserCount; /* How many users are on this server? [note: doesn't care about +i] */
- unsigned int ServerOperCount; /* How many opers are on this server? */
- TreeSocket* Socket; /* For directly connected servers this points at the socket object */
- time_t NextPing; /* After this time, the server should be PINGed*/
- bool LastPingWasGood; /* True if the server responded to the last PING with a PONG */
- SpanningTreeUtilities* Utils; /* Utility class */
+
+ /** Full version string including patch version and other info
+ */
+ std::string fullversion;
+ std::string rawversion;
+
+ TreeSocket* Socket; /* Socket used to communicate with this server */
std::string sid; /* Server ID */
- /** Set server ID
- * @param id Server ID
- * @throws CoreException on duplicate ID
+ /** Counter counting how many servers are bursting in front of this server, including
+ * this server. Set to parents' value on construction then it is increased if the
+ * server itself starts bursting. Decreased when a server on the path to this server
+ * finishes burst.
+ */
+ unsigned int behind_bursting;
+
+ /** True if this server has been lost in a split and is awaiting destruction
+ */
+ bool isdead;
+
+ /** Timer handling PINGing the server and killing it on timeout
+ */
+ PingTimer pingtimer;
+
+ /** This method is used to add this TreeServer to the
+ * hash maps. It is only called by the constructors.
+ */
+ void AddHashEntry();
+
+ /** Used by SQuit logic to recursively remove servers
+ */
+ void SQuitInternal(unsigned int& num_lost_servers);
+
+ /** Remove the reference to this server from the hash maps
*/
- void SetID(const std::string &id);
+ void RemoveHash();
public:
+ typedef std::vector<TreeServer*> ChildServers;
FakeUser* const ServerUser; /* User representing this server */
- time_t age;
+ const time_t age;
- bool Warned; /* True if we've warned opers about high latency on this server */
- bool bursting; /* whether or not this server is bursting */
+ unsigned int UserCount; /* How many users are on this server? [note: doesn't care about +i] */
+ unsigned int OperCount; /* How many opers are on this server? */
/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
* represents our own server. Therefore, it has no route, no parent, and
* no socket associated with it. Its version string is our own local version.
*/
- TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id);
+ TreeServer();
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
* its ping counters so that it will be pinged one minute from now.
*/
- TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide);
+ TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide);
- int QuitUsers(const std::string &reason);
-
- /** This method is used to add the structure to the
- * hash_map for linear searches. It is only called
- * by the constructors.
+ /** SQuit a server connected to this server, removing the given server and all servers behind it
+ * @param server Server to squit, must be directly below this server
+ * @param reason Reason for quitting the server, sent to opers and other servers
*/
- void AddHashEntry();
+ void SQuitChild(TreeServer* server, const std::string& reason);
- /** This method removes the reference to this object
- * from the hash_map which is used for linear searches.
- * It is only called by the default destructor.
+ /** SQuit this server, removing this server and all servers behind it
+ * @param reason Reason for quitting the server, sent to opers and other servers
*/
- void DelHashEntry();
+ void SQuit(const std::string& reason)
+ {
+ GetParent()->SQuitChild(this, reason);
+ }
+
+ static unsigned int QuitUsers(const std::string& reason);
/** Get route.
* The 'route' is defined as the locally-
* connected server which can be used to reach this server.
*/
- TreeServer* GetRoute();
+ TreeServer* GetRoute() const { return Route; }
- /** Get server name
+ /** Returns true if this server is the tree root (i.e.: us)
*/
- std::string GetName();
+ bool IsRoot() const { return (this->Parent == NULL); }
- /** Get server description (GECOS)
+ /** Returns true if this server is locally connected
*/
- const std::string& GetDesc();
+ bool IsLocal() const { return (this->Route == this); }
- /** Get server version string
+ /** Returns true if the server is awaiting destruction
+ * @return True if the server is waiting to be culled and deleted, false otherwise
*/
- const std::string& GetVersion();
+ bool IsDead() const { return isdead; }
- /** Set time we are next due to ping this server
+ /** Get server version string
*/
- void SetNextPingTime(time_t t);
+ const std::string& GetVersion() const { return VersionString; }
- /** Get the time we are next due to ping this server
+ /** Get the full version string of this server
+ * @return The full version string of this server, including patch version and other info
*/
- time_t NextPingTime();
+ const std::string& GetFullVersion() const { return fullversion; }
- /** Last ping time in milliseconds, used to calculate round trip time
+ /** Get the raw version string of this server
*/
- unsigned long LastPingMsec;
+ const std::string& GetRawVersion() const { return rawversion; }
/** Round trip time of last ping
*/
@@ -129,86 +155,91 @@ class TreeServer : public classbase
/** When we recieved BURST from this server, used to calculate total burst time at ENDBURST.
*/
- unsigned long StartBurst;
+ uint64_t StartBurst;
/** True if this server is hidden
*/
bool Hidden;
- /** True if the server answered their last ping
+ /** Get the TreeSocket pointer for local servers.
+ * For remote servers, this returns NULL.
*/
- bool AnsweredLastPing();
+ TreeSocket* GetSocket() const { return Socket; }
- /** Set the server as responding to its last ping
+ /** Get the parent server.
+ * For the root node, this returns NULL.
*/
- void SetPingFlag();
+ TreeServer* GetParent() const { return Parent; }
- /** Get the number of users on this server.
+ /** Set the server version string
*/
- unsigned int GetUserCount();
+ void SetVersion(const std::string& verstr) { VersionString = verstr; }
- /** Increment or decrement the user count by diff.
+ /** Set the full version string
+ * @param verstr The version string to set
*/
- void SetUserCount(int diff);
+ void SetFullVersion(const std::string& verstr) { fullversion = verstr; }
- /** Gets the numbers of opers on this server.
+ /** Set the raw version string
*/
- unsigned int GetOperCount();
+ void SetRawVersion(const std::string& verstr) { rawversion = verstr; }
- /** Increment or decrement the oper count by diff.
+ /** Sets the description of this server. Called when the description of a remote server changes
+ * and we are notified about it.
+ * @param descstr The description to set
*/
- void SetOperCount(int diff);
+ void SetDesc(const std::string& descstr) { description = descstr; }
- /** Get the TreeSocket pointer for local servers.
- * For remote servers, this returns NULL.
+ /** Return all child servers
*/
- TreeSocket* GetSocket();
+ const ChildServers& GetChildren() const { return Children; }
- /** Get the parent server.
- * For the root node, this returns NULL.
+ /** Get server ID
*/
- TreeServer* GetParent();
+ const std::string& GetID() const { return sid; }
- /** Set the server version string
+ /** Marks a server as having finished bursting and performs appropriate actions.
*/
- void SetVersion(const std::string &Version);
+ void FinishBurst();
+ /** Recursive call for child servers */
+ void FinishBurstInternal();
- /** Return number of child servers
+ /** (Re)check the uline state of this server
*/
- unsigned int ChildCount();
+ void CheckULine();
- /** Return a child server indexed 0..n
+ /** Get the bursting state of this server
+ * @return True if this server is bursting, false if it isn't
*/
- TreeServer* GetChild(unsigned int n);
+ bool IsBursting() const { return (StartBurst != 0); }
- /** Add a child server
+ /** Check whether this server is behind a bursting server or is itself bursting.
+ * This can tell whether a user is on a part of the network that is still bursting.
+ * @return True if this server is bursting or is behind a server that is bursting, false if it isn't
*/
- void AddChild(TreeServer* Child);
+ bool IsBehindBursting() const { return (behind_bursting != 0); }
- /** Delete a child server, return false if it didn't exist.
+ /** Set the bursting state of the server
+ * @param startms Time the server started bursting, if 0 or omitted, use current time
*/
- bool DelChild(TreeServer* Child);
+ void BeginBurst(uint64_t startms = 0);
- /** Removes child nodes of this node, and of that node, etc etc.
- * This is used during netsplits to automatically tidy up the
- * server tree. It is slow, we don't use it for much else.
+ /** Register a PONG from the server
*/
- bool Tidy();
+ void OnPong() { pingtimer.OnPong(); }
- /** Get server ID
- */
- const std::string& GetID();
+ CullResult cull() CXX11_OVERRIDE;
- /** Marks a server as having finished bursting and performs appropriate actions.
+ /** Destructor, deletes ServerUser unless IsRoot()
*/
- void FinishBurst();
- /** Recursive call for child servers */
- void FinishBurstInternal();
+ ~TreeServer();
- CullResult cull();
- /** Destructor
+ /** Returns the TreeServer the given user is connected to
+ * @param user The user whose server to return
+ * @return The TreeServer this user is connected to.
*/
- ~TreeServer();
+ static TreeServer* Get(User* user)
+ {
+ return static_cast<TreeServer*>(user->server);
+ }
};
-
-#endif
diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h
index efcce5f7a..d99dc9104 100644
--- a/src/modules/m_spanningtree/treesocket.h
+++ b/src/modules/m_spanningtree/treesocket.h
@@ -20,12 +20,9 @@
*/
-#ifndef M_SPANNINGTREE_TREESOCKET_H
-#define M_SPANNINGTREE_TREESOCKET_H
+#pragma once
-#include "socket.h"
#include "inspircd.h"
-#include "xline.h"
#include "utils.h"
@@ -76,7 +73,7 @@ struct CapabData
std::string ourchallenge; /* Challenge sent for challenge/response */
std::string theirchallenge; /* Challenge recv for challenge/response */
int capab_phase; /* Have sent CAPAB already */
- bool auth_fingerprint; /* Did we auth using SSL fingerprint */
+ bool auth_fingerprint; /* Did we auth using SSL certificate fingerprint */
bool auth_challenge; /* Did we auth using challenge/response */
// Data saved from incoming SERVER command, for later use when our credentials have been accepted by the other party
@@ -92,39 +89,92 @@ struct CapabData
*/
class TreeSocket : public BufferedSocket
{
- SpanningTreeUtilities* Utils; /* Utility class */
+ struct BurstState;
+
std::string linkID; /* Description for this link */
ServerState LinkState; /* Link state */
CapabData* capab; /* Link setup data (held until burst is sent) */
TreeServer* MyRoot; /* The server we are talking to */
int proto_version; /* Remote protocol version */
- bool ConnectionFailureShown; /* Set to true if a connection failure message was shown */
- static const unsigned int FMODE_MAX_LENGTH = 350;
+ /** True if we've sent our burst.
+ * This only changes the behavior of message translation for 1202 protocol servers and it can be
+ * removed once 1202 support is dropped.
+ */
+ bool burstsent;
/** Checks if the given servername and sid are both free
*/
bool CheckDuplicate(const std::string& servername, const std::string& sid);
+ /** Send all ListModeBase modes set on the channel
+ */
+ void SendListModes(Channel* chan);
+
+ /** Send all known information about a channel */
+ void SyncChannel(Channel* chan, BurstState& bs);
+
+ /** Send all users and their oper state, away state and metadata */
+ void SendUsers(BurstState& bs);
+
+ /** Send all additional info about the given server to this server */
+ void SendServerInfo(TreeServer* from);
+
+ /** Find the User source of a command given a prefix and a command string.
+ * This connection must be fully up when calling this function.
+ * @param prefix Prefix string to find the source User object for. Can be a sid, a uuid or a server name.
+ * @param command The command whose source to find. This is required because certain commands (like mode
+ * changes and kills) must be processed even if their claimed source doesn't exist. If the given command is
+ * such a command and the source does not exist, the function returns a valid FakeUser that can be used to
+ * to process the command with.
+ * @return The command source to use when processing the command or NULL if the source wasn't found.
+ * Note that the direction of the returned source is not verified.
+ */
+ User* FindSource(const std::string& prefix, const std::string& command);
+
+ /** Finish the authentication phase of this connection.
+ * Change the state of the connection to CONNECTED, create a TreeServer object for the server on the
+ * other end of the connection using the details provided in the parameters, and finally send a burst.
+ * @param remotename Name of the remote server
+ * @param remotesid SID of the remote server
+ * @param remotedesc Description of the remote server
+ * @param hidden True if the remote server is hidden according to the configuration
+ */
+ void FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden);
+
+ /** Authenticate the remote server.
+ * Validate the parameters and find the link block that matches the remote server. In case of an error,
+ * an appropriate snotice is generated, an ERROR message is sent and the connection is closed.
+ * Failing to find a matching link block counts as an error.
+ * @param params Parameters they sent in the SERVER command
+ * @return Link block for the remote server, or NULL if an error occurred
+ */
+ Link* AuthRemote(const CommandBase::Params& params);
+
+ /** Write a line on this socket with a new line character appended, skipping all translation for old protocols
+ * @param line Line to write without a new line character at the end
+ */
+ void WriteLineNoCompat(const std::string& line);
+
public:
- time_t age;
+ const time_t age;
/** Because most of the I/O gubbins are encapsulated within
* BufferedSocket, we just call the superclass constructor for
* most of the action, and append a few of our own values
* to it.
*/
- TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr);
+ TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr);
/** When a listening socket gives us a new file descriptor,
* we must associate it with a socket without creating a new
* connection. This constructor is used for this purpose.
*/
- TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
+ TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
/** Get link state
*/
- ServerState GetLinkState();
+ ServerState GetLinkState() const { return LinkState; }
/** Get challenge set in our CAPAB for challenge/response
*/
@@ -150,7 +200,7 @@ class TreeSocket : public BufferedSocket
*/
void CleanNegotiationInfo();
- CullResult cull();
+ CullResult cull() CXX11_OVERRIDE;
/** Destructor
*/
~TreeSocket();
@@ -166,11 +216,11 @@ class TreeSocket : public BufferedSocket
* to server docs on the inspircd.org site, the other side
* will then send back its own server string.
*/
- virtual void OnConnected();
+ void OnConnected() CXX11_OVERRIDE;
/** Handle socket error event
*/
- virtual void OnError(BufferedSocketError e);
+ void OnError(BufferedSocketError e) CXX11_OVERRIDE;
/** Sends an error to the remote server, and displays it locally to show
* that it was sent.
@@ -180,13 +230,8 @@ class TreeSocket : public BufferedSocket
/** Recursively send the server tree with distances as hops.
* This is used during network burst to inform the other server
* (and any of ITS servers too) of what servers we know about.
- * If at any point any of these servers already exist on the other
- * end, our connection may be terminated. The hopcounts given
- * by this function are relative, this doesn't matter so long as
- * they are all >1, as all the remote servers re-calculate them
- * to be relative too, with themselves as hop 0.
*/
- void SendServers(TreeServer* Current, TreeServer* s, int hops);
+ void SendServers(TreeServer* Current, TreeServer* s);
/** Returns module list as a string, filtered by filter
* @param filter a module version bitmask, such as VF_COMMON or VF_OPTCOMMON
@@ -197,31 +242,11 @@ class TreeSocket : public BufferedSocket
*/
void SendCapabilities(int phase);
- /** Add modules to VF_COMMON list for backwards compatability */
- void CompatAddModules(std::vector<std::string>& modlist);
-
/* Isolate and return the elements that are different between two lists */
void ListDifference(const std::string &one, const std::string &two, char sep,
std::string& mleft, std::string& mright);
- bool Capab(const parameterlist &params);
-
- /** This function forces this server to quit, removing this server
- * and any users on it (and servers and users below that, etc etc).
- * It's very slow and pretty clunky, but luckily unless your network
- * is having a REAL bad hair day, this function shouldnt be called
- * too many times a month ;-)
- */
- void SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users);
-
- /** This is a wrapper function for SquitServer above, which
- * does some validation first and passes on the SQUIT to all
- * other remaining servers.
- */
- void Squit(TreeServer* Current, const std::string &reason);
-
- /* Used on nick collision ... XXX ugly function HACK */
- int DoCollision(User *u, time_t remotets, const std::string &remoteident, const std::string &remoteip, const std::string &remoteuid);
+ bool Capab(const CommandBase::Params& params);
/** Send one or more FJOINs for a channel of users.
* If the length of a single line is more than 480-NICKMAX
@@ -232,11 +257,8 @@ class TreeSocket : public BufferedSocket
/** Send G, Q, Z and E lines */
void SendXLines();
- /** Send channel modes and topics */
- void SendChannelModes();
-
- /** send all users and their oper state/modes */
- void SendUsers();
+ /** Send all known information about a channel */
+ void SyncChannel(Channel* chan);
/** This function is called when we want to send a netburst to a local
* server. There is a set order we must do this, because for example
@@ -248,90 +270,41 @@ class TreeSocket : public BufferedSocket
/** This function is called when we receive data from a remote
* server.
*/
- void OnDataReady();
+ void OnDataReady() CXX11_OVERRIDE;
/** Send one or more complete lines down the socket
*/
- void WriteLine(std::string line);
+ void WriteLine(const std::string& line);
/** Handle ERROR command */
- void Error(parameterlist &params);
-
- /** Remote AWAY */
- bool Away(const std::string &prefix, parameterlist &params);
-
- /** SAVE to resolve nick collisions without killing */
- bool ForceNick(const std::string &prefix, parameterlist &params);
-
- /** ENCAP command
- */
- void Encap(User* who, parameterlist &params);
-
- /** OPERQUIT command
- */
- bool OperQuit(const std::string &prefix, parameterlist &params);
-
- /** PONG
- */
- bool LocalPong(const std::string &prefix, parameterlist &params);
-
- /** VERSION
- */
- bool ServerVersion(const std::string &prefix, parameterlist &params);
-
- /** ADDLINE
- */
- bool AddLine(const std::string &prefix, parameterlist &params);
-
- /** DELLINE
- */
- bool DelLine(const std::string &prefix, parameterlist &params);
-
- /** WHOIS
- */
- bool Whois(const std::string &prefix, parameterlist &params);
-
- /** PUSH
- */
- bool Push(const std::string &prefix, parameterlist &params);
-
- /** PING
- */
- bool LocalPing(const std::string &prefix, parameterlist &params);
-
- /** <- (remote) <- SERVER
- */
- bool RemoteServer(const std::string &prefix, parameterlist &params);
+ void Error(CommandBase::Params& params);
/** (local) -> SERVER
*/
- bool Outbound_Reply_Server(parameterlist &params);
+ bool Outbound_Reply_Server(CommandBase::Params& params);
/** (local) <- SERVER
*/
- bool Inbound_Server(parameterlist &params);
+ bool Inbound_Server(CommandBase::Params& params);
/** Handle IRC line split
*/
- void Split(const std::string &line, std::string& prefix, std::string& command, parameterlist &params);
+ void Split(const std::string &line, std::string& prefix, std::string& command, CommandBase::Params& params);
/** Process complete line from buffer
*/
void ProcessLine(std::string &line);
- void ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params);
+ void ProcessConnectedLine(std::string& prefix, std::string& command, CommandBase::Params& params);
/** Handle socket timeout from connect()
*/
- virtual void OnTimeout();
+ void OnTimeout() CXX11_OVERRIDE;
/** Handle server quit on close
*/
- virtual void Close();
+ void Close() CXX11_OVERRIDE;
- /** Returns true if this server was introduced to the rest of the network
+ /** Fixes messages coming from old servers so the new command handlers understand them
*/
- bool Introduced();
+ bool PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params);
};
-
-#endif
-
diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp
index c9729cc0f..062d04222 100644
--- a/src/modules/m_spanningtree/treesocket1.cpp
+++ b/src/modules/m_spanningtree/treesocket1.cpp
@@ -21,78 +21,63 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
+#include "iohook.h"
#include "main.h"
-#include "../spanningtree.h"
+#include "modules/server.h"
#include "utils.h"
#include "treeserver.h"
#include "link.h"
#include "treesocket.h"
-#include "resolvers.h"
+#include "commands.h"
-/** Because most of the I/O gubbins are encapsulated within
- * BufferedSocket, we just call the superclass constructor for
- * most of the action, and append a few of our own values
- * to it.
+/** Constructor for outgoing connections.
+ * Because most of the I/O gubbins are encapsulated within
+ * BufferedSocket, we just call DoConnect() for most of the action,
+ * and only do minor initialization tasks ourselves.
*/
-TreeSocket::TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr)
- : Utils(Util)
+TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr)
+ : linkID(link->Name), LinkState(CONNECTING), MyRoot(NULL), proto_version(0)
+ , burstsent(false), age(ServerInstance->Time())
{
- age = ServerInstance->Time();
- linkID = assign(link->Name);
capab = new CapabData;
capab->link = link;
capab->ac = myac;
capab->capab_phase = 0;
- MyRoot = NULL;
- proto_version = 0;
- ConnectionFailureShown = false;
- LinkState = CONNECTING;
- if (!link->Hook.empty())
- {
- ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook);
- if (!prov)
- {
- SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID);
- return;
- }
- AddIOHook(prov->creator);
- }
+
DoConnect(ipaddr, link->Port, link->Timeout, link->Bind);
- Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout);
+ Utils->timeoutlist[this] = std::pair<std::string, unsigned int>(linkID, link->Timeout);
SendCapabilities(1);
}
-/** When a listening socket gives us a new file descriptor,
- * we must associate it with a socket without creating a new
- * connection. This constructor is used for this purpose.
+/** Constructor for incoming connections
*/
-TreeSocket::TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- : BufferedSocket(newfd), Utils(Util)
+TreeSocket::TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+ : BufferedSocket(newfd)
+ , linkID("inbound from " + client->addr()), LinkState(WAIT_AUTH_1), MyRoot(NULL), proto_version(0)
+ , burstsent(false), age(ServerInstance->Time())
{
capab = new CapabData;
capab->capab_phase = 0;
- MyRoot = NULL;
- age = ServerInstance->Time();
- LinkState = WAIT_AUTH_1;
- proto_version = 0;
- ConnectionFailureShown = false;
- linkID = "inbound from " + client->addr();
- FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
- if (GetIOHook())
- GetIOHook()->OnStreamSocketAccept(this, client, server);
- SendCapabilities(1);
+ for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
+ {
+ ListenSocket::IOHookProvRef& iohookprovref = *i;
+ if (!iohookprovref)
+ continue;
- Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
-}
+ iohookprovref->OnAccept(this, client, server);
+ // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version
+ if (!getError().empty())
+ {
+ TreeSocket::OnError(I_ERR_OTHER);
+ return;
+ }
+ }
-ServerState TreeSocket::GetLinkState()
-{
- return this->LinkState;
+ SendCapabilities(1);
+
+ Utils->timeoutlist[this] = std::pair<std::string, unsigned int>(linkID, 30);
}
void TreeSocket::CleanNegotiationInfo()
@@ -114,20 +99,30 @@ CullResult TreeSocket::cull()
TreeSocket::~TreeSocket()
{
- if (capab)
- delete capab;
+ delete capab;
}
/** When an outbound connection finishes connecting, we receive
- * this event, and must send our SERVER string to the other
+ * this event, and must do CAPAB negotiation with the other
* side. If the other side is happy, as outlined in the server
* to server docs on the inspircd.org site, the other side
- * will then send back its own server string.
+ * will then send back its own SERVER string eventually.
*/
void TreeSocket::OnConnected()
{
if (this->LinkState == CONNECTING)
{
+ if (!capab->link->Hook.empty())
+ {
+ ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, capab->link->Hook);
+ if (!prov)
+ {
+ SetError("Could not find hook '" + capab->link->Hook + "' for connection to " + linkID);
+ return;
+ }
+ static_cast<IOHookProvider*>(prov)->OnConnect(this);
+ }
+
ServerInstance->SNO->WriteGlobalSno('l', "Connection to \2%s\2[%s] started.", linkID.c_str(),
(capab->link->HiddenFromStats ? "<hidden>" : capab->link->IPAddr.c_str()));
this->SendCapabilities(1);
@@ -139,6 +134,7 @@ void TreeSocket::OnError(BufferedSocketError e)
ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\002%s\002' failed with error: %s",
linkID.c_str(), getError().c_str());
LinkState = DYING;
+ Close();
}
void TreeSocket::SendError(const std::string &errormessage)
@@ -149,79 +145,31 @@ void TreeSocket::SendError(const std::string &errormessage)
SetError(errormessage);
}
-/** This function forces this server to quit, removing this server
- * and any users on it (and servers and users below that, etc etc).
- * It's very slow and pretty clunky, but luckily unless your network
- * is having a REAL bad hair day, this function shouldnt be called
- * too many times a month ;-)
- */
-void TreeSocket::SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users)
+CmdResult CommandSQuit::HandleServer(TreeServer* server, CommandBase::Params& params)
{
- std::string servername = Current->GetName();
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"SquitServer for %s from %s",
- servername.c_str(), from.c_str());
- /* recursively squit the servers attached to 'Current'.
- * We're going backwards so we don't remove users
- * while we still need them ;)
- */
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ TreeServer* quitting = Utils->FindServer(params[0]);
+ if (!quitting)
{
- TreeServer* recursive_server = Current->GetChild(q);
- this->SquitServer(from,recursive_server, num_lost_servers, num_lost_users);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Squit from unknown server");
+ return CMD_FAILURE;
}
- /* Now we've whacked the kids, whack self */
- num_lost_servers++;
- num_lost_users += Current->QuitUsers(from);
-}
-/** This is a wrapper function for SquitServer above, which
- * does some validation first and passes on the SQUIT to all
- * other remaining servers.
- */
-void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
-{
- bool LocalSquit = false;
-
- if ((Current) && (Current != Utils->TreeRoot))
+ CmdResult ret = CMD_SUCCESS;
+ if (quitting == server)
{
- DelServerEvent(Utils->Creator, Current->GetName());
+ ret = CMD_FAILURE;
+ server = server->GetParent();
+ }
+ else if (quitting->GetParent() != server)
+ throw ProtocolException("Attempted to SQUIT a non-directly connected server or the parent");
- if (!Current->GetSocket() || Current->GetSocket()->Introduced())
- {
- parameterlist params;
- params.push_back(Current->GetID());
- params.push_back(":"+reason);
- Utils->DoOneToAllButSender(Current->GetParent()->GetID(),"SQUIT",params,Current->GetID());
- }
+ server->SQuitChild(quitting, params[1]);
- if (Current->GetParent() == Utils->TreeRoot)
- {
- ServerInstance->SNO->WriteGlobalSno('l', "Server \002"+Current->GetName()+"\002 split: "+reason);
- LocalSquit = true;
- }
- else
- {
- ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
- }
- int num_lost_servers = 0;
- int num_lost_users = 0;
- std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
- SquitServer(from, Current, num_lost_servers, num_lost_users);
- ServerInstance->SNO->WriteToSnoMask(LocalSquit ? 'l' : 'L', "Netsplit complete, lost \002%d\002 user%s on \002%d\002 server%s.",
- num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
- Current->Tidy();
- Current->GetParent()->DelChild(Current);
- Current->cull();
- const bool ismyroot = (Current == MyRoot);
- delete Current;
- if (ismyroot)
- {
- MyRoot = NULL;
- Close();
- }
- }
- else
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Squit from unknown server");
+ // XXX: Return CMD_FAILURE when servers SQUIT themselves (i.e. :00S SQUIT 00S :Shutting down)
+ // to stop this message from being forwarded.
+ // The squit logic generates a SQUIT message with our sid as the source and sends it to the
+ // remaining servers.
+ return ret;
}
/** This function is called when we receive data from a remote
@@ -235,13 +183,24 @@ void TreeSocket::OnDataReady()
{
std::string::size_type rline = line.find('\r');
if (rline != std::string::npos)
- line = line.substr(0,rline);
+ line.erase(rline);
if (line.find('\0') != std::string::npos)
{
SendError("Read null character from socket");
break;
}
- ProcessLine(line);
+
+ try
+ {
+ ProcessLine(line);
+ }
+ catch (CoreException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error while processing: " + line);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason());
+ SendError(ex.GetReason() + " - check the log file for details");
+ }
+
if (!getError().empty())
break;
}
@@ -249,8 +208,3 @@ void TreeSocket::OnDataReady()
SendError("RecvQ overrun (line too long)");
Utils->Creator->loopCall = false;
}
-
-bool TreeSocket::Introduced()
-{
- return (capab == NULL);
-}
diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp
index acb822fbf..513fa6dbf 100644
--- a/src/modules/m_spanningtree/treesocket2.cpp
+++ b/src/modules/m_spanningtree/treesocket2.cpp
@@ -23,41 +23,38 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "link.h"
#include "treesocket.h"
#include "resolvers.h"
+#include "commands.h"
/* Handle ERROR command */
-void TreeSocket::Error(parameterlist &params)
+void TreeSocket::Error(CommandBase::Params& params)
{
std::string msg = params.size() ? params[0] : "";
SetError("received ERROR " + msg);
}
-void TreeSocket::Split(const std::string& line, std::string& prefix, std::string& command, parameterlist& params)
+void TreeSocket::Split(const std::string& line, std::string& prefix, std::string& command, CommandBase::Params& params)
{
irc::tokenstream tokens(line);
- if (!tokens.GetToken(prefix))
+ if (!tokens.GetMiddle(prefix))
return;
-
+
if (prefix[0] == ':')
{
- prefix = prefix.substr(1);
+ prefix.erase(prefix.begin());
if (prefix.empty())
{
this->SendError("BUG (?) Empty prefix received: " + line);
return;
}
- if (!tokens.GetToken(command))
+ if (!tokens.GetMiddle(command))
{
this->SendError("BUG (?) Empty command received: " + line);
return;
@@ -72,7 +69,7 @@ void TreeSocket::Split(const std::string& line, std::string& prefix, std::string
this->SendError("BUG (?) Empty command received: " + line);
std::string param;
- while (tokens.GetToken(param))
+ while (tokens.GetTrailing(param))
{
params.push_back(param);
}
@@ -82,9 +79,9 @@ void TreeSocket::ProcessLine(std::string &line)
{
std::string prefix;
std::string command;
- parameterlist params;
+ CommandBase::Params params;
- ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());
Split(line, prefix, command, params);
@@ -151,7 +148,7 @@ void TreeSocket::ProcessLine(std::string &line)
{
if (params.size())
{
- time_t them = atoi(params[0].c_str());
+ time_t them = ConvToInt(params[0]);
time_t delta = them - ServerInstance->Time();
if ((delta < -600) || (delta > 600))
{
@@ -171,25 +168,7 @@ void TreeSocket::ProcessLine(std::string &line)
if (!CheckDuplicate(capab->name, capab->sid))
return;
- this->LinkState = CONNECTED;
- Utils->timeoutlist.erase(this);
-
- linkID = capab->name;
-
- MyRoot = new TreeServer(Utils, capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden);
- Utils->TreeRoot->AddChild(MyRoot);
-
- MyRoot->bursting = true;
- this->DoBurst(MyRoot);
-
- parameterlist sparams;
- sparams.push_back(MyRoot->GetName());
- sparams.push_back("*");
- sparams.push_back("0");
- sparams.push_back(MyRoot->GetID());
- sparams.push_back(":" + MyRoot->GetDesc());
- Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(), "SERVER", sparams, MyRoot->GetName());
- Utils->DoOneToAllButSender(MyRoot->GetID(), "BURST", params, MyRoot->GetName());
+ FinishAuth(capab->name, capab->sid, capab->description, capab->hidden);
}
else if (command == "ERROR")
{
@@ -235,52 +214,63 @@ void TreeSocket::ProcessLine(std::string &line)
}
}
-void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+User* TreeSocket::FindSource(const std::string& prefix, const std::string& command)
{
- User* who = ServerInstance->FindUUID(prefix);
- std::string direction;
+ // Empty prefix means the source is the directly connected server that sent this command
+ if (prefix.empty())
+ return MyRoot->ServerUser;
- if (!who)
+ if (prefix.size() == 3)
{
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (prefix.empty())
- ServerSource = MyRoot;
+ // Prefix looks like a sid
+ TreeServer* server = Utils->FindServerID(prefix);
+ if (server)
+ return server->ServerUser;
+ }
+ else
+ {
+ // If the prefix string is a uuid FindUUID() returns the appropriate User object
+ User* user = ServerInstance->FindUUID(prefix);
+ if (user)
+ return user;
+ }
- if (ServerSource)
- {
- who = ServerSource->ServerUser;
- }
- else
- {
- /* It is important that we don't close the link here, unknown prefix can occur
- * due to various race conditions such as the KILL message for a user somehow
- * crossing the users QUIT further upstream from the server. Thanks jilles!
- */
+ // Some implementations wrongly send a server name as prefix occasionally, handle that too for now
+ TreeServer* const server = Utils->FindServer(prefix);
+ if (server)
+ return server->ServerUser;
- if ((prefix.length() == UUID_LENGTH-1) && (isdigit(prefix[0])) &&
- ((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
- {
- /* Special case, we cannot drop these commands as they've been committed already on a
- * part of the network by the time we receive them, so in this scenario pretend the
- * command came from a server to avoid desync.
- */
+ /* It is important that we don't close the link here, unknown prefix can occur
+ * due to various race conditions such as the KILL message for a user somehow
+ * crossing the users QUIT further upstream from the server. Thanks jilles!
+ */
- who = ServerInstance->FindUUID(prefix.substr(0, 3));
- if (!who)
- who = this->MyRoot->ServerUser;
- }
- else
- {
- ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.",
- command.c_str(), prefix.c_str());
- return;
- }
- }
+ if ((prefix.length() == UIDGenerator::UUID_LENGTH) && (isdigit(prefix[0])) &&
+ ((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
+ {
+ /* Special case, we cannot drop these commands as they've been committed already on a
+ * part of the network by the time we receive them, so in this scenario pretend the
+ * command came from a server to avoid desync.
+ */
+
+ TreeServer* const usersserver = Utils->FindServerID(prefix.substr(0, 3));
+ if (usersserver)
+ return usersserver->ServerUser;
+ return this->MyRoot->ServerUser;
}
- // Make sure prefix is still good
- direction = who->server;
- prefix = who->uuid;
+ // Unknown prefix
+ return NULL;
+}
+
+void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, CommandBase::Params& params)
+{
+ User* who = FindSource(prefix, command);
+ if (!who)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.", command.c_str(), prefix.c_str());
+ return;
+ }
/*
* Check for fake direction here, and drop any instances that are found.
@@ -298,214 +288,69 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
* a valid SID or a valid UUID, so that invalid UUID or SID never makes it
* to the higher level functions. -- B
*/
- TreeServer* route_back_again = Utils->BestRouteTo(direction);
- if ((!route_back_again) || (route_back_again->GetSocket() != this))
+ TreeServer* const server = TreeServer::Get(who);
+ if (server->GetSocket() != this)
{
- if (route_back_again)
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Protocol violation: Fake direction '%s' from connection '%s'",
- prefix.c_str(),linkID.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Protocol violation: Fake direction '%s' from connection '%s'", prefix.c_str(), linkID.c_str());
return;
}
- /*
- * First up, check for any malformed commands (e.g. MODE without a timestamp)
- * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
- */
- if (command == "SVSMODE") // This isn't in an "else if" so we still force FMODE for changes on channels.
- command = "MODE";
-
- // TODO move all this into Commands
- if (command == "MAP")
- {
- Utils->Creator->HandleMap(params, who);
- }
- else if (command == "SERVER")
- {
- this->RemoteServer(prefix,params);
- }
- else if (command == "ERROR")
- {
- this->Error(params);
- }
- else if (command == "AWAY")
- {
- this->Away(prefix,params);
- }
- else if (command == "PING")
- {
- this->LocalPing(prefix,params);
- }
- else if (command == "PONG")
- {
- TreeServer *s = Utils->FindServer(prefix);
- if (s && s->bursting)
- {
- ServerInstance->SNO->WriteGlobalSno('l',"Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", prefix.c_str());
- s->FinishBurst();
- }
- this->LocalPong(prefix,params);
- }
- else if (command == "VERSION")
- {
- this->ServerVersion(prefix,params);
- }
- else if (command == "ADDLINE")
+ // Translate commands coming from servers using an older protocol
+ if (proto_version < ProtocolVersion)
{
- this->AddLine(prefix,params);
- }
- else if (command == "DELLINE")
- {
- this->DelLine(prefix,params);
- }
- else if (command == "SAVE")
- {
- this->ForceNick(prefix,params);
- }
- else if (command == "OPERQUIT")
- {
- this->OperQuit(prefix,params);
- }
- else if (command == "IDLE")
- {
- this->Whois(prefix,params);
- }
- else if (command == "PUSH")
- {
- this->Push(prefix,params);
- }
- else if (command == "SQUIT")
- {
- if (params.size() == 2)
- {
- this->Squit(Utils->FindServer(params[0]),params[1]);
- }
- }
- else if (command == "SNONOTICE")
- {
- if (params.size() >= 2)
- {
- ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + who->nick + ": "+ params[1]);
- params[1] = ":" + params[1];
- Utils->DoOneToAllButSender(prefix, command, params, prefix);
- }
- }
- else if (command == "BURST")
- {
- // Set prefix server as bursting
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (!ServerSource)
- {
- ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
+ if (!PreProcessOldProtocolMessage(who, command, params))
return;
- }
-
- ServerSource->bursting = true;
- Utils->DoOneToAllButSender(prefix, command, params, prefix);
}
- else if (command == "ENDBURST")
- {
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (!ServerSource)
- {
- ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
- return;
- }
- ServerSource->FinishBurst();
- Utils->DoOneToAllButSender(prefix, command, params, prefix);
- }
- else if (command == "ENCAP")
+ ServerCommand* scmd = Utils->Creator->CmdManager.GetHandler(command);
+ CommandBase* cmdbase = scmd;
+ Command* cmd = NULL;
+ if (!scmd)
{
- this->Encap(who, params);
- }
- else if (command == "NICK")
- {
- if (params.size() != 2)
- {
- SendError("Protocol violation: Wrong number of parameters for NICK message");
- return;
- }
-
- if (IS_SERVER(who))
- {
- SendError("Protocol violation: Server changing nick");
- return;
- }
-
- if ((isdigit(params[0][0])) && (params[0] != who->uuid))
- {
- SendError("Protocol violation: User changing nick to an invalid UID - " + params[0]);
- return;
- }
-
- /* Update timestamp on user when they change nicks */
- who->age = atoi(params[1].c_str());
-
- /*
- * On nick messages, check that the nick doesnt already exist here.
- * If it does, perform collision logic.
- */
- bool callfnc = true;
- User* x = ServerInstance->FindNickOnly(params[0]);
- if ((x) && (x != who) && (x->registered == REG_ALL))
+ // Not a special server-to-server command
+ cmd = ServerInstance->Parser.GetHandler(command);
+ if (!cmd)
{
- int collideret = 0;
- /* x is local, who is remote */
- collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
- if (collideret != 1)
+ if (command == "ERROR")
{
- // Remote client lost, or both lost, rewrite this nick change as a change to uuid before
- // forwarding and don't call ForceNickChange() because DoCollision() has done it already
- params[0] = who->uuid;
- callfnc = false;
+ this->Error(params);
+ return;
+ }
+ else if (command == "BURST")
+ {
+ // This is sent even when there is no need for it, drop it here for now
+ return;
}
- }
- if (callfnc)
- who->ForceNickChange(params[0].c_str());
- Utils->RouteCommand(route_back_again, command, params, who);
- }
- else
- {
- Command* cmd = ServerInstance->Parser->GetHandler(command);
-
- if (!cmd)
- {
- irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised S2S command :%s %s %s",
- who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
- SendError("Unrecognised command '" + command + "' -- possibly loaded mismatched modules");
- return;
- }
- if (params.size() < cmd->min_params)
- {
- irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Insufficient parameters for S2S command :%s %s %s",
- who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
- SendError("Insufficient parameters for command '" + command + "'");
- return;
+ throw ProtocolException("Unknown command");
}
+ cmdbase = cmd;
+ }
- if ((!params.empty()) && (params.back().empty()) && (!cmd->allow_empty_last_param))
- {
- // the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
- if (params.size()-1 < cmd->min_params)
- return;
- params.pop_back();
- }
+ if (params.size() < cmdbase->min_params)
+ throw ProtocolException("Insufficient parameters");
- CmdResult res = cmd->Handle(params, who);
+ if ((!params.empty()) && (params.back().empty()) && (!cmdbase->allow_empty_last_param))
+ {
+ // the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
+ if (params.size()-1 < cmdbase->min_params)
+ return;
+ params.pop_back();
+ }
+ CmdResult res;
+ if (scmd)
+ res = scmd->Handle(who, params);
+ else
+ {
+ ClientProtocol::TagMap tags;
+ res = cmd->Handle(who, CommandBase::Params(params, tags));
if (res == CMD_INVALID)
- {
- irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Error handling S2S command :%s %s %s",
- who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
- SendError("Error handling '" + command + "' -- possibly loaded mismatched modules");
- }
- else if (res == CMD_SUCCESS)
- Utils->RouteCommand(route_back_again, command, params, who);
+ throw ProtocolException("Error in command handler");
}
+
+ if (res == CMD_SUCCESS)
+ Utils->RouteCommand(server->GetRoute(), cmdbase, params, who);
}
void TreeSocket::OnTimeout()
@@ -515,8 +360,10 @@ void TreeSocket::OnTimeout()
void TreeSocket::Close()
{
- if (fd != -1)
- ServerInstance->GlobalCulls.AddItem(this);
+ if (fd < 0)
+ return;
+
+ ServerInstance->GlobalCulls.AddItem(this);
this->BufferedSocket::Close();
SetError("Remote host closed connection");
@@ -524,18 +371,30 @@ void TreeSocket::Close()
// If the connection is fully up (state CONNECTED)
// then propogate a netsplit to all peers.
if (MyRoot)
- Squit(MyRoot,getError());
+ MyRoot->SQuit(getError());
- if (!ConnectionFailureShown)
- {
- ConnectionFailureShown = true;
- ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
+ ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
- time_t server_uptime = ServerInstance->Time() - this->age;
- if (server_uptime)
- {
- std::string timestr = Utils->Creator->TimeToStr(server_uptime);
- ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
- }
+ time_t server_uptime = ServerInstance->Time() - this->age;
+ if (server_uptime)
+ {
+ std::string timestr = ModuleSpanningTree::TimeToStr(server_uptime);
+ ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
}
}
+
+void TreeSocket::FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden)
+{
+ this->LinkState = CONNECTED;
+ Utils->timeoutlist.erase(this);
+
+ linkID = remotename;
+
+ MyRoot = new TreeServer(remotename, remotedesc, remotesid, Utils->TreeRoot, this, hidden);
+
+ // Mark the server as bursting
+ MyRoot->BeginBurst();
+ this->DoBurst(MyRoot);
+
+ CommandServer::Builder(MyRoot).Forward(MyRoot);
+}
diff --git a/src/modules/m_spanningtree/uid.cpp b/src/modules/m_spanningtree/uid.cpp
index 6620dd13a..01af56fa6 100644
--- a/src/modules/m_spanningtree/uid.cpp
+++ b/src/modules/m_spanningtree/uid.cpp
@@ -23,173 +23,145 @@
#include "commands.h"
#include "utils.h"
-#include "link.h"
-#include "treesocket.h"
#include "treeserver.h"
-#include "resolvers.h"
+#include "remoteuser.h"
-CmdResult CommandUID::Handle(const parameterlist &params, User* serversrc)
+CmdResult CommandUID::HandleServer(TreeServer* remoteserver, CommandBase::Params& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
- /** Do we have enough parameters:
+ /**
* 0 1 2 3 4 5 6 7 8 9 (n-1)
- * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos
+ * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :real
*/
- time_t age_t = ConvToInt(params[1]);
- time_t signon = ConvToInt(params[7]);
+ time_t age_t = ServerCommand::ExtractTS(params[1]);
+ time_t signon = ServerCommand::ExtractTS(params[7]);
std::string empty;
- std::string modestr(params[8]);
-
- TreeServer* remoteserver = Utils->FindServer(serversrc->server);
-
- if (!remoteserver)
- return CMD_INVALID;
- /* Is this a valid UID, and not misrouted? */
- if (params[0].length() != 9 || params[0].substr(0,3) != serversrc->uuid)
- return CMD_INVALID;
- /* Check parameters for validity before introducing the client, discovered by dmb */
- if (!age_t)
- return CMD_INVALID;
- if (!signon)
- return CMD_INVALID;
- if (modestr[0] != '+')
- return CMD_INVALID;
- TreeSocket* sock = remoteserver->GetRoute()->GetSocket();
+ const std::string& modestr = params[8];
- /* check for collision */
- User* const collideswith = ServerInstance->FindNickOnly(params[2]);
+ // Check if the length of the uuid is correct and confirm the sid portion of the uuid matches the sid of the server introducing the user
+ if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].compare(0, 3, remoteserver->GetID()))
+ throw ProtocolException("Bogus UUID");
+ // Sanity check on mode string: must begin with '+'
+ if (modestr[0] != '+')
+ throw ProtocolException("Invalid mode string");
+ // See if there is a nick collision
+ User* collideswith = ServerInstance->FindNickOnly(params[2]);
if ((collideswith) && (collideswith->registered != REG_ALL))
{
// User that the incoming user is colliding with is not fully registered, we force nick change the
// unregistered user to their uuid and tell them what happened
- collideswith->WriteFrom(collideswith, "NICK %s", collideswith->uuid.c_str());
- collideswith->WriteNumeric(433, "%s %s :Nickname overruled.", collideswith->nick.c_str(), collideswith->nick.c_str());
-
- // Clear the bit before calling User::ChangeNick() to make it NOT run the OnUserPostNick() hook
- collideswith->registered &= ~REG_NICK;
- collideswith->ChangeNick(collideswith->uuid, true);
+ LocalUser* const localuser = static_cast<LocalUser*>(collideswith);
+ localuser->OverruleNick();
}
else if (collideswith)
{
- /*
- * Nick collision.
- */
- int collide = sock->DoCollision(collideswith, age_t, params[5], params[6], params[0]);
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"*** Collision on %s, collide=%d", params[2].c_str(), collide);
-
- if (collide != 1)
+ // The user on this side is registered, handle the collision
+ bool they_change = Utils->DoCollision(collideswith, remoteserver, age_t, params[5], params[6], params[0], "UID");
+ if (they_change)
{
- /* remote client lost, make sure we change their nick for the hash too
- *
- * This alters the line that will be sent to other servers, which
- * commands normally shouldn't do; hence the required const_cast.
- */
- const_cast<parameterlist&>(params)[2] = params[0];
+ // The client being introduced needs to change nick to uuid, change the nick in the message before
+ // processing/forwarding it. Also change the nick TS to CommandSave::SavedTimestamp.
+ age_t = CommandSave::SavedTimestamp;
+ params[1] = ConvToStr(CommandSave::SavedTimestamp);
+ params[2] = params[0];
}
}
- /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically
- * sets it up in the UUID hash for us.
+ /* For remote users, we pass the UUID they sent to the constructor.
+ * If the UUID already exists User::User() throws an exception which causes this connection to be closed.
*/
- User* _new = NULL;
- try
- {
- _new = new RemoteUser(params[0], remoteserver->GetName());
- }
- catch (...)
- {
- ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str());
- return CMD_INVALID;
- }
- (*(ServerInstance->Users->clientlist))[params[2]] = _new;
+ RemoteUser* _new = new SpanningTree::RemoteUser(params[0], remoteserver);
+ ServerInstance->Users->clientlist[params[2]] = _new;
_new->nick = params[2];
- _new->host = params[3];
- _new->dhost = params[4];
+ _new->ChangeRealHost(params[3], false);
+ _new->ChangeDisplayedHost(params[4]);
_new->ident = params[5];
- _new->fullname = params[params.size() - 1];
+ _new->ChangeRealName(params.back());
_new->registered = REG_ALL;
_new->signon = signon;
_new->age = age_t;
- /* we need to remove the + from the modestring, so we can do our stuff */
- std::string::size_type pos_after_plus = modestr.find_first_not_of('+');
- if (pos_after_plus != std::string::npos)
- modestr = modestr.substr(pos_after_plus);
-
unsigned int paramptr = 9;
- for (std::string::iterator v = modestr.begin(); v != modestr.end(); v++)
+
+ for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v)
{
- /* For each mode thats set, increase counter */
+ // Accept more '+' chars, for now
+ if (*v == '+')
+ continue;
+
+ /* For each mode thats set, find the mode handler and set it on the new user */
ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER);
+ if (!mh)
+ throw ProtocolException("Unrecognised mode '" + std::string(1, *v) + "'");
- if (mh)
+ if (mh->NeedsParam(true))
{
- if (mh->GetNumParams(true))
- {
- if (paramptr >= params.size() - 1)
- return CMD_INVALID;
- std::string mp = params[paramptr++];
- /* IMPORTANT NOTE:
- * All modes are assumed to succeed here as they are being set by a remote server.
- * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important
- * to note as all but one modules currently cannot ever fail in this situation, except for
- * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE
- * but here, at client introduction. You may safely assume this behaviour is standard and
- * will not change in future versions if you want to make use of this protective behaviour
- * yourself.
- */
- mh->OnModeChange(_new, _new, NULL, mp, true);
- }
- else
- mh->OnModeChange(_new, _new, NULL, empty, true);
- _new->SetMode(*v, true);
+ if (paramptr >= params.size() - 1)
+ throw ProtocolException("Out of parameters while processing modes");
+ std::string mp = params[paramptr++];
+ /* IMPORTANT NOTE:
+ * All modes are assumed to succeed here as they are being set by a remote server.
+ * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important
+ * to note as all but one modules currently cannot ever fail in this situation, except for
+ * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE
+ * but here, at client introduction. You may safely assume this behaviour is standard and
+ * will not change in future versions if you want to make use of this protective behaviour
+ * yourself.
+ */
+ mh->OnModeChange(_new, _new, NULL, mp, true);
}
+ else
+ mh->OnModeChange(_new, _new, NULL, empty, true);
+ _new->SetMode(mh, true);
}
- /* now we've done with modes processing, put the + back for remote servers */
- if (modestr[0] != '+')
- modestr = "+" + modestr;
+ _new->SetClientIP(params[6]);
- _new->SetClientIP(params[6].c_str());
-
- ServerInstance->Users->AddGlobalClone(_new);
- remoteserver->SetUserCount(1); // increment by 1
+ ServerInstance->Users->AddClone(_new);
+ remoteserver->UserCount++;
bool dosend = true;
- if ((Utils->quiet_bursts && remoteserver->bursting) || ServerInstance->SilentULine(_new->server))
+ if ((Utils->quiet_bursts && remoteserver->IsBehindBursting()) || _new->server->IsSilentULine())
dosend = false;
if (dosend)
- ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", _new->server.c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString(), _new->fullname.c_str());
+ ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", remoteserver->GetName().c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->GetRealName().c_str());
- FOREACH_MOD(I_OnPostConnect,OnPostConnect(_new));
+ FOREACH_MOD(OnPostConnect, (_new));
return CMD_SUCCESS;
}
-CmdResult CommandFHost::Handle(const parameterlist &params, User* src)
-{
- if (IS_SERVER(src))
- return CMD_FAILURE;
- src->ChangeDisplayedHost(params[0].c_str());
+CmdResult CommandFHost::HandleRemote(RemoteUser* src, Params& params)
+{
+ src->ChangeDisplayedHost(params[0]);
return CMD_SUCCESS;
}
-CmdResult CommandFIdent::Handle(const parameterlist &params, User* src)
+CmdResult CommandFIdent::HandleRemote(RemoteUser* src, Params& params)
{
- if (IS_SERVER(src))
- return CMD_FAILURE;
- src->ChangeIdent(params[0].c_str());
+ src->ChangeIdent(params[0]);
return CMD_SUCCESS;
}
-CmdResult CommandFName::Handle(const parameterlist &params, User* src)
+CmdResult CommandFName::HandleRemote(RemoteUser* src, Params& params)
{
- if (IS_SERVER(src))
- return CMD_FAILURE;
- src->ChangeName(params[0].c_str());
+ src->ChangeRealName(params[0]);
return CMD_SUCCESS;
}
+CommandUID::Builder::Builder(User* user)
+ : CmdBuilder(TreeServer::Get(user)->GetID(), "UID")
+{
+ push(user->uuid);
+ push_int(user->age);
+ push(user->nick);
+ push(user->GetRealHost());
+ push(user->GetDisplayedHost());
+ push(user->ident);
+ push(user->GetIPString());
+ push_int(user->signon);
+ push(user->GetModeLetters(true));
+ push_last(user->GetRealName());
+}
diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp
index 367a3b921..cde627e21 100644
--- a/src/modules/m_spanningtree/utils.cpp
+++ b/src/modules/m_spanningtree/utils.cpp
@@ -21,21 +21,19 @@
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "link.h"
#include "treesocket.h"
#include "resolvers.h"
+#include "commandbuilder.h"
+
+SpanningTreeUtilities* Utils = NULL;
-/* Create server sockets off a listener. */
ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
{
- if (from->bind_tag->getString("type") != "servers")
+ if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "servers"))
return MOD_RES_PASSTHRU;
std::string incomingip = client->addr();
@@ -45,7 +43,7 @@ ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from
if (*i == "*" || *i == incomingip || irc::sockets::cidr_mask(*i).match(*client))
{
/* we don't need to do anything with the pointer, creating it stores it in the necessary places */
- new TreeSocket(Utils, newsock, from, client, server);
+ new TreeSocket(newsock, from, client, server);
return MOD_RES_ALLOW;
}
}
@@ -53,18 +51,12 @@ ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from
return MOD_RES_DENY;
}
-/** Yay for fast searches!
- * This is hundreds of times faster than recursion
- * or even scanning a linked list, especially when
- * there are more than a few servers to deal with.
- * (read as: lots).
- */
TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
{
- if (ServerInstance->IsSID(ServerName))
+ if (InspIRCd::IsSID(ServerName))
return this->FindServerID(ServerName);
- server_hash::iterator iter = serverlist.find(ServerName.c_str());
+ server_hash::iterator iter = serverlist.find(ServerName);
if (iter != serverlist.end())
{
return iter->second;
@@ -75,41 +67,8 @@ TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
}
}
-/** Returns the locally connected server we must route a
- * message through to reach server 'ServerName'. This
- * only applies to one-to-one and not one-to-many routing.
- * See the comments for the constructor of TreeServer
- * for more details.
- */
-TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
-{
- if (ServerName.c_str() == TreeRoot->GetName() || ServerName == ServerInstance->Config->GetSID())
- return NULL;
- TreeServer* Found = FindServer(ServerName);
- if (Found)
- {
- return Found->GetRoute();
- }
- else
- {
- // Cheat a bit. This allows for (better) working versions of routing commands with nick based prefixes, without hassle
- User *u = ServerInstance->FindNick(ServerName);
- if (u)
- {
- Found = FindServer(u->server);
- if (Found)
- return Found->GetRoute();
- }
-
- return NULL;
- }
-}
-
/** Find the first server matching a given glob mask.
- * Theres no find-using-glob method of hash_map [awwww :-(]
- * so instead, we iterate over the list using an iterator
- * and match each one until we get a hit. Yes its slow,
- * deal with it.
+ * We iterate over the list and match each one until we get a hit.
*/
TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
{
@@ -130,27 +89,36 @@ TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id)
return NULL;
}
-SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C) : Creator(C)
+TreeServer* SpanningTreeUtilities::FindRouteTarget(const std::string& target)
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"***** Using SID for hash: %s *****", ServerInstance->Config->GetSID().c_str());
+ TreeServer* const server = FindServer(target);
+ if (server)
+ return server;
+
+ User* const user = ServerInstance->FindNick(target);
+ if (user)
+ return TreeServer::Get(user);
- this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
- this->ReadConfiguration();
+ return NULL;
+}
+
+SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C)
+ : Creator(C), TreeRoot(NULL)
+ , PingFreq(60) // XXX: TreeServer constructor reads this and TreeRoot is created before the config is read, so init it to something (value doesn't matter) to avoid a valgrind warning in TimerManager on unload
+{
+ ServerInstance->Timers.AddTimer(&RefreshTimer);
}
CullResult SpanningTreeUtilities::cull()
{
- while (TreeRoot->ChildCount())
+ const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+ while (!children.empty())
{
- TreeServer* child_server = TreeRoot->GetChild(0);
- if (child_server)
- {
- TreeSocket* sock = child_server->GetSocket();
- sock->Close();
- }
+ TreeSocket* sock = children.front()->GetSocket();
+ sock->Close();
}
- for(std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = timeoutlist.begin(); i != timeoutlist.end(); ++i)
+ for(TimeoutList::iterator i = timeoutlist.begin(); i != timeoutlist.end(); ++i)
{
TreeSocket* s = i->first;
s->Close();
@@ -165,26 +133,19 @@ SpanningTreeUtilities::~SpanningTreeUtilities()
delete TreeRoot;
}
-void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
-{
- if (list.find(server) == list.end())
- list[server] = server;
-}
-
-/* returns a list of DIRECT servernames for a specific channel */
-void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list)
+// Returns a list of DIRECT servers for a specific channel
+void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list)
{
unsigned int minrank = 0;
if (status)
{
- ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
if (mh)
minrank = mh->GetPrefixRank();
}
- const UserMembList *ulist = c->GetUsers();
-
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
if (IS_LOCAL(i->first))
continue;
@@ -194,86 +155,35 @@ void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerLis
if (exempt_list.find(i->first) == exempt_list.end())
{
- TreeServer* best = this->BestRouteTo(i->first->server);
- if (best)
- AddThisServer(best,list);
+ TreeServer* best = TreeServer::Get(i->first);
+ list.insert(best->GetSocket());
}
}
return;
}
-bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& omit)
+void SpanningTreeUtilities::DoOneToAllButSender(const CmdBuilder& params, TreeServer* omitroute)
{
- TreeServer* omitroute = this->BestRouteTo(omit);
- std::string FullLine = ":" + prefix + " " + command;
- unsigned int words = params.size();
- for (unsigned int x = 0; x < words; x++)
- {
- FullLine = FullLine + " " + params[x];
- }
- unsigned int items = this->TreeRoot->ChildCount();
- for (unsigned int x = 0; x < items; x++)
- {
- TreeServer* Route = this->TreeRoot->GetChild(x);
- // Send the line IF:
- // The route has a socket (its a direct connection)
- // The route isnt the one to be omitted
- // The route isnt the path to the one to be omitted
- if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
- {
- TreeSocket* Sock = Route->GetSocket();
- if (Sock)
- Sock->WriteLine(FullLine);
- }
- }
- return true;
-}
+ const std::string& FullLine = params.str();
-bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params)
-{
- std::string FullLine = ":" + prefix + " " + command;
- unsigned int words = params.size();
- for (unsigned int x = 0; x < words; x++)
- {
- FullLine = FullLine + " " + params[x];
- }
- unsigned int items = this->TreeRoot->ChildCount();
- for (unsigned int x = 0; x < items; x++)
+ const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- TreeServer* Route = this->TreeRoot->GetChild(x);
- if (Route && Route->GetSocket())
+ TreeServer* Route = *i;
+ // Send the line if the route isn't the path to the one to be omitted
+ if (Route != omitroute)
{
- TreeSocket* Sock = Route->GetSocket();
- if (Sock)
- Sock->WriteLine(FullLine);
+ Route->GetSocket()->WriteLine(FullLine);
}
}
- return true;
}
-bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& target)
+void SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, Server* server)
{
- TreeServer* Route = this->BestRouteTo(target);
- if (Route)
- {
- std::string FullLine = ":" + prefix + " " + command;
- unsigned int words = params.size();
- for (unsigned int x = 0; x < words; x++)
- {
- FullLine = FullLine + " " + params[x];
- }
- if (Route && Route->GetSocket())
- {
- TreeSocket* Sock = Route->GetSocket();
- if (Sock)
- Sock->WriteLine(FullLine);
- }
- return true;
- }
- else
- {
- return false;
- }
+ TreeServer* ts = static_cast<TreeServer*>(server);
+ TreeSocket* sock = ts->GetSocket();
+ if (sock)
+ sock->WriteLine(params);
}
void SpanningTreeUtilities::RefreshIPCache()
@@ -284,28 +194,27 @@ void SpanningTreeUtilities::RefreshIPCache()
Link* L = *i;
if (!L->Port)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a link block without a port.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring a link block without a port.");
/* Invalid link block */
continue;
}
- if (L->AllowMask.length())
- ValidIPs.push_back(L->AllowMask);
+ ValidIPs.insert(ValidIPs.end(), L->AllowMasks.begin(), L->AllowMasks.end());
irc::sockets::sockaddrs dummy;
bool ipvalid = irc::sockets::aptosa(L->IPAddr, L->Port, dummy);
if ((L->IPAddr == "*") || (ipvalid))
ValidIPs.push_back(L->IPAddr);
- else
+ else if (this->Creator->DNS)
{
+ SecurityIPResolver* sr = new SecurityIPResolver(Creator, *this->Creator->DNS, L->IPAddr, L, DNS::QUERY_AAAA);
try
{
- bool cached = false;
- SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, DNS_QUERY_AAAA);
- ServerInstance->AddResolver(sr, cached);
+ this->Creator->DNS->Process(sr);
}
- catch (...)
+ catch (DNS::Exception &)
{
+ delete sr;
}
}
}
@@ -319,15 +228,11 @@ void SpanningTreeUtilities::ReadConfiguration()
HideULines = security->getBool("hideulines");
AnnounceTSChange = options->getBool("announcets");
AllowOptCommon = options->getBool("allowmismatch");
- ChallengeResponse = !security->getBool("disablehmac");
quiet_bursts = ServerInstance->Config->ConfValue("performance")->getBool("quietbursts");
- PingWarnTime = options->getInt("pingwarning");
- PingFreq = options->getInt("serverpingfreq");
+ PingWarnTime = options->getDuration("pingwarning", 15);
+ PingFreq = options->getDuration("serverpingfreq", 60, 1);
- if (PingFreq == 0)
- PingFreq = 60;
-
- if (PingWarnTime < 0 || PingWarnTime > PingFreq - 1)
+ if (PingWarnTime >= PingFreq)
PingWarnTime = 0;
AutoconnectBlocks.clear();
@@ -339,14 +244,18 @@ void SpanningTreeUtilities::ReadConfiguration()
reference<Link> L = new Link(tag);
std::string linkname = tag->getString("name");
L->Name = linkname.c_str();
- L->AllowMask = tag->getString("allowmask");
+
+ irc::spacesepstream sep = tag->getString("allowmask");
+ for (std::string s; sep.GetToken(s);)
+ L->AllowMasks.push_back(s);
+
L->IPAddr = tag->getString("ipaddr");
- L->Port = tag->getInt("port");
+ L->Port = tag->getUInt("port", 0);
L->SendPass = tag->getString("sendpass", tag->getString("password"));
L->RecvPass = tag->getString("recvpass", tag->getString("password"));
L->Fingerprint = tag->getString("fingerprint");
L->HiddenFromStats = tag->getBool("statshidden");
- L->Timeout = tag->getInt("timeout", 30);
+ L->Timeout = tag->getDuration("timeout", 30);
L->Hook = tag->getString("ssl");
L->Bind = tag->getString("bind");
L->Hidden = tag->getBool("hidden");
@@ -355,31 +264,31 @@ void SpanningTreeUtilities::ReadConfiguration()
throw ModuleException("Invalid configuration, found a link tag without a name!" + (!L->IPAddr.empty() ? " IP address: "+L->IPAddr : ""));
if (L->Name.find('.') == std::string::npos)
- throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it must contain at least one '.' character");
+ throw ModuleException("The link name '"+L->Name+"' is invalid as it must contain at least one '.' character");
- if (L->Name.length() > 64)
- throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it is longer than 64 characters");
+ if (L->Name.length() > ServerInstance->Config->Limits.MaxHost)
+ throw ModuleException("The link name '"+L->Name+"' is invalid as it is longer than " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters");
if (L->RecvPass.empty())
- throw ModuleException("Invalid configuration for server '"+assign(L->Name)+"', recvpass not defined");
+ throw ModuleException("Invalid configuration for server '"+L->Name+"', recvpass not defined");
if (L->SendPass.empty())
- throw ModuleException("Invalid configuration for server '"+assign(L->Name)+"', sendpass not defined");
+ throw ModuleException("Invalid configuration for server '"+L->Name+"', sendpass not defined");
if ((L->SendPass.find(' ') != std::string::npos) || (L->RecvPass.find(' ') != std::string::npos))
- throw ModuleException("Link block '" + assign(L->Name) + "' has a password set that contains a space character which is invalid");
+ throw ModuleException("Link block '" + L->Name + "' has a password set that contains a space character which is invalid");
if ((L->SendPass[0] == ':') || (L->RecvPass[0] == ':'))
- throw ModuleException("Link block '" + assign(L->Name) + "' has a password set that begins with a colon (:) which is invalid");
+ throw ModuleException("Link block '" + L->Name + "' has a password set that begins with a colon (:) which is invalid");
if (L->IPAddr.empty())
{
L->IPAddr = "*";
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Configuration warning: Link block '" + assign(L->Name) + "' has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + L->Name + "' has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
}
if (!L->Port)
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Configuration warning: Link block '" + assign(L->Name) + "' has no port defined, you will not be able to /connect it.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + L->Name + "' has no port defined, you will not be able to /connect it.");
L->Fingerprint.erase(std::remove(L->Fingerprint.begin(), L->Fingerprint.end(), ':'), L->Fingerprint.end());
LinkBlocks.push_back(L);
@@ -390,7 +299,7 @@ void SpanningTreeUtilities::ReadConfiguration()
{
ConfigTag* tag = i->second;
reference<Autoconnect> A = new Autoconnect(tag);
- A->Period = tag->getInt("period");
+ A->Period = tag->getDuration("period", 60, 1);
A->NextConnectTime = ServerInstance->Time() + A->Period;
A->position = -1;
irc::spacesepstream ss(tag->getString("server"));
@@ -400,11 +309,6 @@ void SpanningTreeUtilities::ReadConfiguration()
A->servers.push_back(server);
}
- if (A->Period <= 0)
- {
- throw ModuleException("Invalid configuration for autoconnect, period not a positive integer!");
- }
-
if (A->servers.empty())
{
throw ModuleException("Invalid configuration for autoconnect, server cannot be empty!");
@@ -413,6 +317,9 @@ void SpanningTreeUtilities::ReadConfiguration()
AutoconnectBlocks.push_back(A);
}
+ for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+ i->second->CheckULine();
+
RefreshIPCache();
}
@@ -421,7 +328,7 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name)
for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
{
Link* x = *i;
- if (InspIRCd::Match(x->Name.c_str(), name.c_str(), rfc_case_insensitive_map))
+ if (InspIRCd::Match(x->Name, name, ascii_case_insensitive_map))
{
return x;
}
@@ -429,15 +336,20 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name)
return NULL;
}
-void SpanningTreeUtilities::Rehash()
+void SpanningTreeUtilities::SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit)
{
- server_hash temp;
- for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
- temp.insert(std::make_pair(i->first, i->second));
- serverlist.swap(temp);
- temp.clear();
-
- for (server_hash::const_iterator i = sidlist.begin(); i != sidlist.end(); ++i)
- temp.insert(std::make_pair(i->first, i->second));
- sidlist.swap(temp);
+ CmdBuilder msg(prefix, message_type);
+ msg.push_raw(' ');
+ if (status != 0)
+ msg.push_raw(status);
+ msg.push_raw(target->name).push_last(text);
+
+ TreeSocketSet list;
+ this->GetListOfServersForChannel(target, list, status, exempt_list);
+ for (TreeSocketSet::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ TreeSocket* Sock = *i;
+ if (Sock != omit)
+ Sock->WriteLine(msg);
+ }
}
diff --git a/src/modules/m_spanningtree/utils.h b/src/modules/m_spanningtree/utils.h
index 5559b3459..495b09414 100644
--- a/src/modules/m_spanningtree/utils.h
+++ b/src/modules/m_spanningtree/utils.h
@@ -20,37 +20,35 @@
*/
-#ifndef M_SPANNINGTREE_UTILS_H
-#define M_SPANNINGTREE_UTILS_H
+#pragma once
#include "inspircd.h"
+#include "cachetimer.h"
-/* Foward declarations */
class TreeServer;
class TreeSocket;
class Link;
class Autoconnect;
class ModuleSpanningTree;
class SpanningTreeUtilities;
+class CmdBuilder;
-/* This hash_map holds the hash equivalent of the server
- * tree, used for rapid linear lookups.
- */
-#ifdef HASHMAP_DEPRECATED
- typedef nspace::hash_map<std::string, TreeServer*, nspace::insensitive, irc::StrHashComp> server_hash;
-#else
- typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<std::string>, irc::StrHashComp> server_hash;
-#endif
+extern SpanningTreeUtilities* Utils;
-typedef std::map<TreeServer*,TreeServer*> TreeServerList;
+/** Associative container type, mapping server names/ids to TreeServers
+ */
+typedef TR1NS::unordered_map<std::string, TreeServer*, irc::insensitive, irc::StrHashComp> server_hash;
/** Contains helper functions and variables for this module,
* and keeps them out of the global namespace
*/
class SpanningTreeUtilities : public classbase
{
+ CacheRefreshTimer RefreshTimer;
+
public:
- typedef std::map<TreeSocket*, std::pair<std::string, int> > TimeoutList;
+ typedef std::set<TreeSocket*> TreeSocketSet;
+ typedef std::map<TreeSocket*, std::pair<std::string, unsigned int> > TimeoutList;
/** Creator module
*/
@@ -77,7 +75,7 @@ class SpanningTreeUtilities : public classbase
/* Number of seconds that a server can go without ping
* before opers are warned of high latency.
*/
- int PingWarnTime;
+ unsigned int PingWarnTime;
/** This variable represents the root of the server tree
*/
TreeServer *TreeRoot;
@@ -100,17 +98,9 @@ class SpanningTreeUtilities : public classbase
*/
std::vector<reference<Autoconnect> > AutoconnectBlocks;
- /** True (default) if we are to use challenge-response HMAC
- * to authenticate passwords.
- *
- * NOTE: This defaults to on, but should be turned off if
- * you are linking to an older version of inspircd.
- */
- bool ChallengeResponse;
-
/** Ping frequency of server to server links
*/
- int PingFreq;
+ unsigned int PingFreq;
/** Initialise utility class
*/
@@ -118,39 +108,39 @@ class SpanningTreeUtilities : public classbase
/** Prepare for class destruction
*/
- CullResult cull();
+ CullResult cull() CXX11_OVERRIDE;
/** Destroy class and free listeners etc
*/
~SpanningTreeUtilities();
- void RouteCommand(TreeServer*, const std::string&, const parameterlist&, User*);
+ void RouteCommand(TreeServer* origin, CommandBase* cmd, const CommandBase::Params& parameters, User* user);
/** Send a message from this server to one other local or remote
*/
- bool DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& target);
+ void DoOneToOne(const CmdBuilder& params, Server* target);
/** Send a message from this server to all but one other, local or remote
*/
- bool DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& omit);
+ void DoOneToAllButSender(const CmdBuilder& params, TreeServer* omit);
/** Send a message from this server to all others
*/
- bool DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params);
+ void DoOneToMany(const CmdBuilder& params);
/** Read the spanningtree module's tags from the config file
*/
void ReadConfiguration();
- /** Add a server to the server list for GetListOfServersForChannel
+ /** Handle nick collision
*/
- void AddThisServer(TreeServer* server, TreeServerList &list);
+ bool DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid, const char* collidecmd);
/** Compile a list of servers which contain members of channel c
*/
- void GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list);
+ void GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list);
- /** Find a server by name
+ /** Find a server by name or SID
*/
TreeServer* FindServer(const std::string &ServerName);
@@ -158,9 +148,10 @@ class SpanningTreeUtilities : public classbase
*/
TreeServer* FindServerID(const std::string &id);
- /** Find a route to a server by name
+ /** Find a server based on a target string.
+ * @param target Target string where a command should be routed to. May be a server name, a sid, a nickname or a uuid.
*/
- TreeServer* BestRouteTo(const std::string &ServerName);
+ TreeServer* FindRouteTarget(const std::string& target);
/** Find a server by glob mask
*/
@@ -174,10 +165,12 @@ class SpanningTreeUtilities : public classbase
*/
void RefreshIPCache();
- /** Recreate serverlist and sidlist, this is needed because of m_nationalchars changing
- * national_case_insensitive_map which is used by the hash function
+ /** Sends a PRIVMSG or a NOTICE to a channel obeying an exempt list and an optional prefix
*/
- void Rehash();
+ void SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit = NULL);
};
-#endif
+inline void SpanningTreeUtilities::DoOneToMany(const CmdBuilder& params)
+{
+ DoOneToAllButSender(params, NULL);
+}
diff --git a/src/modules/m_spanningtree/version.cpp b/src/modules/m_spanningtree/version.cpp
deleted file mode 100644
index e08d13e6e..000000000
--- a/src/modules/m_spanningtree/version.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::ServerVersion(const std::string &prefix, parameterlist &params)
-{
- if (params.size() < 1)
- return true;
-
- TreeServer* ServerSource = Utils->FindServer(prefix);
-
- if (ServerSource)
- {
- ServerSource->SetVersion(params[0]);
- }
- params[0] = ":" + params[0];
- Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix);
- return true;
-}
-
diff --git a/src/modules/m_sqlauth.cpp b/src/modules/m_sqlauth.cpp
index df97145be..5c3c5a84e 100644
--- a/src/modules/m_sqlauth.cpp
+++ b/src/modules/m_sqlauth.cpp
@@ -18,10 +18,9 @@
#include "inspircd.h"
-#include "sql.h"
-#include "hash.h"
-
-/* $ModDesc: Allow/Deny connections based upon an arbitrary SQL table */
+#include "modules/sql.h"
+#include "modules/hash.h"
+#include "modules/ssl.h"
enum AuthState {
AUTH_STATE_NONE = 0,
@@ -29,24 +28,69 @@ enum AuthState {
AUTH_STATE_FAIL = 2
};
-class AuthQuery : public SQLQuery
+class AuthQuery : public SQL::Query
{
public:
const std::string uid;
LocalIntExt& pendingExt;
bool verbose;
- AuthQuery(Module* me, const std::string& u, LocalIntExt& e, bool v)
- : SQLQuery(me), uid(u), pendingExt(e), verbose(v)
+ const std::string& kdf;
+ const std::string& pwcolumn;
+
+ AuthQuery(Module* me, const std::string& u, LocalIntExt& e, bool v, const std::string& kd, const std::string& pwcol)
+ : SQL::Query(me)
+ , uid(u)
+ , pendingExt(e)
+ , verbose(v)
+ , kdf(kd)
+ , pwcolumn(pwcol)
{
}
-
- void OnResult(SQLResult& res)
+
+ void OnResult(SQL::Result& res) CXX11_OVERRIDE
{
- User* user = ServerInstance->FindNick(uid);
+ LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(uid));
if (!user)
return;
+
if (res.Rows())
{
+ if (!kdf.empty())
+ {
+ HashProvider* hashprov = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + kdf);
+ if (!hashprov)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (a provider for %s was not loaded)", user->GetFullRealHost().c_str(), kdf.c_str());
+ pendingExt.set(user, AUTH_STATE_FAIL);
+ return;
+ }
+
+ size_t colindex = 0;
+ if (!pwcolumn.empty() && !res.HasColumn(pwcolumn, colindex))
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (the column specified (%s) was not returned)", user->GetFullRealHost().c_str(), pwcolumn.c_str());
+ pendingExt.set(user, AUTH_STATE_FAIL);
+ return;
+ }
+
+ SQL::Row row;
+ while (res.GetRow(row))
+ {
+ if (hashprov->Compare(user->password, row[colindex]))
+ {
+ pendingExt.set(user, AUTH_STATE_NONE);
+ return;
+ }
+ }
+
+ if (verbose)
+ ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (Password from the SQL query did not match the user provided password)", user->GetFullRealHost().c_str());
+ pendingExt.set(user, AUTH_STATE_FAIL);
+ return;
+ }
+
pendingExt.set(user, AUTH_STATE_NONE);
}
else
@@ -57,41 +101,38 @@ class AuthQuery : public SQLQuery
}
}
- void OnError(SQLerror& error)
+ void OnError(SQL::Error& error) CXX11_OVERRIDE
{
User* user = ServerInstance->FindNick(uid);
if (!user)
return;
pendingExt.set(user, AUTH_STATE_FAIL);
if (verbose)
- ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL query failed: %s)", user->GetFullRealHost().c_str(), error.Str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL query failed: %s)", user->GetFullRealHost().c_str(), error.ToString());
}
};
class ModuleSQLAuth : public Module
{
LocalIntExt pendingExt;
- dynamic_reference<SQLProvider> SQL;
+ dynamic_reference<SQL::Provider> SQL;
std::string freeformquery;
std::string killreason;
std::string allowpattern;
bool verbose;
+ std::vector<std::string> hash_algos;
+ std::string kdf;
+ std::string pwcolumn;
public:
- ModuleSQLAuth() : pendingExt("sqlauth-wait", this), SQL(this, "SQL")
+ ModuleSQLAuth()
+ : pendingExt("sqlauth-wait", ExtensionItem::EXT_USER, this)
+ , SQL(this, "SQL")
{
}
- void init()
- {
- ServerInstance->Modules->AddService(pendingExt);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnCheckReady, I_OnRehash, I_OnUserRegister };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("sqlauth");
std::string dbid = conf->getString("dbid");
@@ -103,9 +144,17 @@ class ModuleSQLAuth : public Module
killreason = conf->getString("killreason");
allowpattern = conf->getString("allowpattern");
verbose = conf->getBool("verbose");
+ kdf = conf->getString("kdf");
+ pwcolumn = conf->getString("column");
+
+ hash_algos.clear();
+ irc::commasepstream algos(conf->getString("hash", "md5,sha256"));
+ std::string algo;
+ while (algos.GetToken(algo))
+ hash_algos.push_back(algo);
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
// Note this is their initial (unresolved) connect block
ConfigTag* tag = user->MyClass->config;
@@ -127,24 +176,26 @@ class ModuleSQLAuth : public Module
pendingExt.set(user, AUTH_STATE_BUSY);
- ParamM userinfo;
- SQL->PopulateUserInfo(user, userinfo);
+ SQL::ParamMap userinfo;
+ SQL::PopulateUserInfo(user, userinfo);
userinfo["pass"] = user->password;
- HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
- if (md5)
- userinfo["md5pass"] = md5->hexsum(user->password);
+ for (std::vector<std::string>::const_iterator it = hash_algos.begin(); it != hash_algos.end(); ++it)
+ {
+ HashProvider* hashprov = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + *it);
+ if (hashprov && !hashprov->IsKDF())
+ userinfo[*it + "pass"] = hashprov->Generate(user->password);
+ }
- HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
- if (sha256)
- userinfo["sha256pass"] = sha256->hexsum(user->password);
+ const std::string certfp = SSLClientCert::GetFingerprint(&user->eh);
+ userinfo["certfp"] = certfp;
- SQL->submit(new AuthQuery(this, user->uuid, pendingExt, verbose), freeformquery, userinfo);
+ SQL->Submit(new AuthQuery(this, user->uuid, pendingExt, verbose, kdf, pwcolumn), freeformquery, userinfo);
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
switch (pendingExt.get(user))
{
@@ -159,7 +210,7 @@ class ModuleSQLAuth : public Module
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allow/Deny connections based upon an arbitrary SQL table", VF_VENDOR);
}
diff --git a/src/modules/m_sqloper.cpp b/src/modules/m_sqloper.cpp
index ae581cc4b..da538caef 100644
--- a/src/modules/m_sqloper.cpp
+++ b/src/modules/m_sqloper.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2017 Dylan Frank <b00mx0r@aureus.pw>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
@@ -18,138 +19,161 @@
#include "inspircd.h"
-#include "sql.h"
-#include "hash.h"
+#include "modules/sql.h"
-/* $ModDesc: Allows storage of oper credentials in an SQL table */
-
-static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
-
-class OpMeQuery : public SQLQuery
+class OperQuery : public SQL::Query
{
public:
+ // This variable will store all the OPER blocks from the DB
+ std::vector<std::string>& my_blocks;
+ /** We want to store the username and password if this is called during an /OPER, as we're responsible for /OPER post-DB fetch
+ * Note: uid will be empty if this DB update was not called as a result of a user command (i.e. /REHASH)
+ */
const std::string uid, username, password;
- OpMeQuery(Module* me, const std::string& u, const std::string& un, const std::string& pw)
- : SQLQuery(me), uid(u), username(un), password(pw)
+ OperQuery(Module* me, std::vector<std::string>& mb, const std::string& u, const std::string& un, const std::string& pw)
+ : SQL::Query(me)
+ , my_blocks(mb)
+ , uid(u)
+ , username(un)
+ , password(pw)
+ {
+ }
+ OperQuery(Module* me, std::vector<std::string>& mb)
+ : SQL::Query(me)
+ , my_blocks(mb)
{
}
- void OnResult(SQLResult& res)
+ void OnResult(SQL::Result& res) CXX11_OVERRIDE
{
- ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: result for %s", uid.c_str());
- User* user = ServerInstance->FindNick(uid);
- if (!user)
- return;
+ ServerConfig::OperIndex& oper_blocks = ServerInstance->Config->oper_blocks;
- // multiple rows may exist
- SQLEntries row;
+ // Remove our previous blocks from oper_blocks for a clean update
+ for (std::vector<std::string>::const_iterator i = my_blocks.begin(); i != my_blocks.end(); ++i)
+ {
+ oper_blocks.erase(*i);
+ }
+ my_blocks.clear();
+
+ SQL::Row row;
+ // Iterate through DB results to create oper blocks from sqloper rows
while (res.GetRow(row))
{
-#if 0
- parameterlist cols;
+ std::vector<std::string> cols;
res.GetCols(cols);
- std::vector<KeyVal>* items;
- reference<ConfigTag> tag = ConfigTag::create("oper", "<m_sqloper>", 0, items);
- for(unsigned int i=0; i < cols.size(); i++)
+ // Create the oper tag as if we were the conf file.
+ ConfigItems* items;
+ reference<ConfigTag> tag = ConfigTag::create("oper", MODNAME, 0, items);
+
+ /** Iterate through each column in the SQLOpers table. An infinite number of fields can be specified.
+ * Column 'x' with cell value 'y' will be the same as x=y in an OPER block in opers.conf.
+ */
+ for (unsigned int i=0; i < cols.size(); ++i)
{
- if (!row[i].nul)
- items->insert(std::make_pair(cols[i], row[i]));
+ if (!row[i].IsNull())
+ (*items)[cols[i]] = row[i];
}
-#else
- if (OperUser(user, row[0], row[1]))
- return;
-#endif
+ const std::string name = tag->getString("name");
+
+ // Skip both duplicate sqloper blocks and sqloper blocks that attempt to override conf blocks.
+ if (oper_blocks.find(name) != oper_blocks.end())
+ continue;
+
+ const std::string type = tag->getString("type");
+ ServerConfig::OperIndex::iterator tblk = ServerInstance->Config->OperTypes.find(type);
+ if (tblk == ServerInstance->Config->OperTypes.end())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sqloper block " + name + " has missing type " + type);
+ ServerInstance->SNO->WriteGlobalSno('a', "m_sqloper: Oper block %s has missing type %s", name.c_str(), type.c_str());
+ continue;
+ }
+
+ OperInfo* ifo = new OperInfo(type);
+
+ ifo->type_block = tblk->second->type_block;
+ ifo->oper_block = tag;
+ ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
+ oper_blocks[name] = ifo;
+ my_blocks.push_back(name);
+ }
+
+ // If this was done as a result of /OPER and not a config read
+ if (!uid.empty())
+ {
+ // Now that we've updated the DB, call any other /OPER hooks and then call /OPER
+ OperExec();
}
- ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: no matches for %s (checked %d rows)", uid.c_str(), res.Rows());
- // nobody succeeded... fall back to OPER
- fallback();
}
- void OnError(SQLerror& error)
+ void OnError(SQL::Error& error) CXX11_OVERRIDE
{
- ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: query failed (%s)", error.Str());
- fallback();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "query failed (%s)", error.ToString());
+ ServerInstance->SNO->WriteGlobalSno('a', "m_sqloper: failed to update blocks from database");
+ if (!uid.empty())
+ {
+ // Fallback. We don't want to block a netadmin from /OPER
+ OperExec();
+ }
}
- void fallback()
+ // Call /oper after placing all blocks from the SQL table into the config->oper_blocks list.
+ void OperExec()
{
User* user = ServerInstance->FindNick(uid);
- if (!user)
+ LocalUser* localuser = IS_LOCAL(user);
+ // This should never be true
+ if (!localuser)
return;
- Command* oper_command = ServerInstance->Parser->GetHandler("OPER");
+ Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
if (oper_command)
{
- std::vector<std::string> params;
+ CommandBase::Params params;
params.push_back(username);
params.push_back(password);
- oper_command->Handle(params, user);
- }
- else
- {
- ServerInstance->Logs->Log("m_sqloper",SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
- }
- }
-
- bool OperUser(User* user, const std::string &pattern, const std::string &type)
- {
- OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(" " + type);
- if (iter == ServerInstance->Config->oper_blocks.end())
- {
- ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: bad type '%s' in returned row for oper %s", type.c_str(), username.c_str());
- return false;
- }
- OperInfo* ifo = iter->second;
- std::string hostname(user->ident);
+ // Begin callback to other modules (i.e. sslinfo) now that we completed the DB fetch
+ ModResult MOD_RESULT;
- hostname.append("@").append(user->host);
+ std::string origin = "OPER";
+ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (origin, params, localuser, true));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return;
- if (OneOfMatches(hostname.c_str(), user->GetIPString(), pattern))
+ // Now handle /OPER.
+ ClientProtocol::TagMap tags;
+ oper_command->Handle(user, CommandBase::Params(params, tags));
+ }
+ else
{
- /* Opertype and host match, looks like this is it. */
-
- user->Oper(ifo);
- return true;
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
}
-
- return false;
}
};
class ModuleSQLOper : public Module
{
+ // Whether OperQuery is running
+ bool active;
std::string query;
- std::string hashtype;
- dynamic_reference<SQLProvider> SQL;
+ // Stores oper blocks from DB
+ std::vector<std::string> my_blocks;
+ dynamic_reference<SQL::Provider> SQL;
public:
- ModuleSQLOper() : SQL(this, "SQL") {}
-
- void init()
+ ModuleSQLOper()
+ : active(false)
+ , SQL(this, "SQL")
{
- OnRehash(NULL);
-
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
+ // Clear list of our blocks, as ConfigReader just wiped them anyway
+ my_blocks.clear();
+
ConfigTag* tag = ServerInstance->Config->ConfValue("sqloper");
std::string dbid = tag->getString("dbid");
@@ -158,42 +182,67 @@ public:
else
SQL.SetProvider("SQL/" + dbid);
- hashtype = tag->getString("hash");
- query = tag->getString("query", "SELECT hostname as host, type FROM ircd_opers WHERE username='$username' AND password='$password'");
+ query = tag->getString("query", "SELECT * FROM ircd_opers WHERE active=1;");
+ // Update sqloper list from the database.
+ GetOperBlocks();
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ~ModuleSQLOper()
{
- if (validated && command == "OPER" && parameters.size() >= 2)
+ // Remove all oper blocks that were from the DB
+ for (std::vector<std::string>::const_iterator i = my_blocks.begin(); i != my_blocks.end(); ++i)
+ {
+ ServerInstance->Config->oper_blocks.erase(*i);
+ }
+ }
+
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+ {
+ // If we are not in the middle of an existing /OPER and someone is trying to oper-up
+ if (validated && command == "OPER" && parameters.size() >= 2 && !active)
{
if (SQL)
{
- LookupOper(user, parameters[0], parameters[1]);
- /* Query is in progress, it will re-invoke OPER if needed */
+ GetOperBlocks(user->uuid, parameters[0], parameters[1]);
+ /** We need to reload oper blocks from the DB before other
+ * hooks can run (i.e. sslinfo). We will re-call /OPER later.
+ */
return MOD_RES_DENY;
}
- ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: database not present");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "database not present");
+ }
+ else if (active)
+ {
+ active = false;
}
+ // There is either no DB or we successfully reloaded oper blocks
return MOD_RES_PASSTHRU;
}
- void LookupOper(User* user, const std::string &username, const std::string &password)
+ // The one w/o params is for non-/OPER DB updates, such as a rehash.
+ void GetOperBlocks()
{
- HashProvider* hash = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + hashtype);
-
- ParamM userinfo;
- SQL->PopulateUserInfo(user, userinfo);
- userinfo["username"] = username;
- userinfo["password"] = hash ? hash->hexsum(password) : password;
+ SQL->Submit(new OperQuery(this, my_blocks), query);
+ }
+ void GetOperBlocks(const std::string u, const std::string& un, const std::string& pw)
+ {
+ active = true;
+ // Call to SQL query to fetch oper list from SQL table.
+ SQL->Submit(new OperQuery(this, my_blocks, u, un, pw), query);
+ }
- SQL->submit(new OpMeQuery(this, user->uuid, username, password), query, userinfo);
+ void Prioritize() CXX11_OVERRIDE
+ {
+ /** Run before other /OPER hooks that expect populated blocks, i.e. sslinfo or a TOTP module.
+ * We issue a DENY first, and will re-run OnPreCommand later to trigger the other hooks post-DB update.
+ */
+ ServerInstance->Modules.SetPriority(this, I_OnPreCommand, PRIORITY_FIRST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows storage of oper credentials in an SQL table", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSQLOper)
diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp
index 083ac0f04..6d0a84249 100644
--- a/src/modules/m_sslinfo.cpp
+++ b/src/modules/m_sslinfo.cpp
@@ -18,13 +18,25 @@
#include "inspircd.h"
-#include "ssl.h"
+#include "modules/ssl.h"
+#include "modules/whois.h"
-/* $ModDesc: Provides SSL metadata, including /WHOIS information and /SSLINFO command */
+enum
+{
+ // From oftc-hybrid.
+ RPL_WHOISCERTFP = 276,
+
+ // From UnrealIRCd.
+ RPL_WHOISSECURE = 671
+};
class SSLCertExt : public ExtensionItem {
public:
- SSLCertExt(Module* parent) : ExtensionItem("ssl_cert", parent) {}
+ SSLCertExt(Module* parent)
+ : ExtensionItem("ssl_cert", ExtensionItem::EXT_USER, parent)
+ {
+ }
+
ssl_cert* get(const Extensible* item) const
{
return static_cast<ssl_cert*>(get_raw(item));
@@ -37,12 +49,12 @@ class SSLCertExt : public ExtensionItem {
delete old;
}
- std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
{
return static_cast<ssl_cert*>(item)->GetMetaLine();
}
- void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
{
ssl_cert* cert = new ssl_cert;
set(container, cert);
@@ -67,7 +79,7 @@ class SSLCertExt : public ExtensionItem {
}
}
- void free(void* item)
+ void free(Extensible* container, void* item) CXX11_OVERRIDE
{
ssl_cert* old = static_cast<ssl_cert*>(item);
if (old && old->refcount_dec())
@@ -87,107 +99,99 @@ class CommandSSLInfo : public Command
this->syntax = "<nick>";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* target = ServerInstance->FindNickOnly(parameters[0]);
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nickname", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
- if (operonlyfp && !IS_OPER(user) && target != user)
+ if (operonlyfp && !user->IsOper() && target != user)
{
- user->WriteServ("NOTICE %s :*** You cannot view SSL certificate information for other users", user->nick.c_str());
+ user->WriteNotice("*** You cannot view SSL certificate information for other users");
return CMD_FAILURE;
}
ssl_cert* cert = CertExt.get(target);
if (!cert)
{
- user->WriteServ("NOTICE %s :*** No SSL certificate for this user", user->nick.c_str());
+ user->WriteNotice("*** No SSL certificate for this user");
}
else if (cert->GetError().length())
{
- user->WriteServ("NOTICE %s :*** No SSL certificate information for this user (%s).", user->nick.c_str(), cert->GetError().c_str());
+ user->WriteNotice("*** No SSL certificate information for this user (" + cert->GetError() + ").");
}
else
{
- user->WriteServ("NOTICE %s :*** Distinguished Name: %s", user->nick.c_str(), cert->GetDN().c_str());
- user->WriteServ("NOTICE %s :*** Issuer: %s", user->nick.c_str(), cert->GetIssuer().c_str());
- user->WriteServ("NOTICE %s :*** Key Fingerprint: %s", user->nick.c_str(), cert->GetFingerprint().c_str());
+ user->WriteNotice("*** Distinguished Name: " + cert->GetDN());
+ user->WriteNotice("*** Issuer: " + cert->GetIssuer());
+ user->WriteNotice("*** Key Fingerprint: " + cert->GetFingerprint());
}
return CMD_SUCCESS;
}
};
-class ModuleSSLInfo : public Module
+class UserCertificateAPIImpl : public UserCertificateAPIBase
{
- CommandSSLInfo cmd;
+ SSLCertExt& ext;
public:
- ModuleSSLInfo() : cmd(this)
+ UserCertificateAPIImpl(Module* mod, SSLCertExt& certext)
+ : UserCertificateAPIBase(mod), ext(certext)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
+ ssl_cert* GetCertificate(User* user) CXX11_OVERRIDE
+ {
+ return ext.get(user);
+ }
+};
- ServerInstance->Modules->AddService(cmd.CertExt);
+class ModuleSSLInfo : public Module, public Whois::EventListener
+{
+ CommandSSLInfo cmd;
+ UserCertificateAPIImpl APIImpl;
- Implementation eventlist[] = { I_OnWhois, I_OnPreCommand, I_OnSetConnectClass, I_OnUserConnect, I_OnPostConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ public:
+ ModuleSSLInfo()
+ : Whois::EventListener(this)
+ , cmd(this)
+ , APIImpl(this, cmd.CertExt)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("SSL Certificate Utilities", VF_VENDOR);
}
- void OnWhois(User* source, User* dest)
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- ssl_cert* cert = cmd.CertExt.get(dest);
+ ssl_cert* cert = cmd.CertExt.get(whois.GetTarget());
if (cert)
{
- ServerInstance->SendWhoisLine(source, dest, 671, "%s %s :is using a secure connection", source->nick.c_str(), dest->nick.c_str());
+ whois.SendLine(RPL_WHOISSECURE, "is using a secure connection");
bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
- if ((!operonlyfp || source == dest || IS_OPER(source)) && !cert->fingerprint.empty())
- ServerInstance->SendWhoisLine(source, dest, 276, "%s %s :has client certificate fingerprint %s",
- source->nick.c_str(), dest->nick.c_str(), cert->fingerprint.c_str());
- }
- }
-
- bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
- {
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
+ if ((!operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
+ whois.SendLine(RPL_WHOISCERTFP, InspIRCd::Format("has client certificate fingerprint %s", cert->fingerprint.c_str()));
}
- return false;
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
if ((command == "OPER") && (validated))
{
- OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+ ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
if (i != ServerInstance->Config->oper_blocks.end())
{
OperInfo* ifo = i->second;
- if (!ifo->oper_block)
- return MOD_RES_PASSTHRU;
-
ssl_cert* cert = cmd.CertExt.get(user);
if (ifo->oper_block->getBool("sslonly") && !cert)
{
- user->WriteNumeric(491, "%s :This oper login requires an SSL connection.", user->nick.c_str());
+ user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires an SSL connection.");
user->CommandFloodPenalty += 10000;
return MOD_RES_DENY;
}
@@ -195,7 +199,7 @@ class ModuleSSLInfo : public Module
std::string fingerprint;
if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
{
- user->WriteNumeric(491, "%s :This oper login requires a matching SSL fingerprint.",user->nick.c_str());
+ user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires a matching SSL certificate fingerprint.");
user->CommandFloodPenalty += 10000;
return MOD_RES_DENY;
}
@@ -206,59 +210,68 @@ class ModuleSSLInfo : public Module
return MOD_RES_PASSTHRU;
}
- void OnUserConnect(LocalUser* user)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- SocketCertificateRequest req(&user->eh, this);
- if (!req.cert)
- return;
- cmd.CertExt.set(user, req.cert);
+ ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
+ if (cert)
+ cmd.CertExt.set(user, cert);
}
- void OnPostConnect(User* user)
+ void OnPostConnect(User* user) CXX11_OVERRIDE
{
- ssl_cert *cert = cmd.CertExt.get(user);
- if (!cert || cert->fingerprint.empty())
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (!localuser)
+ return;
+
+ const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(&localuser->eh);
+ if (!ssliohook)
+ return;
+
+ ssl_cert* const cert = ssliohook->GetCertificate();
+
+ {
+ std::string text = "*** You are connected to ";
+ if (!ssliohook->GetServerName(text))
+ text.append(ServerInstance->Config->ServerName);
+ text.append(" using SSL cipher '");
+ ssliohook->GetCiphersuite(text);
+ text.push_back('\'');
+ if ((cert) && (!cert->GetFingerprint().empty()))
+ text.append(" and your SSL certificate fingerprint is ").append(cert->GetFingerprint());
+ user->WriteNotice(text);
+ }
+
+ if (!cert)
return;
// find an auto-oper block for this user
- for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
{
OperInfo* ifo = i->second;
- if (!ifo->oper_block)
- continue;
-
std::string fp = ifo->oper_block->getString("fingerprint");
if (fp == cert->fingerprint && ifo->oper_block->getBool("autologin"))
user->Oper(ifo);
}
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
- SocketCertificateRequest req(&user->eh, this);
+ ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
bool ok = true;
if (myclass->config->getString("requiressl") == "trusted")
{
- ok = (req.cert && req.cert->IsCAVerified());
+ ok = (cert && cert->IsCAVerified());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires a trusted SSL cert. Client %s one.", (ok ? "has" : "does not have"));
}
else if (myclass->config->getBool("requiressl"))
{
- ok = (req.cert != NULL);
+ ok = (cert != NULL);
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires any SSL cert. Client %s one.", (ok ? "has" : "does not have"));
}
if (!ok)
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
-
- void OnRequest(Request& request)
- {
- if (strcmp("GET_USER_CERT", request.id) == 0)
- {
- UserCertificateRequest& req = static_cast<UserCertificateRequest&>(request);
- req.cert = cmd.CertExt.get(req.user);
- }
- }
};
MODULE_INIT(ModuleSSLInfo)
-
diff --git a/src/modules/m_sslmodes.cpp b/src/modules/m_sslmodes.cpp
index c81c74207..dc0063d29 100644
--- a/src/modules/m_sslmodes.cpp
+++ b/src/modules/m_sslmodes.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2013 Shawn Smith <shawn@inspircd.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
* Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
@@ -22,38 +23,64 @@
#include "inspircd.h"
-#include "ssl.h"
+#include "modules/ssl.h"
-/* $ModDesc: Provides channel mode +z to allow for Secure/SSL only channels */
+enum
+{
+ // From UnrealIRCd.
+ ERR_SECUREONLYCHAN = 489,
+ ERR_ALLMUSTSSL = 490
+};
+
+namespace
+{
+ bool IsSSLUser(UserCertificateAPI& api, User* user)
+ {
+ if (!api)
+ return false;
+
+ ssl_cert* cert = api->GetCertificate(user);
+ return (cert != NULL);
+ }
+}
/** Handle channel mode +z
*/
class SSLMode : public ModeHandler
{
+ private:
+ UserCertificateAPI& API;
+
public:
- SSLMode(Module* Creator) : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL) { }
+ SSLMode(Module* Creator, UserCertificateAPI& api)
+ : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL)
+ , API(api)
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
{
if (adding)
{
- if (!channel->IsModeSet('z'))
+ if (!channel->IsModeSet(this))
{
if (IS_LOCAL(source))
{
- const UserMembList* userlist = channel->GetUsers();
- for(UserMembCIter i = userlist->begin(); i != userlist->end(); i++)
+ if (!API)
+ return MODEACTION_DENY;
+
+ const Channel::MemberMap& userlist = channel->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
{
- UserCertificateRequest req(i->first, creator);
- req.Send();
- if(!req.cert && !ServerInstance->ULine(i->first->server))
+ ssl_cert* cert = API->GetCertificate(i->first);
+ if (!cert && !i->first->server->IsULine())
{
- source->WriteNumeric(ERR_ALLMUSTSSL, "%s %s :all members of the channel must be connected via SSL", source->nick.c_str(), channel->name.c_str());
+ source->WriteNumeric(ERR_ALLMUSTSSL, channel->name, "all members of the channel must be connected via SSL");
return MODEACTION_DENY;
}
}
}
- channel->SetMode('z',true);
+ channel->SetMode(this, true);
return MODEACTION_ALLOW;
}
else
@@ -63,9 +90,9 @@ class SSLMode : public ModeHandler
}
else
{
- if (channel->IsModeSet('z'))
+ if (channel->IsModeSet(this))
{
- channel->SetMode('z',false);
+ channel->SetMode(this, false);
return MODEACTION_ALLOW;
}
@@ -74,31 +101,72 @@ class SSLMode : public ModeHandler
}
};
-class ModuleSSLModes : public Module
+/** Handle user mode +z
+*/
+class SSLModeUser : public ModeHandler
{
-
- SSLMode sslm;
+ private:
+ UserCertificateAPI& API;
public:
- ModuleSSLModes()
- : sslm(this)
+ SSLModeUser(Module* Creator, UserCertificateAPI& api)
+ : ModeHandler(Creator, "sslqueries", 'z', PARAM_NONE, MODETYPE_USER)
+ , API(api)
{
+ if (!ServerInstance->Config->ConfValue("sslmodes")->getBool("enableumode"))
+ DisableAutoRegister();
+ }
+
+ ModeAction OnModeChange(User* user, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
+ {
+ if (adding)
+ {
+ if (!dest->IsModeSet(this))
+ {
+ if (!IsSSLUser(API, user))
+ return MODEACTION_DENY;
+
+ dest->SetMode(this, true);
+ return MODEACTION_ALLOW;
+ }
+ }
+ else
+ {
+ if (dest->IsModeSet(this))
+ {
+ dest->SetMode(this, false);
+ return MODEACTION_ALLOW;
+ }
+ }
+
+ return MODEACTION_DENY;
}
+};
+
+class ModuleSSLModes : public Module
+{
+ private:
+ UserCertificateAPI api;
+ SSLMode sslm;
+ SSLModeUser sslquery;
- void init()
+ public:
+ ModuleSSLModes()
+ : api(this)
+ , sslm(this, api)
+ , sslquery(this, api)
{
- ServerInstance->Modules->AddService(sslm);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- if(chan && chan->IsModeSet('z'))
+ if(chan && chan->IsModeSet(sslm))
{
- UserCertificateRequest req(user, this);
- req.Send();
- if (req.cert)
+ if (!api)
+ return MOD_RES_DENY;
+
+ ssl_cert* cert = api->GetCertificate(user);
+ if (cert)
{
// Let them in
return MOD_RES_PASSTHRU;
@@ -106,7 +174,7 @@ class ModuleSSLModes : public Module
else
{
// Deny
- user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick.c_str(), cname);
+ user->WriteNumeric(ERR_SECUREONLYCHAN, cname, "Cannot join channel; SSL users only (+z)");
return MOD_RES_DENY;
}
}
@@ -114,33 +182,63 @@ class ModuleSSLModes : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& msgtarget, MessageDetails& details) CXX11_OVERRIDE
{
- if ((mask.length() > 2) && (mask[0] == 'z') && (mask[1] == ':'))
+ if (msgtarget.type != MessageTarget::TYPE_USER)
+ return MOD_RES_PASSTHRU;
+
+ User* target = msgtarget.Get<User>();
+
+ /* If one or more of the parties involved is a ulined service, we wont stop it. */
+ if (user->server->IsULine() || target->server->IsULine())
+ return MOD_RES_PASSTHRU;
+
+ /* If the target is +z */
+ if (target->IsModeSet(sslquery))
{
- UserCertificateRequest req(user, this);
- req.Send();
- if (req.cert && InspIRCd::Match(req.cert->GetFingerprint(), mask.substr(2)))
+ if (!IsSSLUser(api, user))
+ {
+ /* The sending user is not on an SSL connection */
+ user->WriteNumeric(ERR_CANTSENDTOUSER, target->nick, "You are not permitted to send private messages to this user (+z set)");
return MOD_RES_DENY;
+ }
}
+ /* If the user is +z */
+ else if (user->IsModeSet(sslquery))
+ {
+ if (!IsSSLUser(api, target))
+ {
+ user->WriteNumeric(ERR_CANTSENDTOUSER, target->nick, "You must remove usermode 'z' before you are able to send private messages to a non-ssl user.");
+ return MOD_RES_DENY;
+ }
+ }
+
return MOD_RES_PASSTHRU;
}
- ~ModuleSSLModes()
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
+ if ((mask.length() > 2) && (mask[0] == 'z') && (mask[1] == ':'))
+ {
+ if (!api)
+ return MOD_RES_DENY;
+
+ ssl_cert* cert = api->GetCertificate(user);
+ if (cert && InspIRCd::Match(cert->GetFingerprint(), mask.substr(2)))
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('z');
+ tokens["EXTBAN"].push_back('z');
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides channel mode +z to allow for Secure/SSL only channels", VF_VENDOR);
+ return Version("Provides user and channel mode +z to allow for SSL-only channels, queries and notices.", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleSSLModes)
-
diff --git a/src/modules/m_starttls.cpp b/src/modules/m_starttls.cpp
new file mode 100644
index 000000000..ea6536ed1
--- /dev/null
+++ b/src/modules/m_starttls.cpp
@@ -0,0 +1,111 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Adam <Adam@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ssl.h"
+#include "modules/cap.h"
+
+// From IRCv3 tls-3.1
+enum
+{
+ RPL_STARTTLS = 670,
+ ERR_STARTTLS = 691
+};
+
+class CommandStartTLS : public SplitCommand
+{
+ dynamic_reference_nocheck<IOHookProvider>& ssl;
+
+ public:
+ CommandStartTLS(Module* mod, dynamic_reference_nocheck<IOHookProvider>& s)
+ : SplitCommand(mod, "STARTTLS")
+ , ssl(s)
+ {
+ works_before_reg = true;
+ }
+
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if (!ssl)
+ {
+ user->WriteNumeric(ERR_STARTTLS, "STARTTLS is not enabled");
+ return CMD_FAILURE;
+ }
+
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNumeric(ERR_STARTTLS, "STARTTLS is not permitted after client registration is complete");
+ return CMD_FAILURE;
+ }
+
+ if (user->eh.GetIOHook())
+ {
+ user->WriteNumeric(ERR_STARTTLS, "STARTTLS failure");
+ return CMD_FAILURE;
+ }
+
+ user->WriteNumeric(RPL_STARTTLS, "STARTTLS successful, go ahead with TLS handshake");
+ /* We need to flush the write buffer prior to adding the IOHook,
+ * otherwise we'll be sending this line inside the SSL session - which
+ * won't start its handshake until the client gets this line. Currently,
+ * we assume the write will not block here; this is usually safe, as
+ * STARTTLS is sent very early on in the registration phase, where the
+ * user hasn't built up much sendq. Handling a blocked write here would
+ * be very annoying.
+ */
+ user->eh.DoWrite();
+
+ ssl->OnAccept(&user->eh, NULL, NULL);
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleStartTLS : public Module
+{
+ CommandStartTLS starttls;
+ Cap::Capability tls;
+ dynamic_reference_nocheck<IOHookProvider> ssl;
+
+ public:
+ ModuleStartTLS()
+ : starttls(this, ssl)
+ , tls(this, "tls")
+ , ssl(this, "ssl")
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("starttls");
+
+ std::string newprovider = conf->getString("provider");
+ if (newprovider.empty())
+ ssl.SetProvider("ssl");
+ else
+ ssl.SetProvider("ssl/" + newprovider);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for the STARTTLS command", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleStartTLS)
diff --git a/src/modules/m_stripcolor.cpp b/src/modules/m_stripcolor.cpp
index f1504edaf..3699fc3aa 100644
--- a/src/modules/m_stripcolor.cpp
+++ b/src/modules/m_stripcolor.cpp
@@ -20,89 +20,75 @@
#include "inspircd.h"
-
-/* $ModDesc: Provides channel +S mode (strip ansi color) */
-
-/** Handles channel mode +S
- */
-class ChannelStripColor : public SimpleChannelModeHandler
-{
- public:
- ChannelStripColor(Module* Creator) : SimpleChannelModeHandler(Creator, "stripcolor", 'S') { }
-};
-
-/** Handles user mode +S
- */
-class UserStripColor : public SimpleUserModeHandler
-{
- public:
- UserStripColor(Module* Creator) : SimpleUserModeHandler(Creator, "u_stripcolor", 'S') { }
-};
-
+#include "modules/exemption.h"
class ModuleStripColor : public Module
{
- ChannelStripColor csc;
- UserStripColor usc;
+ CheckExemption::EventProvider exemptionprov;
+ SimpleChannelModeHandler csc;
+ SimpleUserModeHandler usc;
public:
- ModuleStripColor() : csc(this), usc(this)
+ ModuleStripColor()
+ : exemptionprov(this)
+ , csc(this, "stripcolor", 'S')
+ , usc(this, "u_stripcolor", 'S')
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(usc);
- ServerInstance->Modules->AddService(csc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["EXTBAN"].push_back('S');
}
- virtual ~ModuleStripColor()
- {
- }
-
- virtual void On005Numeric(std::string &output)
- {
- ServerInstance->AddExtBanChar('S');
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
bool active = false;
- if (target_type == TYPE_USER)
+ if (target.type == MessageTarget::TYPE_USER)
{
- User* t = (User*)dest;
- active = t->IsModeSet('S');
+ User* t = target.Get<User>();
+ active = t->IsModeSet(usc);
}
- else if (target_type == TYPE_CHANNEL)
+ else if (target.type == MessageTarget::TYPE_CHANNEL)
{
- Channel* t = (Channel*)dest;
- ModResult res = ServerInstance->OnCheckExemption(user,t,"stripcolor");
+ Channel* t = target.Get<Channel>();
+ ModResult res = CheckExemption::Call(exemptionprov, user, t, "stripcolor");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- active = !t->GetExtBanStatus(user, 'S').check(!t->IsModeSet('S'));
+ active = !t->GetExtBanStatus(user, 'S').check(!t->IsModeSet(csc));
}
if (active)
{
- InspIRCd::StripColor(text);
+ InspIRCd::StripColor(details.text);
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ void OnUserPart(Membership* memb, std::string& partmessage, CUList& except_list) CXX11_OVERRIDE
{
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
+ User* user = memb->user;
+ Channel* channel = memb->chan;
+
+ if (!IS_LOCAL(user))
+ return;
+
+ if (channel->GetExtBanStatus(user, 'S').check(!user->IsModeSet(csc)))
+ {
+ ModResult res = CheckExemption::Call(exemptionprov, user, channel, "stripcolor");
+
+ if (res != MOD_RES_ALLOW)
+ InspIRCd::StripColor(partmessage);
+ }
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel +S mode (strip ansi color)", VF_VENDOR);
}
diff --git a/src/modules/m_svshold.cpp b/src/modules/m_svshold.cpp
index e666b0fe2..e74f3324b 100644
--- a/src/modules/m_svshold.cpp
+++ b/src/modules/m_svshold.cpp
@@ -22,8 +22,7 @@
#include "inspircd.h"
#include "xline.h"
-
-/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */
+#include "modules/stats.h"
namespace
{
@@ -35,44 +34,38 @@ namespace
class SVSHold : public XLine
{
public:
- irc::string nickname;
+ std::string nickname;
SVSHold(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& nick)
: XLine(s_time, d, src, re, "SVSHOLD")
{
- this->nickname = nick.c_str();
- }
-
- ~SVSHold()
- {
+ this->nickname = nick;
}
- bool Matches(User *u)
+ bool Matches(User* u) CXX11_OVERRIDE
{
if (u->nick == nickname)
return true;
return false;
}
- bool Matches(const std::string &s)
+ bool Matches(const std::string& s) CXX11_OVERRIDE
{
- if (nickname == s)
- return true;
- return false;
+ return InspIRCd::Match(s, nickname);
}
- void DisplayExpiry()
+ void DisplayExpiry() CXX11_OVERRIDE
{
if (!silent)
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired SVSHOLD %s (set by %s %ld seconds ago)",
- this->nickname.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
+ ServerInstance->SNO->WriteToSnoMask('x', "Removing expired SVSHOLD %s (set by %s %ld seconds ago)",
+ nickname.c_str(), source.c_str(), (long)(ServerInstance->Time() - set_time));
}
}
- const char* Displayable()
+ const std::string& Displayable() CXX11_OVERRIDE
{
- return nickname.c_str();
+ return nickname;
}
};
@@ -85,12 +78,12 @@ class SVSHoldFactory : public XLineFactory
/** Generate a shun
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
return new SVSHold(set_time, duration, source, reason, xline_specific_mask);
}
- bool AutoApplyToUserList(XLine *x)
+ bool AutoApplyToUserList(XLine* x) CXX11_OVERRIDE
{
return false;
}
@@ -104,15 +97,14 @@ class CommandSvshold : public Command
CommandSvshold(Module* Creator) : Command(Creator, "SVSHOLD", 1)
{
flags_needed = 'o'; this->syntax = "<nickname> [<duration> :<reason>]";
- TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
/* syntax: svshold nickname time :reason goes here */
/* 'time' is a human-readable timestring, like 2d3h2s. */
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
{
/* don't allow SVSHOLD from non-ulined clients */
return CMD_FAILURE;
@@ -127,7 +119,7 @@ class CommandSvshold : public Command
}
else
{
- user->WriteServ("NOTICE %s :*** SVSHOLD %s not found in list, try /stats S.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** SVSHOLD " + parameters[0] + " not found in list, try /stats S.");
}
}
else
@@ -135,8 +127,7 @@ class CommandSvshold : public Command
if (parameters.size() < 3)
return CMD_FAILURE;
- // Adding - XXX todo make this respect <insane> tag perhaps..
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
SVSHold* r = new SVSHold(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
if (ServerInstance->XLines->AddLine(r, user))
@@ -151,7 +142,7 @@ class CommandSvshold : public Command
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteGlobalSno('x', "%s added timed SVSHOLD for %s, expires on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), parameters[2].c_str());
}
}
@@ -165,67 +156,65 @@ class CommandSvshold : public Command
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
};
-class ModuleSVSHold : public Module
+class ModuleSVSHold : public Module, public Stats::EventListener
{
CommandSvshold cmd;
SVSHoldFactory s;
public:
- ModuleSVSHold() : cmd(this)
+ ModuleSVSHold()
+ : Stats::EventListener(this)
+ , cmd(this)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->XLines->RegisterFactory(&s);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnUserPreNick, I_OnStats, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("svshold");
- silent = tag->getBool("silent");
+ silent = tag->getBool("silent", true);
}
- virtual ModResult OnStats(char symbol, User* user, string_list &out)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if(symbol != 'S')
+ if (stats.GetSymbol() != 'S')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("SVSHOLD", 210, user, out);
+ ServerInstance->XLines->InvokeStats("SVSHOLD", 210, stats);
return MOD_RES_DENY;
}
- virtual ModResult OnUserPreNick(User *user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
XLine *rl = ServerInstance->XLines->MatchesLine("SVSHOLD", newnick);
if (rl)
{
- user->WriteServ( "432 %s %s :Services reserved nickname: %s", user->nick.c_str(), newnick.c_str(), rl->reason.c_str());
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, InspIRCd::Format("Services reserved nickname: %s", rl->reason.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleSVSHold()
+ ~ModuleSVSHold()
{
ServerInstance->XLines->DelAll("SVSHOLD");
ServerInstance->XLines->UnregisterFactory(&s);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services.", VF_COMMON | VF_VENDOR);
}
diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp
index b29d268d1..6c420ca3a 100644
--- a/src/modules/m_swhois.cpp
+++ b/src/modules/m_swhois.cpp
@@ -24,8 +24,13 @@
#include "inspircd.h"
+#include "modules/whois.h"
-/* $ModDesc: Provides the SWHOIS command which allows setting of arbitrary WHOIS lines */
+enum
+{
+ // From UnrealIRCd.
+ RPL_WHOISSPECIAL = 320
+};
/** Handle /SWHOIS
*/
@@ -35,21 +40,21 @@ class CommandSwhois : public Command
LocalIntExt operblock;
StringExtItem swhois;
CommandSwhois(Module* Creator)
- : Command(Creator,"SWHOIS", 2,2)
- , operblock("swhois_operblock", Creator)
- , swhois("swhois", Creator)
+ : Command(Creator, "SWHOIS", 2, 2)
+ , operblock("swhois_operblock", ExtensionItem::EXT_USER, Creator)
+ , swhois("swhois", ExtensionItem::EXT_USER, Creator)
{
flags_needed = 'o'; syntax = "<nick> :<swhois>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle(const std::vector<std::string> &parameters, User* user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* dest = ServerInstance->FindNick(parameters[0]);
- if ((!dest) || (IS_SERVER(dest))) // allow setting swhois using SWHOIS before reg
+ if (!dest) // allow setting swhois using SWHOIS before reg
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
@@ -57,11 +62,11 @@ class CommandSwhois : public Command
if (text)
{
// We already had it set...
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
// Ulines set SWHOISes silently
ServerInstance->SNO->WriteGlobalSno('a', "%s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick.c_str(), dest->nick.c_str(), text->c_str(), parameters[1].c_str());
}
- else if (!ServerInstance->ULine(user->server))
+ else if (!user->server->IsULine())
{
// Ulines set SWHOISes silently
ServerInstance->SNO->WriteGlobalSno('a', "%s used SWHOIS to set %s's extra whois to '%s'", user->nick.c_str(), dest->nick.c_str(), parameters[1].c_str());
@@ -86,34 +91,28 @@ class CommandSwhois : public Command
};
-class ModuleSWhois : public Module
+class ModuleSWhois : public Module, public Whois::LineEventListener
{
CommandSwhois cmd;
public:
- ModuleSWhois() : cmd(this)
- {
- }
-
- void init()
+ ModuleSWhois()
+ : Whois::LineEventListener(this)
+ , cmd(this)
{
- ServiceProvider* providerlist[] = { &cmd, &cmd.operblock, &cmd.swhois };
- ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
- Implementation eventlist[] = { I_OnWhoisLine, I_OnPostOper, I_OnPostDeoper, I_OnDecodeMetaData };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* We use this and not OnWhois because this triggers for remote, too */
- if (numeric == 312)
+ if (numeric.GetNumeric() == 312)
{
/* Insert our numeric before 312 */
- std::string* swhois = cmd.swhois.get(dest);
+ std::string* swhois = cmd.swhois.get(whois.GetTarget());
if (swhois)
{
- ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), swhois->c_str());
+ whois.SendLine(RPL_WHOISSPECIAL, *swhois);
}
}
@@ -121,7 +120,7 @@ class ModuleSWhois : public Module
return MOD_RES_PASSTHRU;
}
- void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+ void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return;
@@ -136,7 +135,7 @@ class ModuleSWhois : public Module
ServerInstance->PI->SendMetaData(user, "swhois", swhois);
}
- void OnPostDeoper(User* user)
+ void OnPostDeoper(User* user) CXX11_OVERRIDE
{
std::string* swhois = cmd.swhois.get(user);
if (!swhois)
@@ -150,20 +149,14 @@ class ModuleSWhois : public Module
ServerInstance->PI->SendMetaData(user, "swhois", "");
}
- void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string&)
+ void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string&) CXX11_OVERRIDE
{
- // XXX: We use a dynamic_cast in m_services_account so I used one
- // here but do we actually need it or is static_cast okay?
- User* dest = dynamic_cast<User*>(target);
+ User* dest = static_cast<User*>(target);
if (dest && (extname == "swhois"))
cmd.operblock.set(dest, 0);
}
- ~ModuleSWhois()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the SWHOIS command which allows setting of arbitrary WHOIS lines", VF_OPTCOMMON | VF_VENDOR);
}
diff --git a/src/modules/m_testnet.cpp b/src/modules/m_testnet.cpp
deleted file mode 100644
index 401766d8a..000000000
--- a/src/modules/m_testnet.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $ModDesc: Provides a module for testing the server while linked in a network */
-
-#include "inspircd.h"
-
-struct vtbase
-{
- virtual void isok(const char* name, int impl, Module* basemod, std::vector<std::string>& allmods) = 0;
- virtual ~vtbase() {}
-};
-
-template<typename T> struct vtable : public vtbase
-{
- union u {
- T function;
- struct v {
- size_t delta;
- size_t vtoff;
- } v;
- } u;
- vtable(T t) {
- u.function = t;
- }
- /** member function pointer dereference from vtable; depends on the GCC 4.4 ABI (x86_64) */
- template<typename E> void* read(E* obj)
- {
- if (u.v.delta & 1)
- {
- uint8_t* optr = reinterpret_cast<uint8_t*>(obj);
- optr += u.v.vtoff;
- uint8_t* vptr = *reinterpret_cast<uint8_t**>(optr);
- vptr += u.v.delta - 1;
- return *reinterpret_cast<void**>(vptr);
- }
- else
- return reinterpret_cast<void*>(u.v.delta);
- }
- void isok(const char* name, int impl, Module* basemod, std::vector<std::string>& allmods)
- {
- void* base = read(basemod);
- for(unsigned int i=0; i < allmods.size(); ++i)
- {
- Module* mod = ServerInstance->Modules->Find(allmods[i]);
- void* fptr = read(mod);
- for(EventHandlerIter j = ServerInstance->Modules->EventHandlers[impl].begin();
- j != ServerInstance->Modules->EventHandlers[impl].end(); j++)
- {
- if (mod == *j)
- {
- if (fptr == base)
- {
- ServerInstance->SNO->WriteToSnoMask('a', "Module %s implements %s but uses default function",
- mod->ModuleSourceFile.c_str(), name);
- }
- goto done;
- }
- }
- if (fptr != base)
- {
- ServerInstance->SNO->WriteToSnoMask('a', "Module %s does not implement %s but overrides function",
- mod->ModuleSourceFile.c_str(), name);
- }
- done:;
- }
- }
-};
-
-template<typename T> vtbase* vtinit(T t)
-{
- return new vtable<T>(t);
-}
-
-static void checkall(Module* noimpl)
-{
- std::vector<std::string> allmods = ServerInstance->Modules->GetAllModuleNames(0);
-#define CHK(name) do { \
- vtbase* vt = vtinit(&Module::name); \
- vt->isok(#name, I_ ## name, noimpl, allmods); \
- delete vt; \
-} while (0)
- CHK(OnUserConnect);
- CHK(OnUserQuit);
- CHK(OnUserDisconnect);
- CHK(OnUserJoin);
- CHK(OnUserPart);
- CHK(OnRehash);
- CHK(OnSendSnotice);
- CHK(OnUserPreJoin);
- CHK(OnUserPreKick);
- CHK(OnUserKick);
- CHK(OnOper);
- CHK(OnInfo);
- CHK(OnWhois);
- CHK(OnUserPreInvite);
- CHK(OnUserInvite);
- CHK(OnUserPreMessage);
- CHK(OnUserPreNotice);
- CHK(OnUserPreNick);
- CHK(OnUserMessage);
- CHK(OnUserNotice);
- CHK(OnMode);
- CHK(OnGetServerDescription);
- CHK(OnSyncUser);
- CHK(OnSyncChannel);
- CHK(OnDecodeMetaData);
- CHK(OnWallops);
- CHK(OnAcceptConnection);
- CHK(OnChangeHost);
- CHK(OnChangeName);
- CHK(OnAddLine);
- CHK(OnDelLine);
- CHK(OnExpireLine);
- CHK(OnUserPostNick);
- CHK(OnPreMode);
- CHK(On005Numeric);
- CHK(OnKill);
- CHK(OnRemoteKill);
- CHK(OnLoadModule);
- CHK(OnUnloadModule);
- CHK(OnBackgroundTimer);
- CHK(OnPreCommand);
- CHK(OnCheckReady);
- CHK(OnCheckInvite);
- CHK(OnRawMode);
- CHK(OnCheckKey);
- CHK(OnCheckLimit);
- CHK(OnCheckBan);
- CHK(OnCheckChannelBan);
- CHK(OnExtBanCheck);
- CHK(OnStats);
- CHK(OnChangeLocalUserHost);
- CHK(OnPreTopicChange);
- CHK(OnPostTopicChange);
- CHK(OnEvent);
- CHK(OnGlobalOper);
- CHK(OnPostConnect);
- CHK(OnAddBan);
- CHK(OnDelBan);
- CHK(OnChangeLocalUserGECOS);
- CHK(OnUserRegister);
- CHK(OnChannelPreDelete);
- CHK(OnChannelDelete);
- CHK(OnPostOper);
- CHK(OnSyncNetwork);
- CHK(OnSetAway);
- CHK(OnPostCommand);
- CHK(OnPostJoin);
- CHK(OnWhoisLine);
- CHK(OnBuildNeighborList);
- CHK(OnGarbageCollect);
- CHK(OnText);
- CHK(OnPassCompare);
- CHK(OnRunTestSuite);
- CHK(OnNamesListItem);
- CHK(OnNumeric);
- CHK(OnHookIO);
- CHK(OnPreRehash);
- CHK(OnModuleRehash);
- CHK(OnSendWhoLine);
- CHK(OnChangeIdent);
-}
-
-class CommandTest : public Command
-{
- public:
- CommandTest(Module* parent) : Command(parent, "TEST", 1)
- {
- syntax = "<action> <parameters>";
- }
-
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
- {
- if (parameters[0] == "flood")
- {
- unsigned int count = parameters.size() > 1 ? atoi(parameters[1].c_str()) : 100;
- std::string line = parameters.size() > 2 ? parameters[2] : ":z.z NOTICE !flood :Flood text";
- for(unsigned int i=0; i < count; i++)
- user->Write(line);
- }
- else if (parameters[0] == "freeze" && IS_LOCAL(user) && parameters.size() > 1)
- {
- IS_LOCAL(user)->CommandFloodPenalty += atoi(parameters[1].c_str());
- }
- else if (parameters[0] == "check")
- {
- checkall(creator);
- ServerInstance->SNO->WriteToSnoMask('a', "Module check complete");
- }
- return CMD_SUCCESS;
- }
-};
-
-class ModuleTest : public Module
-{
- CommandTest cmd;
- public:
- ModuleTest() : cmd(this)
- {
- }
-
- void init()
- {
- if (!strstr(ServerInstance->Config->ServerName.c_str(), ".test"))
- throw ModuleException("Don't load modules without reading their descriptions!");
- ServerInstance->Modules->AddService(cmd);
- }
-
- Version GetVersion()
- {
- return Version("Provides a module for testing the server while linked in a network", VF_VENDOR|VF_OPTCOMMON);
- }
-};
-
-MODULE_INIT(ModuleTest)
-
diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp
index bbbc518bd..058028f61 100644
--- a/src/modules/m_timedbans.cpp
+++ b/src/modules/m_timedbans.cpp
@@ -20,16 +20,14 @@
*/
-/* $ModDesc: Adds timed bans */
-
#include "inspircd.h"
+#include "listmode.h"
/** Holds a timed ban
*/
class TimedBan
{
public:
- std::string channel;
std::string mask;
time_t expire;
Channel* chan;
@@ -42,76 +40,79 @@ timedbans TimedBanList;
*/
class CommandTban : public Command
{
- static bool IsBanSet(Channel* chan, const std::string& mask)
+ ChanModeReference banmode;
+
+ bool IsBanSet(Channel* chan, const std::string& mask)
{
- for (BanList::const_iterator i = chan->bans.begin(); i != chan->bans.end(); ++i)
+ ListModeBase* banlm = static_cast<ListModeBase*>(*banmode);
+ if (!banlm)
+ return false;
+ const ListModeBase::ModeList* bans = banlm->GetList(chan);
+ if (bans)
{
- if (!strcasecmp(i->data.c_str(), mask.c_str()))
- return true;
+ for (ListModeBase::ModeList::const_iterator i = bans->begin(); i != bans->end(); ++i)
+ {
+ const ListModeBase::ListItem& ban = *i;
+ if (!strcasecmp(ban.mask.c_str(), mask.c_str()))
+ return true;
+ }
}
+
return false;
}
public:
CommandTban(Module* Creator) : Command(Creator,"TBAN", 3)
+ , banmode(Creator, "ban")
{
syntax = "<channel> <duration> <banmask>";
- TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
Channel* channel = ServerInstance->FindChan(parameters[0]);
if (!channel)
{
- user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
return CMD_FAILURE;
}
- int cm = channel->GetPrefixValue(user);
+ unsigned int cm = channel->GetPrefixValue(user);
if (cm < HALFOP_VALUE)
{
- user->WriteNumeric(482, "%s %s :You do not have permission to set bans on this channel",
- user->nick.c_str(), channel->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
return CMD_FAILURE;
- }
+ }
TimedBan T;
- std::string channelname = parameters[0];
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
unsigned long expire = duration + ServerInstance->Time();
if (duration < 1)
{
- user->WriteServ("NOTICE "+user->nick+" :Invalid ban time");
+ user->WriteNotice("Invalid ban time");
return CMD_FAILURE;
}
std::string mask = parameters[2];
- std::vector<std::string> setban;
- setban.push_back(parameters[0]);
- setban.push_back("+b");
bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
- if (!isextban && !ServerInstance->IsValidMask(mask))
+ if (!isextban && !InspIRCd::IsValidMask(mask))
mask.append("!*@*");
- if ((mask.length() > 250) || (!ServerInstance->IsValidMask(mask) && !isextban))
- {
- user->WriteServ("NOTICE "+user->nick+" :Invalid ban mask");
- return CMD_FAILURE;
- }
if (IsBanSet(channel, mask))
{
- user->WriteServ("NOTICE %s :Ban already set", user->nick.c_str());
+ user->WriteNotice("Ban already set");
return CMD_FAILURE;
}
- setban.push_back(mask);
- // use CallHandler to make it so that the user sets the mode
- // themselves
- ServerInstance->Parser->CallHandler("MODE",setban,user);
- if (!IsBanSet(channel, mask))
+ Modes::ChangeList setban;
+ setban.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ // Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
+ // make it so that the user sets the mode themselves
+ ServerInstance->Modes->Process(user, channel, NULL, setban);
+ if (ServerInstance->Modes->GetLastChangeList().empty())
+ {
+ user->WriteNotice("Invalid ban mask");
return CMD_FAILURE;
+ }
- CUList tmp;
- T.channel = channelname;
T.mask = mask;
T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
T.chan = channel;
@@ -119,20 +120,49 @@ class CommandTban : public Command
const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + ConvToStr(duration) + " seconds.";
// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
- ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
- channel->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, tmp, "NOTICE %s :%s", channel->name.c_str(), addban.c_str());
+ ClientProtocol::Messages::Privmsg notice(ServerInstance->FakeClient, channel, addban, MSG_NOTICE);
+ channel->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
ServerInstance->PI->SendChannelNotice(channel, pfxchar, addban);
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
};
+class BanWatcher : public ModeWatcher
+{
+ public:
+ BanWatcher(Module* parent)
+ : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
+ {
+ }
+
+ void AfterMode(User* source, User* dest, Channel* chan, const std::string& banmask, bool adding) CXX11_OVERRIDE
+ {
+ if (adding)
+ return;
+
+ for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
+ {
+ if (i->chan != chan)
+ continue;
+
+ const std::string& target = i->mask;
+ if (irc::equals(banmask, target))
+ {
+ TimedBanList.erase(i);
+ break;
+ }
+ }
+ }
+};
+
class ChannelMatcher
{
Channel* const chan;
@@ -152,37 +182,16 @@ class ChannelMatcher
class ModuleTimedBans : public Module
{
CommandTban cmd;
+ BanWatcher banwatcher;
+
public:
ModuleTimedBans()
: cmd(this)
+ , banwatcher(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnDelBan, I_OnBackgroundTimer, I_OnChannelDelete };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ModResult OnDelBan(User* source, Channel* chan, const std::string &banmask)
- {
- irc::string listitem = banmask.c_str();
- irc::string thischan = chan->name.c_str();
- for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); i++)
- {
- irc::string target = i->mask.c_str();
- irc::string tchan = i->channel.c_str();
- if ((listitem == target) && (tchan == thischan))
- {
- TimedBanList.erase(i);
- break;
- }
- }
- return MOD_RES_PASSTHRU;
- }
-
- virtual void OnBackgroundTimer(time_t curtime)
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
{
timedbans expired;
for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
@@ -198,41 +207,35 @@ class ModuleTimedBans : public Module
for (timedbans::iterator i = expired.begin(); i != expired.end(); i++)
{
- std::string chan = i->channel;
std::string mask = i->mask;
- Channel* cr = ServerInstance->FindChan(chan);
- if (cr)
+ Channel* cr = i->chan;
{
- std::vector<std::string> setban;
- setban.push_back(chan);
- setban.push_back("-b");
- setban.push_back(mask);
-
- CUList empty;
- const std::string expiry = "*** Timed ban on " + chan + " expired.";
+ const std::string expiry = "*** Timed ban on " + cr->name + " expired.";
// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
- ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
- cr->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
+ ClientProtocol::Messages::Privmsg notice(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, cr, expiry, MSG_NOTICE);
+ cr->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
ServerInstance->PI->SendChannelNotice(cr, pfxchar, expiry);
- ServerInstance->SendGlobalMode(setban, ServerInstance->FakeClient);
+ Modes::ChangeList setban;
+ setban.push_remove(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, cr, NULL, setban);
}
}
}
- void OnChannelDelete(Channel* chan)
+ void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
{
// Remove all timed bans affecting the channel from internal bookkeeping
TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Adds timed bans", VF_COMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleTimedBans)
-
diff --git a/src/modules/m_tline.cpp b/src/modules/m_tline.cpp
index b4e7e5a99..692472e11 100644
--- a/src/modules/m_tline.cpp
+++ b/src/modules/m_tline.cpp
@@ -20,8 +20,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides /tline command used to test who a mask matches */
-
/** Handle /TLINE
*/
class CommandTline : public Command
@@ -32,16 +30,15 @@ class CommandTline : public Command
flags_needed = 'o'; this->syntax = "<mask>";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- float n_counted = 0;
- float n_matched = 0;
- float n_match_host = 0;
- float n_match_ip = 0;
+ unsigned int n_matched = 0;
+ unsigned int n_match_host = 0;
+ unsigned int n_match_ip = 0;
- for (user_hash::const_iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
{
- n_counted++;
if (InspIRCd::Match(u->second->GetFullRealHost(),parameters[0]))
{
n_matched++;
@@ -57,10 +54,15 @@ class CommandTline : public Command
}
}
}
+
+ unsigned long n_counted = users.size();
if (n_matched)
- user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against %0.0f user(s) (%0.2f%% of the userbase). %0.0f by hostname and %0.0f by IP address.",user->nick.c_str(), n_counted, parameters[0].c_str(), n_matched, (n_matched/n_counted)*100, n_match_host, n_match_ip);
+ {
+ float p = (n_matched / (float)n_counted) * 100;
+ user->WriteNotice(InspIRCd::Format("*** TLINE: Counted %lu user(s). Matched '%s' against %u user(s) (%0.2f%% of the userbase). %u by hostname and %u by IP address.", n_counted, parameters[0].c_str(), n_matched, p, n_match_host, n_match_ip));
+ }
else
- user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against no user(s).", user->nick.c_str(), n_counted, parameters[0].c_str());
+ user->WriteNotice(InspIRCd::Format("*** TLINE: Counted %lu user(s). Matched '%s' against no user(s).", n_counted, parameters[0].c_str()));
return CMD_SUCCESS;
}
@@ -75,20 +77,10 @@ class ModuleTLine : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleTLine()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides /tline command used to test who a mask matches", VF_VENDOR);
}
};
MODULE_INIT(ModuleTLine)
-
diff --git a/src/modules/m_topiclock.cpp b/src/modules/m_topiclock.cpp
index 3e8a846e7..c65f27668 100644
--- a/src/modules/m_topiclock.cpp
+++ b/src/modules/m_topiclock.cpp
@@ -16,10 +16,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* $ModDesc: Implements server-side topic locks and the server-to-server command SVSTOPIC */
-
#include "inspircd.h"
+enum
+{
+ // InspIRCd-specific.
+ ERR_TOPICLOCK = 744
+};
+
class CommandSVSTOPIC : public Command
{
public:
@@ -29,9 +33,9 @@ class CommandSVSTOPIC : public Command
flags_needed = FLAG_SERVERONLY;
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
{
// Ulines only
return CMD_FAILURE;
@@ -47,42 +51,23 @@ class CommandSVSTOPIC : public Command
time_t topicts = ConvToInt(parameters[1]);
if (!topicts)
{
- ServerInstance->Logs->Log("m_topiclock", DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped.");
return CMD_INVALID;
}
- std::string newtopic;
- newtopic.assign(parameters[3], 0, ServerInstance->Config->Limits.MaxTopic);
- bool topics_differ = (chan->topic != newtopic);
- if ((topics_differ) || (chan->topicset != topicts) || (chan->setby != parameters[2]))
- {
- // Update when any parameter differs
- chan->topicset = topicts;
- chan->setby.assign(parameters[2], 0, 127);
- chan->topic = newtopic;
- // Send TOPIC to clients only if the actual topic has changed, be silent otherwise
- if (topics_differ)
- chan->WriteChannel(user, "TOPIC %s :%s", chan->name.c_str(), chan->topic.c_str());
- }
+ chan->SetTopic(user, parameters[3], topicts, &parameters[2]);
}
else
{
// 1 parameter version, nuke the topic
- bool topic_empty = chan->topic.empty();
- if (!topic_empty || !chan->setby.empty())
- {
- chan->topicset = 0;
- chan->setby.clear();
- chan->topic.clear();
- if (!topic_empty)
- chan->WriteChannel(user, "TOPIC %s :", chan->name.c_str());
- }
+ chan->SetTopic(user, std::string(), 0);
+ chan->setby.clear();
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
return ROUTE_BROADCAST;
}
@@ -92,11 +77,7 @@ class FlagExtItem : public ExtensionItem
{
public:
FlagExtItem(const std::string& key, Module* owner)
- : ExtensionItem(key, owner)
- {
- }
-
- virtual ~FlagExtItem()
+ : ExtensionItem(key, ExtensionItem::EXT_CHANNEL, owner)
{
}
@@ -105,7 +86,7 @@ class FlagExtItem : public ExtensionItem
return (get_raw(container) != NULL);
}
- std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
{
if (format == FORMAT_USER)
return "true";
@@ -113,7 +94,7 @@ class FlagExtItem : public ExtensionItem
return "1";
}
- void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
{
if (value == "1")
set_raw(container, this);
@@ -134,7 +115,7 @@ class FlagExtItem : public ExtensionItem
unset_raw(container);
}
- void free(void* item)
+ void free(Extensible* container, void* item) CXX11_OVERRIDE
{
// nothing to free
}
@@ -151,26 +132,19 @@ class ModuleTopicLock : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(topiclock);
- ServerInstance->Modules->Attach(I_OnPreTopicChange, this);
- }
-
- ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic)
+ ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE
{
// Only fired for local users currently, but added a check anyway
if ((IS_LOCAL(user)) && (topiclock.get(chan)))
{
- user->WriteNumeric(744, "%s :TOPIC cannot be changed due to topic lock being active on the channel", chan->name.c_str());
+ user->WriteNumeric(ERR_TOPICLOCK, chan->name, "TOPIC cannot be changed due to topic lock being active on the channel");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements server-side topic locks and the server-to-server command SVSTOPIC", VF_COMMON | VF_VENDOR);
}
diff --git a/src/modules/m_uhnames.cpp b/src/modules/m_uhnames.cpp
index 2cd090f97..a8814b343 100644
--- a/src/modules/m_uhnames.cpp
+++ b/src/modules/m_uhnames.cpp
@@ -20,40 +20,28 @@
#include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the UHNAMES facility. */
+#include "modules/cap.h"
class ModuleUHNames : public Module
{
- public:
- GenericCap cap;
+ Cap::Capability cap;
+ public:
ModuleUHNames() : cap(this, "userhost-in-names")
{
}
- void init()
- {
- Implementation eventlist[] = { I_OnEvent, I_OnPreCommand, I_OnNamesListItem, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleUHNames()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the UHNAMES facility.",VF_VENDOR);
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" UHNAMES");
+ tokens["UHNAMES"];
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
{
/* We don't actually create a proper command handler class for PROTOCTL,
* because other modules might want to have PROTOCTL hooks too.
@@ -64,27 +52,19 @@ class ModuleUHNames : public Module
{
if ((parameters.size()) && (!strcasecmp(parameters[0].c_str(),"UHNAMES")))
{
- cap.ext.set(user, 1);
+ cap.set(user, true);
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (!cap.ext.get(issuer))
- return;
+ if (cap.get(issuer))
+ nick = memb->user->GetFullHost();
- if (nick.empty())
- return;
-
- nick = memb->user->GetFullHost();
- }
-
- void OnEvent(Event& ev)
- {
- cap.HandleEvent(ev);
+ return MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp
index ff392edc3..edaabf9fe 100644
--- a/src/modules/m_uninvite.cpp
+++ b/src/modules/m_uninvite.cpp
@@ -20,22 +20,30 @@
*/
-/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */
-
#include "inspircd.h"
+#include "modules/invite.h"
+
+enum
+{
+ // InspIRCd-specific.
+ RPL_UNINVITED = 493
+};
/** Handle /UNINVITE
*/
class CommandUninvite : public Command
{
+ Invite::API invapi;
public:
- CommandUninvite(Module* Creator) : Command(Creator,"UNINVITE", 2)
+ CommandUninvite(Module* Creator)
+ : Command(Creator, "UNINVITE", 2)
+ , invapi(Creator)
{
syntax = "<nick> <channel>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
User* u;
if (IS_LOCAL(user))
@@ -49,11 +57,11 @@ class CommandUninvite : public Command
{
if (!c)
{
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[1]));
}
else
{
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
}
return CMD_FAILURE;
@@ -63,7 +71,7 @@ class CommandUninvite : public Command
{
if (c->GetPrefixValue(user) < HALFOP_VALUE)
{
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator", user->nick.c_str(), c->name.c_str(), c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-");
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator", c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-"));
return CMD_FAILURE;
}
}
@@ -75,29 +83,35 @@ class CommandUninvite : public Command
LocalUser* lu = IS_LOCAL(u);
if (lu)
{
- irc::string xname(c->name.c_str());
- if (!lu->IsInvited(xname))
+ // XXX: The source of the numeric we send must be the server of the user doing the /UNINVITE,
+ // so they don't see where the target user is connected to
+ if (!invapi->Remove(lu, c))
{
- user->SendText(":%s 505 %s %s %s :Is not invited to channel %s", user->server.c_str(), user->nick.c_str(), u->nick.c_str(), c->name.c_str(), c->name.c_str());
+ Numeric::Numeric n(505);
+ n.SetServer(user->server);
+ n.push(u->nick).push(c->name).push(InspIRCd::Format("Is not invited to channel %s", c->name.c_str()));
+ user->WriteRemoteNumeric(n);
return CMD_FAILURE;
}
- user->SendText(":%s 494 %s %s %s :Uninvited", user->server.c_str(), user->nick.c_str(), c->name.c_str(), u->nick.c_str());
- lu->RemoveInvite(xname);
- lu->WriteNumeric(493, "%s :You were uninvited from %s by %s", u->nick.c_str(), c->name.c_str(), user->nick.c_str());
+ Numeric::Numeric n(494);
+ n.SetServer(user->server);
+ n.push(c->name).push(u->nick).push("Uninvited");
+ user->WriteRemoteNumeric(n);
+
+ lu->WriteNumeric(RPL_UNINVITED, InspIRCd::Format("You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str()));
std::string msg = "*** " + user->nick + " uninvited " + u->nick + ".";
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE " + c->name + " :" + msg);
+ c->WriteNotice(msg);
ServerInstance->PI->SendChannelNotice(c, 0, msg);
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- User* u = ServerInstance->FindNick(parameters[0]);
- return u ? ROUTE_OPT_UCAST(u->server) : ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -111,20 +125,10 @@ class ModuleUninvite : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleUninvite()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the UNINVITE command which lets users un-invite other users from channels", VF_VENDOR | VF_OPTCOMMON);
}
};
MODULE_INIT(ModuleUninvite)
-
diff --git a/src/modules/m_userip.cpp b/src/modules/m_userip.cpp
index 9502c91b1..c64f53684 100644
--- a/src/modules/m_userip.cpp
+++ b/src/modules/m_userip.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides support for USERIP command */
-
/** Handle /USERIP
*/
class CommandUserip : public Command
@@ -33,14 +31,14 @@ class CommandUserip : public Command
syntax = "<nick> [<nick> ...]";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
- std::string retbuf = "340 " + user->nick + " :";
+ std::string retbuf;
int nicks = 0;
bool checked_privs = false;
bool has_privs = false;
- for (int i = 0; i < (int)parameters.size(); i++)
+ for (size_t i = 0; i < parameters.size(); i++)
{
User *u = ServerInstance->FindNickOnly(parameters[i]);
if ((u) && (u->registered == REG_ALL))
@@ -54,15 +52,15 @@ class CommandUserip : public Command
checked_privs = true;
has_privs = user->HasPrivPermission("users/auspex");
if (!has_privs)
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
}
if (!has_privs)
continue;
}
- retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=";
- if (IS_AWAY(u))
+ retbuf = retbuf + u->nick + (u->IsOper() ? "*" : "") + "=";
+ if (u->IsAway())
retbuf += "-";
else
retbuf += "+";
@@ -72,7 +70,7 @@ class CommandUserip : public Command
}
if (nicks != 0)
- user->WriteServ(retbuf);
+ user->WriteNumeric(RPL_USERIP, retbuf);
return CMD_SUCCESS;
}
@@ -87,28 +85,15 @@ class ModuleUserIP : public Module
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["USERIP"];
}
- virtual void On005Numeric(std::string &output)
- {
- output = output + " USERIP";
- }
-
- virtual ~ModuleUserIP()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for USERIP command",VF_VENDOR);
}
-
};
MODULE_INIT(ModuleUserIP)
-
diff --git a/src/modules/m_vhost.cpp b/src/modules/m_vhost.cpp
index 31c504af8..01df33cae 100644
--- a/src/modules/m_vhost.cpp
+++ b/src/modules/m_vhost.cpp
@@ -22,8 +22,6 @@
#include "inspircd.h"
-/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */
-
/** Handle /VHOST
*/
class CommandVhost : public Command
@@ -34,7 +32,7 @@ class CommandVhost : public Command
syntax = "<username> <password>";
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
ConfigTagList tags = ServerInstance->Config->ConfTags("vhost");
for(ConfigIter i = tags.first; i != tags.second; ++i)
@@ -45,25 +43,24 @@ class CommandVhost : public Command
std::string pass = tag->getString("pass");
std::string hash = tag->getString("hash");
- if (parameters[0] == username && !ServerInstance->PassCompare(user, pass, parameters[1], hash))
+ if (parameters[0] == username && ServerInstance->PassCompare(user, pass, parameters[1], hash))
{
if (!mask.empty())
{
- user->WriteServ("NOTICE "+user->nick+" :Setting your VHost: " + mask);
- user->ChangeDisplayedHost(mask.c_str());
+ user->WriteNotice("Setting your VHost: " + mask);
+ user->ChangeDisplayedHost(mask);
return CMD_SUCCESS;
}
}
}
- user->WriteServ("NOTICE "+user->nick+" :Invalid username or password.");
+ user->WriteNotice("Invalid username or password.");
return CMD_FAILURE;
}
};
class ModuleVHost : public Module
{
- private:
CommandVhost cmd;
public:
@@ -71,22 +68,10 @@ class ModuleVHost : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleVHost()
- {
- }
-
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides masking of user hostnames via traditional /VHOST command",VF_VENDOR);
}
-
};
MODULE_INIT(ModuleVHost)
-
diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp
index a86483291..8b84132b2 100644
--- a/src/modules/m_watch.cpp
+++ b/src/modules/m_watch.cpp
@@ -1,10 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -21,525 +18,256 @@
#include "inspircd.h"
+#include "modules/away.h"
-/* $ModDesc: Provides support for the /WATCH command */
+#define INSPIRCD_MONITOR_MANAGER_ONLY
+#include "m_monitor.cpp"
+enum
+{
+ RPL_GONEAWAY = 598,
+ RPL_NOTAWAY = 599,
+ RPL_LOGON = 600,
+ RPL_LOGOFF = 601,
+ RPL_WATCHOFF = 602,
+ RPL_WATCHSTAT = 603,
+ RPL_NOWON = 604,
+ RPL_NOWOFF = 605,
+ RPL_WATCHLIST = 606,
+ RPL_ENDOFWATCHLIST = 607,
+ // RPL_CLEARWATCH = 608, // unused
+ RPL_NOWISAWAY = 609,
+ ERR_TOOMANYWATCH = 512,
+ ERR_INVALIDWATCHNICK = 942
+};
-/*
- * Okay, it's nice that this was documented and all, but I at least understood very little
- * of it, so I'm going to attempt to explain the data structures in here a bit more.
- *
- * For efficiency, many data structures are kept.
- *
- * The first is a global list `watchentries':
- * hash_map<irc::string, std::deque<User*> >
- *
- * That is, if nick 'w00t' is being watched by user pointer 'Brain' and 'Om', <w00t, (Brain, Om)>
- * will be in the watchentries list.
- *
- * The second is that each user has a per-user data structure attached to their user record via Extensible:
- * std::map<irc::string, std::string> watchlist;
- * So, in the above example with w00t watched by Brain and Om, we'd have:
- * Brain-
- * `- w00t
- * Om-
- * `- w00t
- *
- * Hopefully this helps any brave soul that ventures into this file other than me. :-)
- * -- w00t (mar 30, 2008)
- */
-
-
-/* This module has been refactored to provide a very efficient (in terms of cpu time)
- * implementation of /WATCH.
- *
- * To improve the efficiency of watch, many lists are kept. The first primary list is
- * a hash_map of who's being watched by who. For example:
- *
- * KEY: Brain ---> Watched by: Boo, w00t, Om
- * KEY: Boo ---> Watched by: Brain, w00t
- *
- * This is used when we want to tell all the users that are watching someone that
- * they are now available or no longer available. For example, if the hash was
- * populated as shown above, then when Brain signs on, messages are sent to Boo, w00t
- * and Om by reading their 'watched by' list. When this occurs, their online status
- * in each of these users lists (see below) is also updated.
- *
- * Each user also has a seperate (smaller) map attached to their User whilst they
- * have any watch entries, which is managed by class Extensible. When they add or remove
- * a watch entry from their list, it is inserted here, as well as the main list being
- * maintained. This map also contains the user's online status. For users that are
- * offline, the key points at an empty string, and for users that are online, the key
- * points at a string containing "users-ident users-host users-signon-time". This is
- * stored in this manner so that we don't have to FindUser() to fetch this info, the
- * users signon can populate the field for us.
- *
- * For example, going again on the example above, this would be w00t's watchlist:
- *
- * KEY: Boo ---> Status: "Boo brains.sexy.babe 535342348"
- * KEY: Brain ---> Status: ""
- *
- * In this list we can see that Boo is online, and Brain is offline. We can then
- * use this list for 'WATCH L', and 'WATCH S' can be implemented as a combination
- * of the above two data structures, with minimum CPU penalty for doing so.
- *
- * In short, the least efficient this ever gets is O(n), and thats only because
- * there are parts that *must* loop (e.g. telling all users that are watching a
- * nick that the user online), however this is a *major* improvement over the
- * 1.0 implementation, which in places had O(n^n) and worse in it, because this
- * implementation scales based upon the sizes of the watch entries, whereas the
- * old system would scale (or not as the case may be) according to the total number
- * of users using WATCH.
- */
-
-/*
- * Before you start screaming, this definition is only used here, so moving it to a header is pointless.
- * Yes, it's horrid. Blame cl for being different. -- w00t
- */
-
-typedef nspace::hash_map<irc::string, std::deque<User*>, irc::hash> watchentries;
-typedef std::map<irc::string, std::string> watchlist;
+class CommandWatch : public SplitCommand
+{
+ // Additional penalty for /WATCH commands that request a list from the server
+ static const unsigned int ListPenalty = 4000;
-/* Who's watching each nickname.
- * NOTE: We do NOT iterate this to display a user's WATCH list!
- * See the comments above!
- */
-watchentries* whos_watching_me;
+ IRCv3::Monitor::Manager& manager;
-class CommandSVSWatch : public Command
-{
- public:
- CommandSVSWatch(Module* Creator) : Command(Creator,"SVSWATCH", 2)
+ static void SendOnlineOffline(LocalUser* user, const std::string& nick, bool show_offline = true)
{
- syntax = "<target> [C|L|S]|[+|-<nick>]";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
+ User* target = IRCv3::Monitor::Manager::FindNick(nick);
+ if (target)
+ {
+ // The away state should only be sent if the client requests away notifications for a nick but 2.0 always sends them so we do that too
+ if (target->IsAway())
+ user->WriteNumeric(RPL_NOWISAWAY, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->awaytime, "is away");
+ else
+ user->WriteNumeric(RPL_NOWON, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->age, "is online");
+ }
+ else if (show_offline)
+ user->WriteNumeric(RPL_NOWOFF, nick, "*", "*", "0", "is offline");
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ void HandlePlus(LocalUser* user, const std::string& nick)
{
- if (!ServerInstance->ULine(user->server))
- return CMD_FAILURE;
-
- User *u = ServerInstance->FindNick(parameters[0]);
- if (!u)
- return CMD_FAILURE;
-
- if (IS_LOCAL(u))
+ IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxwatch);
+ if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
+ {
+ // List is full, send error numeric
+ user->WriteNumeric(ERR_TOOMANYWATCH, nick, "Too many WATCH entries");
+ return;
+ }
+ else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK)
{
- ServerInstance->Parser->CallHandler("WATCH", parameters, u);
+ user->WriteNumeric(ERR_INVALIDWATCHNICK, nick, "Invalid nickname");
+ return;
}
+ else if (result != IRCv3::Monitor::Manager::WR_OK)
+ return;
- return CMD_SUCCESS;
+ SendOnlineOffline(user, nick);
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ void HandleMinus(LocalUser* user, const std::string& nick)
{
- User* target = ServerInstance->FindNick(parameters[0]);
+ if (!manager.Unwatch(user, nick))
+ return;
+
+ User* target = IRCv3::Monitor::Manager::FindNick(nick);
if (target)
- return ROUTE_OPT_UCAST(target->server);
- return ROUTE_LOCALONLY;
+ user->WriteNumeric(RPL_WATCHOFF, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->age, "stopped watching");
+ else
+ user->WriteNumeric(RPL_WATCHOFF, nick, "*", "*", "0", "stopped watching");
}
-};
-/** Handle /WATCH
- */
-class CommandWatch : public Command
-{
- unsigned int& MAX_WATCH;
- public:
- SimpleExtItem<watchlist> ext;
- CmdResult remove_watch(User* user, const char* nick)
+ void HandleList(LocalUser* user, bool show_offline)
{
- // removing an item from the list
- if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
+ user->CommandFloodPenalty += ListPenalty;
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- user->WriteNumeric(942, "%s %s :Invalid nickname", user->nick.c_str(), nick);
- return CMD_FAILURE;
+ const IRCv3::Monitor::Entry* entry = *i;
+ SendOnlineOffline(user, entry->GetNick(), show_offline);
}
+ user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH list");
+ }
- watchlist* wl = ext.get(user);
- if (wl)
- {
- /* Yup, is on my list */
- watchlist::iterator n = wl->find(nick);
-
- if (n != wl->end())
- {
- if (!n->second.empty())
- user->WriteNumeric(602, "%s %s %s :stopped watching", user->nick.c_str(), n->first.c_str(), n->second.c_str());
- else
- user->WriteNumeric(602, "%s %s * * 0 :stopped watching", user->nick.c_str(), nick);
+ void HandleStats(LocalUser* user)
+ {
+ user->CommandFloodPenalty += ListPenalty;
- wl->erase(n);
- }
+ // Do not show how many clients are watching this nick, it's pointless
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ user->WriteNumeric(RPL_WATCHSTAT, InspIRCd::Format("You have %lu and are on 0 WATCH entries", (unsigned long)list.size()));
- if (wl->empty())
- {
- ext.unset(user);
- }
-
- watchentries::iterator x = whos_watching_me->find(nick);
- if (x != whos_watching_me->end())
- {
- /* People are watching this user, am i one of them? */
- std::deque<User*>::iterator n2 = std::find(x->second.begin(), x->second.end(), user);
- if (n2 != x->second.end())
- /* I'm no longer watching you... */
- x->second.erase(n2);
-
- if (x->second.empty())
- /* nobody else is, either. */
- whos_watching_me->erase(nick);
- }
+ Numeric::Builder<' '> out(user, RPL_WATCHLIST);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const IRCv3::Monitor::Entry* entry = *i;
+ out.Add(entry->GetNick());
}
+ out.Flush();
+ user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH S");
+ }
- return CMD_SUCCESS;
+ public:
+ unsigned int maxwatch;
+
+ CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref)
+ : SplitCommand(mod, "WATCH")
+ , manager(managerref)
+ {
+ allow_empty_last_param = false;
+ syntax = "[<C|L|S|l|+<nick1>|-<nick>>]";
}
- CmdResult add_watch(User* user, const char* nick)
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
- if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
+ if (parameters.empty())
{
- user->WriteNumeric(942, "%s %s :Invalid nickname",user->nick.c_str(),nick);
- return CMD_FAILURE;
+ HandleList(user, false);
+ return CMD_SUCCESS;
}
- watchlist* wl = ext.get(user);
- if (!wl)
- {
- wl = new watchlist();
- ext.set(user, wl);
- }
+ bool watch_l_done = false;
+ bool watch_s_done = false;
- if (wl->size() >= MAX_WATCH)
+ for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
{
- user->WriteNumeric(512, "%s %s :Too many WATCH entries", user->nick.c_str(), nick);
- return CMD_FAILURE;
- }
-
- watchlist::iterator n = wl->find(nick);
- if (n == wl->end())
- {
- /* Don't already have the user on my watch list, proceed */
- watchentries::iterator x = whos_watching_me->find(nick);
- if (x != whos_watching_me->end())
+ const std::string& token = *i;
+ char subcmd = toupper(token[0]);
+ if (subcmd == '+')
{
- /* People are watching this user, add myself */
- x->second.push_back(user);
+ HandlePlus(user, token.substr(1));
}
- else
+ else if (subcmd == '-')
{
- std::deque<User*> newlist;
- newlist.push_back(user);
- (*(whos_watching_me))[nick] = newlist;
+ HandleMinus(user, token.substr(1));
}
-
- User* target = ServerInstance->FindNick(nick);
- if ((target) && (target->registered == REG_ALL))
+ else if (subcmd == 'C')
{
- (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age));
- user->WriteNumeric(604, "%s %s %s :is online",user->nick.c_str(), nick, (*wl)[nick].c_str());
- if (IS_AWAY(target))
- {
- user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime);
- }
+ manager.UnwatchAll(user);
}
- else
+ else if ((subcmd == 'L') && (!watch_l_done))
{
- (*wl)[nick].clear();
- user->WriteNumeric(605, "%s %s * * 0 :is offline",user->nick.c_str(), nick);
+ watch_l_done = true;
+ // WATCH L requests a full list with online and offline nicks
+ // WATCH l requests a list with only online nicks
+ HandleList(user, (token[0] == 'L'));
}
- }
-
- return CMD_SUCCESS;
- }
-
- CommandWatch(Module* parent, unsigned int &maxwatch) : Command(parent,"WATCH", 0), MAX_WATCH(maxwatch), ext("watchlist", parent)
- {
- syntax = "[C|L|S]|[+|-<nick>]";
- TRANSLATE2(TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
- }
-
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
- {
- if (parameters.empty())
- {
- watchlist* wl = ext.get(user);
- if (wl)
+ else if ((subcmd == 'S') && (!watch_s_done))
{
- for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
- {
- if (!q->second.empty())
- user->WriteNumeric(604, "%s %s %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
- }
- }
- user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
- }
- else if (parameters.size() > 0)
- {
- for (int x = 0; x < (int)parameters.size(); x++)
- {
- const char *nick = parameters[x].c_str();
- if (!strcasecmp(nick,"C"))
- {
- // watch clear
- watchlist* wl = ext.get(user);
- if (wl)
- {
- for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
- {
- watchentries::iterator i2 = whos_watching_me->find(i->first);
- if (i2 != whos_watching_me->end())
- {
- /* People are watching this user, am i one of them? */
- std::deque<User*>::iterator n = std::find(i2->second.begin(), i2->second.end(), user);
- if (n != i2->second.end())
- /* I'm no longer watching you... */
- i2->second.erase(n);
-
- if (i2->second.empty())
- /* nobody else is, either. */
- whos_watching_me->erase(i2);
- }
- }
-
- ext.unset(user);
- }
- }
- else if (!strcasecmp(nick,"L"))
- {
- watchlist* wl = ext.get(user);
- if (wl)
- {
- for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
- {
- User* targ = ServerInstance->FindNick(q->first.c_str());
- if (targ && !q->second.empty())
- {
- user->WriteNumeric(604, "%s %s %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
- if (IS_AWAY(targ))
- {
- user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
- }
- }
- else
- user->WriteNumeric(605, "%s %s * * 0 :is offline", user->nick.c_str(), q->first.c_str());
- }
- }
- user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
- }
- else if (!strcasecmp(nick,"S"))
- {
- watchlist* wl = ext.get(user);
- int you_have = 0;
- int youre_on = 0;
- std::string list;
-
- if (wl)
- {
- for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
- list.append(q->first.c_str()).append(" ");
- you_have = wl->size();
- }
-
- watchentries::iterator i2 = whos_watching_me->find(user->nick.c_str());
- if (i2 != whos_watching_me->end())
- youre_on = i2->second.size();
-
- user->WriteNumeric(603, "%s :You have %d and are on %d WATCH entries", user->nick.c_str(), you_have, youre_on);
- user->WriteNumeric(606, "%s :%s",user->nick.c_str(), list.c_str());
- user->WriteNumeric(607, "%s :End of WATCH S",user->nick.c_str());
- }
- else if (nick[0] == '-')
- {
- nick++;
- remove_watch(user, nick);
- }
- else if (nick[0] == '+')
- {
- nick++;
- add_watch(user, nick);
- }
+ watch_s_done = true;
+ HandleStats(user);
}
}
return CMD_SUCCESS;
}
};
-class Modulewatch : public Module
+class ModuleWatch
+ : public Module
+ , public Away::EventListener
{
- unsigned int maxwatch;
- CommandWatch cmdw;
- CommandSVSWatch sw;
+ IRCv3::Monitor::Manager manager;
+ CommandWatch cmd;
- public:
- Modulewatch()
- : maxwatch(32), cmdw(this, maxwatch), sw(this)
+ void SendAlert(User* user, const std::string& nick, unsigned int numeric, const char* numerictext, time_t shownts)
{
- whos_watching_me = new watchentries();
+ const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
+ if (!list)
+ return;
+
+ Numeric::Numeric num(numeric);
+ num.push(nick).push(user->ident).push(user->GetDisplayedHost()).push(ConvToStr(shownts)).push(numerictext);
+ for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ LocalUser* curr = *i;
+ curr->WriteNumeric(num);
+ }
}
- void init()
+ void Online(User* user)
{
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmdw);
- ServerInstance->Modules->AddService(sw);
- ServerInstance->Modules->AddService(cmdw.ext);
- Implementation eventlist[] = { I_OnRehash, I_OnGarbageCollect, I_OnUserQuit, I_OnPostConnect, I_OnUserPostNick, I_On005Numeric, I_OnSetAway };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ SendAlert(user, user->nick, RPL_LOGON, "arrived online", user->age);
}
- virtual void OnRehash(User* user)
+ void Offline(User* user, const std::string& nick)
{
- maxwatch = ServerInstance->Config->ConfValue("watch")->getInt("maxentries", 32);
- if (!maxwatch)
- maxwatch = 32;
+ SendAlert(user, nick, RPL_LOGOFF, "went offline", user->age);
}
- virtual ModResult OnSetAway(User *user, const std::string &awaymsg)
+ public:
+ ModuleWatch()
+ : Away::EventListener(this)
+ , manager(this, "watch")
+ , cmd(this, manager)
{
- std::string numeric;
- int inum;
-
- if (awaymsg.empty())
- {
- numeric = user->nick + " " + user->ident + " " + user->dhost + " " + ConvToStr(ServerInstance->Time()) + " :is no longer away";
- inum = 599;
- }
- else
- {
- numeric = user->nick + " " + user->ident + " " + user->dhost + " " + ConvToStr(ServerInstance->Time()) + " :" + awaymsg;
- inum = 598;
- }
-
- watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
- if (x != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
- {
- (*n)->WriteNumeric(inum, (*n)->nick + " " + numeric);
- }
- }
-
- return MOD_RES_PASSTHRU;
}
- virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
- if (x != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
- {
- (*n)->WriteNumeric(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str() ,user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) ServerInstance->Time());
-
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- /* We were on somebody's notify list, set ourselves offline */
- (*wl)[user->nick.c_str()].clear();
- }
- }
-
- /* Now im quitting, if i have a notify list, im no longer watching anyone */
- watchlist* wl = cmdw.ext.get(user);
- if (wl)
- {
- /* Iterate every user on my watch list, and take me out of the whos_watching_me map for each one we're watching */
- for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
- {
- watchentries::iterator i2 = whos_watching_me->find(i->first);
- if (i2 != whos_watching_me->end())
- {
- /* People are watching this user, am i one of them? */
- std::deque<User*>::iterator n = std::find(i2->second.begin(), i2->second.end(), user);
- if (n != i2->second.end())
- /* I'm no longer watching you... */
- i2->second.erase(n);
-
- if (i2->second.empty())
- /* and nobody else is, either. */
- whos_watching_me->erase(i2);
- }
- }
- }
+ ConfigTag* tag = ServerInstance->Config->ConfValue("watch");
+ cmd.maxwatch = tag->getUInt("maxwatch", 30, 1);
}
- virtual void OnGarbageCollect()
+ void OnPostConnect(User* user) CXX11_OVERRIDE
{
- watchentries* old_watch = whos_watching_me;
- whos_watching_me = new watchentries();
-
- for (watchentries::const_iterator n = old_watch->begin(); n != old_watch->end(); n++)
- whos_watching_me->insert(*n);
-
- delete old_watch;
+ Online(user);
}
- virtual void OnPostConnect(User* user)
+ void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
{
- watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
- if (x != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
- {
- (*n)->WriteNumeric(600, "%s %s %s %s %lu :arrived online", (*n)->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
+ // Detect and ignore nickname case change
+ if (ServerInstance->FindNickOnly(oldnick) == user)
+ return;
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- /* We were on somebody's notify list, set ourselves online */
- (*wl)[user->nick.c_str()] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
- }
- }
+ Offline(user, oldnick);
+ Online(user);
}
- virtual void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
{
- watchentries::iterator new_offline = whos_watching_me->find(oldnick.c_str());
- watchentries::iterator new_online = whos_watching_me->find(user->nick.c_str());
-
- if (new_offline != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = new_offline->second.begin(); n != new_offline->second.end(); n++)
- {
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- {
- (*n)->WriteNumeric(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str(), oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
- (*wl)[oldnick.c_str()].clear();
- }
- }
- }
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ manager.UnwatchAll(localuser);
+ Offline(user, user->nick);
+ }
- if (new_online != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = new_online->second.begin(); n != new_online->second.end(); n++)
- {
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- {
- (*wl)[user->nick.c_str()] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
- (*n)->WriteNumeric(600, "%s %s %s :arrived online", (*n)->nick.c_str(), user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
- }
- }
- }
+ void OnUserAway(User* user) CXX11_OVERRIDE
+ {
+ SendAlert(user, user->nick, RPL_GONEAWAY, user->awaymsg.c_str(), user->awaytime);
}
- virtual void On005Numeric(std::string &output)
+ void OnUserBack(User* user) CXX11_OVERRIDE
{
- // we don't really have a limit...
- output = output + " WATCH=" + ConvToStr(maxwatch);
+ SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time());
}
- virtual ~Modulewatch()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- delete whos_watching_me;
+ tokens["WATCH"] = ConvToStr(cmd.maxwatch);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides support for the /WATCH command", VF_OPTCOMMON | VF_VENDOR);
+ return Version("Provides WATCH support", VF_VENDOR);
}
};
-MODULE_INIT(Modulewatch)
-
+MODULE_INIT(ModuleWatch)
diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp
new file mode 100644
index 000000000..12102d215
--- /dev/null
+++ b/src/modules/m_websocket.cpp
@@ -0,0 +1,405 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "iohook.h"
+#include "modules/hash.h"
+
+static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+static const char whitespace[] = " \t\r\n";
+static dynamic_reference_nocheck<HashProvider>* sha1;
+
+class WebSocketHookProvider : public IOHookProvider
+{
+ public:
+ WebSocketHookProvider(Module* mod)
+ : IOHookProvider(mod, "websocket", IOHookProvider::IOH_UNKNOWN, true)
+ {
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ }
+};
+
+class WebSocketHook : public IOHookMiddle
+{
+ class HTTPHeaderFinder
+ {
+ std::string::size_type bpos;
+ std::string::size_type len;
+
+ public:
+ bool Find(const std::string& req, const char* header, std::string::size_type headerlen, std::string::size_type maxpos)
+ {
+ std::string::size_type keybegin = req.find(header);
+ if ((keybegin == std::string::npos) || (keybegin > maxpos) || (keybegin == 0) || (req[keybegin-1] != '\n'))
+ return false;
+
+ keybegin += headerlen;
+
+ bpos = req.find_first_not_of(whitespace, keybegin, sizeof(whitespace)-1);
+ if ((bpos == std::string::npos) || (bpos > maxpos))
+ return false;
+
+ const std::string::size_type epos = req.find_first_of(whitespace, bpos, sizeof(whitespace)-1);
+ len = epos - bpos;
+
+ return true;
+ }
+
+ std::string ExtractValue(const std::string& req) const
+ {
+ return std::string(req, bpos, len);
+ }
+ };
+
+ enum OpCode
+ {
+ OP_CONTINUATION = 0x00,
+ OP_TEXT = 0x01,
+ OP_BINARY = 0x02,
+ OP_CLOSE = 0x08,
+ OP_PING = 0x09,
+ OP_PONG = 0x0a
+ };
+
+ enum State
+ {
+ STATE_HTTPREQ,
+ STATE_ESTABLISHED
+ };
+
+ static const unsigned char WS_MASKBIT = (1 << 7);
+ static const unsigned char WS_FINBIT = (1 << 7);
+ static const unsigned char WS_PAYLOAD_LENGTH_MAGIC_LARGE = 126;
+ static const unsigned char WS_PAYLOAD_LENGTH_MAGIC_HUGE = 127;
+ static const size_t WS_MAX_PAYLOAD_LENGTH_SMALL = 125;
+ static const size_t WS_MAX_PAYLOAD_LENGTH_LARGE = 65535;
+ static const size_t MAXHEADERSIZE = sizeof(uint64_t) + 2;
+
+ // Clients sending ping or pong frames faster than this are killed
+ static const time_t MINPINGPONGDELAY = 10;
+
+ State state;
+ time_t lastpingpong;
+
+ static size_t FillHeader(unsigned char* outbuf, size_t sendlength, OpCode opcode)
+ {
+ size_t pos = 0;
+ outbuf[pos++] = WS_FINBIT | opcode;
+
+ if (sendlength <= WS_MAX_PAYLOAD_LENGTH_SMALL)
+ {
+ outbuf[pos++] = sendlength;
+ }
+ else if (sendlength <= WS_MAX_PAYLOAD_LENGTH_LARGE)
+ {
+ outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_LARGE;
+ outbuf[pos++] = (sendlength >> 8) & 0xff;
+ outbuf[pos++] = sendlength & 0xff;
+ }
+ else
+ {
+ outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_HUGE;
+ const uint64_t len = sendlength;
+ for (int i = sizeof(uint64_t)-1; i >= 0; i--)
+ outbuf[pos++] = ((len >> i*8) & 0xff);
+ }
+
+ return pos;
+ }
+
+ static StreamSocket::SendQueue::Element PrepareSendQElem(size_t size, OpCode opcode)
+ {
+ unsigned char header[MAXHEADERSIZE];
+ const size_t n = FillHeader(header, size, opcode);
+
+ return StreamSocket::SendQueue::Element(reinterpret_cast<const char*>(header), n);
+ }
+
+ int HandleAppData(StreamSocket* sock, std::string& appdataout, bool allowlarge)
+ {
+ std::string& myrecvq = GetRecvQ();
+ // Need 1 byte opcode, minimum 1 byte len, 4 bytes masking key
+ if (myrecvq.length() < 6)
+ return 0;
+
+ const std::string& cmyrecvq = myrecvq;
+ unsigned char len1 = (unsigned char)cmyrecvq[1];
+ if (!(len1 & WS_MASKBIT))
+ {
+ sock->SetError("WebSocket protocol violation: unmasked client frame");
+ return -1;
+ }
+
+ len1 &= ~WS_MASKBIT;
+
+ // Assume the length is a single byte, if not, update values later
+ unsigned int len = len1;
+ unsigned int payloadstartoffset = 6;
+ const unsigned char* maskkey = reinterpret_cast<const unsigned char*>(&cmyrecvq[2]);
+
+ if (len1 == WS_PAYLOAD_LENGTH_MAGIC_LARGE)
+ {
+ // allowlarge is false for control frames according to the RFC meaning large pings, etc. are not allowed
+ if (!allowlarge)
+ {
+ sock->SetError("WebSocket protocol violation: large control frame");
+ return -1;
+ }
+
+ // Large frame, has 2 bytes len after the magic byte indicating the length
+ // Need 1 byte opcode, 3 bytes len, 4 bytes masking key
+ if (myrecvq.length() < 8)
+ return 0;
+
+ unsigned char len2 = (unsigned char)cmyrecvq[2];
+ unsigned char len3 = (unsigned char)cmyrecvq[3];
+ len = (len2 << 8) | len3;
+
+ if (len <= WS_MAX_PAYLOAD_LENGTH_SMALL)
+ {
+ sock->SetError("WebSocket protocol violation: non-minimal length encoding used");
+ return -1;
+ }
+
+ maskkey += 2;
+ payloadstartoffset += 2;
+ }
+ else if (len1 == WS_PAYLOAD_LENGTH_MAGIC_HUGE)
+ {
+ sock->SetError("WebSocket: Huge frames are not supported");
+ return -1;
+ }
+
+ if (myrecvq.length() < payloadstartoffset + len)
+ return 0;
+
+ unsigned int maskkeypos = 0;
+ const std::string::iterator endit = myrecvq.begin() + payloadstartoffset + len;
+ for (std::string::const_iterator i = myrecvq.begin() + payloadstartoffset; i != endit; ++i)
+ {
+ const unsigned char c = (unsigned char)*i;
+ appdataout.push_back(c ^ maskkey[maskkeypos++]);
+ maskkeypos %= 4;
+ }
+
+ myrecvq.erase(myrecvq.begin(), endit);
+ return 1;
+ }
+
+ int HandlePingPongFrame(StreamSocket* sock, bool isping)
+ {
+ if (lastpingpong + MINPINGPONGDELAY >= ServerInstance->Time())
+ {
+ sock->SetError("WebSocket: Ping/pong flood");
+ return -1;
+ }
+
+ lastpingpong = ServerInstance->Time();
+
+ std::string appdata;
+ const int result = HandleAppData(sock, appdata, false);
+ // If it's a pong stop here regardless of the result so we won't generate a reply
+ if ((result <= 0) || (!isping))
+ return result;
+
+ StreamSocket::SendQueue::Element elem = PrepareSendQElem(appdata.length(), OP_PONG);
+ elem.append(appdata);
+ GetSendQ().push_back(elem);
+
+ SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_WRITE);
+ return 1;
+ }
+
+ int HandleWS(StreamSocket* sock, std::string& destrecvq)
+ {
+ if (GetRecvQ().empty())
+ return 0;
+
+ unsigned char opcode = (unsigned char)GetRecvQ().c_str()[0];
+ opcode &= ~WS_FINBIT;
+
+ switch (opcode)
+ {
+ case OP_CONTINUATION:
+ case OP_TEXT:
+ case OP_BINARY:
+ {
+ return HandleAppData(sock, destrecvq, true);
+ }
+
+ case OP_PING:
+ {
+ return HandlePingPongFrame(sock, true);
+ }
+
+ case OP_PONG:
+ {
+ // A pong frame may be sent unsolicited, so we have to handle it.
+ // It may carry application data which we need to remove from the recvq as well.
+ return HandlePingPongFrame(sock, false);
+ }
+
+ case OP_CLOSE:
+ {
+ sock->SetError("Connection closed");
+ return -1;
+ }
+
+ default:
+ {
+ sock->SetError("WebSocket: Invalid opcode");
+ return -1;
+ }
+ }
+ }
+
+ void FailHandshake(StreamSocket* sock, const char* httpreply, const char* sockerror)
+ {
+ GetSendQ().push_back(StreamSocket::SendQueue::Element(httpreply));
+ sock->DoWrite();
+ sock->SetError(sockerror);
+ }
+
+ int HandleHTTPReq(StreamSocket* sock)
+ {
+ std::string& recvq = GetRecvQ();
+ const std::string::size_type reqend = recvq.find("\r\n\r\n");
+ if (reqend == std::string::npos)
+ return 0;
+
+ HTTPHeaderFinder keyheader;
+ if (!keyheader.Find(recvq, "Sec-WebSocket-Key:", 18, reqend))
+ {
+ FailHandshake(sock, "HTTP/1.1 501 Not Implemented\r\nConnection: close\r\n\r\n", "WebSocket: Received HTTP request which is not a websocket upgrade");
+ return -1;
+ }
+
+ if (!*sha1)
+ {
+ FailHandshake(sock, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n", "WebSocket: SHA-1 provider missing");
+ return -1;
+ }
+
+ state = STATE_ESTABLISHED;
+
+ std::string key = keyheader.ExtractValue(recvq);
+ key.append(MagicGUID);
+
+ std::string reply = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
+ reply.append(BinToBase64((*sha1)->GenerateRaw(key), NULL, '=')).append("\r\n\r\n");
+ GetSendQ().push_back(StreamSocket::SendQueue::Element(reply));
+
+ SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_WRITE);
+
+ recvq.erase(0, reqend + 4);
+
+ return 1;
+ }
+
+ public:
+ WebSocketHook(IOHookProvider* Prov, StreamSocket* sock)
+ : IOHookMiddle(Prov)
+ , state(STATE_HTTPREQ)
+ , lastpingpong(0)
+ {
+ sock->AddIOHook(this);
+ }
+
+ int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
+ {
+ StreamSocket::SendQueue& mysendq = GetSendQ();
+
+ // Return 1 to allow sending back an error HTTP response
+ if (state != STATE_ESTABLISHED)
+ return (mysendq.empty() ? 0 : 1);
+
+ if (!uppersendq.empty())
+ {
+ StreamSocket::SendQueue::Element elem = PrepareSendQElem(uppersendq.bytes(), OP_BINARY);
+ mysendq.push_back(elem);
+ mysendq.moveall(uppersendq);
+ }
+
+ return 1;
+ }
+
+ int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
+ {
+ if (state == STATE_HTTPREQ)
+ {
+ int httpret = HandleHTTPReq(sock);
+ if (httpret <= 0)
+ return httpret;
+ }
+
+ int wsret;
+ do
+ {
+ wsret = HandleWS(sock, destrecvq);
+ }
+ while ((!GetRecvQ().empty()) && (wsret > 0));
+
+ return wsret;
+ }
+
+ void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ }
+};
+
+void WebSocketHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+ new WebSocketHook(this, sock);
+}
+
+class ModuleWebSocket : public Module
+{
+ dynamic_reference_nocheck<HashProvider> hash;
+ reference<WebSocketHookProvider> hookprov;
+
+ public:
+ ModuleWebSocket()
+ : hash(this, "hash/sha1")
+ , hookprov(new WebSocketHookProvider(this))
+ {
+ sha1 = &hash;
+ }
+
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+ {
+ if (type != ExtensionItem::EXT_USER)
+ return;
+
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+ if ((user) && (user->eh.GetModHook(this)))
+ ServerInstance->Users.QuitUser(user, "WebSocket module unloading");
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides RFC 6455 WebSocket support", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleWebSocket)
diff --git a/src/modules/m_xline_db.cpp b/src/modules/m_xline_db.cpp
index fb2a6f65a..90f9de9d2 100644
--- a/src/modules/m_xline_db.cpp
+++ b/src/modules/m_xline_db.cpp
@@ -20,45 +20,36 @@
#include "inspircd.h"
#include "xline.h"
-
-/* $ModConfig: <xlinedb filename="data/xline.db">
- * Specify the filename for the xline database here*/
-/* $ModDesc: Keeps a dynamic log of all XLines created, and stores them in a seperate conf file (xline.db). */
+#include <fstream>
class ModuleXLineDB : public Module
{
bool dirty;
std::string xlinedbpath;
public:
- void init()
+ void init() CXX11_OVERRIDE
{
/* Load the configuration
* Note:
- * this is on purpose not in the OnRehash() method. It would be non-trivial to change the database on-the-fly.
+ * This is on purpose not changed on a rehash. It would be non-trivial to change the database on-the-fly.
* Imagine a scenario where the new file already exists. Merging the current XLines with the existing database is likely a bad idea
* ...and so is discarding all current in-memory XLines for the ones in the database.
*/
ConfigTag* Conf = ServerInstance->Config->ConfValue("xlinedb");
- xlinedbpath = Conf->getString("filename", DATA_PATH "/xline.db");
+ xlinedbpath = ServerInstance->Config->Paths.PrependData(Conf->getString("filename", "xline.db"));
// Read xlines before attaching to events
ReadDatabase();
- Implementation eventlist[] = { I_OnAddLine, I_OnDelLine, I_OnExpireLine, I_OnBackgroundTimer };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
dirty = false;
}
- virtual ~ModuleXLineDB()
- {
- }
-
/** Called whenever an xline is added by a local user.
* This method is triggered after the line is added.
* @param source The sender of the line or NULL for local server
* @param line The xline being added
*/
- void OnAddLine(User* source, XLine* line)
+ void OnAddLine(User* source, XLine* line) CXX11_OVERRIDE
{
dirty = true;
}
@@ -68,17 +59,12 @@ class ModuleXLineDB : public Module
* @param source The user removing the line or NULL for local server
* @param line the line being deleted
*/
- void OnDelLine(User* source, XLine* line)
- {
- dirty = true;
- }
-
- void OnExpireLine(XLine *line)
+ void OnDelLine(User* source, XLine* line) CXX11_OVERRIDE
{
dirty = true;
}
- void OnBackgroundTimer(time_t now)
+ void OnBackgroundTimer(time_t now) CXX11_OVERRIDE
{
if (dirty)
{
@@ -89,25 +75,23 @@ class ModuleXLineDB : public Module
bool WriteDatabase()
{
- FILE *f;
-
/*
* We need to perform an atomic write so as not to fuck things up.
- * So, let's write to a temporary file, flush and sync the FD, then rename the file..
+ * So, let's write to a temporary file, flush it, then rename the file..
* Technically, that means that this can block, but I have *never* seen that.
- * -- w00t
+ * -- w00t
*/
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opening temporary database");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Opening temporary database");
std::string xlinenewdbpath = xlinedbpath + ".new";
- f = fopen(xlinenewdbpath.c_str(), "w");
- if (!f)
+ std::ofstream stream(xlinenewdbpath.c_str());
+ if (!stream.is_open())
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot create database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot create database \"%s\"! %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('x', "database: cannot create new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opened. Writing..");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Opened. Writing..");
/*
* Now, much as I hate writing semi-unportable formats, additional
@@ -116,7 +100,7 @@ class ModuleXLineDB : public Module
* semblance of backwards compatibility for reading on startup..
* -- w00t
*/
- fprintf(f, "VERSION 1\n");
+ stream << "VERSION 1" << std::endl;
// Now, let's write.
std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
@@ -129,22 +113,21 @@ class ModuleXLineDB : public Module
for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
{
XLine* line = i->second;
- fprintf(f, "LINE %s %s %s %lu %lu :%s\n", line->type.c_str(), line->Displayable(),
- line->source.c_str(), (unsigned long)line->set_time, (unsigned long)line->duration, line->reason.c_str());
+ stream << "LINE " << line->type << " " << line->Displayable() << " "
+ << line->source << " " << line->set_time << " "
+ << line->duration << " :" << line->reason << std::endl;
}
}
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Finished writing XLines. Checking for error..");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Finished writing XLines. Checking for error..");
- int write_error = 0;
- write_error = ferror(f);
- write_error |= fclose(f);
- if (write_error)
+ if (stream.fail())
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot write to new database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot write to new database \"%s\"! %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('x', "database: cannot write to new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
+ stream.close();
#ifdef _WIN32
remove(xlinedbpath.c_str());
@@ -152,8 +135,8 @@ class ModuleXLineDB : public Module
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
if (rename(xlinenewdbpath.c_str(), xlinedbpath.c_str()) < 0)
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot move new to old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot replace old database \"%s\" with new database \"%s\"! %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('x', "database: cannot replace old xline db \"%s\" with new db \"%s\": %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
@@ -162,65 +145,42 @@ class ModuleXLineDB : public Module
bool ReadDatabase()
{
- FILE *f;
- char linebuf[MAXBUF];
+ // If the xline database doesn't exist then we don't need to load it.
+ if (!FileSystem::FileExists(xlinedbpath))
+ return true;
- f = fopen(xlinedbpath.c_str(), "r");
- if (!f)
+ std::ifstream stream(xlinedbpath.c_str());
+ if (!stream.is_open())
{
- if (errno == ENOENT)
- {
- /* xline.db doesn't exist, fake good return value (we don't care about this) */
- return true;
- }
- else
- {
- /* this might be slightly more problematic. */
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot read database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", strerror(errno), errno);
- return false;
- }
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot read database \"%s\"! %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('x', "database: cannot read xline db \"%s\": %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+ return false;
}
- while (fgets(linebuf, MAXBUF, f))
+ std::string line;
+ while (std::getline(stream, line))
{
- char *c = linebuf;
-
- while (c && *c)
- {
- if (*c == '\n')
- {
- *c = '\0';
- }
-
- c++;
- }
-
// Inspired by the command parser. :)
- irc::tokenstream tokens(linebuf);
+ irc::tokenstream tokens(line);
int items = 0;
std::string command_p[7];
std::string tmp;
- while (tokens.GetToken(tmp) && (items < 7))
+ while (tokens.GetTrailing(tmp) && (items < 7))
{
command_p[items] = tmp;
items++;
}
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Processing %s", linebuf);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing %s", line.c_str());
if (command_p[0] == "VERSION")
{
- if (command_p[1] == "1")
+ if (command_p[1] != "1")
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Reading db version %s", command_p[1].c_str());
- }
- else
- {
- fclose(f);
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: I got database version %s - I don't understand it", command_p[1].c_str());
- ServerInstance->SNO->WriteToSnoMask('a', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
+ stream.close();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "I got database version %s - I don't understand it", command_p[1].c_str());
+ ServerInstance->SNO->WriteToSnoMask('x', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
return false;
}
}
@@ -231,7 +191,7 @@ class ModuleXLineDB : public Module
if (!xlf)
{
- ServerInstance->SNO->WriteToSnoMask('a', "database: Unknown line type (%s).", command_p[1].c_str());
+ ServerInstance->SNO->WriteToSnoMask('x', "database: Unknown line type (%s).", command_p[1].c_str());
continue;
}
@@ -246,18 +206,14 @@ class ModuleXLineDB : public Module
delete xl;
}
}
-
- fclose(f);
+ stream.close();
return true;
}
-
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Keeps a dynamic log of all XLines created, and stores them in a separate conf file (xline.db).", VF_VENDOR);
}
};
MODULE_INIT(ModuleXLineDB)
-
diff --git a/src/modules/sql.h b/src/modules/sql.h
deleted file mode 100644
index 436cd1da8..000000000
--- a/src/modules/sql.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef INSPIRCD_SQLAPI_3
-#define INSPIRCD_SQLAPI_3
-
-/** Defines the error types which SQLerror may be set to
- */
-enum SQLerrorNum { SQL_NO_ERROR, SQL_BAD_DBID, SQL_BAD_CONN, SQL_QSEND_FAIL, SQL_QREPLY_FAIL };
-
-/** A list of format parameters for an SQLquery object.
- */
-typedef std::vector<std::string> ParamL;
-
-typedef std::map<std::string, std::string> ParamM;
-
-class SQLEntry
-{
- public:
- std::string value;
- bool nul;
- SQLEntry() : nul(true) {}
- SQLEntry(const std::string& v) : value(v), nul(false) {}
- inline operator std::string&() { return value; }
-};
-
-typedef std::vector<SQLEntry> SQLEntries;
-
-/**
- * Result of an SQL query. Only valid inside OnResult
- */
-class SQLResult : public classbase
-{
- public:
- /**
- * Return the number of rows in the result.
- *
- * Note that if you have perfomed an INSERT or UPDATE query or other
- * query which will not return rows, this will return the number of
- * affected rows. In this case you SHOULD NEVER access any of the result
- * set rows, as there aren't any!
- * @returns Number of rows in the result set.
- */
- virtual int Rows() = 0;
-
- /**
- * Return a single row (result of the query). The internal row counter
- * is incremented by one.
- *
- * @param result Storage for the result data.
- * @returns true if there was a row, false if no row exists (end of
- * iteration)
- */
- virtual bool GetRow(SQLEntries& result) = 0;
-
- /** Returns column names for the items in this row
- */
- virtual void GetCols(std::vector<std::string>& result) = 0;
-};
-
-/** SQLerror holds the error state of a request.
- * The error string varies from database software to database software
- * and should be used to display informational error messages to users.
- */
-class SQLerror
-{
- public:
- /** The error id
- */
- SQLerrorNum id;
-
- /** The error string
- */
- std::string str;
-
- /** Initialize an SQLerror
- * @param i The error ID to set
- * @param s The (optional) error string to set
- */
- SQLerror(SQLerrorNum i, const std::string &s = "")
- : id(i), str(s)
- {
- }
-
- /** Return the error string for an error
- */
- const char* Str()
- {
- if(str.length())
- return str.c_str();
-
- switch(id)
- {
- case SQL_BAD_DBID:
- return "Invalid database ID";
- case SQL_BAD_CONN:
- return "Invalid connection";
- case SQL_QSEND_FAIL:
- return "Sending query failed";
- case SQL_QREPLY_FAIL:
- return "Getting query result failed";
- default:
- return "Unknown error";
- }
- }
-};
-
-/**
- * Object representing an SQL query. This should be allocated on the heap and
- * passed to an SQLProvider, which will free it when the query is complete or
- * when the querying module is unloaded.
- *
- * You should store whatever information is needed to have the callbacks work in
- * this object (UID of user, channel name, etc).
- */
-class SQLQuery : public classbase
-{
- public:
- ModuleRef creator;
-
- SQLQuery(Module* Creator) : creator(Creator) {}
- virtual ~SQLQuery() {}
-
- virtual void OnResult(SQLResult& result) = 0;
- /**
- * Called when the query fails
- */
- virtual void OnError(SQLerror& error) { }
-};
-
-/**
- * Provider object for SQL servers
- */
-class SQLProvider : public DataProvider
-{
- public:
- SQLProvider(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
- /** Submit an asynchronous SQL request
- * @param callback The result reporting point
- * @param query The hardcoded query string. If you have parameters to substitute, see below.
- */
- virtual void submit(SQLQuery* callback, const std::string& query) = 0;
-
- /** Submit an asynchronous SQL request
- * @param callback The result reporting point
- * @param format The simple parameterized query string ('?' parameters)
- * @param p Parameters to fill in for the '?' entries
- */
- virtual void submit(SQLQuery* callback, const std::string& format, const ParamL& p) = 0;
-
- /** Submit an asynchronous SQL request.
- * @param callback The result reporting point
- * @param format The parameterized query string ('$name' parameters)
- * @param p Parameters to fill in for the '$name' entries
- */
- virtual void submit(SQLQuery* callback, const std::string& format, const ParamM& p) = 0;
-
- /** Convenience function to prepare a map from a User* */
- void PopulateUserInfo(User* user, ParamM& userinfo)
- {
- userinfo["nick"] = user->nick;
- userinfo["host"] = user->host;
- userinfo["ip"] = user->GetIPString();
- userinfo["gecos"] = user->fullname;
- userinfo["ident"] = user->ident;
- userinfo["server"] = user->server;
- userinfo["uuid"] = user->uuid;
- }
-};
-
-#endif
diff --git a/src/modules/ssl.h b/src/modules/ssl.h
deleted file mode 100644
index 4c877551d..000000000
--- a/src/modules/ssl.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef SSL_H
-#define SSL_H
-
-#include <map>
-#include <string>
-
-/** ssl_cert is a class which abstracts SSL certificate
- * and key information.
- *
- * Because gnutls and openssl represent key information in
- * wildly different ways, this class allows it to be accessed
- * in a unified manner. These classes are attached to ssl-
- * connected local users using SSLCertExt
- */
-class ssl_cert : public refcountbase
-{
- public:
- std::string dn;
- std::string issuer;
- std::string error;
- std::string fingerprint;
- bool trusted, invalid, unknownsigner, revoked;
-
- ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {}
-
- /** Get certificate distinguished name
- * @return Certificate DN
- */
- const std::string& GetDN()
- {
- return dn;
- }
-
- /** Get Certificate issuer
- * @return Certificate issuer
- */
- const std::string& GetIssuer()
- {
- return issuer;
- }
-
- /** Get error string if an error has occured
- * @return The error associated with this users certificate,
- * or an empty string if there is no error.
- */
- const std::string& GetError()
- {
- return error;
- }
-
- /** Get key fingerprint.
- * @return The key fingerprint as a hex string.
- */
- const std::string& GetFingerprint()
- {
- return fingerprint;
- }
-
- /** Get trust status
- * @return True if this is a trusted certificate
- * (the certificate chain validates)
- */
- bool IsTrusted()
- {
- return trusted;
- }
-
- /** Get validity status
- * @return True if the certificate itself is
- * correctly formed.
- */
- bool IsInvalid()
- {
- return invalid;
- }
-
- /** Get signer status
- * @return True if the certificate appears to be
- * self-signed.
- */
- bool IsUnknownSigner()
- {
- return unknownsigner;
- }
-
- /** Get revokation status.
- * @return True if the certificate is revoked.
- * Note that this only works properly for GnuTLS
- * right now.
- */
- bool IsRevoked()
- {
- return revoked;
- }
-
- bool IsCAVerified()
- {
- return trusted && !invalid && !revoked && !unknownsigner && error.empty();
- }
-
- std::string GetMetaLine()
- {
- std::stringstream value;
- bool hasError = !error.empty();
- value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r")
- << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " ";
- if (hasError)
- value << GetError();
- else
- value << GetFingerprint() << " " << GetDN() << " " << GetIssuer();
- return value.str();
- }
-};
-
-/** Get certificate from a socket (only useful with an SSL module) */
-struct SocketCertificateRequest : public Request
-{
- StreamSocket* const sock;
- ssl_cert* cert;
-
- SocketCertificateRequest(StreamSocket* ss, Module* Me)
- : Request(Me, ss->GetIOHook(), "GET_SSL_CERT"), sock(ss), cert(NULL)
- {
- Send();
- }
-
- std::string GetFingerprint()
- {
- if (cert)
- return cert->GetFingerprint();
- return "";
- }
-};
-
-/** Get certificate from a user (requires m_sslinfo) */
-struct UserCertificateRequest : public Request
-{
- User* const user;
- ssl_cert* cert;
-
- UserCertificateRequest(User* u, Module* Me, Module* info = ServerInstance->Modules->Find("m_sslinfo.so"))
- : Request(Me, info, "GET_USER_CERT"), user(u), cert(NULL)
- {
- Send();
- }
-
- std::string GetFingerprint()
- {
- if (cert)
- return cert->GetFingerprint();
- return "";
- }
-};
-
-class SSLRawSessionRequest : public Request
-{
- public:
- const int fd;
- void* data;
-
- SSLRawSessionRequest(int FD, Module* srcmod, Module* destmod)
- : Request(srcmod, destmod, "GET_RAW_SSL_SESSION")
- , fd(FD)
- , data(NULL)
- {
- Send();
- }
-};
-
-#endif
diff --git a/src/modules/u_listmode.h b/src/modules/u_listmode.h
deleted file mode 100644
index a728eb839..000000000
--- a/src/modules/u_listmode.h
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef INSPIRCD_LISTMODE_PROVIDER
-#define INSPIRCD_LISTMODE_PROVIDER
-
-/** Get the time as a string
- */
-inline std::string stringtime()
-{
- std::ostringstream TIME;
- TIME << ServerInstance->Time();
- return TIME.str();
-}
-
-/** An item in a listmode's list
- */
-class ListItem
-{
-public:
- std::string nick;
- std::string mask;
- std::string time;
-};
-
-/** The number of items a listmode's list may contain
- */
-class ListLimit
-{
-public:
- std::string mask;
- unsigned int limit;
-};
-
-/** Items stored in the channel's list
- */
-typedef std::list<ListItem> modelist;
-/** Max items per channel by name
- */
-typedef std::list<ListLimit> limitlist;
-
-/** The base class for list modes, should be inherited.
- */
-class ListModeBase : public ModeHandler
-{
- protected:
- /** Numeric to use when outputting the list
- */
- unsigned int listnumeric;
- /** Numeric to indicate end of list
- */
- unsigned int endoflistnumeric;
- /** String to send for end of list
- */
- std::string endofliststring;
- /** Automatically tidy up entries
- */
- bool tidy;
- /** Config tag to check for max items per channel
- */
- std::string configtag;
- /** Limits on a per-channel basis read from the tag
- * specified in ListModeBase::configtag
- */
- limitlist chanlimits;
-
- public:
- /** Storage key
- */
- SimpleExtItem<modelist> extItem;
-
- /** Constructor.
- * @param Instance The creator of this class
- * @param modechar Mode character
- * @param eolstr End of list string
- * @pram lnum List numeric
- * @param eolnum End of list numeric
- * @param autotidy Automatically tidy list entries on add
- * @param ctag Configuration tag to get limits from
- */
- ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist")
- : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL),
- listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
- configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
- {
- list = true;
- }
-
- /** See mode.h
- */
- std::pair<bool,std::string> ModeSet(User*, User*, Channel* channel, const std::string &parameter)
- {
- modelist* el = extItem.get(channel);
- if (el)
- {
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if(parameter == it->mask)
- {
- return std::make_pair(true, parameter);
- }
- }
- }
- return std::make_pair(false, parameter);
- }
-
- /** Display the list for this mode
- * @param user The user to send the list to
- * @param channel The channel the user is requesting the list for
- */
- virtual void DisplayList(User* user, Channel* channel)
- {
- modelist* el = extItem.get(channel);
- if (el)
- {
- for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
- {
- user->WriteNumeric(listnumeric, "%s %s %s %s %s", user->nick.c_str(), channel->name.c_str(), it->mask.c_str(), (it->nick.length() ? it->nick.c_str() : ServerInstance->Config->ServerName.c_str()), it->time.c_str());
- }
- }
- user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
- }
-
- virtual void DisplayEmptyList(User* user, Channel* channel)
- {
- user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
- }
-
- /** Remove all instances of the mode from a channel.
- * See mode.h
- * @param channel The channel to remove all instances of the mode from
- */
- virtual void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- modelist* el = extItem.get(channel);
- if (el)
- {
- irc::modestacker modestack(false);
-
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if (stack)
- stack->Push(this->GetModeChar(), it->mask);
- else
- modestack.Push(this->GetModeChar(), it->mask);
- }
-
- if (stack)
- return;
-
- std::vector<std::string> stackresult;
- stackresult.push_back(channel->name);
- while (modestack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, ServerInstance->FakeClient);
- stackresult.clear();
- stackresult.push_back(channel->name);
- }
- }
- }
-
- /** See mode.h
- */
- virtual void RemoveMode(User*, irc::modestacker* stack)
- {
- /* Listmodes dont get set on users */
- }
-
- /** Perform a rehash of this mode's configuration data
- */
- virtual void DoRehash()
- {
- ConfigTagList tags = ServerInstance->Config->ConfTags(configtag);
-
- chanlimits.clear();
-
- for (ConfigIter i = tags.first; i != tags.second; i++)
- {
- // For each <banlist> tag
- ConfigTag* c = i->second;
- ListLimit limit;
- limit.mask = c->getString("chan");
- limit.limit = c->getInt("limit");
-
- if (limit.mask.size() && limit.limit > 0)
- chanlimits.push_back(limit);
- }
-
- // Add the default entry. This is inserted last so if the user specifies a
- // wildcard record in the config it will take precedence over this entry.
- ListLimit limit;
- limit.mask = "*";
- limit.limit = 64;
- chanlimits.push_back(limit);
- }
-
- /** Populate the Implements list with the correct events for a List Mode
- */
- virtual void DoImplements(Module* m)
- {
- ServerInstance->Modules->AddService(extItem);
- this->DoRehash();
- Implementation eventlist[] = { I_OnSyncChannel, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, m, sizeof(eventlist)/sizeof(Implementation));
- }
-
- /** Handle the list mode.
- * See mode.h
- */
- virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
- {
- // Try and grab the list
- modelist* el = extItem.get(channel);
-
- if (adding)
- {
- if (tidy)
- ModeParser::CleanMask(parameter);
-
- if (parameter.length() > 250)
- return MODEACTION_DENY;
-
- // If there was no list
- if (!el)
- {
- // Make one
- el = new modelist;
- extItem.set(channel, el);
- }
-
- // Check if the item already exists in the list
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if (parameter == it->mask)
- {
- /* Give a subclass a chance to error about this */
- TellAlreadyOnList(source, channel, parameter);
-
- // it does, deny the change
- return MODEACTION_DENY;
- }
- }
-
- unsigned int maxsize = 0;
-
- for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
- {
- if (InspIRCd::Match(channel->name, it->mask))
- {
- // We have a pattern matching the channel...
- maxsize = el->size();
- if (!IS_LOCAL(source) || (maxsize < it->limit))
- {
- /* Ok, it *could* be allowed, now give someone subclassing us
- * a chance to validate the parameter.
- * The param is passed by reference, so they can both modify it
- * and tell us if we allow it or not.
- *
- * eg, the subclass could:
- * 1) allow
- * 2) 'fix' parameter and then allow
- * 3) deny
- */
- if (ValidateParam(source, channel, parameter))
- {
- // And now add the mask onto the list...
- ListItem e;
- e.mask = parameter;
- e.nick = source->nick;
- e.time = stringtime();
-
- el->push_back(e);
- return MODEACTION_ALLOW;
- }
- else
- {
- /* If they deny it they have the job of giving an error message */
- return MODEACTION_DENY;
- }
- }
- else
- break;
- }
- }
-
- /* List is full, give subclass a chance to send a custom message */
- if (!TellListTooLong(source, channel, parameter))
- {
- source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
- }
-
- parameter.clear();
- return MODEACTION_DENY;
- }
- else
- {
- // We're taking the mode off
- if (el)
- {
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if (parameter == it->mask)
- {
- el->erase(it);
- if (el->empty())
- {
- extItem.unset(channel);
- }
- return MODEACTION_ALLOW;
- }
- }
- /* Tried to remove something that wasn't set */
- TellNotSet(source, channel, parameter);
- parameter.clear();
- return MODEACTION_DENY;
- }
- else
- {
- /* Hmm, taking an exception off a non-existant list, DIE */
- TellNotSet(source, channel, parameter);
- parameter.clear();
- return MODEACTION_DENY;
- }
- }
- return MODEACTION_DENY;
- }
-
- /** Syncronize channel item list with another server.
- * See modules.h
- * @param chan Channel to syncronize
- * @param proto Protocol module pointer
- * @param opaque Opaque connection handle
- */
- virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- modelist* mlist = extItem.get(chan);
- irc::modestacker modestack(true);
- std::vector<std::string> stackresult;
- std::vector<TranslateType> types;
- types.push_back(TR_TEXT);
- if (mlist)
- {
- for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
- {
- modestack.Push(std::string(1, mode)[0], it->mask);
- }
- }
- while (modestack.GetStackedLine(stackresult))
- {
- types.assign(stackresult.size(), this->GetTranslateType());
- proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
- stackresult.clear();
- }
- }
-
- /** Clean up module on unload
- * @param target_type Type of target to clean
- * @param item Item to clean
- */
- virtual void DoCleanup(int, void*)
- {
- }
-
- /** Validate parameters.
- * Overridden by implementing module.
- * @param source Source user adding the parameter
- * @param channel Channel the parameter is being added to
- * @param parameter The actual parameter being added
- * @return true if the parameter is valid
- */
- virtual bool ValidateParam(User*, Channel*, std::string&)
- {
- return true;
- }
-
- /** Tell the user the list is too long.
- * Overridden by implementing module.
- * @param source Source user adding the parameter
- * @param channel Channel the parameter is being added to
- * @param parameter The actual parameter being added
- * @return Ignored
- */
- virtual bool TellListTooLong(User*, Channel*, std::string&)
- {
- return false;
- }
-
- /** Tell the user an item is already on the list.
- * Overridden by implementing module.
- * @param source Source user adding the parameter
- * @param channel Channel the parameter is being added to
- * @param parameter The actual parameter being added
- */
- virtual void TellAlreadyOnList(User*, Channel*, std::string&)
- {
- }
-
- /** Tell the user that the parameter is not in the list.
- * Overridden by implementing module.
- * @param source Source user removing the parameter
- * @param channel Channel the parameter is being removed from
- * @param parameter The actual parameter being removed
- */
- virtual void TellNotSet(User*, Channel*, std::string&)
- {
- }
-};
-
-#endif
diff --git a/src/server.cpp b/src/server.cpp
index d05ece8a4..ff8f6211f 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -23,7 +23,6 @@
#include <signal.h>
#include "exitcodes.h"
#include "inspircd.h"
-#include "inspircd_version.h"
void InspIRCd::SignalHandler(int signal)
{
@@ -32,12 +31,13 @@ void InspIRCd::SignalHandler(int signal)
#else
if (signal == SIGHUP)
{
- Rehash("Caught SIGHUP");
+ ServerInstance->SNO->WriteGlobalSno('a', "Rehashing due to SIGHUP");
+ Rehash();
}
else if (signal == SIGTERM)
#endif
{
- Exit(signal);
+ Exit(EXIT_STATUS_SIGTERM);
}
}
@@ -46,55 +46,43 @@ void InspIRCd::Exit(int status)
#ifdef _WIN32
SetServiceStopped(status);
#endif
- this->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
this->Cleanup();
- delete this;
ServerInstance = NULL;
+ delete this;
exit (status);
}
-void RehashHandler::Call(const std::string &reason)
+void InspIRCd::Rehash(const std::string& uuid)
{
- ServerInstance->SNO->WriteToSnoMask('a', "Rehashing config file %s %s",ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()), reason.c_str());
- ServerInstance->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
if (!ServerInstance->ConfigThread)
{
- ServerInstance->ConfigThread = new ConfigReaderThread("");
- ServerInstance->Threads->Start(ServerInstance->ConfigThread);
+ ServerInstance->ConfigThread = new ConfigReaderThread(uuid);
+ ServerInstance->Threads.Start(ServerInstance->ConfigThread);
}
}
-std::string InspIRCd::GetVersionString(bool operstring)
+std::string InspIRCd::GetVersionString(bool getFullVersion)
{
- char versiondata[MAXBUF];
- if (operstring)
- {
- std::string sename = SE->GetName();
- snprintf(versiondata,MAXBUF,"%s %s :%s [%s,%s,%s]",VERSION, Config->ServerName.c_str(), SYSTEM,REVISION, sename.c_str(), Config->sid.c_str());
- }
- else
- snprintf(versiondata,MAXBUF,"%s %s :%s",BRANCH,Config->ServerName.c_str(),Config->CustomVersion.c_str());
- return versiondata;
+ if (getFullVersion)
+ return INSPIRCD_VERSION ". " + Config->ServerName + " :[" + Config->sid + "] " + Config->CustomVersion;
+ return INSPIRCD_BRANCH ". " + Config->ServerName + " :" + Config->CustomVersion;
}
-const char InspIRCd::LogHeader[] =
- "Log started for " VERSION " (" REVISION ", " MODULE_INIT_STR ")"
- " - compiled on " SYSTEM;
-
-void InspIRCd::BuildISupport()
+std::string UIDGenerator::GenerateSID(const std::string& servername, const std::string& serverdesc)
{
- // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
- std::stringstream v;
- v << "WALLCHOPS WALLVOICES MODES=" << Config->Limits.MaxModes << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << Config->Limits.NickMax;
- v << " CASEMAPPING=rfc1459 STATUSMSG=" << Modes->BuildPrefixes(false) << " CHARSET=ascii TOPICLEN=" << Config->Limits.MaxTopic << " KICKLEN=" << Config->Limits.MaxKick << " MAXTARGETS=" << Config->MaxTargets;
- v << " AWAYLEN=" << Config->Limits.MaxAway << " CHANMODES=" << this->Modes->GiveModeList(MASK_CHANNEL) << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU" << " CHANNELLEN=" << Config->Limits.ChanMax;
- Config->data005 = v.str();
- FOREACH_MOD(I_On005Numeric,On005Numeric(Config->data005));
- Config->Update005();
+ unsigned int sid = 0;
+
+ for (std::string::const_iterator i = servername.begin(); i != servername.end(); ++i)
+ sid = 5 * sid + *i;
+ for (std::string::const_iterator i = serverdesc.begin(); i != serverdesc.end(); ++i)
+ sid = 5 * sid + *i;
+
+ std::string sidstr = ConvToStr(sid % 1000);
+ sidstr.insert(0, 3 - sidstr.length(), '0');
+ return sidstr;
}
-void InspIRCd::IncrementUID(int pos)
+void UIDGenerator::IncrementUID(unsigned int pos)
{
/*
* Okay. The rules for generating a UID go like this...
@@ -103,85 +91,155 @@ void InspIRCd::IncrementUID(int pos)
* A again, in an iterative fashion.. so..
* AAA9 -> AABA, and so on. -- w00t
*/
- if ((pos == 3) && (current_uid[3] == '9'))
+
+ // If we hit Z, wrap around to 0.
+ if (current_uid[pos] == 'Z')
+ {
+ current_uid[pos] = '0';
+ }
+ else if (current_uid[pos] == '9')
{
- // At pos 3, if we hit '9', we've run out of available UIDs, and need to reset to AAA..AAA.
- for (int i = 3; i < UUID_LENGTH-1; i++)
+ /*
+ * Or, if we hit 9, wrap around to pos = 'A' and (pos - 1)++,
+ * e.g. A9 -> BA -> BB ..
+ */
+ current_uid[pos] = 'A';
+ if (pos == 3)
{
- current_uid[i] = 'A';
+ // At pos 3, if we hit '9', we've run out of available UIDs, and reset to AAA..AAA.
+ return;
}
+ this->IncrementUID(pos - 1);
}
else
{
- // If we hit Z, wrap around to 0.
- if (current_uid[pos] == 'Z')
- {
- current_uid[pos] = '0';
- }
- else if (current_uid[pos] == '9')
- {
- /*
- * Or, if we hit 9, wrap around to pos = 'A' and (pos - 1)++,
- * e.g. A9 -> BA -> BB ..
- */
- current_uid[pos] = 'A';
- this->IncrementUID(pos - 1);
- }
- else
- {
- // Anything else, nobody gives a shit. Just increment.
- current_uid[pos]++;
- }
+ // Anything else, nobody gives a shit. Just increment.
+ current_uid[pos]++;
}
}
-/*
- * Retrieve the next valid UUID that is free for this server.
- */
-std::string InspIRCd::GetUID()
+void UIDGenerator::init(const std::string& sid)
{
- static bool inited = false;
-
/*
- * If we're setting up, copy SID into the first three digits, 9's to the rest, null term at the end
+ * Copy SID into the first three digits, 9's to the rest, null term at the end
* Why 9? Well, we increment before we find, otherwise we have an unnecessary copy, and I want UID to start at AAA..AA
* and not AA..AB. So by initialising to 99999, we force it to rollover to AAAAA on the first IncrementUID call.
* Kind of silly, but I like how it looks.
* -- w
*/
- if (!inited)
- {
- inited = true;
- current_uid[0] = Config->sid[0];
- current_uid[1] = Config->sid[1];
- current_uid[2] = Config->sid[2];
- for (int i = 3; i < (UUID_LENGTH - 1); i++)
- current_uid[i] = '9';
-
- // Null terminator. Important.
- current_uid[UUID_LENGTH - 1] = '\0';
- }
+ current_uid.resize(UUID_LENGTH, '9');
+ current_uid[0] = sid[0];
+ current_uid[1] = sid[1];
+ current_uid[2] = sid[2];
+}
+/*
+ * Retrieve the next valid UUID that is free for this server.
+ */
+std::string UIDGenerator::GetUID()
+{
while (1)
{
// Add one to the last UID
- this->IncrementUID(UUID_LENGTH - 2);
+ this->IncrementUID(UUID_LENGTH - 1);
- if (this->FindUUID(current_uid))
- {
- /*
- * It's in use. We need to try the loop again.
- */
- continue;
- }
+ if (!ServerInstance->FindUUID(current_uid))
+ break;
- return current_uid;
+ /*
+ * It's in use. We need to try the loop again.
+ */
}
- /* not reached. */
- return "";
+ return current_uid;
}
+void ISupportManager::AppendValue(std::string& buffer, const std::string& value)
+{
+ // If this token has no value then we have nothing to do.
+ if (value.empty())
+ return;
+
+ // This function implements value escaping according to the rules of the ISUPPORT draft:
+ // https://tools.ietf.org/html/draft-brocklesby-irc-isupport-03
+ buffer.push_back('=');
+ for (std::string::const_iterator iter = value.begin(); iter != value.end(); ++iter)
+ {
+ // The value must be escaped if:
+ // (1) It is a banned character in an IRC <middle> parameter (NUL, LF, CR, SPACE).
+ // (2) It has special meaning within an ISUPPORT token (EQUALS, BACKSLASH).
+ if (*iter == '\0' || *iter == '\n' || *iter == '\r' || *iter == ' ' || *iter == '=' || *iter == '\\')
+ buffer.append(InspIRCd::Format("\\x%X", *iter));
+ else
+ buffer.push_back(*iter);
+ }
+}
+
+void ISupportManager::Build()
+{
+ /**
+ * This is currently the neatest way we can build the initial ISUPPORT map. In
+ * the future we can use an initializer list here.
+ */
+ std::map<std::string, std::string> tokens;
+
+ tokens["AWAYLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxAway);
+ tokens["CASEMAPPING"] = ServerInstance->Config->CaseMapping;
+ tokens["CHANLIMIT"] = InspIRCd::Format("#:%u", ServerInstance->Config->MaxChans);
+ tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL);
+ tokens["CHANNELLEN"] = ConvToStr(ServerInstance->Config->Limits.ChanMax);
+ tokens["CHANTYPES"] = "#";
+ tokens["HOSTLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxHost);
+ tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick);
+ tokens["LINELEN"] = ConvToStr(ServerInstance->Config->Limits.MaxLine);
+ tokens["MAXTARGETS"] = ConvToStr(ServerInstance->Config->MaxTargets);
+ tokens["MODES"] = ConvToStr(ServerInstance->Config->Limits.MaxModes);
+ tokens["NETWORK"] = ServerInstance->Config->Network;
+ tokens["NICKLEN"] = ConvToStr(ServerInstance->Config->Limits.NickMax);
+ tokens["PREFIX"] = ServerInstance->Modes->BuildPrefixes();
+ tokens["STATUSMSG"] = ServerInstance->Modes->BuildPrefixes(false);
+ tokens["TOPICLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxTopic);
+ tokens["USERLEN"] = ConvToStr(ServerInstance->Config->Limits.IdentMax);
+ tokens["VBANLIST"];
+
+ // Modules can add new tokens and also edit or remove existing tokens
+ FOREACH_MOD(On005Numeric, (tokens));
+
+ // EXTBAN is a special case as we need to sort it and prepend a comma.
+ std::map<std::string, std::string>::iterator extban = tokens.find("EXTBAN");
+ if (extban != tokens.end())
+ {
+ std::sort(extban->second.begin(), extban->second.end());
+ extban->second.insert(0, ",");
+ }
+
+ // Transform the map into a list of lines, ready to be sent to clients
+ Numeric::Numeric numeric(RPL_ISUPPORT);
+ unsigned int token_count = 0;
+ cachedlines.clear();
+ for (std::map<std::string, std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
+ {
+ numeric.push(it->first);
+ std::string& token = numeric.GetParams().back();
+ AppendValue(token, it->second);
+
+ token_count++;
+ if (token_count % 13 == 12 || it == --tokens.end())
+ {
+ // Reached maximum number of tokens for this line or the current token
+ // is the last one; finalize the line and store it for later use
+ numeric.push("are supported by this server");
+ cachedlines.push_back(numeric);
+ numeric.GetParams().clear();
+ }
+ }
+}
+
+void ISupportManager::SendTo(LocalUser* user)
+{
+ for (std::vector<Numeric::Numeric>::const_iterator i = cachedlines.begin(); i != cachedlines.end(); ++i)
+ user->WriteNumeric(*i);
+}
diff --git a/src/snomasks.cpp b/src/snomasks.cpp
index 4b9c9d86b..0b266ff7e 100644
--- a/src/snomasks.cpp
+++ b/src/snomasks.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
-#include <stdarg.h>
void SnomaskManager::FlushSnotices()
{
@@ -47,31 +46,21 @@ void SnomaskManager::WriteGlobalSno(char letter, const std::string& text)
{
WriteToSnoMask(letter, text);
letter = toupper(letter);
- ServerInstance->PI->SendSNONotice(std::string(1, letter), text);
+ ServerInstance->PI->SendSNONotice(letter, text);
}
void SnomaskManager::WriteToSnoMask(char letter, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteToSnoMask(letter, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteToSnoMask(letter, textbuffer);
}
void SnomaskManager::WriteGlobalSno(char letter, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteGlobalSno(letter, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteGlobalSno(letter, textbuffer);
}
SnomaskManager::SnomaskManager()
@@ -79,54 +68,41 @@ SnomaskManager::SnomaskManager()
EnableSnomask('c',"CONNECT"); /* Local connect notices */
EnableSnomask('q',"QUIT"); /* Local quit notices */
EnableSnomask('k',"KILL"); /* Kill notices */
- EnableSnomask('l',"LINK"); /* Linking notices */
EnableSnomask('o',"OPER"); /* Oper up/down notices */
EnableSnomask('a',"ANNOUNCEMENT"); /* formerly WriteOpers() - generic notices to all opers */
- EnableSnomask('d',"DEBUG"); /* Debug notices */
EnableSnomask('x',"XLINE"); /* Xline notice (g/z/q/k/e) */
EnableSnomask('t',"STATS"); /* Local or remote stats request */
- EnableSnomask('f',"FLOOD"); /* Flooding notices */
}
-/*************************************************************************************/
+bool SnomaskManager::IsSnomaskUsable(char ch) const
+{
+ return ((isalpha(ch)) && (!masks[tolower(ch) - 'a'].Description.empty()));
+}
-void Snomask::SendMessage(const std::string &message, char mysnomask)
+Snomask::Snomask()
+ : Count(0)
{
- if (ServerInstance->Config->NoSnoticeStack || message != LastMessage || mysnomask != LastLetter)
+}
+
+void Snomask::SendMessage(const std::string& message, char letter)
+{
+ if ((!ServerInstance->Config->NoSnoticeStack) && (message == LastMessage) && (letter == LastLetter))
{
- this->Flush();
- LastMessage = message;
- LastLetter = mysnomask;
-
- std::string desc = Description;
- if (desc.empty())
- desc = std::string("SNO-") + (char)tolower(mysnomask);
- if (isupper(mysnomask))
- desc = "REMOTE" + desc;
- ModResult MOD_RESULT;
- ServerInstance->Logs->Log("snomask", DEFAULT, "%s: %s", desc.c_str(), message.c_str());
-
- FIRST_MOD_RESULT(OnSendSnotice, MOD_RESULT, (mysnomask, desc, message));
-
- LastBlocked = (MOD_RESULT == MOD_RES_DENY);
-
- if (!LastBlocked)
- {
- /* Only opers can receive snotices, so we iterate the oper list */
- std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin();
-
- while (i != ServerInstance->Users->all_opers.end())
- {
- User* a = *i;
- if (IS_LOCAL(a) && a->IsModeSet('s') && a->IsNoticeMaskSet(mysnomask) && !a->quitting)
- {
- a->WriteServ("NOTICE %s :*** %s: %s", a->nick.c_str(), desc.c_str(), message.c_str());
- }
-
- i++;
- }
- }
+ Count++;
+ return;
}
+
+ this->Flush();
+
+ std::string desc = GetDescription(letter);
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnSendSnotice, MOD_RESULT, (letter, desc, message));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return;
+
+ Snomask::Send(letter, desc, message);
+ LastMessage = message;
+ LastLetter = letter;
Count++;
}
@@ -134,36 +110,41 @@ void Snomask::Flush()
{
if (Count > 1)
{
- std::string desc = Description;
- if (desc.empty())
- desc = std::string("SNO-") + (char)tolower(LastLetter);
- if (isupper(LastLetter))
- desc = "REMOTE" + desc;
- std::string mesg = "(last message repeated "+ConvToStr(Count)+" times)";
-
- ServerInstance->Logs->Log("snomask", DEFAULT, "%s: %s", desc.c_str(), mesg.c_str());
-
- FOREACH_MOD(I_OnSendSnotice, OnSendSnotice(LastLetter, desc, mesg));
-
- if (!LastBlocked)
- {
- /* Only opers can receive snotices, so we iterate the oper list */
- std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin();
-
- while (i != ServerInstance->Users->all_opers.end())
- {
- User* a = *i;
- if (IS_LOCAL(a) && a->IsModeSet('s') && a->IsNoticeMaskSet(LastLetter) && !a->quitting)
- {
- a->WriteServ("NOTICE %s :*** %s: %s", a->nick.c_str(), desc.c_str(), mesg.c_str());
- }
-
- i++;
- }
- }
+ std::string desc = GetDescription(LastLetter);
+ std::string msg = "(last message repeated " + ConvToStr(Count) + " times)";
+ FOREACH_MOD(OnSendSnotice, (LastLetter, desc, msg));
+ Snomask::Send(LastLetter, desc, msg);
}
+
LastMessage.clear();
- LastBlocked = false;
Count = 0;
}
+
+void Snomask::Send(char letter, const std::string& desc, const std::string& msg)
+{
+ ServerInstance->Logs->Log(desc, LOG_DEFAULT, msg);
+ const std::string finalmsg = InspIRCd::Format("*** %s: %s", desc.c_str(), msg.c_str());
+
+ /* Only opers can receive snotices, so we iterate the oper list */
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* user = *i;
+ // IsNoticeMaskSet() returns false for opers who aren't +s, no need to check for it seperately
+ if (IS_LOCAL(user) && user->IsNoticeMaskSet(letter))
+ user->WriteNotice(finalmsg);
+ }
+}
+
+std::string Snomask::GetDescription(char letter) const
+{
+ std::string ret;
+ if (isupper(letter))
+ ret = "REMOTE";
+ if (!Description.empty())
+ ret += Description;
+ else
+ ret += std::string("SNO-") + (char)tolower(letter);
+ return ret;
+}
diff --git a/src/socket.cpp b/src/socket.cpp
index e73d01af9..cdbc83e44 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -22,111 +22,102 @@
#include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
-using irc::sockets::sockaddrs;
-/** This will bind a socket to a port. It works for UDP/TCP.
- * It can only bind to IP addresses, if you wish to bind to hostnames
- * you should first resolve them using class 'Resolver'.
- */
-bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten)
+bool InspIRCd::BindPort(ConfigTag* tag, const irc::sockets::sockaddrs& sa, std::vector<ListenSocket*>& old_ports)
{
- sockaddrs servaddr;
- int ret;
-
- if ((*addr == '*' || *addr == '\0') && port == -1)
- {
- /* Port -1: Means UDP IPV4 port binding - Special case
- * used by DNS engine.
- */
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.in4.sin_family = AF_INET;
- }
- else if (!irc::sockets::aptosa(addr, port, servaddr))
- return false;
-
- ret = SE->Bind(sockfd, servaddr);
-
- if (ret < 0)
- {
- return false;
- }
- else
+ for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
{
- if (dolisten)
- {
- if (SE->Listen(sockfd, Config->MaxConn) == -1)
- {
- this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno));
- return false;
- }
- else
- {
- this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
- SE->NonBlocking(sockfd);
- return true;
- }
- }
- else
+ if ((**n).bind_sa == sa)
{
- this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
+ // Replace tag, we know addr and port match, but other info (type, ssl) may not.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Replacing listener on %s from old tag at %s with new tag from %s",
+ sa.str().c_str(), (*n)->bind_tag->getTagLocation().c_str(), tag->getTagLocation().c_str());
+ (*n)->bind_tag = tag;
+ (*n)->ResetIOHookProvider();
+
+ old_ports.erase(n);
return true;
}
}
+
+ ListenSocket* ll = new ListenSocket(tag, sa);
+ if (ll->GetFd() < 0)
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to listen on %s from tag at %s: %s",
+ sa.str().c_str(), tag->getTagLocation().c_str(), strerror(errno));
+ delete ll;
+ return false;
+ }
+
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Added a listener on %s from tag at %s", sa.str().c_str(), tag->getTagLocation().c_str());
+ ports.push_back(ll);
+ return true;
}
-int InspIRCd::BindPorts(FailedPortList &failed_ports)
+int InspIRCd::BindPorts(FailedPortList& failed_ports)
{
int bound = 0;
std::vector<ListenSocket*> old_ports(ports.begin(), ports.end());
ConfigTagList tags = ServerInstance->Config->ConfTags("bind");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- std::string porttag = tag->getString("port");
- std::string Addr = tag->getString("address");
- if (strncasecmp(Addr.c_str(), "::ffff:", 7) == 0)
- this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
-
- irc::portparser portrange(porttag, false);
- int portno = -1;
- while (0 != (portno = portrange.GetToken()))
+ // Are we creating a TCP/IP listener?
+ const std::string address = tag->getString("address");
+ const std::string portlist = tag->getString("port");
+ if (!address.empty() || !portlist.empty())
{
- irc::sockets::sockaddrs bindspec;
- if (!irc::sockets::aptosa(Addr, portno, bindspec))
- continue;
- std::string bind_readable = bindspec.str();
+ // InspIRCd supports IPv4 and IPv6 natively; no 4in6 required.
+ if (strncasecmp(address.c_str(), "::ffff:", 7) == 0)
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
- bool skip = false;
- for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
- {
- if ((**n).bind_desc == bind_readable)
- {
- (*n)->bind_tag = tag; // Replace tag, we know addr and port match, but other info (type, ssl) may not
- skip = true;
- old_ports.erase(n);
- break;
- }
- }
- if (!skip)
+ // A TCP listener with no ports is not very useful.
+ if (portlist.empty())
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "TCP listener on %s at %s has no ports specified!",
+ address.empty() ? "*" : address.c_str(), tag->getTagLocation().c_str());
+
+ irc::portparser portrange(portlist, false);
+ for (int port; (port = portrange.GetToken()); )
{
- ListenSocket* ll = new ListenSocket(tag, bindspec);
+ irc::sockets::sockaddrs bindspec;
+ if (!irc::sockets::aptosa(address, port, bindspec))
+ continue;
- if (ll->GetFd() > -1)
- {
- bound++;
- ports.push_back(ll);
- }
+ if (!BindPort(tag, bindspec, old_ports))
+ failed_ports.push_back(std::make_pair(bindspec, errno));
else
- {
- failed_ports.push_back(std::make_pair(bind_readable, strerror(errno)));
- delete ll;
- }
+ bound++;
}
+ continue;
}
+
+#ifndef _WIN32
+ // Are we creating a UNIX listener?
+ const std::string path = tag->getString("path");
+ if (!path.empty())
+ {
+ // UNIX socket paths are length limited to less than PATH_MAX.
+ irc::sockets::sockaddrs bindspec;
+ if (path.length() > std::min(ServerInstance->Config->Limits.MaxHost, sizeof(bindspec.un.sun_path)))
+ {
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "UNIX listener on %s at %s specified a path that is too long!",
+ path.c_str(), tag->getTagLocation().c_str());
+ continue;
+ }
+
+ // Create the bindspec manually (aptosa doesn't work with AF_UNIX yet).
+ memset(&bindspec, 0, sizeof(bindspec));
+ bindspec.un.sun_family = AF_UNIX;
+ memcpy(&bindspec.un.sun_path, path.c_str(), sizeof(bindspec.un.sun_path));
+
+ if (!BindPort(tag, bindspec, old_ports))
+ failed_ports.push_back(std::make_pair(bindspec, errno));
+ else
+ bound++;
+ }
+#endif
}
std::vector<ListenSocket*>::iterator n = ports.begin();
@@ -136,12 +127,12 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports)
n++;
if (n == ports.end())
{
- this->Logs->Log("SOCKET",DEFAULT,"Port bindings slipped out of vector, aborting close!");
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "Port bindings slipped out of vector, aborting close!");
break;
}
- this->Logs->Log("SOCKET",DEFAULT, "Port binding %s was removed from the config file, closing.",
- (**n).bind_desc.c_str());
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "Port binding %s was removed from the config file, closing.",
+ (**n).bind_sa.str().c_str());
delete *n;
// this keeps the iterator valid, pointing to the next element
@@ -183,111 +174,157 @@ bool irc::sockets::aptosa(const std::string& addr, int port, irc::sockets::socka
return false;
}
-int irc::sockets::sockaddrs::port() const
+int irc::sockets::sockaddrs::family() const
{
- if (sa.sa_family == AF_INET)
- return ntohs(in4.sin_port);
- if (sa.sa_family == AF_INET6)
- return ntohs(in6.sin6_port);
- return -1;
+ return sa.sa_family;
}
-std::string irc::sockets::sockaddrs::addr() const
+int irc::sockets::sockaddrs::port() const
{
- char addrv[INET6_ADDRSTRLEN+1];
- if (sa.sa_family == AF_INET)
- {
- if (!inet_ntop(AF_INET, &in4.sin_addr, addrv, sizeof(addrv)))
- return "";
- return addrv;
- }
- else if (sa.sa_family == AF_INET6)
+ switch (family())
{
- if (!inet_ntop(AF_INET6, &in6.sin6_addr, addrv, sizeof(addrv)))
- return "";
- return addrv;
+ case AF_INET:
+ return ntohs(in4.sin_port);
+
+ case AF_INET6:
+ return ntohs(in6.sin6_port);
+
+ case AF_UNIX:
+ return 0;
}
- return "";
+
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::port(): socket type %d is unknown!", family());
+ return 0;
}
-bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port)
+std::string irc::sockets::sockaddrs::addr() const
{
- port = sa.port();
- addr = sa.addr();
- return !addr.empty();
+ switch (family())
+ {
+ case AF_INET:
+ char ip4addr[INET_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, ip4addr, sizeof(ip4addr)))
+ return "0.0.0.0";
+ return ip4addr;
+
+ case AF_INET6:
+ char ip6addr[INET6_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ip6addr, sizeof(ip6addr)))
+ return "0:0:0:0:0:0:0:0";
+ return ip6addr;
+
+ case AF_UNIX:
+ return un.sun_path;
+ }
+
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::addr(): socket type %d is unknown!", family());
+ return "<unknown>";
}
std::string irc::sockets::sockaddrs::str() const
{
- char buffer[MAXBUF];
- if (sa.sa_family == AF_INET)
- {
- const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
- sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
- }
- else if (sa.sa_family == AF_INET6)
+ switch (family())
{
- buffer[0] = '[';
- if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
- return "<unknown>"; // should never happen, buffer is large enough
- int len = strlen(buffer);
- // no need for snprintf, buffer has at least 9 chars left, max short len = 5
- sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
+ case AF_INET:
+ char ip4addr[INET_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, ip4addr, sizeof(ip4addr)))
+ strcpy(ip4addr, "0.0.0.0");
+ return InspIRCd::Format("%s:%u", ip4addr, ntohs(in4.sin_port));
+
+ case AF_INET6:
+ char ip6addr[INET6_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ip6addr, sizeof(ip6addr)))
+ strcpy(ip6addr, "0:0:0:0:0:0:0:0");
+ return InspIRCd::Format("[%s]:%u", ip6addr, ntohs(in6.sin6_port));
+
+ case AF_UNIX:
+ return un.sun_path;
}
- else
- return "<unknown>";
- return std::string(buffer);
+
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::str(): socket type %d is unknown!", family());
+ return "<unknown>";
}
-int irc::sockets::sockaddrs::sa_size() const
+socklen_t irc::sockets::sockaddrs::sa_size() const
{
- if (sa.sa_family == AF_INET)
- return sizeof(in4);
- if (sa.sa_family == AF_INET6)
- return sizeof(in6);
+ switch (family())
+ {
+ case AF_INET:
+ return sizeof(in4);
+
+ case AF_INET6:
+ return sizeof(in6);
+
+ case AF_UNIX:
+ return sizeof(un);
+ }
+
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::sa_size(): socket type %d is unknown!", family());
return 0;
}
bool irc::sockets::sockaddrs::operator==(const irc::sockets::sockaddrs& other) const
{
- if (sa.sa_family != other.sa.sa_family)
+ if (family() != other.family())
return false;
- if (sa.sa_family == AF_INET)
- return (in4.sin_port == other.in4.sin_port) && (in4.sin_addr.s_addr == other.in4.sin_addr.s_addr);
- if (sa.sa_family == AF_INET6)
- return (in6.sin6_port == other.in6.sin6_port) && !memcmp(in6.sin6_addr.s6_addr, other.in6.sin6_addr.s6_addr, 16);
+
+ switch (family())
+ {
+ case AF_INET:
+ return (in4.sin_port == other.in4.sin_port) && (in4.sin_addr.s_addr == other.in4.sin_addr.s_addr);
+
+ case AF_INET6:
+ return (in6.sin6_port == other.in6.sin6_port) && !memcmp(in6.sin6_addr.s6_addr, other.in6.sin6_addr.s6_addr, 16);
+
+ case AF_UNIX:
+ return !strcmp(un.sun_path, other.un.sun_path);
+ }
+
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::operator==(): socket type %d is unknown!", family());
return !memcmp(this, &other, sizeof(*this));
}
-static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, int range)
+static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, unsigned char range)
{
const unsigned char* base;
unsigned char target_byte;
- cidr.type = sa.sa.sa_family;
memset(cidr.bits, 0, sizeof(cidr.bits));
- if (cidr.type == AF_INET)
+ cidr.type = sa.family();
+ switch (cidr.type)
{
- target_byte = sizeof(sa.in4.sin_addr);
- base = (unsigned char*)&sa.in4.sin_addr;
- if (range > 32)
- range = 32;
- }
- else if (cidr.type == AF_INET6)
- {
- target_byte = sizeof(sa.in6.sin6_addr);
- base = (unsigned char*)&sa.in6.sin6_addr;
- if (range > 128)
- range = 128;
- }
- else
- {
- cidr.length = 0;
- return;
+ case AF_UNIX:
+ // XXX: UNIX sockets don't support CIDR. This fix is non-ideal but I can't
+ // really think of another way to handle it.
+ cidr.length = 0;
+ return;
+
+ case AF_INET:
+ cidr.length = range > 32 ? 32 : range;
+ target_byte = sizeof(sa.in4.sin_addr);
+ base = (unsigned char*)&sa.in4.sin_addr;
+ break;
+
+ case AF_INET6:
+ cidr.length = range > 128 ? 128 : range;
+ target_byte = sizeof(sa.in6.sin6_addr);
+ base = (unsigned char*)&sa.in6.sin6_addr;
+ break;
+
+ default:
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: sa2cidr(): socket type %d is unknown!", cidr.type);
+ cidr.length = 0;
+ return;
}
- cidr.length = range;
- unsigned int border = range / 8;
+
+ unsigned int border = cidr.length / 8;
unsigned int bitmask = (0xFF00 >> (range & 7)) & 0xFF;
for(unsigned int i=0; i < target_byte; i++)
{
@@ -300,7 +337,7 @@ static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs
}
}
-irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, int range)
+irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, unsigned char range)
{
sa2cidr(*this, sa, range);
}
@@ -327,20 +364,30 @@ std::string irc::sockets::cidr_mask::str() const
{
irc::sockets::sockaddrs sa;
sa.sa.sa_family = type;
+
unsigned char* base;
- int len;
- if (type == AF_INET)
- {
- base = (unsigned char*)&sa.in4.sin_addr;
- len = 4;
- }
- else if (type == AF_INET6)
+ size_t len;
+ switch (type)
{
- base = (unsigned char*)&sa.in6.sin6_addr;
- len = 16;
+ case AF_INET:
+ base = (unsigned char*)&sa.in4.sin_addr;
+ len = 4;
+ break;
+
+ case AF_INET6:
+ base = (unsigned char*)&sa.in6.sin6_addr;
+ len = 16;
+ break;
+
+ case AF_UNIX:
+ return sa.un.sun_path;
+
+ default:
+ // If we have reached this point then we have encountered a bug.
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::cidr_mask::str(): socket type %d is unknown!", type);
+ return "<unknown>";
}
- else
- return "";
+
memcpy(base, bits, len);
return sa.addr() + "/" + ConvToStr((int)length);
}
@@ -362,9 +409,8 @@ bool irc::sockets::cidr_mask::operator<(const cidr_mask& other) const
bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
{
- if (addr.sa.sa_family != type)
+ if (addr.family() != type)
return false;
irc::sockets::cidr_mask tmp(addr, length);
return tmp == *this;
}
-
diff --git a/src/socketengine.cpp b/src/socketengine.cpp
index 4a9a2ef10..df6ff5a02 100644
--- a/src/socketengine.cpp
+++ b/src/socketengine.cpp
@@ -21,8 +21,29 @@
*/
+#include "exitcodes.h"
#include "inspircd.h"
+#include <iostream>
+
+/** Reference table, contains all current handlers
+ **/
+std::vector<EventHandler*> SocketEngine::ref;
+
+/** Current number of descriptors in the engine
+ */
+size_t SocketEngine::CurrentSetSize = 0;
+
+/** List of handlers that want a trial read/write
+ */
+std::set<int> SocketEngine::trials;
+
+size_t SocketEngine::MaxSetSize = 0;
+
+/** Socket engine statistics: count of various events, bandwidth usage
+ */
+SocketEngine::Statistics SocketEngine::stats;
+
EventHandler::EventHandler()
{
fd = -1;
@@ -34,20 +55,37 @@ void EventHandler::SetFd(int FD)
this->fd = FD;
}
-SocketEngine::SocketEngine()
+void EventHandler::OnEventHandlerWrite()
{
- TotalEvents = WriteEvents = ReadEvents = ErrorEvents = 0;
- lastempty = ServerInstance->Time();
- indata = outdata = 0;
}
-SocketEngine::~SocketEngine()
+void EventHandler::OnEventHandlerError(int errornum)
{
}
-void SocketEngine::SetEventMask(EventHandler* eh, int mask)
+void SocketEngine::InitError()
{
- eh->event_mask = mask;
+ std::cerr << con_red << "FATAL ERROR!" << con_reset << " Socket engine initialization failed. " << strerror(errno) << '.' << std::endl;
+ exit(EXIT_STATUS_SOCKETENGINE);
+}
+
+void SocketEngine::LookupMaxFds()
+{
+#if defined _WIN32
+ MaxSetSize = FD_SETSIZE;
+#else
+ struct rlimit limits;
+ if (!getrlimit(RLIMIT_NOFILE, &limits))
+ MaxSetSize = limits.rlim_cur;
+
+#if defined __APPLE__
+ limits.rlim_cur = limits.rlim_max == RLIM_INFINITY ? OPEN_MAX : limits.rlim_max;
+#else
+ limits.rlim_cur = limits.rlim_max;
+#endif
+ if (!setrlimit(RLIMIT_NOFILE, &limits))
+ MaxSetSize = limits.rlim_cur;
+#endif
}
void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
@@ -60,7 +98,7 @@ void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
new_m &= ~FD_WANT_READ_MASK;
if (change & FD_WANT_WRITE_MASK)
new_m &= ~FD_WANT_WRITE_MASK;
-
+
// if adding a trial read/write, insert it into the set
if (change & FD_TRIAL_NOTE_MASK && !(old_m & FD_TRIAL_NOTE_MASK))
trials.insert(eh->GetFd());
@@ -88,23 +126,44 @@ void SocketEngine::DispatchTrialWrites()
int mask = eh->event_mask;
eh->event_mask &= ~(FD_ADD_TRIAL_READ | FD_ADD_TRIAL_WRITE);
if ((mask & (FD_ADD_TRIAL_READ | FD_READ_WILL_BLOCK)) == FD_ADD_TRIAL_READ)
- eh->HandleEvent(EVENT_READ, 0);
+ eh->OnEventHandlerRead();
if ((mask & (FD_ADD_TRIAL_WRITE | FD_WRITE_WILL_BLOCK)) == FD_ADD_TRIAL_WRITE)
- eh->HandleEvent(EVENT_WRITE, 0);
+ eh->OnEventHandlerWrite();
}
}
-bool SocketEngine::HasFd(int fd)
+bool SocketEngine::AddFdRef(EventHandler* eh)
{
- if ((fd < 0) || (fd > GetMaxFds()))
+ int fd = eh->GetFd();
+ if (HasFd(fd))
return false;
- return (ref[fd] != NULL);
+
+ while (static_cast<unsigned int>(fd) >= ref.size())
+ ref.resize(ref.empty() ? 1 : (ref.size() * 2));
+ ref[fd] = eh;
+ CurrentSetSize++;
+ return true;
+}
+
+void SocketEngine::DelFdRef(EventHandler *eh)
+{
+ int fd = eh->GetFd();
+ if (GetRef(fd) == eh)
+ {
+ ref[fd] = NULL;
+ CurrentSetSize--;
+ }
+}
+
+bool SocketEngine::HasFd(int fd)
+{
+ return GetRef(fd) != NULL;
}
EventHandler* SocketEngine::GetRef(int fd)
{
- if ((fd < 0) || (fd > GetMaxFds()))
- return 0;
+ if (fd < 0 || static_cast<unsigned int>(fd) >= ref.size())
+ return NULL;
return ref[fd];
}
@@ -112,7 +171,7 @@ bool SocketEngine::BoundsCheckFd(EventHandler* eh)
{
if (!eh)
return false;
- if ((eh->GetFd() < 0) || (eh->GetFd() > GetMaxFds()))
+ if (eh->GetFd() < 0)
return false;
return true;
}
@@ -123,13 +182,12 @@ int SocketEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen)
return accept(fd->GetFd(), addr, addrlen);
}
-int SocketEngine::Close(EventHandler* fd)
+int SocketEngine::Close(EventHandler* eh)
{
-#ifdef _WIN32
- return closesocket(fd->GetFd());
-#else
- return close(fd->GetFd());
-#endif
+ DelFd(eh);
+ int ret = Close(eh->GetFd());
+ eh->SetFd(-1);
+ return ret;
}
int SocketEngine::Close(int fd)
@@ -172,38 +230,60 @@ void SocketEngine::SetReuse(int fd)
int SocketEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen)
{
int nbRecvd = recvfrom(fd->GetFd(), (char*)buf, len, flags, from, fromlen);
- if (nbRecvd > 0)
- this->UpdateStats(nbRecvd, 0);
+ stats.UpdateReadCounters(nbRecvd);
return nbRecvd;
}
int SocketEngine::Send(EventHandler* fd, const void *buf, size_t len, int flags)
{
int nbSent = send(fd->GetFd(), (const char*)buf, len, flags);
- if (nbSent > 0)
- this->UpdateStats(0, nbSent);
+ stats.UpdateWriteCounters(nbSent);
return nbSent;
}
int SocketEngine::Recv(EventHandler* fd, void *buf, size_t len, int flags)
{
int nbRecvd = recv(fd->GetFd(), (char*)buf, len, flags);
- if (nbRecvd > 0)
- this->UpdateStats(nbRecvd, 0);
+ stats.UpdateReadCounters(nbRecvd);
return nbRecvd;
}
-int SocketEngine::SendTo(EventHandler* fd, const void *buf, size_t len, int flags, const sockaddr *to, socklen_t tolen)
+int SocketEngine::SendTo(EventHandler* fd, const void* buf, size_t len, int flags, const irc::sockets::sockaddrs& address)
{
- int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, to, tolen);
- if (nbSent > 0)
- this->UpdateStats(0, nbSent);
+ int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, &address.sa, address.sa_size());
+ stats.UpdateWriteCounters(nbSent);
return nbSent;
}
-int SocketEngine::Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen)
+int SocketEngine::WriteV(EventHandler* fd, const IOVector* iovec, int count)
+{
+ int sent = writev(fd->GetFd(), iovec, count);
+ stats.UpdateWriteCounters(sent);
+ return sent;
+}
+
+#ifdef _WIN32
+int SocketEngine::WriteV(EventHandler* fd, const iovec* iovec, int count)
+{
+ // On Windows the fields in iovec are not in the order required by the Winsock API; IOVector has
+ // the fields in the correct order.
+ // Create temporary IOVectors from the iovecs and pass them to the WriteV() method that accepts the
+ // platform's native struct.
+ IOVector wiovec[128];
+ count = std::min(count, static_cast<int>(sizeof(wiovec) / sizeof(IOVector)));
+
+ for (int i = 0; i < count; i++)
+ {
+ wiovec[i].iov_len = iovec[i].iov_len;
+ wiovec[i].iov_base = reinterpret_cast<char*>(iovec[i].iov_base);
+ }
+ return WriteV(fd, wiovec, count);
+}
+#endif
+
+int SocketEngine::Connect(EventHandler* fd, const irc::sockets::sockaddrs& address)
{
- int ret = connect(fd->GetFd(), serv_addr, addrlen);
+ int ret = connect(fd->GetFd(), &address.sa, address.sa_size());
#ifdef _WIN32
if ((ret == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
errno = EINPROGRESS;
@@ -231,24 +311,42 @@ int SocketEngine::Shutdown(int fd, int how)
return shutdown(fd, how);
}
-void SocketEngine::RecoverFromFork()
+void SocketEngine::Statistics::UpdateReadCounters(int len_in)
+{
+ CheckFlush();
+
+ ReadEvents++;
+ if (len_in > 0)
+ indata += len_in;
+ else if (len_in < 0)
+ ErrorEvents++;
+}
+
+void SocketEngine::Statistics::UpdateWriteCounters(int len_out)
{
+ CheckFlush();
+
+ WriteEvents++;
+ if (len_out > 0)
+ outdata += len_out;
+ else if (len_out < 0)
+ ErrorEvents++;
}
-void SocketEngine::UpdateStats(size_t len_in, size_t len_out)
+void SocketEngine::Statistics::CheckFlush() const
{
- if (lastempty != ServerInstance->Time())
+ // Reset the in/out byte counters if it has been more than a second
+ time_t now = ServerInstance->Time();
+ if (lastempty != now)
{
- lastempty = ServerInstance->Time();
+ lastempty = now;
indata = outdata = 0;
}
- indata += len_in;
- outdata += len_out;
}
-void SocketEngine::GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total)
+void SocketEngine::Statistics::GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const
{
- UpdateStats(0, 0); /* Forces emptying of the values if its been more than a second */
+ CheckFlush();
float in_kbit = indata * 8;
float out_kbit = outdata * 8;
kbitpersec_total = ((in_kbit + out_kbit) / 1024);
diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp
index d5f017347..60b365ee1 100644
--- a/src/socketengines/socketengine_epoll.cpp
+++ b/src/socketengines/socketengine_epoll.cpp
@@ -18,79 +18,40 @@
*/
-#include <vector>
-#include <string>
-#include <map>
#include "inspircd.h"
-#include "exitcodes.h"
-#include "socketengine.h"
+
#include <sys/epoll.h>
#include <sys/resource.h>
-#include <iostream>
-#define EP_DELAY 5
/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
*/
-class EPollEngine : public SocketEngine
+namespace
{
-private:
- /** These are used by epoll() to hold socket events
- */
- struct epoll_event* events;
int EngineHandle;
-public:
- /** Create a new EPollEngine
- */
- EPollEngine();
- /** Delete an EPollEngine
+
+ /** These are used by epoll() to hold socket events
*/
- virtual ~EPollEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
+ std::vector<struct epoll_event> events(1);
+}
-EPollEngine::EPollEngine()
+void SocketEngine::Init()
{
- CurrentSetSize = 0;
-
- struct rlimit limit;
- if (!getrlimit(RLIMIT_NOFILE, &limit))
- {
- MAX_DESCRIPTORS = limit.rlim_cur;
- }
- else
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
-
- // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
- EngineHandle = epoll_create(GetMaxFds() / 4);
+ LookupMaxFds();
+ // 128 is not a maximum, just a hint at the eventual number of sockets that may be polled,
+ // and it is completely ignored by 2.6.8 and later kernels, except it must be larger than zero.
+ EngineHandle = epoll_create(128);
if (EngineHandle == -1)
- {
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
- std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
- std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
-
- ref = new EventHandler* [GetMaxFds()];
- events = new struct epoll_event[GetMaxFds()];
+ InitError();
+}
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::RecoverFromFork()
+{
}
-EPollEngine::~EPollEngine()
+void SocketEngine::Deinit()
{
- this->Close(EngineHandle);
- delete[] ref;
- delete[] events;
+ Close(EngineHandle);
}
static unsigned mask_to_epoll(int event_mask)
@@ -116,41 +77,41 @@ static unsigned mask_to_epoll(int event_mask)
return rv;
}
-bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
return false;
}
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
struct epoll_event ev;
- memset(&ev,0,sizeof(ev));
+ memset(&ev, 0, sizeof(ev));
ev.events = mask_to_epoll(event_mask);
- ev.data.fd = fd;
+ ev.data.ptr = static_cast<void*>(eh);
int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
if (i < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
return false;
}
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
+
+ eh->SetEventMask(event_mask);
+ ResizeDouble(events);
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
- CurrentSetSize++;
return true;
}
-void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
unsigned old_events = mask_to_epoll(old_mask);
unsigned new_events = mask_to_epoll(new_mask);
@@ -158,75 +119,78 @@ void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
// ok, we actually have something to tell the kernel about
struct epoll_event ev;
- memset(&ev,0,sizeof(ev));
+ memset(&ev, 0, sizeof(ev));
ev.events = new_events;
- ev.data.fd = eh->GetFd();
+ ev.data.ptr = static_cast<void*>(eh);
epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
}
}
-void EPollEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
return;
}
+ // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL.
+ // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event,
+ // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.
struct epoll_event ev;
- memset(&ev,0,sizeof(ev));
- ev.data.fd = fd;
int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
if (i < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
}
- ref[fd] = NULL;
+ SocketEngine::DelFdRef(eh);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
- CurrentSetSize--;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-int EPollEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
- socklen_t codesize = sizeof(int);
- int errcode;
- int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
+ int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
ServerInstance->UpdateTime();
- TotalEvents += i;
+ stats.TotalEvents += i;
for (int j = 0; j < i; j++)
{
- EventHandler* eh = ref[events[j].data.fd];
- if (!eh)
- {
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
- epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
+ // Copy these in case the vector gets resized and ev invalidated
+ const epoll_event ev = events[j];
+
+ EventHandler* const eh = static_cast<EventHandler*>(ev.data.ptr);
+ const int fd = eh->GetFd();
+ if (fd < 0)
continue;
- }
- if (events[j].events & EPOLLHUP)
+
+ if (ev.events & EPOLLHUP)
{
- ErrorEvents++;
- eh->HandleEvent(EVENT_ERROR, 0);
+ stats.ErrorEvents++;
+ eh->OnEventHandlerError(0);
continue;
}
- if (events[j].events & EPOLLERR)
+
+ if (ev.events & EPOLLERR)
{
- ErrorEvents++;
+ stats.ErrorEvents++;
/* Get error number */
- if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+ socklen_t codesize = sizeof(int);
+ int errcode;
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
errcode = errno;
- eh->HandleEvent(EVENT_ERROR, errcode);
+ eh->OnEventHandlerError(errcode);
continue;
}
+
int mask = eh->GetEventMask();
- if (events[j].events & EPOLLIN)
+ if (ev.events & EPOLLIN)
mask &= ~FD_READ_WILL_BLOCK;
- if (events[j].events & EPOLLOUT)
+ if (ev.events & EPOLLOUT)
{
mask &= ~FD_WRITE_WILL_BLOCK;
if (mask & FD_WANT_SINGLE_WRITE)
@@ -236,31 +200,19 @@ int EPollEngine::DispatchEvents()
mask = nm;
}
}
- SetEventMask(eh, mask);
- if (events[j].events & EPOLLIN)
+ eh->SetEventMask(mask);
+ if (ev.events & EPOLLIN)
{
- ReadEvents++;
- eh->HandleEvent(EVENT_READ);
- if (eh != ref[events[j].data.fd])
+ eh->OnEventHandlerRead();
+ if (eh != GetRef(fd))
// whoa! we got deleted, better not give out the write event
continue;
}
- if (events[j].events & EPOLLOUT)
+ if (ev.events & EPOLLOUT)
{
- WriteEvents++;
- eh->HandleEvent(EVENT_WRITE);
+ eh->OnEventHandlerWrite();
}
}
return i;
}
-
-std::string EPollEngine::GetName()
-{
- return "epoll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new EPollEngine;
-}
diff --git a/src/socketengines/socketengine_kqueue.cpp b/src/socketengines/socketengine_kqueue.cpp
index 8694a0bdd..b23cfbd9d 100644
--- a/src/socketengines/socketengine_kqueue.cpp
+++ b/src/socketengines/socketengine_kqueue.cpp
@@ -20,70 +20,36 @@
#include "inspircd.h"
-#include "exitcodes.h"
+
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
-#include "socketengine.h"
-#include <iostream>
+#include <sys/sysctl.h>
/** A specialisation of the SocketEngine class, designed to use BSD kqueue().
*/
-class KQueueEngine : public SocketEngine
+namespace
{
-private:
int EngineHandle;
+ unsigned int ChangePos = 0;
/** These are used by kqueue() to hold socket events
*/
- struct kevent* ke_list;
- /** This is a specialised time value used by kqueue()
- */
- struct timespec ts;
-public:
- /** Create a new KQueueEngine
- */
- KQueueEngine();
- /** Delete a KQueueEngine
- */
- virtual ~KQueueEngine();
- bool AddFd(EventHandler* eh, int event_mask);
- void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
- virtual void RecoverFromFork();
-};
+ std::vector<struct kevent> ke_list(16);
-#include <sys/sysctl.h>
+ /** Pending changes
+ */
+ std::vector<struct kevent> changelist(8);
+}
-KQueueEngine::KQueueEngine()
+/** Initialize the kqueue engine
+ */
+void SocketEngine::Init()
{
- MAX_DESCRIPTORS = 0;
- int mib[2];
- size_t len;
-
- mib[0] = CTL_KERN;
-#ifdef KERN_MAXFILESPERPROC
- mib[1] = KERN_MAXFILESPERPROC;
-#else
- mib[1] = KERN_MAXFILES;
-#endif
- len = sizeof(MAX_DESCRIPTORS);
- sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
- if (MAX_DESCRIPTORS <= 0)
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
-
- this->RecoverFromFork();
- ke_list = new struct kevent[GetMaxFds()];
- ref = new EventHandler* [GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+ LookupMaxFds();
+ RecoverFromFork();
}
-void KQueueEngine::RecoverFromFork()
+void SocketEngine::RecoverFromFork()
{
/*
* The only bad thing about kqueue is that its fd cant survive a fork and is not inherited.
@@ -92,177 +58,141 @@ void KQueueEngine::RecoverFromFork()
*/
EngineHandle = kqueue();
if (EngineHandle == -1)
- {
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
- std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl;
- std::cout << "ERROR: this is a fatal error, exiting now." << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
- CurrentSetSize = 0;
+ InitError();
}
-KQueueEngine::~KQueueEngine()
+/** Shutdown the kqueue engine
+ */
+void SocketEngine::Deinit()
{
- this->Close(EngineHandle);
- delete[] ref;
- delete[] ke_list;
+ Close(EngineHandle);
}
-bool KQueueEngine::AddFd(EventHandler* eh, int event_mask)
+static struct kevent* GetChangeKE()
+{
+ if (ChangePos >= changelist.size())
+ changelist.resize(changelist.size() * 2);
+ return &changelist[ChangePos++];
+}
+
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
return false;
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
return false;
// We always want to read from the socket...
- struct kevent ke;
- EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<void*>(eh));
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i == -1)
- {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to add fd: %d %s",
- fd, strerror(errno));
- return false;
- }
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
+ eh->SetEventMask(event_mask);
OnSetEvent(eh, 0, event_mask);
- CurrentSetSize++;
+ ResizeDouble(ke_list);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
return true;
}
-void KQueueEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"DelFd() on invalid fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd);
return;
}
- struct kevent ke;
-
// First remove the write filter ignoring errors, since we can't be
// sure if there are actually any write filters registered.
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
- kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
// Then remove the read filter.
- EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
- int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
-
- if (j < 0)
- {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
- fd, strerror(errno));
- }
+ ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
- CurrentSetSize--;
- ref[fd] = NULL;
+ SocketEngine::DelFdRef(eh);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
if ((new_mask & FD_WANT_POLL_WRITE) && !(old_mask & FD_WANT_POLL_WRITE))
{
// new poll-style write
- struct kevent ke;
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
- eh->GetFd(), strerror(errno));
- }
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, static_cast<void*>(eh));
}
else if ((old_mask & FD_WANT_POLL_WRITE) && !(new_mask & FD_WANT_POLL_WRITE))
{
// removing poll-style write
- struct kevent ke;
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
- eh->GetFd(), strerror(errno));
- }
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
}
if ((new_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !(old_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)))
{
- // new one-shot write
- struct kevent ke;
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
- eh->GetFd(), strerror(errno));
- }
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast<void*>(eh));
}
}
-int KQueueEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
+ struct timespec ts;
ts.tv_nsec = 0;
ts.tv_sec = 1;
- int i = kevent(EngineHandle, NULL, 0, &ke_list[0], GetMaxFds(), &ts);
+ int i = kevent(EngineHandle, &changelist.front(), ChangePos, &ke_list.front(), ke_list.size(), &ts);
+ ChangePos = 0;
ServerInstance->UpdateTime();
- TotalEvents += i;
+ if (i < 0)
+ return i;
+
+ stats.TotalEvents += i;
for (int j = 0; j < i; j++)
{
- EventHandler* eh = ref[ke_list[j].ident];
+ struct kevent& kev = ke_list[j];
+ EventHandler* eh = static_cast<EventHandler*>(kev.udata);
if (!eh)
continue;
- if (ke_list[j].flags & EV_EOF)
+
+ // Copy these in case the vector gets resized and kev invalidated
+ const int fd = eh->GetFd();
+ const short filter = kev.filter;
+ if (fd < 0)
+ continue;
+
+ if (kev.flags & EV_EOF)
{
- ErrorEvents++;
- eh->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
+ stats.ErrorEvents++;
+ eh->OnEventHandlerError(kev.fflags);
continue;
}
- if (ke_list[j].filter == EVFILT_WRITE)
+ if (filter == EVFILT_WRITE)
{
- WriteEvents++;
/* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE,
* we set a one-shot write, so we need to clear that bit
* to detect when it set again.
*/
const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
- SetEventMask(eh, eh->GetEventMask() & ~bits_to_clr);
- eh->HandleEvent(EVENT_WRITE);
-
- if (eh != ref[ke_list[j].ident])
- // whoops, deleted out from under us
- continue;
+ eh->SetEventMask(eh->GetEventMask() & ~bits_to_clr);
+ eh->OnEventHandlerWrite();
}
- if (ke_list[j].filter == EVFILT_READ)
+ else if (filter == EVFILT_READ)
{
- ReadEvents++;
- SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
- eh->HandleEvent(EVENT_READ);
+ eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ eh->OnEventHandlerRead();
}
}
return i;
}
-
-std::string KQueueEngine::GetName()
-{
- return "kqueue";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new KQueueEngine;
-}
diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp
index e38e0fac1..339045a8c 100644
--- a/src/socketengines/socketengine_poll.cpp
+++ b/src/socketengines/socketengine_poll.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2014 Adam <Adam@anope.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
* Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc>
@@ -21,88 +22,33 @@
#include "inspircd.h"
-#include "exitcodes.h"
-#ifndef SOCKETENGINE_POLL
-#define SOCKETENGINE_POLL
-
-#include <iostream>
-#include <vector>
-#include <string>
-#include <map>
-#include "inspircd_config.h"
-#include "inspircd.h"
-#include "socketengine.h"
-
-#ifndef _WIN32
-# ifndef __USE_XOPEN
-# define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
-# endif
-# include <poll.h>
-# include <sys/poll.h>
-# include <sys/resource.h>
-#else
-# define struct pollfd WSAPOLLFD
-# define poll WSAPoll
-#endif
-
-class InspIRCd;
+#include <sys/poll.h>
+#include <sys/resource.h>
/** A specialisation of the SocketEngine class, designed to use poll().
*/
-class PollEngine : public SocketEngine
+namespace
{
-private:
/** These are used by poll() to hold socket events
*/
- struct pollfd *events;
- /** This map maps fds to an index in the events array.
- */
- std::map<int, unsigned int> fd_mappings;
-public:
- /** Create a new PollEngine
+ std::vector<struct pollfd> events(16);
+ /** This vector maps fds to an index in the events array.
*/
- PollEngine();
- /** Delete a PollEngine
- */
- virtual ~PollEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual EventHandler* GetRef(int fd);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
-
-#endif
-
-PollEngine::PollEngine()
-{
- CurrentSetSize = 0;
- struct rlimit limits;
- if (!getrlimit(RLIMIT_NOFILE, &limits))
- {
- MAX_DESCRIPTORS = limits.rlim_cur;
- }
- else
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
- std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
+ std::vector<int> fd_mappings(16, -1);
+}
- ref = new EventHandler* [GetMaxFds()];
- events = new struct pollfd[GetMaxFds()];
+void SocketEngine::Init()
+{
+ LookupMaxFds();
+}
- memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::Deinit()
+{
}
-PollEngine::~PollEngine()
+void SocketEngine::RecoverFromFork()
{
- // No destruction required, either.
- delete[] ref;
- delete[] events;
}
static int mask_to_poll(int event_mask)
@@ -115,71 +61,70 @@ static int mask_to_poll(int event_mask)
return rv;
}
-bool PollEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
return false;
}
- if (fd_mappings.find(fd) != fd_mappings.end())
+ if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
unsigned int index = CurrentSetSize;
+ if (!SocketEngine::AddFdRef(eh))
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
+ return false;
+ }
+
+ while (static_cast<unsigned int>(fd) >= fd_mappings.size())
+ fd_mappings.resize(fd_mappings.size() * 2, -1);
fd_mappings[fd] = index;
- ref[index] = eh;
+
+ ResizeDouble(events);
events[index].fd = fd;
events[index].events = mask_to_poll(event_mask);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
- SocketEngine::SetEventMask(eh, event_mask);
- CurrentSetSize++;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
+ eh->SetEventMask(event_mask);
return true;
}
-EventHandler* PollEngine::GetRef(int fd)
-{
- std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
- if (it == fd_mappings.end())
- return NULL;
- return ref[it->second];
-}
-
-void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
- std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
- if (it == fd_mappings.end())
+ int fd = eh->GetFd();
+ if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
return;
}
- events[it->second].events = mask_to_poll(new_mask);
+ events[fd_mappings[fd]].events = mask_to_poll(new_mask);
}
-void PollEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
return;
}
- std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
- if (it == fd_mappings.end())
+ if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
return;
}
- unsigned int index = it->second;
+ unsigned int index = fd_mappings[fd];
unsigned int last_index = CurrentSetSize - 1;
int last_fd = events[last_index].fd;
@@ -193,89 +138,78 @@ void PollEngine::DelFd(EventHandler* eh)
// move last_fd from last_index into index
events[index].fd = last_fd;
events[index].events = events[last_index].events;
-
- ref[index] = ref[last_index];
}
// Now remove all data for the last fd we got into out list.
// Above code made sure this always is right
- fd_mappings.erase(it);
+ fd_mappings[fd] = -1;
events[last_index].fd = 0;
events[last_index].events = 0;
- ref[last_index] = NULL;
- CurrentSetSize--;
+ SocketEngine::DelFdRef(eh);
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
"(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
}
-int PollEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
- int i = poll(events, CurrentSetSize, 1000);
- int index;
- socklen_t codesize = sizeof(int);
- int errcode;
+ int i = poll(&events[0], CurrentSetSize, 1000);
int processed = 0;
ServerInstance->UpdateTime();
- if (i > 0)
+ for (size_t index = 0; index < CurrentSetSize && processed < i; index++)
{
- for (index = 0; index < CurrentSetSize && processed != i; index++)
+ struct pollfd& pfd = events[index];
+
+ // Copy these in case the vector gets resized and pfd invalidated
+ const int fd = pfd.fd;
+ const short revents = pfd.revents;
+
+ if (revents)
+ processed++;
+
+ EventHandler* eh = GetRef(fd);
+ if (!eh)
+ continue;
+
+ if (revents & POLLHUP)
{
- if (events[index].revents)
- processed++;
- EventHandler* eh = ref[index];
- if (!eh)
- continue;
+ eh->OnEventHandlerError(0);
+ continue;
+ }
+
+ if (revents & POLLERR)
+ {
+ // Get error number
+ socklen_t codesize = sizeof(int);
+ int errcode;
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+ errcode = errno;
+ eh->OnEventHandlerError(errcode);
+ continue;
+ }
- if (events[index].revents & POLLHUP)
- {
- eh->HandleEvent(EVENT_ERROR, 0);
+ if (revents & POLLIN)
+ {
+ eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ eh->OnEventHandlerRead();
+ if (eh != GetRef(fd))
+ // whoops, deleted out from under us
continue;
- }
+ }
- if (events[index].revents & POLLERR)
- {
- // Get fd
- int fd = events[index].fd;
+ if (revents & POLLOUT)
+ {
+ int mask = eh->GetEventMask();
+ mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
+ eh->SetEventMask(mask);
- // Get error number
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
- errcode = errno;
- eh->HandleEvent(EVENT_ERROR, errcode);
- continue;
- }
-
- if (events[index].revents & POLLIN)
- {
- SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
- eh->HandleEvent(EVENT_READ);
- if (eh != ref[index])
- // whoops, deleted out from under us
- continue;
- }
-
- if (events[index].revents & POLLOUT)
- {
- int mask = eh->GetEventMask();
- mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
- SetEventMask(eh, mask);
- events[index].events = mask_to_poll(mask);
- eh->HandleEvent(EVENT_WRITE);
- }
+ // The vector could've been resized, reference can be invalid by now; don't use it
+ events[index].events = mask_to_poll(mask);
+ eh->OnEventHandlerWrite();
}
}
return i;
}
-
-std::string PollEngine::GetName()
-{
- return "poll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new PollEngine;
-}
diff --git a/src/socketengines/socketengine_ports.cpp b/src/socketengines/socketengine_ports.cpp
deleted file mode 100644
index f7c547d45..000000000
--- a/src/socketengines/socketengine_ports.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "exitcodes.h"
-#include <port.h>
-
-#ifndef SOCKETENGINE_PORTS
-#define SOCKETENGINE_PORTS
-
-#ifndef __sun
-# error You need Solaris 10 or later to make use of this code.
-#endif
-
-#include <vector>
-#include <string>
-#include <map>
-#include "inspircd_config.h"
-#include "inspircd.h"
-#include "socketengine.h"
-#include <port.h>
-#include <iostream>
-
-/** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports
- */
-class PortsEngine : public SocketEngine
-{
-private:
- /** These are used by epoll() to hold socket events
- */
- port_event_t* events;
- int EngineHandle;
-public:
- /** Create a new PortsEngine
- */
- PortsEngine();
- /** Delete a PortsEngine
- */
- virtual ~PortsEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
-
-#endif
-
-
-#include <ulimit.h>
-
-PortsEngine::PortsEngine()
-{
- int max = ulimit(4, 0);
- if (max > 0)
- {
- MAX_DESCRIPTORS = max;
- }
- else
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
- EngineHandle = port_create();
-
- if (EngineHandle == -1)
- {
- ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
- ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: This is a fatal error, exiting now.");
- std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl;
- std::cout << "ERROR: This is a fatal error, exiting now." << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
- CurrentSetSize = 0;
-
- ref = new EventHandler* [GetMaxFds()];
- events = new port_event_t[GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
-}
-
-PortsEngine::~PortsEngine()
-{
- this->Close(EngineHandle);
- delete[] ref;
- delete[] events;
-}
-
-static int mask_to_events(int event_mask)
-{
- int rv = 0;
- if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
- rv |= POLLRDNORM;
- if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
- rv |= POLLWRNORM;
- return rv;
-}
-
-bool PortsEngine::AddFd(EventHandler* eh, int event_mask)
-{
- int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
- return false;
-
- if (ref[fd])
- return false;
-
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
- port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh);
-
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
- CurrentSetSize++;
- return true;
-}
-
-void PortsEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
-{
- if (mask_to_events(new_mask) != mask_to_events(old_mask))
- port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), mask_to_events(new_mask), eh);
-}
-
-void PortsEngine::DelFd(EventHandler* eh)
-{
- int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
- return;
-
- port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
-
- CurrentSetSize--;
- ref[fd] = NULL;
-
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
-}
-
-int PortsEngine::DispatchEvents()
-{
- struct timespec poll_time;
-
- poll_time.tv_sec = 1;
- poll_time.tv_nsec = 0;
-
- unsigned int nget = 1; // used to denote a retrieve request.
- int ret = port_getn(EngineHandle, this->events, GetMaxFds() - 1, &nget, &poll_time);
- ServerInstance->UpdateTime();
-
- // first handle an error condition
- if (ret == -1)
- return -1;
-
- TotalEvents += nget;
-
- unsigned int i;
- for (i = 0; i < nget; i++)
- {
- switch (this->events[i].portev_source)
- {
- case PORT_SOURCE_FD:
- {
- int fd = this->events[i].portev_object;
- EventHandler* eh = ref[fd];
- if (eh)
- {
- int mask = eh->GetEventMask();
- if (events[i].portev_events & POLLWRNORM)
- mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE);
- if (events[i].portev_events & POLLRDNORM)
- mask &= ~FD_READ_WILL_BLOCK;
- // reinsert port for next time around, pretending to be one-shot for writes
- SetEventMask(eh, mask);
- port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
- if (events[i].portev_events & POLLRDNORM)
- {
- ReadEvents++;
- eh->HandleEvent(EVENT_READ);
- if (eh != ref[fd])
- continue;
- }
- if (events[i].portev_events & POLLWRNORM)
- {
- WriteEvents++;
- eh->HandleEvent(EVENT_WRITE);
- }
- }
- }
- default:
- break;
- }
- }
-
- return (int)i;
-}
-
-std::string PortsEngine::GetName()
-{
- return "ports";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new PortsEngine;
-}
diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp
index 0b5abaf30..03f0aca62 100644
--- a/src/socketengines/socketengine_select.cpp
+++ b/src/socketengines/socketengine_select.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2014 Adam <Adam@anope.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
*
@@ -18,10 +19,7 @@
*/
-#include "inspircd_config.h"
-
#include "inspircd.h"
-#include "socketengine.h"
#ifndef _WIN32
#include <sys/select.h>
@@ -29,76 +27,63 @@
/** A specialisation of the SocketEngine class, designed to use traditional select().
*/
-class SelectEngine : public SocketEngine
+namespace
{
fd_set ReadSet, WriteSet, ErrSet;
- int MaxFD;
-
-public:
- /** Create a new SelectEngine
- */
- SelectEngine();
- /** Delete a SelectEngine
- */
- virtual ~SelectEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void DelFd(EventHandler* eh);
- void OnSetEvent(EventHandler* eh, int, int);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
-
-SelectEngine::SelectEngine()
-{
- MAX_DESCRIPTORS = FD_SETSIZE;
- CurrentSetSize = 0;
+ int MaxFD = 0;
+}
- ref = new EventHandler* [GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::Init()
+{
+ MaxSetSize = FD_SETSIZE;
FD_ZERO(&ReadSet);
FD_ZERO(&WriteSet);
FD_ZERO(&ErrSet);
- MaxFD = 0;
}
-SelectEngine::~SelectEngine()
+void SocketEngine::Deinit()
{
- delete[] ref;
}
-bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
+void SocketEngine::RecoverFromFork()
+{
+}
+
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+
+ if (fd < 0)
return false;
- if (ref[fd])
+ if (static_cast<size_t>(fd) >= GetMaxFds())
return false;
- ref[fd] = eh;
+ if (!SocketEngine::AddFdRef(eh))
+ return false;
- SocketEngine::SetEventMask(eh, event_mask);
+ eh->SetEventMask(event_mask);
OnSetEvent(eh, 0, event_mask);
FD_SET(fd, &ErrSet);
if (fd > MaxFD)
MaxFD = fd;
- CurrentSetSize++;
-
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
return true;
}
-void SelectEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
+ return;
+
+ if (static_cast<size_t>(fd) >= GetMaxFds())
return;
- CurrentSetSize--;
- ref[fd] = NULL;
+ SocketEngine::DelFdRef(eh);
FD_CLR(fd, &ReadSet);
FD_CLR(fd, &WriteSet);
@@ -106,10 +91,10 @@ void SelectEngine::DelFd(EventHandler* eh)
if (fd == MaxFD)
--MaxFD;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
int fd = eh->GetFd();
int diff = old_mask ^ new_mask;
@@ -130,7 +115,7 @@ void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
}
}
-int SelectEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
timeval tval;
tval.tv_sec = 1;
@@ -141,63 +126,48 @@ int SelectEngine::DispatchEvents()
int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
ServerInstance->UpdateTime();
- /* Nothing to process this time around */
- if (sresult < 1)
- return 0;
-
for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
{
int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
- if (has_read || has_write || has_error)
- {
- --j;
+ if (!(has_read || has_write || has_error))
+ continue;
- EventHandler* ev = ref[i];
- if (!ev)
- continue;
+ --j;
- if (has_error)
- {
- ErrorEvents++;
+ EventHandler* ev = GetRef(i);
+ if (!ev)
+ continue;
- socklen_t codesize = sizeof(int);
- int errcode = 0;
- if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
- errcode = errno;
+ if (has_error)
+ {
+ stats.ErrorEvents++;
+
+ socklen_t codesize = sizeof(int);
+ int errcode = 0;
+ if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
+ errcode = errno;
+
+ ev->OnEventHandlerError(errcode);
+ continue;
+ }
- ev->HandleEvent(EVENT_ERROR, errcode);
+ if (has_read)
+ {
+ ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ ev->OnEventHandlerRead();
+ if (ev != GetRef(i))
continue;
- }
-
- if (has_read)
- {
- ReadEvents++;
- SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
- ev->HandleEvent(EVENT_READ);
- if (ev != ref[i])
- continue;
- }
- if (has_write)
- {
- WriteEvents++;
- int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
- this->OnSetEvent(ev, ev->GetEventMask(), newmask);
- SetEventMask(ev, newmask);
- ev->HandleEvent(EVENT_WRITE);
- }
+ }
+
+ if (has_write)
+ {
+ int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+ SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
+ ev->SetEventMask(newmask);
+ ev->OnEventHandlerWrite();
}
}
return sresult;
}
-
-std::string SelectEngine::GetName()
-{
- return "select";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new SelectEngine;
-}
diff --git a/src/testsuite.cpp b/src/testsuite.cpp
index 58b72ee3e..a7a9ec99b 100644
--- a/src/testsuite.cpp
+++ b/src/testsuite.cpp
@@ -19,11 +19,10 @@
*/
-/* $Core */
+#ifdef INSPIRCD_ENABLE_TESTSUITE
#include "inspircd.h"
#include "testsuite.h"
-#include "threadengine.h"
#include <iostream>
class TestSuiteThread : public Thread
@@ -76,8 +75,12 @@ TestSuite::TestSuite()
switch (choice)
{
case '1':
- FOREACH_MOD(I_OnRunTestSuite, OnRunTestSuite());
+ {
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+ i->second->OnRunTestSuite();
break;
+ }
case '2':
std::cout << "Enter module filename to load: ";
std::cin >> modname;
@@ -331,36 +334,25 @@ bool TestSuite::DoThreadTests()
bool TestSuite::DoGenerateUIDTests()
{
- bool success = RealGenerateUIDTests();
+ const unsigned int UUID_LENGTH = UIDGenerator::UUID_LENGTH;
+ UIDGenerator uidgen;
+ uidgen.init(ServerInstance->Config->GetSID());
+ std::string first_uid = uidgen.GetUID();
- // Reset the UID generation state so running the tests multiple times won't mess things up
- for (unsigned int i = 0; i < 3; i++)
- ServerInstance->current_uid[i] = ServerInstance->Config->sid[i];
- for (unsigned int i = 3; i < UUID_LENGTH-1; i++)
- ServerInstance->current_uid[i] = '9';
-
- ServerInstance->current_uid[UUID_LENGTH-1] = '\0';
-
- return success;
-}
-
-bool TestSuite::RealGenerateUIDTests()
-{
- std::string first_uid = ServerInstance->GetUID();
- if (first_uid.length() != UUID_LENGTH-1)
+ if (first_uid.length() != UUID_LENGTH)
{
std::cout << "GENERATEUID: Generated UID is " << first_uid.length() << " characters long instead of " << UUID_LENGTH-1 << std::endl;
return false;
}
- if (ServerInstance->current_uid[UUID_LENGTH-1] != '\0')
+ if (uidgen.current_uid.c_str()[UUID_LENGTH] != '\0')
{
std::cout << "GENERATEUID: The null terminator is missing from the end of current_uid" << std::endl;
return false;
}
// The correct UID when generating one for the first time is ...AAAAAA
- std::string correct_uid = ServerInstance->Config->sid + std::string(UUID_LENGTH - 4, 'A');
+ std::string correct_uid = ServerInstance->Config->sid + std::string(UUID_LENGTH - 3, 'A');
if (first_uid != correct_uid)
{
std::cout << "GENERATEUID: Generated an invalid first UID: " << first_uid << " instead of " << correct_uid << std::endl;
@@ -368,16 +360,16 @@ bool TestSuite::RealGenerateUIDTests()
}
// Set current_uid to be ...Z99999
- ServerInstance->current_uid[3] = 'Z';
- for (unsigned int i = 4; i < UUID_LENGTH-1; i++)
- ServerInstance->current_uid[i] = '9';
+ uidgen.current_uid[3] = 'Z';
+ for (unsigned int i = 4; i < UUID_LENGTH; i++)
+ uidgen.current_uid[i] = '9';
// Store the UID we'll be incrementing so we can display what's wrong later if necessary
- std::string before_increment(ServerInstance->current_uid);
- std::string generated_uid = ServerInstance->GetUID();
+ std::string before_increment(uidgen.current_uid);
+ std::string generated_uid = uidgen.GetUID();
// Correct UID after incrementing ...Z99999 is ...0AAAAA
- correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 5, 'A');
+ correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 4, 'A');
if (generated_uid != correct_uid)
{
@@ -386,11 +378,11 @@ bool TestSuite::RealGenerateUIDTests()
}
// Set current_uid to be ...999999 to see if it rolls over correctly
- for (unsigned int i = 3; i < UUID_LENGTH-1; i++)
- ServerInstance->current_uid[i] = '9';
+ for (unsigned int i = 3; i < UUID_LENGTH; i++)
+ uidgen.current_uid[i] = '9';
- before_increment.assign(ServerInstance->current_uid);
- generated_uid = ServerInstance->GetUID();
+ before_increment.assign(uidgen.current_uid);
+ generated_uid = uidgen.GetUID();
// Correct UID after rolling over is the first UID we've generated (...AAAAAA)
if (generated_uid != first_uid)
@@ -407,3 +399,4 @@ TestSuite::~TestSuite()
std::cout << "\n\n*** END OF TEST SUITE ***\n";
}
+#endif
diff --git a/src/threadengine.cpp b/src/threadengine.cpp
index 8f1895c0f..f757aa56c 100644
--- a/src/threadengine.cpp
+++ b/src/threadengine.cpp
@@ -17,14 +17,7 @@
*/
-/* $Core */
-
-/********* DEFAULTS **********/
-/* $ExtraSources: threadengines/threadengine_pthread.cpp */
-/* $ExtraObjects: threadengine_pthread.o */
-
#include "inspircd.h"
-#include "threadengine.h"
void Thread::SetExitFlag()
{
@@ -33,14 +26,5 @@ void Thread::SetExitFlag()
void Thread::join()
{
- state->FreeThread(this);
- delete state;
- state = 0;
-}
-
-/** If this thread has a Creator set, call it to
- * free the thread
- */
-Thread::~Thread()
-{
+ ServerInstance->Threads.Stop(this);
}
diff --git a/src/threadengines/threadengine_pthread.cpp b/src/threadengines/threadengine_pthread.cpp
index 40205da31..3249f442b 100644
--- a/src/threadengines/threadengine_pthread.cpp
+++ b/src/threadengines/threadengine_pthread.cpp
@@ -21,13 +21,8 @@
#include "inspircd.h"
#include "threadengines/threadengine_pthread.h"
#include <pthread.h>
-#include <signal.h>
#include <fcntl.h>
-ThreadEngine::ThreadEngine()
-{
-}
-
static void* entry_point(void* parameter)
{
/* Recommended by nenolod, signal safety on a per-thread basis */
@@ -44,25 +39,14 @@ static void* entry_point(void* parameter)
void ThreadEngine::Start(Thread* thread)
{
- ThreadData* data = new ThreadData;
- thread->state = data;
-
- if (pthread_create(&data->pthread_id, NULL, entry_point, thread) != 0)
- {
- thread->state = NULL;
- delete data;
+ if (pthread_create(&thread->state.pthread_id, NULL, entry_point, thread) != 0)
throw CoreException("Unable to create new thread: " + std::string(strerror(errno)));
- }
}
-ThreadEngine::~ThreadEngine()
-{
-}
-
-void ThreadData::FreeThread(Thread* thread)
+void ThreadEngine::Stop(Thread* thread)
{
thread->SetExitFlag();
- pthread_join(pthread_id, NULL);
+ pthread_join(thread->state.pthread_id, NULL);
}
#ifdef HAS_EVENTFD
@@ -75,13 +59,12 @@ class ThreadSignalSocket : public EventHandler
ThreadSignalSocket(SocketThread* p, int newfd) : parent(p)
{
SetFd(newfd);
- ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
+ SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
}
~ThreadSignalSocket()
{
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(GetFd());
+ SocketEngine::Close(this);
}
void Notify()
@@ -89,18 +72,21 @@ class ThreadSignalSocket : public EventHandler
eventfd_write(fd, 1);
}
- void HandleEvent(EventType et, int errornum)
+ void OnEventHandlerRead() CXX11_OVERRIDE
{
- if (et == EVENT_READ)
- {
- eventfd_t dummy;
- eventfd_read(fd, &dummy);
- parent->OnNotify();
- }
- else
- {
- ServerInstance->GlobalCulls.AddItem(this);
- }
+ eventfd_t dummy;
+ eventfd_read(fd, &dummy);
+ parent->OnNotify();
+ }
+
+ void OnEventHandlerWrite() CXX11_OVERRIDE
+ {
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE
+ {
+ ThreadSignalSocket::OnEventHandlerWrite();
}
};
@@ -123,15 +109,14 @@ class ThreadSignalSocket : public EventHandler
parent(p), send_fd(sendfd)
{
SetFd(recvfd);
- ServerInstance->SE->NonBlocking(fd);
- ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
+ SocketEngine::NonBlocking(fd);
+ SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
}
~ThreadSignalSocket()
{
close(send_fd);
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(GetFd());
+ SocketEngine::Close(this);
}
void Notify()
@@ -140,18 +125,21 @@ class ThreadSignalSocket : public EventHandler
write(send_fd, &dummy, 1);
}
- void HandleEvent(EventType et, int errornum)
+ void OnEventHandlerRead() CXX11_OVERRIDE
+ {
+ char dummy[128];
+ read(fd, dummy, 128);
+ parent->OnNotify();
+ }
+
+ void OnEventHandlerWrite() CXX11_OVERRIDE
+ {
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE
{
- if (et == EVENT_READ)
- {
- char dummy[128];
- read(fd, dummy, 128);
- parent->OnNotify();
- }
- else
- {
- ServerInstance->GlobalCulls.AddItem(this);
- }
+ ThreadSignalSocket::OnEventHandlerWrite();
}
};
diff --git a/src/threadengines/threadengine_win32.cpp b/src/threadengines/threadengine_win32.cpp
index ea37892f8..0f0d1f277 100644
--- a/src/threadengines/threadengine_win32.cpp
+++ b/src/threadengines/threadengine_win32.cpp
@@ -21,33 +21,19 @@
#include "inspircd.h"
#include "threadengines/threadengine_win32.h"
-ThreadEngine::ThreadEngine()
-{
-}
-
void ThreadEngine::Start(Thread* thread)
{
- ThreadData* data = new ThreadData;
- thread->state = data;
-
- DWORD ThreadId = 0;
- data->handle = CreateThread(NULL,0,ThreadEngine::Entry,thread,0,&ThreadId);
+ thread->state.handle = CreateThread(NULL, 0, ThreadEngine::Entry, thread, 0, NULL);
- if (data->handle == NULL)
+ if (thread->state.handle == NULL)
{
DWORD lasterr = GetLastError();
- thread->state = NULL;
- delete data;
std::string err = "Unable to create new thread: " + ConvToStr(lasterr);
SetLastError(ERROR_SUCCESS);
throw CoreException(err);
}
}
-ThreadEngine::~ThreadEngine()
-{
-}
-
DWORD WINAPI ThreadEngine::Entry(void* parameter)
{
Thread* pt = static_cast<Thread*>(parameter);
@@ -55,9 +41,10 @@ DWORD WINAPI ThreadEngine::Entry(void* parameter)
return 0;
}
-void ThreadData::FreeThread(Thread* thread)
+void ThreadEngine::Stop(Thread* thread)
{
thread->SetExitFlag();
+ HANDLE handle = thread->state.handle;
WaitForSingleObject(handle,INFINITE);
CloseHandle(handle);
}
@@ -83,6 +70,24 @@ class ThreadSignalSocket : public BufferedSocket
}
};
+static bool BindAndListen(int sockfd, int port, const char* addr)
+{
+ irc::sockets::sockaddrs servaddr;
+ if (!irc::sockets::aptosa(addr, port, servaddr))
+ return false;
+
+ if (SocketEngine::Bind(sockfd, servaddr) != 0)
+ return false;
+
+ if (SocketEngine::Listen(sockfd, ServerInstance->Config->MaxConn) != 0)
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR in listen(): %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
SocketThread::SocketThread()
{
int listenFD = socket(AF_INET, SOCK_STREAM, 0);
@@ -92,22 +97,22 @@ SocketThread::SocketThread()
if (connFD == -1)
throw CoreException("Could not create ITC pipe");
- if (!ServerInstance->BindSocket(listenFD, 0, "127.0.0.1", true))
+ if (!BindAndListen(listenFD, 0, "127.0.0.1"))
throw CoreException("Could not create ITC pipe");
- ServerInstance->SE->NonBlocking(connFD);
+ SocketEngine::NonBlocking(connFD);
struct sockaddr_in addr;
socklen_t sz = sizeof(addr);
getsockname(listenFD, reinterpret_cast<struct sockaddr*>(&addr), &sz);
connect(connFD, reinterpret_cast<struct sockaddr*>(&addr), sz);
- ServerInstance->SE->Blocking(listenFD);
+ SocketEngine::Blocking(listenFD);
int nfd = accept(listenFD, reinterpret_cast<struct sockaddr*>(&addr), &sz);
if (nfd < 0)
throw CoreException("Could not create ITC pipe");
new ThreadSignalSocket(this, nfd);
closesocket(listenFD);
- ServerInstance->SE->Blocking(connFD);
+ SocketEngine::Blocking(connFD);
this->signal.connFD = connFD;
}
diff --git a/src/timer.cpp b/src/timer.cpp
index e04a186cf..48ad79df7 100644
--- a/src/timer.cpp
+++ b/src/timer.cpp
@@ -20,60 +20,64 @@
*/
-/* $Core */
-
#include "inspircd.h"
-#include "timer.h"
-TimerManager::TimerManager()
+void Timer::SetInterval(unsigned int newinterval)
{
+ ServerInstance->Timers.DelTimer(this);
+ secs = newinterval;
+ SetTrigger(ServerInstance->Time() + newinterval);
+ ServerInstance->Timers.AddTimer(this);
}
-TimerManager::~TimerManager()
+Timer::Timer(unsigned int secs_from_now, bool repeating)
+ : trigger(ServerInstance->Time() + secs_from_now)
+ , secs(secs_from_now)
+ , repeat(repeating)
{
- for(std::vector<Timer *>::iterator i = Timers.begin(); i != Timers.end(); i++)
- delete *i;
+}
+
+Timer::~Timer()
+{
+ ServerInstance->Timers.DelTimer(this);
}
void TimerManager::TickTimers(time_t TIME)
{
- while ((Timers.size()) && (TIME > (*Timers.begin())->GetTimer()))
+ for (TimerMap::iterator i = Timers.begin(); i != Timers.end(); )
{
- std::vector<Timer *>::iterator i = Timers.begin();
- Timer *t = (*i);
+ Timer* t = i->second;
+ if (t->GetTrigger() > TIME)
+ break;
+
+ Timers.erase(i++);
- // Probable fix: move vector manipulation to *before* we modify the vector.
- Timers.erase(i);
+ if (!t->Tick(TIME))
+ continue;
- t->Tick(TIME);
if (t->GetRepeat())
{
- t->SetTimer(TIME + t->GetSecs());
+ t->SetTrigger(TIME + t->GetInterval());
AddTimer(t);
}
- else
- delete t;
}
}
-void TimerManager::DelTimer(Timer* T)
+void TimerManager::DelTimer(Timer* t)
{
- std::vector<Timer *>::iterator i = std::find(Timers.begin(), Timers.end(), T);
+ std::pair<TimerMap::iterator, TimerMap::iterator> itpair = Timers.equal_range(t->GetTrigger());
- if (i != Timers.end())
+ for (TimerMap::iterator i = itpair.first; i != itpair.second; ++i)
{
- delete (*i);
- Timers.erase(i);
+ if (i->second == t)
+ {
+ Timers.erase(i);
+ break;
+ }
}
}
-void TimerManager::AddTimer(Timer* T)
-{
- Timers.push_back(T);
- std::sort(Timers.begin(), Timers.end(), TimerManager::TimerComparison);
-}
-
-bool TimerManager::TimerComparison( Timer *one, Timer *two)
+void TimerManager::AddTimer(Timer* t)
{
- return (one->GetTimer()) < (two->GetTimer());
+ Timers.insert(std::make_pair(t->GetTrigger(), t));
}
diff --git a/src/user_resolver.cpp b/src/user_resolver.cpp
deleted file mode 100644
index f18fc9a03..000000000
--- a/src/user_resolver.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-UserResolver::UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache) :
- Resolver(to_resolve, qt, cache, NULL), uuid(user->uuid)
-{
- this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);
-}
-
-void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
-{
- UserResolver *res_forward; // for forward-resolution
- LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
- if (!bound_user)
- {
- ServerInstance->Logs->Log("RESOLVER", DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
- return;
- }
-
- ServerInstance->Logs->Log("RESOLVER", DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), input.c_str(), result.c_str());
-
- if (!fwd)
- {
- // first half of resolution is done. We now need to verify that the host matches.
- bound_user->stored_host = result;
- try
- {
- /* Check we didnt time out */
- if (bound_user->registered != REG_ALL)
- {
- bool lcached = false;
- if (bound_user->client_sa.sa.sa_family == AF_INET6)
- {
- /* IPV6 forward lookup */
- res_forward = new UserResolver(bound_user, result, DNS_QUERY_AAAA, lcached);
- }
- else
- {
- /* IPV4 lookup */
- res_forward = new UserResolver(bound_user, result, DNS_QUERY_A, lcached);
- }
- ServerInstance->AddResolver(res_forward, lcached);
- }
- }
- catch (CoreException& e)
- {
- ServerInstance->Logs->Log("RESOLVER", DEBUG,"Error in resolver: %s",e.GetReason());
- }
- }
- else
- {
- /* Both lookups completed */
-
- irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
- bool rev_match = false;
- if (user_ip->sa.sa_family == AF_INET6)
- {
- struct in6_addr res_bin;
- if (inet_pton(AF_INET6, result.c_str(), &res_bin))
- {
- rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
- }
- }
- else
- {
- struct in_addr res_bin;
- if (inet_pton(AF_INET, result.c_str(), &res_bin))
- {
- rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
- }
- }
-
- if (rev_match)
- {
- std::string hostname = bound_user->stored_host;
- if (hostname.length() < 65)
- {
- /* Check we didnt time out */
- if ((bound_user->registered != REG_ALL) && (!bound_user->dns_done))
- {
- /* Hostnames starting with : are not a good thing (tm) */
- if (hostname[0] == ':')
- hostname.insert(0, "0");
-
- bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));
- bound_user->dns_done = true;
- bound_user->dhost.assign(hostname, 0, 64);
- bound_user->host.assign(hostname, 0, 64);
- /* Invalidate cache */
- bound_user->InvalidateCache();
- }
- }
- else
- {
- if (!bound_user->dns_done)
- {
- bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", bound_user->GetIPString());
- bound_user->dns_done = true;
- }
- }
- }
- else
- {
- if (!bound_user->dns_done)
- {
- bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", bound_user->GetIPString());
- bound_user->dns_done = true;
- }
- }
-
- // Save some memory by freeing this up; it's never used again in the user's lifetime.
- bound_user->stored_host.resize(0);
- }
-}
-
-void UserResolver::OnError(ResolverError e, const std::string &errormessage)
-{
- LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
- if (bound_user)
- {
- bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), bound_user->GetIPString());
- bound_user->dns_done = true;
- bound_user->stored_host.resize(0);
- ServerInstance->stats->statsDnsBad++;
- }
-}
diff --git a/src/usermanager.cpp b/src/usermanager.cpp
index 2cb7ad511..7466f385b 100644
--- a/src/usermanager.cpp
+++ b/src/usermanager.cpp
@@ -22,83 +22,95 @@
#include "inspircd.h"
#include "xline.h"
-#include "bancache.h"
+#include "iohook.h"
+
+namespace
+{
+ class WriteCommonQuit : public User::ForEachNeighborHandler
+ {
+ ClientProtocol::Messages::Quit quitmsg;
+ ClientProtocol::Event quitevent;
+ ClientProtocol::Messages::Quit operquitmsg;
+ ClientProtocol::Event operquitevent;
+
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ user->Send(user->IsOper() ? operquitevent : quitevent);
+ }
+
+ public:
+ WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
+ : quitmsg(user, msg)
+ , quitevent(ServerInstance->GetRFCEvents().quit, quitmsg)
+ , operquitmsg(user, opermsg)
+ , operquitevent(ServerInstance->GetRFCEvents().quit, operquitmsg)
+ {
+ user->ForEachNeighbor(*this, false);
+ }
+ };
+}
UserManager::UserManager()
- : unregistered_count(0), local_count(0)
+ : already_sent_id(0)
+ , unregistered_count(0)
{
}
-/* add a client connection to the sockets list */
-void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+UserManager::~UserManager()
{
- /* NOTE: Calling this one parameter constructor for User automatically
- * allocates a new UUID and places it in the hash_map.
- */
- LocalUser* New = NULL;
- try
+ for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
{
- New = new LocalUser(socket, client, server);
+ delete i->second;
}
- catch (...)
+}
+
+void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+ // User constructor allocates a new UUID for the user and inserts it into the uuidlist
+ LocalUser* const New = new LocalUser(socket, client, server);
+ UserIOHandler* eh = &New->eh;
+
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
+
+ this->unregistered_count++;
+ this->clientlist[New->nick] = New;
+ this->AddClone(New);
+ this->local_users.push_front(New);
+
+ if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
{
- ServerInstance->Logs->Log("USERS", DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
- ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
+ this->QuitUser(New, "Internal error handling connection");
return;
}
- UserIOHandler* eh = &New->eh;
- /* Give each of the modules an attempt to hook the user for I/O */
- FOREACH_MOD(I_OnHookIO, OnHookIO(eh, via));
-
- if (eh->GetIOHook())
+ // If this listener has an IO hook provider set then tell it about the connection
+ for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
{
- try
- {
- eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
- }
- catch (CoreException& modexcept)
+ ListenSocket::IOHookProvRef& iohookprovref = *i;
+ if (!iohookprovref)
+ continue;
+
+ iohookprovref->OnAccept(eh, client, server);
+ // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version
+ if (!eh->getError().empty())
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ QuitUser(New, eh->getError());
+ return;
}
}
- ServerInstance->Logs->Log("USERS", DEBUG,"New user fd: %d", socket);
-
- this->unregistered_count++;
-
- /* The users default nick is their UUID */
- New->nick = New->uuid;
- (*(this->clientlist))[New->nick] = New;
-
- New->registered = REG_NONE;
- New->signon = ServerInstance->Time();
- New->lastping = 1;
-
- ServerInstance->Users->AddLocalClone(New);
- ServerInstance->Users->AddGlobalClone(New);
-
- New->localuseriter = this->local_users.insert(local_users.end(), New);
- local_count++;
-
- if ((this->local_users.size() > ServerInstance->Config->SoftLimit) || (this->local_users.size() >= (unsigned int)ServerInstance->SE->GetMaxFds()))
+ if (this->local_users.size() > ServerInstance->Config->SoftLimit)
{
ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
this->QuitUser(New,"No more connections allowed");
return;
}
- /*
- * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
- * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
- */
+ // First class check. We do this again in LocalUser::FullConnect() after DNS is done, and NICK/USER is received.
New->SetClass();
-
- /*
- * Check connect class settings and initialise settings into User.
- * This will be done again after DNS resolution. -- w00t
- */
- New->CheckClass();
+ // If the user doesn't have an acceptable connect class CheckClass() quits them
+ New->CheckClass(ServerInstance->Config->CCOnConnect);
if (New->quitting)
return;
@@ -109,20 +121,21 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
*/
New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
- if (BanCacheHit *b = ServerInstance->BanCache->GetHit(New->GetIPString()))
+ BanCacheHit* const b = ServerInstance->BanCache.GetHit(New->GetIPString());
+ if (b)
{
if (!b->Type.empty() && !New->exempt)
{
/* user banned */
- ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Positive hit for ") + New->GetIPString());
- if (!ServerInstance->Config->MoronBanner.empty())
- New->WriteServ("NOTICE %s :*** %s", New->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
+ if (!ServerInstance->Config->XLineMessage.empty())
+ New->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
this->QuitUser(New, b->Reason);
return;
}
else
{
- ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Negative hit for ") + New->GetIPString());
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
}
}
else
@@ -139,277 +152,226 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
}
}
- if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
- {
- ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
- this->QuitUser(New, "Internal error handling connection");
- return;
- }
-
- /* NOTE: even if dns lookups are *off*, we still need to display this.
- * BOPM and other stuff requires it.
- */
- New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
if (ServerInstance->Config->RawLog)
- New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
+ New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
- FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
+ FOREACH_MOD(OnSetUserIP, (New));
if (New->quitting)
return;
- FOREACH_MOD(I_OnUserInit,OnUserInit(New));
-
- if (ServerInstance->Config->NoUserDns)
- {
- New->WriteServ("NOTICE %s :*** Skipping host resolution (disabled by server administrator)", New->nick.c_str());
- New->dns_done = true;
- }
- else
- {
- New->StartDNSLookup();
- }
+ FOREACH_MOD(OnUserInit, (New));
}
-void UserManager::QuitUser(User *user, const std::string &quitreason, const char* operreason)
+void UserManager::QuitUser(User* user, const std::string& quitreason, const std::string* operreason)
{
if (user->quitting)
{
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
return;
}
if (IS_SERVER(user))
{
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
return;
}
user->quitting = true;
- ServerInstance->Logs->Log("USERS", DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
- user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), *operreason ? operreason : quitreason.c_str());
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (localuser)
+ {
+ ClientProtocol::Messages::Error errormsg(InspIRCd::Format("Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operreason ? operreason->c_str() : quitreason.c_str()));
+ localuser->Send(ServerInstance->GetRFCEvents().error, errormsg);
+ }
std::string reason;
- std::string oper_reason;
reason.assign(quitreason, 0, ServerInstance->Config->Limits.MaxQuit);
- if (operreason && *operreason)
- oper_reason.assign(operreason, 0, ServerInstance->Config->Limits.MaxQuit);
- else
- oper_reason = quitreason;
+ if (!operreason)
+ operreason = &reason;
ServerInstance->GlobalCulls.AddItem(user);
if (user->registered == REG_ALL)
{
- FOREACH_MOD(I_OnUserQuit,OnUserQuit(user, reason, oper_reason));
- user->WriteCommonQuit(reason, oper_reason);
+ FOREACH_MOD(OnUserQuit, (user, reason, *operreason));
+ WriteCommonQuit(user, reason, *operreason);
}
-
- if (user->registered != REG_ALL)
- if (ServerInstance->Users->unregistered_count)
- ServerInstance->Users->unregistered_count--;
+ else
+ unregistered_count--;
if (IS_LOCAL(user))
{
LocalUser* lu = IS_LOCAL(user);
- FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(lu));
+ FOREACH_MOD(OnUserDisconnect, (lu));
lu->eh.Close();
- }
- /*
- * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
- * if they were an oper with +s +qQ.
- */
- if (user->registered == REG_ALL)
- {
- if (IS_LOCAL(user))
- {
- if (!user->quietquit)
- {
- ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]",
- user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
- }
- }
- else
- {
- if ((!ServerInstance->SilentULine(user->server)) && (!user->quietquit))
- {
- ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s (%s) [%s]",
- user->server.c_str(), user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
- }
- }
- user->AddToWhoWas();
+ if (lu->registered == REG_ALL)
+ ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]", user->GetFullRealHost().c_str(), user->GetIPString().c_str(), operreason->c_str());
+ local_users.erase(lu);
}
- user_hash::iterator iter = this->clientlist->find(user->nick);
-
- if (iter != this->clientlist->end())
- this->clientlist->erase(iter);
- else
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
-
- ServerInstance->Users->uuidlist->erase(user->uuid);
-}
-
+ if (!clientlist.erase(user->nick))
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
-void UserManager::AddLocalClone(User *user)
-{
- local_clones[user->GetCIDRMask()]++;
+ uuidlist.erase(user->uuid);
+ user->PurgeEmptyChannels();
}
-void UserManager::AddGlobalClone(User *user)
+void UserManager::AddClone(User* user)
{
- global_clones[user->GetCIDRMask()]++;
+ CloneCounts& counts = clonemap[user->GetCIDRMask()];
+ counts.global++;
+ if (IS_LOCAL(user))
+ counts.local++;
}
void UserManager::RemoveCloneCounts(User *user)
{
- if (IS_LOCAL(user))
+ CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
+ if (it != clonemap.end())
{
- clonemap::iterator x = local_clones.find(user->GetCIDRMask());
- if (x != local_clones.end())
+ CloneCounts& counts = it->second;
+ counts.global--;
+ if (counts.global == 0)
{
- x->second--;
- if (!x->second)
- {
- local_clones.erase(x);
- }
+ // No more users from this IP, remove entry from the map
+ clonemap.erase(it);
+ return;
}
- }
- clonemap::iterator y = global_clones.find(user->GetCIDRMask());
- if (y != global_clones.end())
- {
- y->second--;
- if (!y->second)
- {
- global_clones.erase(y);
- }
+ if (IS_LOCAL(user))
+ counts.local--;
}
}
void UserManager::RehashCloneCounts()
{
- local_clones.clear();
- global_clones.clear();
+ clonemap.clear();
- const user_hash& hash = *ServerInstance->Users->clientlist;
+ const user_hash& hash = ServerInstance->Users.GetUsers();
for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
{
User* u = i->second;
-
- if (IS_LOCAL(u))
- AddLocalClone(u);
- AddGlobalClone(u);
+ AddClone(u);
}
}
-unsigned long UserManager::GlobalCloneCount(User *user)
+const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
{
- clonemap::iterator x = global_clones.find(user->GetCIDRMask());
- if (x != global_clones.end())
- return x->second;
+ CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
+ if (it != clonemap.end())
+ return it->second;
else
- return 0;
-}
-
-unsigned long UserManager::LocalCloneCount(User *user)
-{
- clonemap::iterator x = local_clones.find(user->GetCIDRMask());
- if (x != local_clones.end())
- return x->second;
- else
- return 0;
-}
-
-/* this function counts all users connected, wether they are registered or NOT. */
-unsigned int UserManager::UserCount()
-{
- /*
- * XXX: Todo:
- * As part of this restructuring, move clientlist/etc fields into usermanager.
- * -- w00t
- */
- return this->clientlist->size();
-}
-
-/* this counts only registered users, so that the percentages in /MAP don't mess up */
-unsigned int UserManager::RegisteredUserCount()
-{
- return this->clientlist->size() - this->UnregisteredUserCount();
+ return zeroclonecounts;
}
-/* return how many users are opered */
-unsigned int UserManager::OperCount()
+void UserManager::ServerNoticeAll(const char* text, ...)
{
- return this->all_opers.size();
-}
+ std::string message;
+ VAFORMAT(message, text, text);
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, ServerInstance->Config->ServerName, message, MSG_NOTICE);
+ ClientProtocol::Event msgevent(ServerInstance->GetRFCEvents().privmsg, msg);
-/* return how many users are unregistered */
-unsigned int UserManager::UnregisteredUserCount()
-{
- return this->unregistered_count;
+ for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
+ {
+ LocalUser* user = *i;
+ user->Send(msgevent);
+ }
}
-/* return how many local registered users there are */
-unsigned int UserManager::LocalUserCount()
+/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
+ * (until this returns true, a user will block in the waiting state, waiting to connect up to the
+ * registration timeout maximum seconds)
+ */
+bool UserManager::AllModulesReportReady(LocalUser* user)
{
- /* Doesnt count unregistered clients */
- return (this->local_count - this->UnregisteredUserCount());
+ ModResult res;
+ FIRST_MOD_RESULT(OnCheckReady, res, (user));
+ return (res == MOD_RES_PASSTHRU);
}
-void UserManager::ServerNoticeAll(const char* text, ...)
+/**
+ * This function is called once a second from the mainloop.
+ * It is intended to do background checking on all the users, e.g. do
+ * ping checks, registration timeouts, etc.
+ */
+void UserManager::DoBackgroundUserStuff()
{
- if (!text)
- return;
-
- char textbuffer[MAXBUF];
- char formatbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
-
- for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ for (LocalList::iterator i = local_users.begin(); i != local_users.end(); )
{
- User* t = *i;
- t->WriteServ(std::string(formatbuffer));
- }
-}
-
-void UserManager::ServerPrivmsgAll(const char* text, ...)
-{
- if (!text)
- return;
+ // It's possible that we quit the user below due to ping timeout etc. and QuitUser() removes it from the list
+ LocalUser* curr = *i;
+ ++i;
- char textbuffer[MAXBUF];
- char formatbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
+ if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
+ {
+ unsigned int rate = curr->MyClass->GetCommandRate();
+ if (curr->CommandFloodPenalty > rate)
+ curr->CommandFloodPenalty -= rate;
+ else
+ curr->CommandFloodPenalty = 0;
+ curr->eh.OnDataReady();
+ }
- snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
+ switch (curr->registered)
+ {
+ case REG_ALL:
+ if (ServerInstance->Time() >= curr->nping)
+ {
+ // This user didn't answer the last ping, remove them
+ if (!curr->lastping)
+ {
+ time_t time = ServerInstance->Time() - (curr->nping - curr->MyClass->GetPingTime());
+ const std::string message = "Ping timeout: " + ConvToStr(time) + (time != 1 ? " seconds" : " second");
+ this->QuitUser(curr, message);
+ continue;
+ }
+ ClientProtocol::Messages::Ping ping;
+ curr->Send(ServerInstance->GetRFCEvents().ping, ping);
+ curr->lastping = 0;
+ curr->nping = ServerInstance->Time() + curr->MyClass->GetPingTime();
+ }
+ break;
+ case REG_NICKUSER:
+ if (AllModulesReportReady(curr))
+ {
+ /* User has sent NICK/USER, modules are okay, DNS finished. */
+ curr->FullConnect();
+ continue;
+ }
+
+ // If the user has been quit in OnCheckReady then we shouldn't
+ // quit them again for having a registration timeout.
+ if (curr->quitting)
+ continue;
+ break;
+ }
- for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
- {
- User* t = *i;
- t->WriteServ(std::string(formatbuffer));
+ if (curr->registered != REG_ALL && curr->MyClass && (ServerInstance->Time() > (curr->signon + curr->MyClass->GetRegTimeout())))
+ {
+ /*
+ * registration timeout -- didnt send USER/NICK/HOST
+ * in the time specified in their connection class.
+ */
+ this->QuitUser(curr, "Registration timeout");
+ continue;
+ }
}
}
-
-/* return how many users have a given mode e.g. 'a' */
-int UserManager::ModeCount(const char mode)
+already_sent_t UserManager::NextAlreadySentId()
{
- int c = 0;
- for(user_hash::iterator i = clientlist->begin(); i != clientlist->end(); ++i)
+ if (++already_sent_id == 0)
{
- User* u = i->second;
- if (u->modes[mode-65])
- c++;
+ // Wrapped around, reset the already_sent ids of all users
+ already_sent_id = 1;
+ for (LocalList::iterator i = local_users.begin(); i != local_users.end(); ++i)
+ {
+ LocalUser* user = *i;
+ user->already_sent = 0;
+ }
}
- return c;
+ return already_sent_id;
}
diff --git a/src/userprocess.cpp b/src/userprocess.cpp
deleted file mode 100644
index 69c31f840..000000000
--- a/src/userprocess.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig McLure <craig@chatspike.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $Core */
-
-#include "inspircd.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "command_parse.h"
-
-void FloodQuitUserHandler::Call(User* current)
-{
- ServerInstance->Logs->Log("USERS",DEFAULT,"Excess flood from: %s@%s", current->ident.c_str(), current->host.c_str());
- ServerInstance->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s",
- current->registered == REG_ALL ? current->nick.c_str() : "",
- current->registered == REG_ALL ? "!" : "", current->ident.c_str(), current->host.c_str());
- ServerInstance->Users->QuitUser(current, "Excess flood");
-
- if (current->registered != REG_ALL)
- {
- ZLine* zl = new ZLine(ServerInstance->Time(), 0, ServerInstance->Config->ServerName, "Flood from unregistered connection", current->GetIPString());
- if (ServerInstance->XLines->AddLine(zl,NULL))
- ServerInstance->XLines->ApplyLines();
- else
- delete zl;
- }
-}
-
-/**
- * This function is called once a second from the mainloop.
- * It is intended to do background checking on all the user structs, e.g.
- * stuff like ping checks, registration timeouts, etc.
- */
-void InspIRCd::DoBackgroundUserStuff()
-{
- /*
- * loop over all local users..
- */
- LocalUserList::reverse_iterator count2 = this->Users->local_users.rbegin();
- while (count2 != this->Users->local_users.rend())
- {
- LocalUser *curr = *count2;
- count2++;
-
- if (curr->quitting)
- continue;
-
- if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
- {
- unsigned int rate = curr->MyClass->GetCommandRate();
- if (curr->CommandFloodPenalty > rate)
- curr->CommandFloodPenalty -= rate;
- else
- curr->CommandFloodPenalty = 0;
- curr->eh.OnDataReady();
- }
-
- switch (curr->registered)
- {
- case REG_ALL:
- if (Time() > curr->nping)
- {
- // This user didn't answer the last ping, remove them
- if (!curr->lastping)
- {
- time_t time = this->Time() - (curr->nping - curr->MyClass->GetPingTime());
- char message[MAXBUF];
- snprintf(message, MAXBUF, "Ping timeout: %ld second%s", (long)time, time > 1 ? "s" : "");
- curr->lastping = 1;
- curr->nping = Time() + curr->MyClass->GetPingTime();
- this->Users->QuitUser(curr, message);
- continue;
- }
-
- curr->Write("PING :%s",this->Config->ServerName.c_str());
- curr->lastping = 0;
- curr->nping = Time() +curr->MyClass->GetPingTime();
- }
- break;
- case REG_NICKUSER:
- if (AllModulesReportReady(curr) && curr->dns_done)
- {
- /* User has sent NICK/USER, modules are okay, DNS finished. */
- curr->FullConnect();
- continue;
- }
-
- // If the user has been quit in OnCheckReady then we shouldn't
- // quit them again for having a registration timeout.
- if (curr->quitting)
- continue;
- break;
- }
-
- if (curr->registered != REG_ALL && curr->MyClass && (Time() > (curr->signon + curr->MyClass->GetRegTimeout())))
- {
- /*
- * registration timeout -- didnt send USER/NICK/HOST
- * in the time specified in their connection class.
- */
- this->Users->QuitUser(curr, "Registration timeout");
- continue;
- }
- }
-}
-
diff --git a/src/users.cpp b/src/users.cpp
index ac87f1187..8f20b7523 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -24,111 +24,9 @@
#include "inspircd.h"
-#include <stdarg.h>
-#include "socketengine.h"
#include "xline.h"
-#include "bancache.h"
-#include "commands/cmd_whowas.h"
-already_sent_t LocalUser::already_sent_id = 0;
-
-std::string User::ProcessNoticeMasks(const char *sm)
-{
- bool adding = true, oldadding = false;
- const char *c = sm;
- std::string output;
-
- while (c && *c)
- {
- switch (*c)
- {
- case '+':
- adding = true;
- break;
- case '-':
- adding = false;
- break;
- case '*':
- for (unsigned char d = 'a'; d <= 'z'; d++)
- {
- if (!ServerInstance->SNO->masks[d - 'a'].Description.empty())
- {
- if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
- {
- if ((oldadding != adding) || (!output.length()))
- output += (adding ? '+' : '-');
-
- this->SetNoticeMask(d, adding);
-
- output += d;
- }
- oldadding = adding;
- char u = toupper(d);
- if ((!IsNoticeMaskSet(u) && adding) || (IsNoticeMaskSet(u) && !adding))
- {
- if ((oldadding != adding) || (!output.length()))
- output += (adding ? '+' : '-');
-
- this->SetNoticeMask(u, adding);
-
- output += u;
- }
- oldadding = adding;
- }
- }
- break;
- default:
- if (isalpha(*c))
- {
- if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
- {
- if ((oldadding != adding) || (!output.length()))
- output += (adding ? '+' : '-');
-
- this->SetNoticeMask(*c, adding);
-
- output += *c;
- oldadding = adding;
- }
- }
- else
- this->WriteNumeric(ERR_UNKNOWNSNOMASK, "%s %c :is unknown snomask char to me", this->nick.c_str(), *c);
-
- break;
- }
-
- c++;
- }
-
- std::string s = this->FormatNoticeMasks();
- if (s.length() == 0)
- {
- this->modes[UM_SNOMASK] = false;
- }
-
- return output;
-}
-
-void LocalUser::StartDNSLookup()
-{
- try
- {
- bool cached = false;
- const char* sip = this->GetIPString();
- UserResolver *res_reverse;
-
- QueryType resolvtype = this->client_sa.sa.sa_family == AF_INET6 ? DNS_QUERY_PTR6 : DNS_QUERY_PTR4;
- res_reverse = new UserResolver(this, sip, resolvtype, cached);
-
- ServerInstance->AddResolver(res_reverse, cached);
- }
- catch (CoreException& e)
- {
- ServerInstance->Logs->Log("USERS", DEBUG,"Error in resolver: %s",e.GetReason());
- dns_done = true;
- ServerInstance->stats->statsDnsBad++;
- }
-}
+ClientProtocol::MessageList LocalUser::sendmsglist;
bool User::IsNoticeMaskSet(unsigned char sm)
{
@@ -137,104 +35,85 @@ bool User::IsNoticeMaskSet(unsigned char sm)
return (snomasks[sm-65]);
}
-void User::SetNoticeMask(unsigned char sm, bool value)
+bool User::IsModeSet(unsigned char m) const
{
- if (!isalpha(sm))
- return;
- snomasks[sm-65] = value;
+ ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
+ return (mh && modes[mh->GetId()]);
}
-const char* User::FormatNoticeMasks()
+std::string User::GetModeLetters(bool includeparams) const
{
- static char data[MAXBUF];
- int offset = 0;
-
- for (int n = 0; n < 64; n++)
- {
- if (snomasks[n])
- data[offset++] = n+65;
- }
-
- data[offset] = 0;
- return data;
-}
-
-bool User::IsModeSet(unsigned char m)
-{
- if (!isalpha(m))
- return false;
- return (modes[m-65]);
-}
-
-void User::SetMode(unsigned char m, bool value)
-{
- if (!isalpha(m))
- return;
- modes[m-65] = value;
-}
-
-const char* User::FormatModes(bool showparameters)
-{
- static char data[MAXBUF];
+ std::string ret(1, '+');
std::string params;
- int offset = 0;
- for (unsigned char n = 0; n < 64; n++)
+ for (unsigned char i = 'A'; i < 'z'; i++)
{
- if (modes[n])
+ const ModeHandler* const mh = ServerInstance->Modes.FindMode(i, MODETYPE_USER);
+ if ((!mh) || (!IsModeSet(mh)))
+ continue;
+
+ ret.push_back(mh->GetModeChar());
+ if ((includeparams) && (mh->NeedsParam(true)))
{
- data[offset++] = n + 65;
- ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
- if (showparameters && mh && mh->GetNumParams(true))
- {
- std::string p = mh->GetUserParameter(this);
- if (p.length())
- params.append(" ").append(p);
- }
+ const std::string val = mh->GetUserParameter(this);
+ if (!val.empty())
+ params.append(1, ' ').append(val);
}
}
- data[offset] = 0;
- strlcat(data, params.c_str(), MAXBUF);
- return data;
+
+ ret += params;
+ return ret;
}
-User::User(const std::string &uid, const std::string& sid, int type)
- : uuid(uid), server(sid), usertype(type)
+User::User(const std::string& uid, Server* srv, UserType type)
+ : age(ServerInstance->Time())
+ , signon(0)
+ , uuid(uid)
+ , server(srv)
+ , registered(REG_NONE)
+ , quitting(false)
+ , usertype(type)
{
- age = ServerInstance->Time();
- signon = idle_lastmsg = 0;
- registered = 0;
- quietquit = quitting = exempt = dns_done = false;
- quitting_sendq = false;
client_sa.sa.sa_family = AF_UNSPEC;
- ServerInstance->Logs->Log("USERS", DEBUG, "New UUID for user: %s", uuid.c_str());
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
- user_hash::iterator finduuid = ServerInstance->Users->uuidlist->find(uuid);
- if (finduuid == ServerInstance->Users->uuidlist->end())
- (*ServerInstance->Users->uuidlist)[uuid] = this;
- else
- throw CoreException("Duplicate UUID "+std::string(uuid)+" in User constructor");
+ // Do not insert FakeUsers into the uuidlist so FindUUID() won't return them which is the desired behavior
+ if (type != USERTYPE_SERVER)
+ {
+ if (!ServerInstance->Users.uuidlist.insert(std::make_pair(uuid, this)).second)
+ throw CoreException("Duplicate UUID in User constructor: " + uuid);
+ }
}
LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
- : User(ServerInstance->GetUID(), ServerInstance->Config->ServerName, USERTYPE_LOCAL), eh(this),
- localuseriter(ServerInstance->Users->local_users.end()),
- bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
- already_sent(0)
-{
+ : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
+ , eh(this)
+ , serializer(NULL)
+ , bytes_in(0)
+ , bytes_out(0)
+ , cmds_in(0)
+ , cmds_out(0)
+ , quitting_sendq(false)
+ , lastping(true)
+ , exempt(false)
+ , nping(0)
+ , idle_lastmsg(0)
+ , CommandFloodPenalty(0)
+ , already_sent(0)
+{
+ signon = ServerInstance->Time();
+ // The user's default nick is their UUID
+ nick = uuid;
ident = "unknown";
- lastping = 0;
eh.SetFd(myfd);
memcpy(&client_sa, client, sizeof(irc::sockets::sockaddrs));
memcpy(&server_sa, servaddr, sizeof(irc::sockets::sockaddrs));
- dhost = host = GetIPString();
+ ChangeRealHost(GetIPString(), true);
}
User::~User()
{
- if (ServerInstance->Users->uuidlist->find(uuid) != ServerInstance->Users->uuidlist->end())
- ServerInstance->Logs->Log("USERS", DEFAULT, "User destructor for %s called without cull", uuid.c_str());
}
const std::string& User::MakeHost()
@@ -242,18 +121,8 @@ const std::string& User::MakeHost()
if (!this->cached_makehost.empty())
return this->cached_makehost;
- char nhost[MAXBUF];
- /* This is much faster than snprintf */
- char* t = nhost;
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = host.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_makehost.assign(nhost);
-
+ // XXX: Is there really a need to cache this?
+ this->cached_makehost = ident + "@" + GetRealHost();
return this->cached_makehost;
}
@@ -262,18 +131,8 @@ const std::string& User::MakeHostIP()
if (!this->cached_hostip.empty())
return this->cached_hostip;
- char ihost[MAXBUF];
- /* This is much faster than snprintf */
- char* t = ihost;
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = this->GetIPString(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_hostip = ihost;
-
+ // XXX: Is there really a need to cache this?
+ this->cached_hostip = ident + "@" + this->GetIPString();
return this->cached_hostip;
}
@@ -282,111 +141,35 @@ const std::string& User::GetFullHost()
if (!this->cached_fullhost.empty())
return this->cached_fullhost;
- char result[MAXBUF];
- char* t = result;
- for(const char* n = nick.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '!';
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = dhost.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_fullhost = result;
-
+ // XXX: Is there really a need to cache this?
+ this->cached_fullhost = nick + "!" + ident + "@" + GetDisplayedHost();
return this->cached_fullhost;
}
-char* User::MakeWildHost()
-{
- static char nresult[MAXBUF];
- char* t = nresult;
- *t++ = '*'; *t++ = '!';
- *t++ = '*'; *t++ = '@';
- for(const char* n = dhost.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
- return nresult;
-}
-
const std::string& User::GetFullRealHost()
{
if (!this->cached_fullrealhost.empty())
return this->cached_fullrealhost;
- char fresult[MAXBUF];
- char* t = fresult;
- for(const char* n = nick.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '!';
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = host.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_fullrealhost = fresult;
-
+ // XXX: Is there really a need to cache this?
+ this->cached_fullrealhost = nick + "!" + ident + "@" + GetRealHost();
return this->cached_fullrealhost;
}
-bool LocalUser::IsInvited(const irc::string &channel)
-{
- Channel* chan = ServerInstance->FindChan(channel.c_str());
- if (!chan)
- return false;
-
- return (Invitation::Find(chan, this) != NULL);
-}
-
-InviteList& LocalUser::GetInviteList()
-{
- RemoveExpiredInvites();
- return invites;
-}
-
-void LocalUser::InviteTo(const irc::string &channel, time_t invtimeout)
-{
- Channel* chan = ServerInstance->FindChan(channel.c_str());
- if (chan)
- Invitation::Create(chan, this, invtimeout);
-}
-
-void LocalUser::RemoveInvite(const irc::string &channel)
-{
- Channel* chan = ServerInstance->FindChan(channel.c_str());
- if (chan)
- {
- Invitation* inv = Invitation::Find(chan, this);
- if (inv)
- {
- inv->cull();
- delete inv;
- }
- }
-}
-
-void LocalUser::RemoveExpiredInvites()
-{
- Invitation::Find(NULL, this);
-}
-
-bool User::HasModePermission(unsigned char, ModeType)
+bool User::HasModePermission(const ModeHandler* mh) const
{
return true;
}
-bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
+bool LocalUser::HasModePermission(const ModeHandler* mh) const
{
- if (!IS_OPER(this))
+ if (!this->IsOper())
return false;
+ const unsigned char mode = mh->GetModeChar();
if (mode < 'A' || mode > ('A' + 64)) return false;
- return ((type == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
+ return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
}
/*
@@ -404,17 +187,12 @@ bool User::HasPermission(const std::string&)
bool LocalUser::HasPermission(const std::string &command)
{
// are they even an oper at all?
- if (!IS_OPER(this))
+ if (!this->IsOper())
{
return false;
}
- if (oper->AllowedOperCommands.find(command) != oper->AllowedOperCommands.end())
- return true;
- else if (oper->AllowedOperCommands.find("*") != oper->AllowedOperCommands.end())
- return true;
-
- return false;
+ return oper->AllowedOperCommands.Contains(command);
}
bool User::HasPrivPermission(const std::string &privstr, bool noisy)
@@ -424,24 +202,19 @@ bool User::HasPrivPermission(const std::string &privstr, bool noisy)
bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
{
- if (!IS_OPER(this))
+ if (!this->IsOper())
{
if (noisy)
- this->WriteServ("NOTICE %s :You are not an oper", this->nick.c_str());
+ this->WriteNotice("You are not an oper");
return false;
}
- if (oper->AllowedPrivs.find(privstr) != oper->AllowedPrivs.end())
- {
+ if (oper->AllowedPrivs.Contains(privstr))
return true;
- }
- else if (oper->AllowedPrivs.find("*") != oper->AllowedPrivs.end())
- {
- return true;
- }
if (noisy)
- this->WriteServ("NOTICE %s :Oper type %s does not have access to priv %s", this->nick.c_str(), oper->NameStr(), privstr.c_str());
+ this->WriteNotice("Oper type " + oper->name + " does not have access to priv " + privstr);
+
return false;
}
@@ -457,49 +230,72 @@ void UserIOHandler::OnDataReady()
user->nick.c_str(), (unsigned long)recvq.length(), user->MyClass->GetRecvqMax());
return;
}
+
unsigned long sendqmax = ULONG_MAX;
if (!user->HasPrivPermission("users/flood/increased-buffers"))
sendqmax = user->MyClass->GetSendqSoftMax();
+
unsigned long penaltymax = ULONG_MAX;
if (!user->HasPrivPermission("users/flood/no-fakelag"))
penaltymax = user->MyClass->GetPenaltyThreshold() * 1000;
+ // The maximum size of an IRC message minus the terminating CR+LF.
+ const size_t maxmessage = ServerInstance->Config->Limits.MaxLine - 2;
+ std::string line;
+ line.reserve(maxmessage);
+
+ bool eol_found;
+ std::string::size_type qpos;
+
while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
{
- std::string line;
- line.reserve(MAXBUF);
- std::string::size_type qpos = 0;
- while (qpos < recvq.length())
+ qpos = 0;
+ eol_found = false;
+
+ const size_t qlen = recvq.length();
+ while (qpos < qlen)
{
char c = recvq[qpos++];
switch (c)
{
- case '\0':
- c = ' ';
- break;
- case '\r':
- continue;
- case '\n':
- goto eol_found;
+ case '\0':
+ c = ' ';
+ break;
+ case '\r':
+ continue;
+ case '\n':
+ eol_found = true;
+ break;
}
- if (line.length() < MAXBUF - 2)
+
+ if (eol_found)
+ break;
+
+ if (line.length() < maxmessage)
line.push_back(c);
}
- // if we got here, the recvq ran out before we found a newline
- return;
-eol_found:
+
+ // if we return here, we haven't found a newline and make no modifications to recvq
+ // so we can wait for more data
+ if (!eol_found)
+ return;
+
// just found a newline. Terminate the string, and pull it out of recvq
- recvq = recvq.substr(qpos);
+ recvq.erase(0, qpos);
// TODO should this be moved to when it was inserted in recvq?
- ServerInstance->stats->statsRecv += qpos;
+ ServerInstance->stats.Recv += qpos;
user->bytes_in += qpos;
user->cmds_in++;
- ServerInstance->Parser->ProcessBuffer(line, user);
+ ServerInstance->Parser.ProcessBuffer(user, line);
if (user->quitting)
return;
+
+ // clear() does not reclaim memory associated with the string, so our .reserve() call is safe
+ line.clear();
}
+
if (user->CommandFloodPenalty >= penaltymax && !user->MyClass->fakelag)
ServerInstance->Users->QuitUser(user, "Excess Flood");
}
@@ -522,6 +318,12 @@ void UserIOHandler::AddWriteBuf(const std::string &data)
WriteData(data);
}
+void UserIOHandler::OnSetEndPoint(const irc::sockets::sockaddrs& server, const irc::sockets::sockaddrs& client)
+{
+ memcpy(&user->server_sa, &server, sizeof(irc::sockets::sockaddrs));
+ user->SetClientIP(client);
+}
+
void UserIOHandler::OnError(BufferedSocketError)
{
ServerInstance->Users->QuitUser(user, getError());
@@ -531,9 +333,8 @@ CullResult User::cull()
{
if (!quitting)
ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
- PurgeEmptyChannels();
- if (client_sa.sa.sa_family != AF_UNSPEC)
+ if (client_sa.family() != AF_UNSPEC)
ServerInstance->Users->RemoveCloneCounts(this);
return Extensible::cull();
@@ -541,18 +342,6 @@ CullResult User::cull()
CullResult LocalUser::cull()
{
- // The iterator is initialized to local_users.end() in the constructor. It is
- // overwritten in UserManager::AddUser() with the real iterator so this check
- // is only a precaution currently.
- if (localuseriter != ServerInstance->Users->local_users.end())
- {
- ServerInstance->Users->local_count--;
- ServerInstance->Users->local_users.erase(localuseriter);
- }
- else
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: LocalUserIter does not point to a valid entry for " + this->nick);
-
- ClearInvites();
eh.cull();
return User::cull();
}
@@ -561,20 +350,29 @@ CullResult FakeUser::cull()
{
// Fake users don't quit, they just get culled.
quitting = true;
- ServerInstance->Users->clientlist->erase(nick);
- ServerInstance->Users->uuidlist->erase(uuid);
+ // Fake users are not inserted into UserManager::clientlist or uuidlist, so we don't need to modify those here
return User::cull();
}
void User::Oper(OperInfo* info)
{
- if (this->IsModeSet('o'))
+ ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+ if (this->IsModeSet(opermh))
this->UnOper();
- this->modes[UM_OPERATOR] = 1;
+ this->SetMode(opermh, true);
this->oper = info;
- this->WriteServ("MODE %s :+o", this->nick.c_str());
- FOREACH_MOD(I_OnOper, OnOper(this, info->name));
+
+ LocalUser* localuser = IS_LOCAL(this);
+ if (localuser)
+ {
+ Modes::ChangeList changelist;
+ changelist.push_add(opermh);
+ ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, NULL, localuser, changelist);
+ localuser->Send(modemsg);
+ }
+
+ FOREACH_MOD(OnOper, (this, info->name));
std::string opername;
if (info->oper_block)
@@ -585,30 +383,29 @@ void User::Oper(OperInfo* info)
LocalUser* l = IS_LOCAL(this);
std::string vhost = oper->getConfig("vhost");
if (!vhost.empty())
- l->ChangeDisplayedHost(vhost.c_str());
+ l->ChangeDisplayedHost(vhost);
std::string opClass = oper->getConfig("class");
if (!opClass.empty())
l->SetClass(opClass);
}
ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
- nick.c_str(), ident.c_str(), host.c_str(), oper->NameStr(), opername.c_str());
- this->WriteNumeric(381, "%s :You are now %s %s", nick.c_str(), strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->NameStr());
+ nick.c_str(), ident.c_str(), GetRealHost().c_str(), oper->name.c_str(), opername.c_str());
+ this->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str()));
- ServerInstance->Logs->Log("OPER", DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->NameStr());
ServerInstance->Users->all_opers.push_back(this);
// Expand permissions from config for faster lookup
- if (IS_LOCAL(this))
+ if (localuser)
oper->init();
- FOREACH_MOD(I_OnPostOper,OnPostOper(this, oper->name, opername));
+ FOREACH_MOD(OnPostOper, (this, oper->name, opername));
}
void OperInfo::init()
{
- AllowedOperCommands.clear();
- AllowedPrivs.clear();
+ AllowedOperCommands.Clear();
+ AllowedPrivs.Clear();
AllowedUserModes.reset();
AllowedChanModes.reset();
AllowedUserModes['o' - 'A'] = true; // Call me paranoid if you want.
@@ -616,19 +413,9 @@ void OperInfo::init()
for(std::vector<reference<ConfigTag> >::iterator iter = class_blocks.begin(); iter != class_blocks.end(); ++iter)
{
ConfigTag* tag = *iter;
- std::string mycmd, mypriv;
- /* Process commands */
- irc::spacesepstream CommandList(tag->getString("commands"));
- while (CommandList.GetToken(mycmd))
- {
- AllowedOperCommands.insert(mycmd);
- }
- irc::spacesepstream PrivList(tag->getString("privs"));
- while (PrivList.GetToken(mypriv))
- {
- AllowedPrivs.insert(mypriv);
- }
+ AllowedOperCommands.AddList(tag->getString("commands"));
+ AllowedPrivs.AddList(tag->getString("privs"));
std::string modes = tag->getString("usermodes");
for (std::string::const_iterator c = modes.begin(); c != modes.end(); ++c)
@@ -660,7 +447,7 @@ void OperInfo::init()
void User::UnOper()
{
- if (!IS_OPER(this))
+ if (!this->IsOper())
return;
/*
@@ -672,45 +459,29 @@ void User::UnOper()
/* Remove all oper only modes from the user when the deoper - Bug #466*/
- std::string moderemove("-");
-
- for (unsigned char letter = 'A'; letter <= 'z'; letter++)
+ Modes::ChangeList changelist;
+ const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
+ for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER);
- if (mh && mh->NeedsOper())
- moderemove += letter;
+ ModeHandler* mh = i->second;
+ if (mh->NeedsOper())
+ changelist.push_remove(mh);
}
+ ServerInstance->Modes->Process(this, NULL, this, changelist);
- std::vector<std::string> parameters;
- parameters.push_back(this->nick);
- parameters.push_back(moderemove);
+ // Remove the user from the oper list
+ stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
- ServerInstance->Parser->CallHandler("MODE", parameters, this);
-
- /* remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 */
- ServerInstance->Users->all_opers.remove(this);
-
- this->modes[UM_OPERATOR] = 0;
- FOREACH_MOD(I_OnPostDeoper, OnPostDeoper(this));
-}
-
-/* adds or updates an entry in the whowas list */
-void User::AddToWhoWas()
-{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_ADD);
- req.user = this;
- req.Send();
- }
+ ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+ this->SetMode(opermh, false);
+ FOREACH_MOD(OnPostDeoper, (this));
}
/*
* Check class restrictions
*/
-void LocalUser::CheckClass()
+void LocalUser::CheckClass(bool clone_count)
{
ConnectClass* a = this->MyClass;
@@ -724,25 +495,29 @@ void LocalUser::CheckClass()
ServerInstance->Users->QuitUser(this, a->config->getString("reason", "Unauthorised connection"));
return;
}
- else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal()))
+ else if (clone_count)
{
- ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
- if (a->maxconnwarn)
- ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
- return;
- }
- else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal()))
- {
- ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
- if (a->maxconnwarn)
- ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
- return;
+ const UserManager::CloneCounts& clonecounts = ServerInstance->Users->GetCloneCounts(this);
+ if ((a->GetMaxLocal()) && (clonecounts.local > a->GetMaxLocal()))
+ {
+ ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
+ if (a->maxconnwarn)
+ ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString().c_str());
+ return;
+ }
+ else if ((a->GetMaxGlobal()) && (clonecounts.global > a->GetMaxGlobal()))
+ {
+ ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
+ if (a->maxconnwarn)
+ ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString().c_str());
+ return;
+ }
}
- this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
+ this->nping = ServerInstance->Time() + a->GetPingTime();
}
-bool User::CheckLines(bool doZline)
+bool LocalUser::CheckLines(bool doZline)
{
const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
@@ -765,7 +540,7 @@ bool User::CheckLines(bool doZline)
void LocalUser::FullConnect()
{
- ServerInstance->stats->statsConnects++;
+ ServerInstance->stats.Connects++;
this->idle_lastmsg = ServerInstance->Time();
/*
@@ -782,55 +557,23 @@ void LocalUser::FullConnect()
if (quitting)
return;
- if (ServerInstance->Config->WelcomeNotice)
- this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network.c_str());
- this->WriteNumeric(RPL_WELCOME, "%s :Welcome to the %s IRC Network %s",this->nick.c_str(), ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
- this->WriteNumeric(RPL_YOURHOSTIS, "%s :Your host is %s, running version %s",this->nick.c_str(),ServerInstance->Config->ServerName.c_str(),BRANCH);
- this->WriteNumeric(RPL_SERVERCREATED, "%s :This server was created %s %s", this->nick.c_str(), __TIME__, __DATE__);
-
- std::string umlist = ServerInstance->Modes->UserModeList();
- std::string cmlist = ServerInstance->Modes->ChannelModeList();
- std::string pmlist = ServerInstance->Modes->ParaModeList();
- this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s %s %s %s", this->nick.c_str(), ServerInstance->Config->ServerName.c_str(), BRANCH, umlist.c_str(), cmlist.c_str(), pmlist.c_str());
-
- ServerInstance->Config->Send005(this);
- this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str());
-
- /* Now registered */
- if (ServerInstance->Users->unregistered_count)
- ServerInstance->Users->unregistered_count--;
-
- /* Trigger MOTD and LUSERS output, give modules a chance too */
- ModResult MOD_RESULT;
- std::string command("MOTD");
- std::vector<std::string> parameters;
- FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
- if (!MOD_RESULT)
- ServerInstance->Parser->CallHandler(command, parameters, this);
-
- MOD_RESULT = MOD_RES_PASSTHRU;
- command = "LUSERS";
- FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
- if (!MOD_RESULT)
- ServerInstance->Parser->CallHandler(command, parameters, this);
-
- if (ServerInstance->Config->RawLog)
- WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
-
/*
* We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
* for a user that doesn't exist yet.
*/
- FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
+ FOREACH_MOD(OnUserConnect, (this));
+ /* Now registered */
+ if (ServerInstance->Users->unregistered_count)
+ ServerInstance->Users->unregistered_count--;
this->registered = REG_ALL;
- FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
+ FOREACH_MOD(OnPostConnect, (this));
ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
- this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString(), this->fullname.c_str());
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding NEGATIVE hit for %s", this->GetIPString());
- ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
+ this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->GetRealName().c_str());
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
+ ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
// reset the flood penalty (which could have been raised due to things like auto +x)
CommandFloodPenalty = 0;
}
@@ -845,73 +588,26 @@ void User::InvalidateCache()
cached_fullrealhost.clear();
}
-bool User::ChangeNick(const std::string& newnick, bool force)
+bool User::ChangeNick(const std::string& newnick, time_t newts)
{
if (quitting)
{
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
- return false;
- }
-
- ModResult MOD_RESULT;
-
- if (force)
- ServerInstance->NICKForced.set(this, 1);
- FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
- ServerInstance->NICKForced.set(this, 0);
-
- if (MOD_RESULT == MOD_RES_DENY)
- {
- ServerInstance->stats->statsCollisions++;
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
return false;
}
- if (assign(newnick) == assign(nick))
+ User* const InUse = ServerInstance->FindNickOnly(newnick);
+ if (InUse == this)
{
- // case change, don't need to check Q:lines and such
+ // case change, don't need to check campers
// and, if it's identical including case, we can leave right now
+ // We also don't update the nick TS if it's a case change, either
if (newnick == nick)
return true;
}
else
{
/*
- * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some fucking *moron*
- * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be.
- * Thanks Kein for finding this. -- w00t
- *
- * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves.
- * -- w00t
- */
- if (IS_LOCAL(this) && !force)
- {
- XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick);
- if (mq)
- {
- if (this->registered == REG_ALL)
- {
- ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
- newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str());
- }
- this->WriteNumeric(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), newnick.c_str(), mq->reason.c_str());
- return false;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
- {
- Channel *chan = *i;
- if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
- {
- this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), chan->name.c_str());
- return false;
- }
- }
- }
- }
-
- /*
* Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
* then we have a potential collide. Check whether someone else is camping on the nick
* (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the
@@ -920,76 +616,99 @@ bool User::ChangeNick(const std::string& newnick, bool force)
* If the guy using the nick is already using it, tell the incoming nick change to gtfo,
* because the nick is already (rightfully) in use. -- w00t
*/
- User* InUse = ServerInstance->FindNickOnly(newnick);
- if (InUse && (InUse != this))
+ if (InUse)
{
if (InUse->registered != REG_ALL)
{
/* force the camper to their UUID, and ask them to re-send a NICK. */
- InUse->WriteTo(InUse, "NICK %s", InUse->uuid.c_str());
- InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), InUse->nick.c_str());
-
- ServerInstance->Users->clientlist->erase(InUse->nick);
- (*(ServerInstance->Users->clientlist))[InUse->uuid] = InUse;
-
- InUse->nick = InUse->uuid;
- InUse->InvalidateCache();
- InUse->registered &= ~REG_NICK;
+ LocalUser* const localuser = static_cast<LocalUser*>(InUse);
+ localuser->OverruleNick();
}
else
{
/* No camping, tell the incoming user to stop trying to change nick ;p */
- this->WriteNumeric(433, "%s %s :Nickname is already in use.", this->registered >= REG_NICK ? this->nick.c_str() : "*", newnick.c_str());
+ this->WriteNumeric(ERR_NICKNAMEINUSE, newnick, "Nickname is already in use.");
return false;
}
}
+
+ age = newts ? newts : ServerInstance->Time();
}
if (this->registered == REG_ALL)
- this->WriteCommon("NICK %s",newnick.c_str());
- std::string oldnick = nick;
+ {
+ ClientProtocol::Messages::Nick nickmsg(this, newnick);
+ ClientProtocol::Event nickevent(ServerInstance->GetRFCEvents().nick, nickmsg);
+ this->WriteCommonRaw(nickevent, true);
+ }
+ const std::string oldnick = nick;
nick = newnick;
InvalidateCache();
- ServerInstance->Users->clientlist->erase(oldnick);
- (*(ServerInstance->Users->clientlist))[newnick] = this;
+ ServerInstance->Users->clientlist.erase(oldnick);
+ ServerInstance->Users->clientlist[newnick] = this;
if (registered == REG_ALL)
- FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(this,oldnick));
+ FOREACH_MOD(OnUserPostNick, (this,oldnick));
return true;
}
-int LocalUser::GetServerPort()
+void LocalUser::OverruleNick()
{
- switch (this->server_sa.sa.sa_family)
{
- case AF_INET6:
- return htons(this->server_sa.in6.sin6_port);
- case AF_INET:
- return htons(this->server_sa.in4.sin_port);
+ ClientProtocol::Messages::Nick nickmsg(this, this->uuid);
+ this->Send(ServerInstance->GetRFCEvents().nick, nickmsg);
}
- return 0;
+ this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
+
+ // Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
+ this->registered &= ~REG_NICK;
+ this->ChangeNick(this->uuid);
+}
+
+int LocalUser::GetServerPort()
+{
+ return this->server_sa.port();
}
-const char* User::GetIPString()
+const std::string& User::GetIPString()
{
- int port;
if (cachedip.empty())
{
- irc::sockets::satoap(client_sa, cachedip, port);
+ cachedip = client_sa.addr();
/* IP addresses starting with a : on irc are a Bad Thing (tm) */
- if (cachedip.c_str()[0] == ':')
+ if (cachedip[0] == ':')
cachedip.insert(cachedip.begin(),1,'0');
}
- return cachedip.c_str();
+ return cachedip;
+}
+
+const std::string& User::GetHost(bool uncloak) const
+{
+ return uncloak ? GetRealHost() : GetDisplayedHost();
+}
+
+const std::string& User::GetDisplayedHost() const
+{
+ return displayhost.empty() ? realhost : displayhost;
+}
+
+const std::string& User::GetRealHost() const
+{
+ return realhost;
+}
+
+const std::string& User::GetRealName() const
+{
+ return realname;
}
irc::sockets::cidr_mask User::GetCIDRMask()
{
- int range = 0;
- switch (client_sa.sa.sa_family)
+ unsigned char range = 0;
+ switch (client_sa.family())
{
case AF_INET6:
range = ServerInstance->Config->c_ipv6_range;
@@ -1001,23 +720,33 @@ irc::sockets::cidr_mask User::GetCIDRMask()
return irc::sockets::cidr_mask(client_sa, range);
}
-bool User::SetClientIP(const char* sip, bool recheck_eline)
+bool User::SetClientIP(const std::string& address, bool recheck_eline)
{
- this->InvalidateCache();
- return irc::sockets::aptosa(sip, 0, client_sa);
+ irc::sockets::sockaddrs sa;
+ if (!irc::sockets::aptosa(address, client_sa.port(), sa))
+ return false;
+
+ User::SetClientIP(sa, recheck_eline);
+ return true;
}
void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
{
- this->InvalidateCache();
+ const std::string oldip(GetIPString());
memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
+ this->InvalidateCache();
+
+ // If the users hostname was their IP then update it.
+ if (GetRealHost() == oldip)
+ ChangeRealHost(GetIPString(), false);
+ if (GetDisplayedHost() == oldip)
+ ChangeDisplayedHost(GetIPString());
}
-bool LocalUser::SetClientIP(const char* sip, bool recheck_eline)
+bool LocalUser::SetClientIP(const std::string& address, bool recheck_eline)
{
irc::sockets::sockaddrs sa;
- if (!irc::sockets::aptosa(sip, 0, sa))
- // Invalid
+ if (!irc::sockets::aptosa(address, client_sa.port(), sa))
return false;
LocalUser::SetClientIP(sa, recheck_eline);
@@ -1032,308 +761,178 @@ void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_elin
if (recheck_eline)
this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL);
- FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this));
+ FOREACH_MOD(OnSetUserIP, (this));
}
}
-static std::string wide_newline("\r\n");
-
-void User::Write(const std::string& text)
-{
-}
-
-void User::Write(const char *text, ...)
-{
-}
-
-void LocalUser::Write(const std::string& text)
+void LocalUser::Write(const ClientProtocol::SerializedMessage& text)
{
- if (!ServerInstance->SE->BoundsCheckFd(&eh))
+ if (!SocketEngine::BoundsCheckFd(&eh))
return;
- if (text.length() > MAXBUF - 2)
+ if (ServerInstance->Config->RawLog)
{
- // this should happen rarely or never. Crop the string at 512 and try again.
- std::string try_again = text.substr(0, MAXBUF - 2);
- Write(try_again);
- return;
- }
+ if (text.empty())
+ return;
- ServerInstance->Logs->Log("USEROUTPUT", RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
+ std::string::size_type nlpos = text.find_first_of("\r\n", 0, 2);
+ if (nlpos == std::string::npos)
+ nlpos = text.length(); // TODO is this ok, test it
+
+ ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %.*s", uuid.c_str(), (int) nlpos, text.c_str());
+ }
eh.AddWriteBuf(text);
- eh.AddWriteBuf(wide_newline);
- ServerInstance->stats->statsSent += text.length() + 2;
- this->bytes_out += text.length() + 2;
+ const size_t bytessent = text.length() + 2;
+ ServerInstance->stats.Sent += bytessent;
+ this->bytes_out += bytessent;
this->cmds_out++;
}
-/** Write()
- */
-void LocalUser::Write(const char *text, ...)
+void LocalUser::Send(ClientProtocol::Event& protoev)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->Write(std::string(textbuffer));
-}
+ if (!serializer)
+ return;
-void User::WriteServ(const std::string& text)
-{
- this->Write(":%s %s",ServerInstance->Config->ServerName.c_str(),text.c_str());
+ // In the most common case a static LocalUser field, sendmsglist, is passed to the event to be
+ // populated. The list is cleared before returning.
+ // To handle re-enters, if sendmsglist is non-empty upon entering the method then a temporary
+ // list is used instead of the static one.
+ if (sendmsglist.empty())
+ {
+ Send(protoev, sendmsglist);
+ sendmsglist.clear();
+ }
+ else
+ {
+ ClientProtocol::MessageList msglist;
+ Send(protoev, msglist);
+ }
}
-/** WriteServ()
- * Same as Write(), except `text' is prefixed with `:server.name '.
- */
-void User::WriteServ(const char* text, ...)
+void LocalUser::Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteServ(std::string(textbuffer));
+ // Modules can personalize the messages sent per user for the event
+ protoev.GetMessagesForUser(this, msglist);
+ for (ClientProtocol::MessageList::const_iterator i = msglist.begin(); i != msglist.end(); ++i)
+ {
+ ClientProtocol::Message& curr = **i;
+ ModResult res;
+ FIRST_MOD_RESULT(OnUserWrite, res, (this, curr));
+ if (res != MOD_RES_DENY)
+ Write(serializer->SerializeForUser(this, curr));
+ }
}
-
-void User::WriteNumeric(unsigned int numeric, const char* text, ...)
+void User::WriteNumeric(const Numeric::Numeric& numeric)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteNumeric(numeric, std::string(textbuffer));
-}
+ LocalUser* const localuser = IS_LOCAL(this);
+ if (!localuser)
+ return;
-void User::WriteNumeric(unsigned int numeric, const std::string &text)
-{
- char textbuffer[MAXBUF];
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text));
+ FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric));
if (MOD_RESULT == MOD_RES_DENY)
return;
- snprintf(textbuffer,MAXBUF,":%s %03u %s",ServerInstance->Config->ServerName.c_str(), numeric, text.c_str());
- this->Write(std::string(textbuffer));
-}
-
-void User::WriteFrom(User *user, const std::string &text)
-{
- char tb[MAXBUF];
-
- snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(),text.c_str());
-
- this->Write(std::string(tb));
-}
-
-
-/* write text from an originating user to originating user */
-
-void User::WriteFrom(User *user, const char* text, ...)
-{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteFrom(user, std::string(textbuffer));
+ ClientProtocol::Messages::Numeric numericmsg(numeric, localuser);
+ localuser->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
}
-
-/* write text to an destination user from a source user (e.g. user privmsg) */
-
-void User::WriteTo(User *dest, const char *data, ...)
+void User::WriteRemoteNotice(const std::string& text)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, data);
- vsnprintf(textbuffer, MAXBUF, data, argsPtr);
- va_end(argsPtr);
-
- this->WriteTo(dest, std::string(textbuffer));
+ ServerInstance->PI->SendUserNotice(this, text);
}
-void User::WriteTo(User *dest, const std::string &data)
+void LocalUser::WriteRemoteNotice(const std::string& text)
{
- dest->WriteFrom(this, data);
+ WriteNotice(text);
}
-void User::WriteCommon(const char* text, ...)
+namespace
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (this->registered != REG_ALL || quitting)
- return;
-
- int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
+ class WriteCommonRawHandler : public User::ForEachNeighborHandler
+ {
+ ClientProtocol::Event& ev;
- va_start(argsPtr, text);
- vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
- va_end(argsPtr);
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ user->Send(ev);
+ }
- this->WriteCommonRaw(std::string(textbuffer), true);
+ public:
+ WriteCommonRawHandler(ClientProtocol::Event& protoev)
+ : ev(protoev)
+ {
+ }
+ };
}
-void User::WriteCommonExcept(const char* text, ...)
+void User::WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (this->registered != REG_ALL || quitting)
- return;
-
- int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteCommonRaw(std::string(textbuffer), false);
+ WriteCommonRawHandler handler(protoev);
+ ForEachNeighbor(handler, include_self);
}
-void User::WriteCommonRaw(const std::string &line, bool include_self)
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
{
- if (this->registered != REG_ALL || quitting)
- return;
-
- LocalUser::already_sent_id++;
-
- UserChanList include_c(chans);
- std::map<User*,bool> exceptions;
+ // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
+ // and visit all users on those channels. Because two users may share more than one common channel,
+ // we must skip users that we have already visited.
+ // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
+ // The global counter is incremented every time we do something for each neighbor of a user. Then,
+ // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
+ // skip the member. Otherwise, we set it to the current counter and visit the member.
+ // Ask modules to build a list of exceptions.
+ // Mods may also exclude entire channels by erasing them from include_chans.
+ IncludeChanList include_chans(chans.begin(), chans.end());
+ std::map<User*, bool> exceptions;
exceptions[this] = include_self;
+ FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
- FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
+ // Get next id, guaranteed to differ from the already_sent field of all users
+ const already_sent_t newid = ServerInstance->Users.NextAlreadySentId();
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+ // Handle exceptions first
+ for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
+ LocalUser* curr = IS_LOCAL(i->first);
+ if (curr)
{
- u->already_sent = LocalUser::already_sent_id;
- if (i->second)
- u->Write(line);
+ // Mark as visited to ensure we won't visit again if there is a common channel
+ curr->already_sent = newid;
+ // Always treat quitting users as excluded
+ if ((i->second) && (!curr->quitting))
+ handler.Execute(curr);
}
}
- for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
- {
- Channel* c = *v;
- const UserMembList* ulist = c->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting && u->already_sent != LocalUser::already_sent_id)
- {
- u->already_sent = LocalUser::already_sent_id;
- u->Write(line);
- }
- }
- }
-}
-void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
-{
- char tb1[MAXBUF];
- char tb2[MAXBUF];
-
- if (this->registered != REG_ALL)
- return;
-
- already_sent_t uniq_id = ++LocalUser::already_sent_id;
-
- snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),normal_text.c_str());
- snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),oper_text.c_str());
- std::string out1 = tb1;
- std::string out2 = tb2;
-
- UserChanList include_c(chans);
- std::map<User*,bool> exceptions;
-
- FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+ // Now consider the real neighbors
+ for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
+ Channel* chan = (*i)->chan;
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
{
- u->already_sent = uniq_id;
- if (i->second)
- u->Write(IS_OPER(u) ? out2 : out1);
- }
- }
- for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
- {
- const UserMembList* ulist = (*v)->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting && (u->already_sent != uniq_id))
+ LocalUser* curr = IS_LOCAL(j->first);
+ // User not yet visited?
+ if ((curr) && (curr->already_sent != newid))
{
- u->already_sent = uniq_id;
- u->Write(IS_OPER(u) ? out2 : out1);
+ // Mark as visited and execute function
+ curr->already_sent = newid;
+ handler.Execute(curr);
}
}
}
}
-void LocalUser::SendText(const std::string& line)
+void User::WriteRemoteNumeric(const Numeric::Numeric& numeric)
{
- Write(line);
-}
-
-void RemoteUser::SendText(const std::string& line)
-{
- ServerInstance->PI->PushToClient(this, line);
-}
-
-void FakeUser::SendText(const std::string& line)
-{
-}
-
-void User::SendText(const char *text, ...)
-{
- va_list argsPtr;
- char line[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(line, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- SendText(std::string(line));
-}
-
-void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream)
-{
- std::string line;
- std::string Word;
- while (TextStream >> Word)
- {
- size_t lineLength = LinePrefix.length() + line.length() + Word.length() + 13;
- if (lineLength > MAXBUF)
- {
- SendText(LinePrefix + line);
- line.clear();
- }
- line += " " + Word;
- }
- SendText(LinePrefix + line);
+ WriteNumeric(numeric);
}
/* return 0 or 1 depending if users u and u2 share one or more common channels
@@ -1350,221 +949,103 @@ void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream
*/
bool User::SharesChannelWith(User *other)
{
- if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
- return false;
-
/* Outer loop */
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); ++i)
{
/* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
* by replacing it with a map::find which *should* be more efficient
*/
- if ((*i)->HasUser(other))
+ if ((*i)->chan->HasUser(other))
return true;
}
return false;
}
-bool User::ChangeName(const char* gecos)
+bool User::ChangeRealName(const std::string& real)
{
- if (!this->fullname.compare(gecos))
+ if (!this->realname.compare(real))
return true;
if (IS_LOCAL(this))
{
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
+ FIRST_MOD_RESULT(OnPreChangeRealName, MOD_RESULT, (IS_LOCAL(this), real));
if (MOD_RESULT == MOD_RES_DENY)
return false;
- FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
+ FOREACH_MOD(OnChangeRealName, (this, real));
}
- this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
+ this->realname.assign(real, 0, ServerInstance->Config->Limits.MaxReal);
return true;
}
-void User::DoHostCycle(const std::string &quitline)
-{
- char buffer[MAXBUF];
-
- if (!ServerInstance->Config->CycleHosts)
- return;
-
- already_sent_t silent_id = ++LocalUser::already_sent_id;
- already_sent_t seen_id = ++LocalUser::already_sent_id;
-
- UserChanList include_c(chans);
- std::map<User*,bool> exceptions;
-
- FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
- // Users shouldn't see themselves quitting when host cycling
- exceptions.erase(this);
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
- {
- if (i->second)
- {
- u->already_sent = seen_id;
- u->Write(quitline);
- }
- else
- {
- u->already_sent = silent_id;
- }
- }
- }
- for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
- {
- Channel* c = *v;
- snprintf(buffer, MAXBUF, ":%s JOIN %s", GetFullHost().c_str(), c->name.c_str());
- std::string joinline(buffer);
- Membership* memb = c->GetUser(this);
- std::string modeline = memb->modes;
- if (modeline.length() > 0)
- {
- for(unsigned int i=0; i < memb->modes.length(); i++)
- modeline.append(" ").append(nick);
- snprintf(buffer, MAXBUF, ":%s MODE %s +%s",
- ServerInstance->Config->CycleHostsFromUser ? GetFullHost().c_str() : ServerInstance->Config->ServerName.c_str(),
- c->name.c_str(), modeline.c_str());
- modeline = buffer;
- }
-
- const UserMembList *ulist = c->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u == NULL || u == this)
- continue;
- if (u->already_sent == silent_id)
- continue;
-
- if (u->already_sent != seen_id)
- {
- u->Write(quitline);
- u->already_sent = seen_id;
- }
- u->Write(joinline);
- if (modeline.length() > 0)
- u->Write(modeline);
- }
- }
-}
-
-bool User::ChangeDisplayedHost(const char* shost)
+bool User::ChangeDisplayedHost(const std::string& shost)
{
- if (dhost == shost)
+ if (GetDisplayedHost() == shost)
return true;
- if (IS_LOCAL(this))
+ LocalUser* luser = IS_LOCAL(this);
+ if (luser)
{
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnChangeLocalUserHost, MOD_RESULT, (IS_LOCAL(this),shost));
+ FIRST_MOD_RESULT(OnPreChangeHost, MOD_RESULT, (luser, shost));
if (MOD_RESULT == MOD_RES_DENY)
return false;
}
- FOREACH_MOD(I_OnChangeHost, OnChangeHost(this,shost));
-
- std::string quitstr = ":" + GetFullHost() + " QUIT :Changing host";
+ FOREACH_MOD(OnChangeHost, (this,shost));
- /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
- this->dhost.assign(shost, 0, 64);
+ if (realhost == shost)
+ this->displayhost.clear();
+ else
+ this->displayhost.assign(shost, 0, ServerInstance->Config->Limits.MaxHost);
this->InvalidateCache();
- this->DoHostCycle(quitstr);
-
if (IS_LOCAL(this))
- this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str());
+ this->WriteNumeric(RPL_YOURDISPLAYEDHOST, this->GetDisplayedHost(), "is now your displayed host");
return true;
}
-bool User::ChangeIdent(const char* newident)
+void User::ChangeRealHost(const std::string& host, bool resetdisplay)
{
- if (this->ident == newident)
- return true;
-
- FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
-
- std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
-
- this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
+ // If the real host is the new host and we are not resetting the
+ // display host then we have nothing to do.
+ const bool changehost = (realhost != host);
+ if (!changehost && !resetdisplay)
+ return;
+
+ // If the displayhost is not set and we are not resetting it then
+ // we need to copy it to the displayhost field.
+ if (displayhost.empty() && !resetdisplay)
+ displayhost = realhost;
+
+ // If the displayhost is the new host or we are resetting it then
+ // we clear its contents to save memory.
+ else if (displayhost == host || resetdisplay)
+ displayhost.clear();
+
+ // If we are just resetting the display host then we don't need to
+ // do anything else.
+ if (!changehost)
+ return;
+ realhost = host;
this->InvalidateCache();
-
- this->DoHostCycle(quitstr);
-
- return true;
}
-void User::SendAll(const char* command, const char* text, ...)
+bool User::ChangeIdent(const std::string& newident)
{
- char textbuffer[MAXBUF];
- char formatbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost().c_str(), command, textbuffer);
- std::string fmt = formatbuffer;
-
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
- {
- if ((*i)->registered == REG_ALL)
- (*i)->Write(fmt);
- }
-}
-
-
-std::string User::ChannelList(User* source, bool spy)
-{
- std::string list;
-
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
- {
- Channel* c = *i;
- /* If the target is the sender, neither +p nor +s is set, or
- * the channel contains the user, it is not a spy channel
- */
- if (spy != (source == this || !(c->IsModeSet('p') || c->IsModeSet('s')) || c->HasUser(source)))
- list.append(c->GetPrefixChar(this)).append(c->name).append(" ");
- }
-
- return list;
-}
-
-void User::SplitChanList(User* dest, const std::string &cl)
-{
- std::string line;
- std::ostringstream prefix;
- std::string::size_type start, pos;
-
- prefix << this->nick << " " << dest->nick << " :";
- line = prefix.str();
- int namelen = ServerInstance->Config->ServerName.length() + 6;
+ if (this->ident == newident)
+ return true;
- for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
- {
- if (line.length() + namelen + pos - start > 510)
- {
- ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
- line = prefix.str();
- }
+ FOREACH_MOD(OnChangeIdent, (this,newident));
- line.append(cl.substr(start, pos - start + 1));
- }
+ this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
+ this->InvalidateCache();
- if (line.length() != prefix.str().length())
- {
- ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
- }
+ return true;
}
/*
@@ -1578,27 +1059,27 @@ void LocalUser::SetClass(const std::string &explicit_name)
{
ConnectClass *found = NULL;
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
if (!explicit_name.empty())
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
if (explicit_name == c->name)
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Explicitly set to %s", explicit_name.c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str());
found = c;
}
}
}
else
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Checking %s", c->GetName().c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c));
@@ -1606,7 +1087,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
continue;
if (MOD_RESULT == MOD_RES_ALLOW)
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Class forced by module to %s", c->GetName().c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
found = c;
break;
}
@@ -1620,9 +1101,9 @@ void LocalUser::SetClass(const std::string &explicit_name)
/* check if host matches.. */
if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
- !InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL))
+ !InspIRCd::MatchCIDR(this->GetRealHost(), c->GetHost(), NULL))
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "No host match (for %s)", c->GetHost().c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str());
continue;
}
@@ -1632,26 +1113,26 @@ void LocalUser::SetClass(const std::string &explicit_name)
*/
if (c->limit && (c->GetReferenceCount() >= c->limit))
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
continue;
}
/* if it requires a port ... */
- int port = c->config->getInt("port");
- if (port)
+ if (!c->ports.empty())
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Requires port (%d)", port);
-
/* and our port doesn't match, fail. */
- if (this->GetServerPort() != port)
+ if (!c->ports.count(this->GetServerPort()))
+ {
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires a different port, skipping");
continue;
+ }
}
if (regdone && !c->config->getString("password").empty())
{
- if (ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
+ if (!ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Bad password, skipping");
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping");
continue;
}
}
@@ -1671,62 +1152,82 @@ void LocalUser::SetClass(const std::string &explicit_name)
}
}
-/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
- * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
- * then their ip will be taken as 'priority' anyway, so for example,
- * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
- */
-ConnectClass* LocalUser::GetClass()
-{
- return MyClass;
-}
-
-ConnectClass* User::GetClass()
-{
- return NULL;
-}
-
void User::PurgeEmptyChannels()
{
// firstly decrement the count on each channel
- for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
+ for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); )
{
- Channel* c = *f;
+ Channel* c = (*i)->chan;
+ ++i;
c->DelUser(this);
}
this->UnOper();
}
+void User::WriteNotice(const std::string& text)
+{
+ LocalUser* const localuser = IS_LOCAL(this);
+ if (!localuser)
+ return;
+
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, text, MSG_NOTICE);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+}
+
const std::string& FakeUser::GetFullHost()
{
- if (!ServerInstance->Config->HideWhoisServer.empty())
- return ServerInstance->Config->HideWhoisServer;
- return server;
+ if (!ServerInstance->Config->HideServer.empty())
+ return ServerInstance->Config->HideServer;
+ return server->GetName();
}
const std::string& FakeUser::GetFullRealHost()
{
- if (!ServerInstance->Config->HideWhoisServer.empty())
- return ServerInstance->Config->HideWhoisServer;
- return server;
+ if (!ServerInstance->Config->HideServer.empty())
+ return ServerInstance->Config->HideServer;
+ return server->GetName();
}
ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask)
: config(tag), type(t), fakelag(true), name("unnamed"), registration_timeout(0), host(mask),
pingtime(0), softsendqmax(0), hardsendqmax(0), recvqmax(0),
- penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(0), limit(0)
+ penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(ServerInstance->Config->MaxChans),
+ limit(0), resolvehostnames(true)
{
}
ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, const ConnectClass& parent)
- : config(tag), type(t), fakelag(parent.fakelag), name("unnamed"),
- registration_timeout(parent.registration_timeout), host(mask), pingtime(parent.pingtime),
- softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), recvqmax(parent.recvqmax),
- penaltythreshold(parent.penaltythreshold), commandrate(parent.commandrate),
- maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxconnwarn(parent.maxconnwarn), maxchans(parent.maxchans),
- limit(parent.limit)
{
+ Update(&parent);
+ name = "unnamed";
+ type = t;
+ host = mask;
+
+ // Connect classes can inherit from each other but this is problematic for modules which can't use
+ // ConnectClass::Update so we build a hybrid tag containing all of the values set on this class as
+ // well as the parent class.
+ ConfigItems* items = NULL;
+ config = ConfigTag::create(tag->tag, tag->src_name, tag->src_line, items);
+
+ const ConfigItems& parentkeys = parent.config->getItems();
+ for (ConfigItems::const_iterator piter = parentkeys.begin(); piter != parentkeys.end(); ++piter)
+ {
+ // The class name and parent name are not inherited
+ if (stdalgo::string::equalsci(piter->first, "name") || stdalgo::string::equalsci(piter->first, "parent"))
+ continue;
+
+ // Store the item in the config tag. If this item also
+ // exists in the child it will be overwritten.
+ (*items)[piter->first] = piter->second;
+ }
+
+ const ConfigItems& childkeys = tag->getItems();
+ for (ConfigItems::const_iterator citer = childkeys.begin(); citer != childkeys.end(); ++citer)
+ {
+ // This will overwrite the parent value if present.
+ (*items)[citer->first] = citer->second;
+ }
}
void ConnectClass::Update(const ConnectClass* src)
@@ -1748,4 +1249,6 @@ void ConnectClass::Update(const ConnectClass* src)
maxconnwarn = src->maxconnwarn;
maxchans = src->maxchans;
limit = src->limit;
+ resolvehostnames = src->resolvehostnames;
+ ports = src->ports;
}
diff --git a/src/version.sh b/src/version.sh
index b082ad6e1..4423ad84a 100755
--- a/src/version.sh
+++ b/src/version.sh
@@ -1,2 +1,2 @@
#!/bin/sh
-echo "InspIRCd-2.0.26"
+echo "InspIRCd-3.0.0-a9"
diff --git a/src/whois.cpp b/src/whois.cpp
deleted file mode 100644
index bec9c7ea9..000000000
--- a/src/whois.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-void InspIRCd::DoWhois(User* user, User* dest,unsigned long signon, unsigned long idle, const char* nick)
-{
- this->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
- if (user == dest || user->HasPrivPermission("users/auspex"))
- {
- this->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->host.c_str(), dest->GetIPString());
- }
-
- std::string cl = dest->ChannelList(user, false);
- const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE;
-
- if (state == ServerConfig::SPYWHOIS_SINGLEMSG)
- cl.append(dest->ChannelList(user, true));
-
- user->SplitChanList(dest,cl);
-
- if (state == ServerConfig::SPYWHOIS_SPLITMSG)
- {
- std::string scl = dest->ChannelList(user, true);
- if (scl.length())
- {
- SendWhoisLine(user, dest, 336, "%s %s :is on private/secret channels:",user->nick.c_str(), dest->nick.c_str());
- user->SplitChanList(dest,scl);
- }
- }
- if (user != dest && !this->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- {
- this->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), this->Config->HideWhoisServer.c_str(), this->Config->Network.c_str());
- }
- else
- {
- std::string serverdesc = GetServerDescription(dest->server);
- this->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->server.c_str(), serverdesc.c_str());
- }
-
- if (IS_AWAY(dest))
- {
- this->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
- }
-
- if (IS_OPER(dest))
- {
- if (this->Config->GenericOper)
- this->SendWhoisLine(user, dest, 313, "%s %s :is an IRC operator",user->nick.c_str(), dest->nick.c_str());
- else
- this->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick.c_str(), dest->nick.c_str(), (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"),dest->oper->NameStr(), this->Config->Network.c_str());
- }
-
- if (user == dest || user->HasPrivPermission("users/auspex"))
- {
- if (dest->IsModeSet('s') != 0)
- {
- this->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes(), dest->FormatNoticeMasks());
- }
- else
- {
- this->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes());
- }
- }
-
- FOREACH_MOD(I_OnWhois,OnWhois(user,dest));
-
- /*
- * We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
- * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
- */
- if ((idle) || (signon))
- {
- this->SendWhoisLine(user, dest, 317, "%s %s %lu %lu :seconds idle, signon time",user->nick.c_str(), dest->nick.c_str(), idle, signon);
- }
-
- this->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick.c_str(), dest->nick.c_str());
-}
-
-
-
diff --git a/src/wildcard.cpp b/src/wildcard.cpp
index eb9151293..4a313af76 100644
--- a/src/wildcard.cpp
+++ b/src/wildcard.cpp
@@ -19,15 +19,12 @@
*/
-/* $Core */
-
#include "inspircd.h"
-#include "hashcomp.h"
-#include "inspstring.h"
-static bool match_internal(const unsigned char *str, const unsigned char *mask, unsigned const char *map)
+static bool MatchInternal(const unsigned char* str, const unsigned char* mask, unsigned const char* map)
{
- unsigned char *cp = NULL, *mp = NULL;
+ unsigned char* cp = NULL;
+ unsigned char* mp = NULL;
unsigned char* string = (unsigned char*)str;
unsigned char* wild = (unsigned char*)mask;
@@ -74,46 +71,53 @@ static bool match_internal(const unsigned char *str, const unsigned char *mask,
return !*wild;
}
-/********************************************************************
- * Below here is all wrappers around match_internal
- ********************************************************************/
+// Below here is all wrappers around MatchInternal
-CoreExport bool InspIRCd::Match(const std::string &str, const std::string &mask, unsigned const char *map)
+bool InspIRCd::Match(const std::string& str, const std::string& mask, unsigned const char* map)
{
if (!map)
map = national_case_insensitive_map;
- return match_internal((const unsigned char *)str.c_str(), (const unsigned char *)mask.c_str(), map);
+ return MatchInternal((const unsigned char*)str.c_str(), (const unsigned char*)mask.c_str(), map);
}
-CoreExport bool InspIRCd::Match(const char *str, const char *mask, unsigned const char *map)
+bool InspIRCd::Match(const char* str, const char* mask, unsigned const char* map)
{
if (!map)
map = national_case_insensitive_map;
- return match_internal((const unsigned char *)str, (const unsigned char *)mask, map);
+
+ return MatchInternal((const unsigned char*)str, (const unsigned char*)mask, map);
}
-CoreExport bool InspIRCd::MatchCIDR(const std::string &str, const std::string &mask, unsigned const char *map)
+bool InspIRCd::MatchCIDR(const std::string& str, const std::string& mask, unsigned const char* map)
{
if (irc::sockets::MatchCIDR(str, mask, true))
return true;
- if (!map)
- map = national_case_insensitive_map;
-
// Fall back to regular match
return InspIRCd::Match(str, mask, map);
}
-CoreExport bool InspIRCd::MatchCIDR(const char *str, const char *mask, unsigned const char *map)
+bool InspIRCd::MatchCIDR(const char* str, const char* mask, unsigned const char* map)
{
if (irc::sockets::MatchCIDR(str, mask, true))
return true;
- if (!map)
- map = national_case_insensitive_map;
-
// Fall back to regular match
return InspIRCd::Match(str, mask, map);
}
+bool InspIRCd::MatchMask(const std::string& masks, const std::string& hostname, const std::string& ipaddr)
+{
+ irc::spacesepstream masklist(masks);
+ std::string mask;
+ while (masklist.GetToken(mask))
+ {
+ if (InspIRCd::Match(hostname, mask, ascii_case_insensitive_map) ||
+ InspIRCd::MatchCIDR(ipaddr, mask, ascii_case_insensitive_map))
+ {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/xline.cpp b/src/xline.cpp
index 586c7342a..d07fe0dc5 100644
--- a/src/xline.cpp
+++ b/src/xline.cpp
@@ -23,7 +23,7 @@
#include "inspircd.h"
#include "xline.h"
-#include "bancache.h"
+#include "modules/stats.h"
/** An XLineFactory specialized to generate GLine* pointers
*/
@@ -34,7 +34,7 @@ class GLineFactory : public XLineFactory
/** Generate a GLine
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
return new GLine(set_time, duration, source, reason, ih.first, ih.second);
@@ -50,7 +50,7 @@ class ELineFactory : public XLineFactory
/** Generate an ELine
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
return new ELine(set_time, duration, source, reason, ih.first, ih.second);
@@ -66,7 +66,7 @@ class KLineFactory : public XLineFactory
/** Generate a KLine
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
return new KLine(set_time, duration, source, reason, ih.first, ih.second);
@@ -82,7 +82,7 @@ class QLineFactory : public XLineFactory
/** Generate a QLine
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
return new QLine(set_time, duration, source, reason, xline_specific_mask);
}
@@ -97,7 +97,7 @@ class ZLineFactory : public XLineFactory
/** Generate a ZLine
*/
- XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+ XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) CXX11_OVERRIDE
{
return new ZLine(set_time, duration, source, reason, xline_specific_mask);
}
@@ -156,9 +156,10 @@ void XLineManager::CheckELines()
if (ELines.empty())
return;
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
- User* u = (User*)(*u2);
+ LocalUser* u = *u2;
u->exempt = false;
/* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
@@ -278,7 +279,7 @@ bool XLineManager::AddLine(XLine* line, User* user)
if (!xlf)
return false;
- ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
+ ServerInstance->BanCache.RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
if (xlf->AutoApplyToUserList(line))
pending_lines.push_back(line);
@@ -286,7 +287,7 @@ bool XLineManager::AddLine(XLine* line, User* user)
lookup_lines[line->type][line->Displayable()] = line;
line->OnAdd();
- FOREACH_MOD(I_OnAddLine,OnAddLine(user, line));
+ FOREACH_MOD(OnAddLine, (user, line));
return true;
}
@@ -308,15 +309,13 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
if (simulate)
return true;
- ServerInstance->BanCache->RemoveEntries(y->second->type, true);
+ ServerInstance->BanCache.RemoveEntries(y->second->type, true);
- FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second));
+ FOREACH_MOD(OnDelLine, (user, y->second));
y->second->Unset();
- std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), y->second);
- if (pptr != pending_lines.end())
- pending_lines.erase(pptr);
+ stdalgo::erase(pending_lines, y->second);
delete y->second;
x->second.erase(y);
@@ -404,7 +403,7 @@ XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pat
// removes lines that have expired
void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
{
- FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
+ FOREACH_MOD(OnExpireLine, (item->second));
item->second->DisplayExpiry();
item->second->Unset();
@@ -413,9 +412,7 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
* is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
* -- Brain
*/
- std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
- if (pptr != pending_lines.end())
- pending_lines.erase(pptr);
+ stdalgo::erase(pending_lines, item->second);
delete item->second;
container->second.erase(item);
@@ -425,10 +422,10 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
// applies lines, removing clients and changing nicks etc as applicable
void XLineManager::ApplyLines()
{
- LocalUserList::reverse_iterator u2 = ServerInstance->Users->local_users.rbegin();
- while (u2 != ServerInstance->Users->local_users.rend())
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
{
- User* u = *u2++;
+ LocalUser* u = *j;
// Don't ban people who are exempt.
if (u->exempt)
@@ -445,7 +442,7 @@ void XLineManager::ApplyLines()
pending_lines.clear();
}
-void XLineManager::InvokeStats(const std::string &type, int numeric, User* user, string_list &results)
+void XLineManager::InvokeStats(const std::string& type, unsigned int numeric, Stats::Context& stats)
{
ContainerIter n = lookup_lines.find(type);
@@ -466,7 +463,7 @@ void XLineManager::InvokeStats(const std::string &type, int numeric, User* user,
ExpireLine(n, i);
}
else
- results.push_back(ServerInstance->Config->ServerName+" "+ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
+ stats.AddRow(numeric, i->second->Displayable()+" "+
ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+i->second->source+" :"+i->second->reason);
i = safei;
}
@@ -521,40 +518,38 @@ void XLine::Apply(User* u)
bool XLine::IsBurstable()
{
- return true;
+ return !from_config;
}
void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
{
- char sreason[MAXBUF];
- snprintf(sreason, MAXBUF, "%s-Lined: %s", line.c_str(), this->reason.c_str());
- if (!ServerInstance->Config->MoronBanner.empty())
- u->WriteServ("NOTICE %s :*** %s", u->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
+ const std::string banReason = line + "-Lined: " + reason;
+
+ if (!ServerInstance->Config->XLineMessage.empty())
+ u->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
if (ServerInstance->Config->HideBans)
- ServerInstance->Users->QuitUser(u, line + "-Lined", sreason);
+ ServerInstance->Users->QuitUser(u, line + "-Lined", &banReason);
else
- ServerInstance->Users->QuitUser(u, sreason);
+ ServerInstance->Users->QuitUser(u, banReason);
if (bancache)
{
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
- if (this->duration > 0)
- ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason, this->duration);
- else
- ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason);
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
+ ServerInstance->BanCache.AddHit(u->GetIPString(), this->type, banReason, this->duration);
}
}
bool KLine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
{
- if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
+ if (InspIRCd::MatchCIDR(u->GetRealHost(), this->hostmask, ascii_case_insensitive_map) ||
InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
{
return true;
@@ -571,12 +566,13 @@ void KLine::Apply(User* u)
bool GLine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
{
- if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
+ if (InspIRCd::MatchCIDR(u->GetRealHost(), this->hostmask, ascii_case_insensitive_map) ||
InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
{
return true;
@@ -595,7 +591,7 @@ bool ELine::Matches(User *u)
{
if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
{
- if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
+ if (InspIRCd::MatchCIDR(u->GetRealHost(), this->hostmask, ascii_case_insensitive_map) ||
InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
{
return true;
@@ -607,7 +603,8 @@ bool ELine::Matches(User *u)
bool ZLine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::MatchCIDR(u->GetIPString(), this->ipaddr))
@@ -633,7 +630,7 @@ bool QLine::Matches(User *u)
void QLine::Apply(User* u)
{
/* Force to uuid on apply of qline, no need to disconnect any more :) */
- u->ForceNickChange(u->uuid.c_str());
+ u->ChangeNick(u->uuid);
}
@@ -671,67 +668,45 @@ bool GLine::Matches(const std::string &str)
void ELine::OnAdd()
{
/* When adding one eline, only check the one eline */
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
- User* u = (User*)(*u2);
+ LocalUser* u = *u2;
if (this->Matches(u))
u->exempt = true;
}
}
-void ELine::DisplayExpiry()
+void XLine::DisplayExpiry()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired E-Line %s@%s (set by %s %ld seconds ago)",
- identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+ bool onechar = (type.length() == 1);
+ ServerInstance->SNO->WriteToSnoMask('x', "Removing expired %s%s %s (set by %s %ld seconds ago)",
+ type.c_str(), (onechar ? "-Line" : ""), Displayable().c_str(), source.c_str(), (long)(ServerInstance->Time() - set_time));
}
-void QLine::DisplayExpiry()
+const std::string& ELine::Displayable()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Q-Line %s (set by %s %ld seconds ago)",
- nick.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+ return matchtext;
}
-void ZLine::DisplayExpiry()
+const std::string& KLine::Displayable()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Z-Line %s (set by %s %ld seconds ago)",
- ipaddr.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+ return matchtext;
}
-void KLine::DisplayExpiry()
+const std::string& GLine::Displayable()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired K-Line %s@%s (set by %s %ld seconds ago)",
- identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+ return matchtext;
}
-void GLine::DisplayExpiry()
+const std::string& ZLine::Displayable()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired G-Line %s@%s (set by %s %ld seconds ago)",
- identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+ return ipaddr;
}
-const char* ELine::Displayable()
+const std::string& QLine::Displayable()
{
- return matchtext.c_str();
-}
-
-const char* KLine::Displayable()
-{
- return matchtext.c_str();
-}
-
-const char* GLine::Displayable()
-{
- return matchtext.c_str();
-}
-
-const char* ZLine::Displayable()
-{
- return ipaddr.c_str();
-}
-
-const char* QLine::Displayable()
-{
- return nick.c_str();
+ return nick;
}
bool KLine::IsBurstable()
@@ -772,3 +747,24 @@ XLineFactory* XLineManager::GetFactory(const std::string &type)
return n->second;
}
+
+void XLineManager::ClearConfigLines()
+{
+ // Nothing to do.
+ if (lookup_lines.empty())
+ return;
+
+ ServerInstance->SNO->WriteToSnoMask('x', "Server rehashing; expiring lines defined in the server config ...");
+ for (ContainerIter type = lookup_lines.begin(); type != lookup_lines.end(); ++type)
+ {
+ for (LookupIter xline = type->second.begin(); xline != type->second.end(); )
+ {
+ // We cache this to avoid iterator invalidation.
+ LookupIter cachedxline = xline++;
+ if (cachedxline->second->from_config)
+ {
+ ExpireLine(type, cachedxline);
+ }
+ }
+ }
+}